| 1 | #!/usr/bin/env python | 
|---|
| 2 |  | 
|---|
| 3 | """Send the contents of a directory as a MIME message.""" | 
|---|
| 4 |  | 
|---|
| 5 | import os | 
|---|
| 6 | import sys | 
|---|
| 7 | import smtplib | 
|---|
| 8 | # For guessing MIME type based on file name extension | 
|---|
| 9 | import mimetypes | 
|---|
| 10 |  | 
|---|
| 11 | from optparse import OptionParser | 
|---|
| 12 |  | 
|---|
| 13 | from email import encoders | 
|---|
| 14 | from email.message import Message | 
|---|
| 15 | from email.mime.audio import MIMEAudio | 
|---|
| 16 | from email.mime.base import MIMEBase | 
|---|
| 17 | from email.mime.image import MIMEImage | 
|---|
| 18 | from email.mime.multipart import MIMEMultipart | 
|---|
| 19 | from email.mime.text import MIMEText | 
|---|
| 20 |  | 
|---|
| 21 | COMMASPACE = ', ' | 
|---|
| 22 |  | 
|---|
| 23 |  | 
|---|
| 24 | def main(): | 
|---|
| 25 | parser = OptionParser(usage="""\ | 
|---|
| 26 | Send the contents of a directory as a MIME message. | 
|---|
| 27 |  | 
|---|
| 28 | Usage: %prog [options] | 
|---|
| 29 |  | 
|---|
| 30 | Unless the -o option is given, the email is sent by forwarding to your local | 
|---|
| 31 | SMTP server, which then does the normal delivery process.  Your local machine | 
|---|
| 32 | must be running an SMTP server. | 
|---|
| 33 | """) | 
|---|
| 34 | parser.add_option('-d', '--directory', | 
|---|
| 35 | type='string', action='store', | 
|---|
| 36 | help="""Mail the contents of the specified directory, | 
|---|
| 37 | otherwise use the current directory.  Only the regular | 
|---|
| 38 | files in the directory are sent, and we don't recurse to | 
|---|
| 39 | subdirectories.""") | 
|---|
| 40 | parser.add_option('-o', '--output', | 
|---|
| 41 | type='string', action='store', metavar='FILE', | 
|---|
| 42 | help="""Print the composed message to FILE instead of | 
|---|
| 43 | sending the message to the SMTP server.""") | 
|---|
| 44 | parser.add_option('-s', '--sender', | 
|---|
| 45 | type='string', action='store', metavar='SENDER', | 
|---|
| 46 | help='The value of the From: header (required)') | 
|---|
| 47 | parser.add_option('-r', '--recipient', | 
|---|
| 48 | type='string', action='append', metavar='RECIPIENT', | 
|---|
| 49 | default=[], dest='recipients', | 
|---|
| 50 | help='A To: header value (at least one required)') | 
|---|
| 51 | opts, args = parser.parse_args() | 
|---|
| 52 | if not opts.sender or not opts.recipients: | 
|---|
| 53 | parser.print_help() | 
|---|
| 54 | sys.exit(1) | 
|---|
| 55 | directory = opts.directory | 
|---|
| 56 | if not directory: | 
|---|
| 57 | directory = '.' | 
|---|
| 58 | # Create the enclosing (outer) message | 
|---|
| 59 | outer = MIMEMultipart() | 
|---|
| 60 | outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory) | 
|---|
| 61 | outer['To'] = COMMASPACE.join(opts.recipients) | 
|---|
| 62 | outer['From'] = opts.sender | 
|---|
| 63 | outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' | 
|---|
| 64 |  | 
|---|
| 65 | for filename in os.listdir(directory): | 
|---|
| 66 | path = os.path.join(directory, filename) | 
|---|
| 67 | if not os.path.isfile(path): | 
|---|
| 68 | continue | 
|---|
| 69 | # Guess the content type based on the file's extension.  Encoding | 
|---|
| 70 | # will be ignored, although we should check for simple things like | 
|---|
| 71 | # gzip'd or compressed files. | 
|---|
| 72 | ctype, encoding = mimetypes.guess_type(path) | 
|---|
| 73 | if ctype is None or encoding is not None: | 
|---|
| 74 | # No guess could be made, or the file is encoded (compressed), so | 
|---|
| 75 | # use a generic bag-of-bits type. | 
|---|
| 76 | ctype = 'application/octet-stream' | 
|---|
| 77 | maintype, subtype = ctype.split('/', 1) | 
|---|
| 78 | if maintype == 'text': | 
|---|
| 79 | fp = open(path) | 
|---|
| 80 | # Note: we should handle calculating the charset | 
|---|
| 81 | msg = MIMEText(fp.read(), _subtype=subtype) | 
|---|
| 82 | fp.close() | 
|---|
| 83 | elif maintype == 'image': | 
|---|
| 84 | fp = open(path, 'rb') | 
|---|
| 85 | msg = MIMEImage(fp.read(), _subtype=subtype) | 
|---|
| 86 | fp.close() | 
|---|
| 87 | elif maintype == 'audio': | 
|---|
| 88 | fp = open(path, 'rb') | 
|---|
| 89 | msg = MIMEAudio(fp.read(), _subtype=subtype) | 
|---|
| 90 | fp.close() | 
|---|
| 91 | else: | 
|---|
| 92 | fp = open(path, 'rb') | 
|---|
| 93 | msg = MIMEBase(maintype, subtype) | 
|---|
| 94 | msg.set_payload(fp.read()) | 
|---|
| 95 | fp.close() | 
|---|
| 96 | # Encode the payload using Base64 | 
|---|
| 97 | encoders.encode_base64(msg) | 
|---|
| 98 | # Set the filename parameter | 
|---|
| 99 | msg.add_header('Content-Disposition', 'attachment', filename=filename) | 
|---|
| 100 | outer.attach(msg) | 
|---|
| 101 | # Now send or store the message | 
|---|
| 102 | composed = outer.as_string() | 
|---|
| 103 | if opts.output: | 
|---|
| 104 | fp = open(opts.output, 'w') | 
|---|
| 105 | fp.write(composed) | 
|---|
| 106 | fp.close() | 
|---|
| 107 | else: | 
|---|
| 108 | s = smtplib.SMTP() | 
|---|
| 109 | s.sendmail(opts.sender, opts.recipients, composed) | 
|---|
| 110 | s.quit() | 
|---|
| 111 |  | 
|---|
| 112 |  | 
|---|
| 113 | if __name__ == '__main__': | 
|---|
| 114 | main() | 
|---|