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 | |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 11 | from argparse import ArgumentParser |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 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(): |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 25 | parser = ArgumentParser(description="""\ |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 26 | Send the contents of a directory as a MIME message. |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 27 | Unless the -o option is given, the email is sent by forwarding to your local |
| 28 | SMTP server, which then does the normal delivery process. Your local machine |
| 29 | must be running an SMTP server. |
| 30 | """) |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 31 | parser.add_argument('-d', '--directory', |
| 32 | help="""Mail the contents of the specified directory, |
| 33 | otherwise use the current directory. Only the regular |
| 34 | files in the directory are sent, and we don't recurse to |
| 35 | subdirectories.""") |
| 36 | parser.add_argument('-o', '--output', |
| 37 | metavar='FILE', |
| 38 | help="""Print the composed message to FILE instead of |
| 39 | sending the message to the SMTP server.""") |
| 40 | parser.add_argument('-s', '--sender', required=True, |
| 41 | help='The value of the From: header (required)') |
| 42 | parser.add_argument('-r', '--recipient', required=True, |
| 43 | action='append', metavar='RECIPIENT', |
| 44 | default=[], dest='recipients', |
| 45 | help='A To: header value (at least one required)') |
| 46 | args = parser.parse_args() |
| 47 | directory = args.directory |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 48 | if not directory: |
| 49 | directory = '.' |
| 50 | # Create the enclosing (outer) message |
| 51 | outer = MIMEMultipart() |
| 52 | outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory) |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 53 | outer['To'] = COMMASPACE.join(args.recipients) |
| 54 | outer['From'] = args.sender |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 55 | outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' |
| 56 | |
| 57 | for filename in os.listdir(directory): |
| 58 | path = os.path.join(directory, filename) |
| 59 | if not os.path.isfile(path): |
| 60 | continue |
| 61 | # Guess the content type based on the file's extension. Encoding |
| 62 | # will be ignored, although we should check for simple things like |
| 63 | # gzip'd or compressed files. |
| 64 | ctype, encoding = mimetypes.guess_type(path) |
| 65 | if ctype is None or encoding is not None: |
| 66 | # No guess could be made, or the file is encoded (compressed), so |
| 67 | # use a generic bag-of-bits type. |
| 68 | ctype = 'application/octet-stream' |
| 69 | maintype, subtype = ctype.split('/', 1) |
| 70 | if maintype == 'text': |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 71 | with open(path) as fp: |
| 72 | # Note: we should handle calculating the charset |
| 73 | msg = MIMEText(fp.read(), _subtype=subtype) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 74 | elif maintype == 'image': |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 75 | with open(path, 'rb') as fp: |
| 76 | msg = MIMEImage(fp.read(), _subtype=subtype) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 77 | elif maintype == 'audio': |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 78 | with open(path, 'rb') as fp: |
| 79 | msg = MIMEAudio(fp.read(), _subtype=subtype) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 80 | else: |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 81 | with open(path, 'rb') as fp: |
| 82 | msg = MIMEBase(maintype, subtype) |
| 83 | msg.set_payload(fp.read()) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 84 | # Encode the payload using Base64 |
| 85 | encoders.encode_base64(msg) |
| 86 | # Set the filename parameter |
| 87 | msg.add_header('Content-Disposition', 'attachment', filename=filename) |
| 88 | outer.attach(msg) |
| 89 | # Now send or store the message |
| 90 | composed = outer.as_string() |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 91 | if args.output: |
| 92 | with open(args.output, 'w') as fp: |
| 93 | fp.write(composed) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 94 | else: |
Serhiy Storchaka | 992cf1d | 2013-10-06 11:45:25 +0300 | [diff] [blame] | 95 | with smtplib.SMTP('localhost') as s: |
| 96 | s.sendmail(args.sender, args.recipients, composed) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 97 | |
| 98 | |
| 99 | if __name__ == '__main__': |
| 100 | main() |