Benjamin Peterson | 90f5ba5 | 2010-03-11 22:53:45 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 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() |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 109 | s.sendmail(opts.sender, opts.recipients, composed) |
Jeroen Ruigrok van der Werven | 939c178 | 2009-04-26 20:25:45 +0000 | [diff] [blame] | 110 | s.quit() |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 111 | |
| 112 | |
| 113 | if __name__ == '__main__': |
| 114 | main() |