Source code for email_handler
# labtools, Copyright (C) 2017 Jerry Fowler and Paul Scheet.
# This program comes with ABSOLUTELY NO WARRANTY. It is licensed under
# GNU GPL Version 3. License and warranty may be viewed in the manual.
'''
As simple an interface for email as seems viable.
>>> import email_handler
>>> bm = email_handler.BatchMailer('rgfowler@mdanderson.org', 'Package tester')
>>> response = bm.send_simple_mail('Batch mailer test', 'a better test')
>>> assert(const.EMPTY == response)
'''
import os
import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email import utils as mailutil
from labtools import const
from labtools import labexceptions
from labtools import misc
DOMAIN = 'mdanderson.org' #: The default domain attached to a bare unix userid
MAILHOST = 'mail.mdanderson.org' #:
def prepare_multipart_message(text, subject, from_addr, to_addr):
msg = MIMEMultipart(None, None, [text])
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = const.COMMA.join(to_addr) if isinstance(to_addr, list) else to_addr
return msg
def test_sender(sender, mailhost=MAILHOST):
response = const.OK
smtp = None
try:
smtp = smtplib.SMTP(mailhost)
except Exception as connect:
response = 'SMTP connection failed to %s: %s' % (mailhost, connect.strerror)
return response
try:
code, verification = smtp.verify(sender)
except smtplib.SMTPRecipientsRefused as recipients:
# All recipients were refused. Nobody got the mail.
response = 'SMTP failed to all recipients (%s)' % (msg['To'])
if code >= 400:
return verification
return response
def send_message(msg, mailhost=MAILHOST):
response = const.OK
smtp = None
try:
smtp = smtplib.SMTP(mailhost)
except Exception as connect:
response = 'SMTP connection failed to %s: %s' % (mailhost, connect.strerror)
return response
try:
smtp.sendmail(msg['From'], msg['To'], msg.as_string())
except smtplib.SMTPRecipientsRefused as recipients:
# All recipients were refused. Nobody got the mail.
response = 'SMTP failed to all recipients (%s)' % (msg['To'])
except smtplib.SMTPHeloError as helo:
# target server rejected HELO
response = 'Server (%s) rejected HELO' % (mailhost)
except smtplib.SMTPSenderRefused as sender:
# server rejected sender
response = 'Server (%s) rejected sender (%s)' % (mailhost, msg['From'])
except smtplib.SMTPDataError as err:
response = 'SMTPDataError %r' % (err)
finally:
r = smtp.quit()
if not response and 221 != r[0]:
response = '%s (code %d)' % (r[1], r[0])
return response
def send_simple_message(subject, text, from_addr, to_addr, mailhost=MAILHOST):
if isinstance(text, list):
text = const.NEWLINE.join(text)
if not isinstance(text, str):
raise labexceptions.ProgrammingFlawWarning('Message text is not a string (%s)' %
(str(type(text))))
if not isinstance(subject, str):
raise labexceptions.ProgrammingFlawWarning('Message subject is not a string (%s)' %
(str(type(subject))))
msg = MIMEText(text)
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = const.COMMA.join(to_addr) if isinstance(to_addr, list) else to_addr
return send_message(msg, mailhost=mailhost)
[docs]class BatchMailer():
'''
A very simple mailer that sets up a standard "mail chute" to drop
simple messages in so that it can be referenced in arbitrary places in
a package (specifically SyQADA) without having to track the sender or recipient list
*sender* the address from which the mail will come, or current user if not specified
*alias* the alias to be displayed for the sender if not None
*recipients* a *list* of recipient email addresses. If None, then the *sender*
duplicate addresses will be purged.
address is used (common usage would be to notify the user of a process when it
completes.
*mailhost* the SMTP host through which the mail will be routed.
'''
def __init__(self, alias=None, domain=DOMAIN, sender=None, recipients=None, mailhost=MAILHOST):
if sender is None:
sender = misc.username()
if const.AT not in sender:
sender = '{}@{}'.format(sender, domain)
self.sender = mailutil.formataddr((alias, sender))
self.mailhost = mailhost
temp = recipients if recipients is not None else list(self.sender)
self.recipients = list(set(temp))
self.domain = DOMAIN
[docs] def send_simple_mail(self, subject, text):
'''
Send a pure-text message using the *sender*, *recipients*, and *mailhost*
defined upon creation of the BatchMailer.
*subject* The message subject
*text* The message text
'''
return send_simple_message(subject, text, self.sender, self.recipients, self.mailhost)