Last active
March 31, 2021 21:25
-
-
Save jabofh/13f7b2ef36c273fc7a773ff7abcc7ce2 to your computer and use it in GitHub Desktop.
Python wrapper around mail to allow easy attachments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Python 3.2+ Script to send the contents of a file (or files) as a MIME message. | |
""" | |
import os, sys, pwd | |
import textwrap | |
import smtplib | |
# For guessing MIME type based on file name extension | |
import mimetypes | |
from argparse import ArgumentParser | |
import argparse | |
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 = ', ' | |
debugflag = False | |
version = '1.0a' | |
def debug(message, marker = ""): | |
if debugflag: | |
import os, sys | |
if marker != "": | |
marker = "[" + os.path.basename(__file__).split(".")[0] + "|" + marker + "]: " | |
print(marker + str(message), file = sys.stderr) | |
def getemail(email): | |
import os, sys, pwd | |
from socket import gethostname | |
if email == '': | |
email = pwd.getpwuid(os.geteuid()).pw_name + '@' + gethostname() | |
if len(email.split('@')) > 1: | |
return email | |
else: | |
return '@'.join([ email, gethostname()]) | |
msg = "%r is not a legal email address" % email | |
#raise OptionParser(msg) | |
print(msg) | |
sys.exit(1) | |
def main(): | |
parser = argparse.ArgumentParser(description = '''Send the list of attachments as a MIME message. | |
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.''', add_help=False) | |
optgroup = parser.add_argument_group('Options') | |
optgroup.add_argument('-h', '--help', action='help', help="show this help message and exit") | |
optgroup.add_argument('-V', '--version', action='version', version='%(prog)s' + ' Version %(ver)s' % {'ver': version}) | |
optgroup.add_argument("-d", "--debug", action="store_true", default=False, help="Emit debugging information") | |
arggroup = parser.add_argument_group('Arguments') | |
arggroup.add_argument('-f', '--sender', action='store', metavar='FROM', default=getemail('').lower(), | |
help='The value of the "From" header (the current value is "%s")' % getemail('').lower()) | |
arggroup.add_argument('-t', '--recipient', action='append', metavar='TO@DEST', default=[], dest='recipients', | |
help='A "To" header value (one of more of these are required)') | |
arggroup.add_argument('-s', '--subject', action='store', metavar='SUBJECT', default='Your email attachment', | |
help='The value of the email\'s "Subject" header') | |
arggroup.add_argument('-H', '--html', action='store', metavar='HTMLBODY', | |
default='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html>\n<body>\n<p>Hi,</p>\n\n<p>Attached please find your files.</p>\n\n<p>Kind regards,<br>\n %s\n\n</body>\n</html>\n\n' % | |
pwd.getpwnam(pwd.getpwuid(os.geteuid()).pw_name).pw_gecos.split(',')[0], | |
help='The body of the message with embedded control characters as required') | |
arggroup.add_argument('-b', '-m', '--body', action='store', metavar='BODY', | |
default='Hi,\n\nAttached please find your files.\n\nKind regards,\n\t%s\n\n' % | |
pwd.getpwnam(pwd.getpwuid(os.geteuid()).pw_name).pw_gecos.split(',')[0], | |
help='The body of the message with embedded control characters as required') | |
arggroup.add_argument('-a', '--file', default=[], dest='files', action='append', metavar='FILE', | |
help='A file to be attached. More than one may be used.') | |
arggroup.add_argument('-o', '--output', action='store', metavar='FILE', | |
help="""Print the composed message to FILE instead of sending the message to the SMTP server.""") | |
args = parser.parse_args() | |
if not args.sender or not args.recipients: | |
parser.print_help() | |
sys.exit(1) | |
if args.debug: | |
debugflag = True | |
if args.body == '-': | |
args.body = "\n".join(sys.stdin) | |
# Create the enclosing (outer) message | |
_recipients = [] | |
for _x in args.recipients: | |
for _y in _x.split(','): | |
_recipients.append(_y) | |
args.recipients = _recipients | |
debug(args.sender, "From") | |
debug(COMMASPACE.join(args.recipients), "To") | |
debug(args.subject, "Subject") | |
outer = MIMEMultipart('mixed') | |
outer['Subject'] = args.subject | |
outer['To'] = COMMASPACE.join(args.recipients) | |
outer['From'] = args.sender | |
outer.preamble = 'For the best experience, please use a MIME-aware mail reader.\n' | |
try: | |
body = args.body.decode('ascii') + "\n\n" | |
utf8 = False | |
debug("ASCII", "Encoding") | |
except UnicodeDecodeError: | |
body = args.body.decode('utf-8', 'ignore') + u"\n\n" | |
utf8 = True | |
debug("UTF-8", "Encoding") | |
if utf8: | |
body = args.encode('utf-8') | |
outer.attach(MIMEText(body.decode('string_escape'), 'plain', 'utf-8')) | |
else: | |
outer.attach(MIMEText(body.decode('string_escape'), 'plain')) | |
for filename in args.files: | |
if not os.path.isfile(filename): | |
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(filename) | |
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) | |
with open(filename) as fp: | |
if maintype == 'text': | |
# Note: we should handle calculating the charset | |
msg = MIMEText(fp.read(), _subtype=subtype) | |
elif maintype == 'image': | |
msg = MIMEImage(fp.read(), _subtype=subtype) | |
elif maintype == 'audio': | |
msg = MIMEAudio(fp.read(), _subtype=subtype) | |
else: | |
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=os.path.split(filename)[1]) | |
outer.attach(msg) | |
# Now send or store the message | |
composed = outer.as_string() | |
if args.output: | |
if args.output == '-': | |
args.output = '/dev/stdout' | |
fp = open(args.output, 'w') | |
fp.write(composed) | |
fp.close() | |
else: | |
s = smtplib.SMTP('localhost') | |
s.sendmail(args.sender, args.recipients, composed) | |
s.quit() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment