Created
December 15, 2019 22:44
-
-
Save dwurf/495fc88d621e096c8f667576eeea2a17 to your computer and use it in GitHub Desktop.
Using Python's built-in smtplib to send files
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 python | |
"""Script for sending an email (with an optional file attachment).""" | |
# Modified from the documentation at | |
# https://docs.python.org/2/library/email-examples.html | |
# | |
# Tested against python 2.6, 2.7 and 3.6 | |
# | |
# For help, run this script! | |
import os | |
import sys | |
import smtplib | |
# For guessing MIME type based on file name extension | |
import mimetypes | |
import ssl | |
from optparse import OptionParser | |
from email import encoders | |
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(args=sys.argv): | |
parser = OptionParser(usage="""\ | |
Send an email. Optionally, include a file attachment. The body will be | |
read from standard input, type <Enter>.<Enter> to end input. | |
Usage: %prog [options] | |
The email is sent by forwarding to your local SMTP server (running on | |
localhost), unless you specify either the -t or the -o option. | |
""") | |
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)') | |
parser.add_option('-u', '--subject', | |
type='string', action='store', metavar='SUBJECT', | |
help='The subject of the email. (required)') | |
parser.add_option('-f', '--file', | |
type='string', action='store', metavar='INFILE', | |
help="""Send the specified file as an attachment. | |
(optional)""") | |
parser.add_option('-t', '--smtp-host', | |
type='string', action='store', metavar='HOST', | |
help="""Use this smtp server to send mail, instead | |
of localhost. (optional)""") | |
parser.add_option('-l', '--login-name', | |
type='string', action='store', metavar='USER', | |
help="""Username to authenticate to the SMTP | |
server. (optional)""") | |
parser.add_option('-p', '--password', | |
type='string', action='store', metavar='PASSWORD', | |
help="""Password to authenticate to the SMTP | |
server. (optional)""") | |
parser.add_option('--tls', default=False, action='store_true', | |
metavar='TLS', help="""Use TLS for connection""") | |
parser.add_option('-o', '--output', | |
type='string', action='store', metavar='OUTFILE', | |
help="""Print the composed message to FILE instead of | |
sending the message to the SMTP server. (for | |
testing - optional)""") | |
opts, args = parser.parse_args(args) | |
if not opts.sender or not opts.recipients or not opts.subject: | |
parser.print_help() | |
sys.exit(1) | |
msg = None | |
if opts.file and os.path.isfile(opts.file): | |
print("Adding file " + opts.file) | |
# 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(opts.file) | |
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(opts.file) | |
# Note: we should handle calculating the charset | |
msg = MIMEText(fp.read(), _subtype=subtype) | |
fp.close() | |
elif maintype == 'image': | |
fp = open(opts.file, 'rb') | |
msg = MIMEImage(fp.read(), _subtype=subtype) | |
fp.close() | |
elif maintype == 'audio': | |
fp = open(opts.file, 'rb') | |
msg = MIMEAudio(fp.read(), _subtype=subtype) | |
fp.close() | |
else: | |
fp = open(opts.file, '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 | |
# Use only the base filename, not the whole path | |
fileparts = str(opts.file).split("/") | |
filebase = fileparts[len(fileparts) - 1] | |
msg.add_header('Content-Disposition', 'attachment', filename=filebase) | |
# Get input from stdin | |
lines = "" | |
try: | |
while 1: | |
line = sys.stdin.readline() | |
if not line or line == ".\n": | |
break | |
lines += line | |
except (IOError, OSError): | |
pass | |
# Create the enclosing (outer) message | |
outer = MIMEMultipart('mixed') | |
outer['Subject'] = opts.subject | |
outer['To'] = COMMASPACE.join(opts.recipients) | |
outer['From'] = opts.sender | |
# Add body | |
if lines: | |
body = MIMEText(lines, 'plain') | |
outer.attach(body) | |
else: | |
print("Sending without a message body...\n") | |
# Add attachment | |
if msg is not None: | |
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: | |
host = opts.smtp_host if opts.smtp_host else 'localhost' | |
# Create smtp object | |
if opts.tls: | |
s = smtplib.SMTP_SSL(host, timeout=5) | |
else: | |
s = smtplib.SMTP(host, timeout=5) | |
# Login | |
if opts.login_name and opts.password: | |
s.login(opts.login_name, opts.password) | |
# Send, then quit | |
s.sendmail(opts.sender, opts.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