2007-08-15 11:28:01 -03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""Send the contents of a directory as a MIME message."""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import smtplib
|
|
|
|
# For guessing MIME type based on file name extension
|
|
|
|
import mimetypes
|
|
|
|
|
|
|
|
from optparse import OptionParser
|
|
|
|
|
|
|
|
from email import encoders
|
|
|
|
from email.message import Message
|
|
|
|
from email.mime.audio import MIMEAudio
|
|
|
|
from email.mime.base import MIMEBase
|
|
|
|
from email.mime.image import MIMEImage
|
|
|
|
from email.mime.multipart import MIMEMultipart
|
|
|
|
from email.mime.text import MIMEText
|
|
|
|
|
|
|
|
COMMASPACE = ', '
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = OptionParser(usage="""\
|
|
|
|
Send the contents of a directory as a MIME message.
|
|
|
|
|
|
|
|
Usage: %prog [options]
|
|
|
|
|
|
|
|
Unless the -o option is given, the email is sent by forwarding to your local
|
|
|
|
SMTP server, which then does the normal delivery process. Your local machine
|
|
|
|
must be running an SMTP server.
|
|
|
|
""")
|
|
|
|
parser.add_option('-d', '--directory',
|
|
|
|
type='string', action='store',
|
|
|
|
help="""Mail the contents of the specified directory,
|
|
|
|
otherwise use the current directory. Only the regular
|
|
|
|
files in the directory are sent, and we don't recurse to
|
|
|
|
subdirectories.""")
|
|
|
|
parser.add_option('-o', '--output',
|
|
|
|
type='string', action='store', metavar='FILE',
|
|
|
|
help="""Print the composed message to FILE instead of
|
|
|
|
sending the message to the SMTP server.""")
|
|
|
|
parser.add_option('-s', '--sender',
|
|
|
|
type='string', action='store', metavar='SENDER',
|
|
|
|
help='The value of the From: header (required)')
|
|
|
|
parser.add_option('-r', '--recipient',
|
|
|
|
type='string', action='append', metavar='RECIPIENT',
|
|
|
|
default=[], dest='recipients',
|
|
|
|
help='A To: header value (at least one required)')
|
|
|
|
opts, args = parser.parse_args()
|
|
|
|
if not opts.sender or not opts.recipients:
|
|
|
|
parser.print_help()
|
|
|
|
sys.exit(1)
|
|
|
|
directory = opts.directory
|
|
|
|
if not directory:
|
|
|
|
directory = '.'
|
|
|
|
# Create the enclosing (outer) message
|
|
|
|
outer = MIMEMultipart()
|
|
|
|
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
|
|
|
|
outer['To'] = COMMASPACE.join(opts.recipients)
|
|
|
|
outer['From'] = opts.sender
|
|
|
|
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
|
|
|
|
|
|
|
|
for filename in os.listdir(directory):
|
|
|
|
path = os.path.join(directory, filename)
|
|
|
|
if not os.path.isfile(path):
|
|
|
|
continue
|
|
|
|
# Guess the content type based on the file's extension. Encoding
|
|
|
|
# will be ignored, although we should check for simple things like
|
|
|
|
# gzip'd or compressed files.
|
|
|
|
ctype, encoding = mimetypes.guess_type(path)
|
|
|
|
if ctype is None or encoding is not None:
|
|
|
|
# No guess could be made, or the file is encoded (compressed), so
|
|
|
|
# use a generic bag-of-bits type.
|
|
|
|
ctype = 'application/octet-stream'
|
|
|
|
maintype, subtype = ctype.split('/', 1)
|
|
|
|
if maintype == 'text':
|
|
|
|
fp = open(path)
|
|
|
|
# Note: we should handle calculating the charset
|
|
|
|
msg = MIMEText(fp.read(), _subtype=subtype)
|
|
|
|
fp.close()
|
|
|
|
elif maintype == 'image':
|
|
|
|
fp = open(path, 'rb')
|
|
|
|
msg = MIMEImage(fp.read(), _subtype=subtype)
|
|
|
|
fp.close()
|
|
|
|
elif maintype == 'audio':
|
|
|
|
fp = open(path, 'rb')
|
|
|
|
msg = MIMEAudio(fp.read(), _subtype=subtype)
|
|
|
|
fp.close()
|
|
|
|
else:
|
|
|
|
fp = open(path, 'rb')
|
|
|
|
msg = MIMEBase(maintype, subtype)
|
|
|
|
msg.set_payload(fp.read())
|
|
|
|
fp.close()
|
|
|
|
# Encode the payload using Base64
|
|
|
|
encoders.encode_base64(msg)
|
|
|
|
# Set the filename parameter
|
|
|
|
msg.add_header('Content-Disposition', 'attachment', filename=filename)
|
|
|
|
outer.attach(msg)
|
|
|
|
# Now send or store the message
|
|
|
|
composed = outer.as_string()
|
|
|
|
if opts.output:
|
|
|
|
fp = open(opts.output, 'w')
|
|
|
|
fp.write(composed)
|
|
|
|
fp.close()
|
|
|
|
else:
|
|
|
|
s = smtplib.SMTP()
|
|
|
|
s.sendmail(opts.sender, opts.recipients, composed)
|
Merged revisions 71873-71874,71882,71890,71893,71898-71900,71910,71914-71923,71925-71929,71931-71934,71937 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r71873 | jeroen.ruigrok | 2009-04-25 13:15:06 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to expanding.
........
r71874 | jeroen.ruigrok | 2009-04-25 13:59:09 +0200 (za, 25 apr 2009) | 2 lines
First attempt to document PyObject_HEAD_INIT and PyVarObject_HEAD_INIT.
........
r71882 | jeroen.ruigrok | 2009-04-25 14:49:10 +0200 (za, 25 apr 2009) | 3 lines
Issue #4239: adjust email examples not to use connect() and terminate with
quit() and not close().
........
r71890 | jeroen.ruigrok | 2009-04-25 15:07:40 +0200 (za, 25 apr 2009) | 3 lines
Rewrite a sentence to be more in line with the rest of the documentation with
regard to person and audience.
........
r71893 | jeroen.ruigrok | 2009-04-25 15:58:58 +0200 (za, 25 apr 2009) | 2 lines
Reformat file prior to editing.
........
r71898 | jeroen.ruigrok | 2009-04-25 16:24:30 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71899 | jeroen.ruigrok | 2009-04-25 16:27:00 +0200 (za, 25 apr 2009) | 3 lines
The type for ppos has been Py_ssize_t since 2.5, reflect this in the
documentation.
........
r71900 | jeroen.ruigrok | 2009-04-25 16:28:02 +0200 (za, 25 apr 2009) | 2 lines
Reformat paragraph.
........
r71910 | jeroen.ruigrok | 2009-04-25 19:59:03 +0200 (za, 25 apr 2009) | 4 lines
Issue #4129: Belatedly document which C API functions had their argument(s) or
return type changed from int or int * to Py_ssize_t or Py_ssize_t * as this
might cause problems on 64-bit platforms.
........
r71914 | jeroen.ruigrok | 2009-04-25 20:31:20 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71915 | jeroen.ruigrok | 2009-04-25 20:46:03 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: Document more int -> Py_ssize_t changes.
........
r71916 | jeroen.ruigrok | 2009-04-25 20:53:48 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71917 | jeroen.ruigrok | 2009-04-25 20:57:32 +0200 (za, 25 apr 2009) | 2 lines
Reference to an int type, whereas it's a Py_ssize_t as the synopsis states.
........
r71918 | jeroen.ruigrok | 2009-04-25 21:04:15 +0200 (za, 25 apr 2009) | 2 lines
Since I edited this file, reformat for future edits.
........
r71919 | jeroen.ruigrok | 2009-04-25 21:10:52 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71920 | jeroen.ruigrok | 2009-04-25 21:44:55 +0200 (za, 25 apr 2009) | 5 lines
Issue #4129: More documentation pointers about int -> Py_ssize_t.
Also fix up the documentation for PyObject_GC_Resize(). It seems that since
it first got documented, the documentation was actually for
_PyObject_GC_Resize().
........
r71921 | jeroen.ruigrok | 2009-04-25 21:46:19 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: Documentation notes for int -> Py_ssize_t changes.
........
r71922 | jeroen.ruigrok | 2009-04-25 21:49:05 +0200 (za, 25 apr 2009) | 2 lines
Reformat, since I've been busy here anyway.
........
r71923 | jeroen.ruigrok | 2009-04-25 21:54:34 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: Add a versionchanged notice for a few forgotten entries.
........
r71925 | jeroen.ruigrok | 2009-04-25 22:37:39 +0200 (za, 25 apr 2009) | 2 lines
Since it's a macro, actually refer to it as such instead of function.
........
r71926 | jeroen.ruigrok | 2009-04-25 22:40:10 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71927 | jeroen.ruigrok | 2009-04-25 22:41:40 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: int -> Py_ssize_t documentation.
........
r71928 | jeroen.ruigrok | 2009-04-25 22:43:30 +0200 (za, 25 apr 2009) | 2 lines
Reformat prior to editing.
........
r71929 | jeroen.ruigrok | 2009-04-25 22:44:58 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: int -> Py_ssize_t documentation.
........
r71931 | jeroen.ruigrok | 2009-04-25 22:50:27 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: int -> Py_ssize_t documentation.
........
r71932 | jeroen.ruigrok | 2009-04-25 22:55:39 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: more int -> Py_ssize_t documentation.
........
r71933 | jeroen.ruigrok | 2009-04-25 22:58:35 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: more int -> Py_ssize_t documentation.
........
r71934 | jeroen.ruigrok | 2009-04-25 23:02:34 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: field changed from int to Py_ssize_t.
........
r71937 | jeroen.ruigrok | 2009-04-25 23:16:05 +0200 (za, 25 apr 2009) | 2 lines
Issue #4129: document int -> Py_ssize_t changes.
........
2009-04-29 05:00:05 -03:00
|
|
|
s.quit()
|
2007-08-15 11:28:01 -03:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|