Skip to content

Instantly share code, notes, and snippets.

@nobrowser
Created February 1, 2016 19:33
Show Gist options
  • Save nobrowser/08e87127471efdbda004 to your computer and use it in GitHub Desktop.
Save nobrowser/08e87127471efdbda004 to your computer and use it in GitHub Desktop.
Send multiple files as messages over a smtp connection
#!/usr/bin/python
import os
import pwd
import socket
from argparse import ArgumentParser
from smtplib import ( SMTP , quotedata , SMTPDataError , CRLF ,
SMTPSenderRefused , SMTPRecipientsRefused )
import sys
# stock smtp needs entire message as a string(!!)
class BufferedSMTP(SMTP):
def data_file(self, infile):
self.putcmd("data")
(code, msg) = self.getreply()
if self.debuglevel > 0:
print>>stderr, "data:", (code, msg)
if code != 354:
raise SMTPDataError(code, msg)
else:
for line in infile:
q = quotedata(line)
if q[-2:] != CRLF:
q = q + CRLF
self.send(q)
self.send("." + CRLF)
(code, msg) = self.getreply()
if self.debuglevel > 0:
print>>stderr, "data:", (code, msg)
return (code, msg)
def sendmail_file(self, from_addr, to_addrs, infile, rcpt_options=[]):
self.ehlo_or_helo_if_needed()
(code, resp) = self.mail(from_addr)
if code != 250:
self.rset()
raise SMTPSenderRefused(code, resp, from_addr)
senderrs = {}
if isinstance(to_addrs, basestring):
to_addrs = [to_addrs]
for each in to_addrs:
(code, resp) = self.rcpt(each, rcpt_options)
if (code != 250) and (code != 251):
senderrs[each] = (code, resp)
if len(senderrs) == len(to_addrs):
# the server refused all our recipients
self.rset()
raise SMTPRecipientsRefused(senderrs)
(code, resp) = self.data_file(infile)
if code != 250:
self.rset()
raise SMTPDataError(code, resp)
#if we got here then somebody got our mail
return senderrs
def mynameis():
if 'LOGNAME' in os.environ:
return os.environ['LOGNAME']
elif 'USER' in os.environ:
return os.environ['USER']
else:
return pwd.getpwuid(os.geteuid()).pw_name
def args_generator(args_fn):
if args_fn is not None:
with open(args_fn) as args:
for line in args:
yield line.strip()
def send_files(server, fn_source, fromaddr, toaddrs, do_remove):
for fn in fn_source:
with open(fn) as infile:
server.sendmail_file(fromaddr, toaddrs, infile)
if do_remove:
os.remove(fn)
def main():
fqdn = socket.getfqdn()
me = mynameis() + '@localhost'
df = ' (default %(default)s)'
p = ArgumentParser(prog='smtpsend', description='Send files via smtp.')
p.add_argument('-a', '--args_file',
help='read names of files to mail from ARGS_FILE')
p.add_argument('-f', '--fromaddr', default=me,
help='send files from FROMADDR' + df)
p.add_argument('-H', '--host', default='localhost',
help='connect to SMTP on server HOST' + df)
p.add_argument('-l', '--login', action='store_true',
help='login using SMTP_USER and SMTP_PASS environment')
p.add_argument('-P', '--port', type=int, default=25,
help='connect to SMTP on port PORT' + df)
p.add_argument('-r', '--remove', action='store_true',
help='remove files after successful mailing')
p.add_argument('-s', '--starttls', action='store_true',
help='use encryption via STARTTLS')
p.add_argument('-t', '--to', action='append',
help='send files TO')
p.add_argument('files', nargs='*', help='send FILES')
opts = p.parse_args()
if opts.login:
if 'SMTP_USER' not in os.environ or 'SMTP_PASS' not in os.environ:
sys.stderr.write('SMTP_USER and SMTP_PASS envorinment required\n')
sys.exit(2)
user, password = os.environ['SMTP_USER'], os.environ['SMTP_PASS']
s = BufferedSMTP(opts.host, opts.port)
if opts.starttls:
s.ehlo(fqdn)
if not s.has_extn('STARTTLS'):
sys.stderr.write('smtpsend: STARTTLS requested but not available\n')
sys.exit(2)
s.starttls()
s.ehlo(fqdn)
if opts.login:
s.login(user, password)
args = args_generator(opts.args_file)
send_files(s, args, opts.fromaddr, opts.to, opts.remove)
send_files(s, opts.files, opts.fromaddr, opts.to, opts.remove)
s.quit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment