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()
|
---|