2012-05-03 03:51:54 -05:00
|
|
|
'''This package contains functions for sending email notifications.'''
|
2012-05-08 07:49:45 -05:00
|
|
|
import smtplib, socket
|
2012-05-03 03:51:54 -05:00
|
|
|
from email.MIMEMultipart import MIMEMultipart
|
|
|
|
from email.MIMEBase import MIMEBase
|
|
|
|
from email.MIMEText import MIMEText
|
|
|
|
from email import Encoders
|
|
|
|
from email.Header import Header
|
|
|
|
from appy.shared.utils import sequenceTypes
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2014-09-18 04:08:29 -05:00
|
|
|
class MailConfig:
|
2015-03-09 07:36:48 -05:00
|
|
|
'''Parameters for conneting to a SMTP server'''
|
2014-09-18 04:08:29 -05:00
|
|
|
def __init__(self, fromName=None, fromEmail='info@appyframework.org',
|
|
|
|
server='localhost', port=25, login=None, password=None,
|
|
|
|
enabled=True):
|
|
|
|
# The name that will appear in the "from" part of the messages
|
|
|
|
self.fromName = fromName
|
|
|
|
# The email that will appear in the "from" part of the messages
|
|
|
|
self.fromEmail = fromEmail
|
2015-01-27 05:10:38 -06:00
|
|
|
# The SMTP server address and port
|
|
|
|
if ':' in server:
|
|
|
|
self.server, port = server.split(':')
|
|
|
|
self.port = int(port)
|
|
|
|
else:
|
|
|
|
self.server = server
|
|
|
|
self.port = int(port) # That way, people can specify an int or str
|
|
|
|
# Optional credentials to the SMTP server
|
2014-09-18 04:08:29 -05:00
|
|
|
self.login = login
|
|
|
|
self.password = password
|
|
|
|
# Is this server connection enabled ?
|
|
|
|
self.enabled = enabled
|
|
|
|
|
|
|
|
def getFrom(self):
|
|
|
|
'''Gets the "from" part of the messages to send.'''
|
|
|
|
if self.fromName: return '%s <%s>' % (self.fromName, self.fromEmail)
|
|
|
|
return self.fromEmail
|
|
|
|
|
2015-01-27 05:10:38 -06:00
|
|
|
def __repr__(self):
|
|
|
|
'''Short string representation of this mail config, for logging and
|
|
|
|
debugging purposes.'''
|
|
|
|
res = '%s:%d' % (self.server, self.port)
|
|
|
|
if self.login: res += ' (login as %s)' % self.login
|
|
|
|
return res
|
|
|
|
|
2014-09-18 04:08:29 -05:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
def sendMail(config, to, subject, body, attachments=None, log=None):
|
|
|
|
'''Sends a mail, via the smtp server defined in the p_config (an instance of
|
|
|
|
appy.gen.mail.MailConfig above), to p_to (a single email recipient or a
|
|
|
|
list of recipients). Every (string) recipient can be an email address or
|
|
|
|
a string of the form "[name] <[email]>".
|
2014-09-17 11:09:30 -05:00
|
|
|
|
|
|
|
p_attachment must be a list or tuple whose elements can have 2 forms:
|
|
|
|
1. a tuple (fileName, fileContent): "fileName" is the name of the file
|
|
|
|
as a string; "fileContent" is the file content, also as a string;
|
|
|
|
2. a appy.fields.file.FileInfo instance.
|
2014-09-18 04:08:29 -05:00
|
|
|
|
|
|
|
p_log can be a function/method accepting a single string arg.
|
2014-09-17 11:09:30 -05:00
|
|
|
'''
|
2014-11-14 06:19:47 -06:00
|
|
|
if isinstance(to, str): to = [to]
|
2014-09-18 04:08:29 -05:00
|
|
|
if not config:
|
|
|
|
if log: log('Must send mail but no smtp server configured.')
|
|
|
|
return
|
2012-05-03 03:51:54 -05:00
|
|
|
# Just log things if mail is disabled
|
2014-09-18 04:08:29 -05:00
|
|
|
fromAddress = config.getFrom()
|
|
|
|
if not config.enabled or not config.server:
|
|
|
|
if not config.server:
|
2012-05-05 10:04:19 -05:00
|
|
|
msg = ' (no mailhost defined)'
|
|
|
|
else:
|
|
|
|
msg = ''
|
2014-09-18 04:08:29 -05:00
|
|
|
if log:
|
2014-10-22 15:17:26 -05:00
|
|
|
log('mail disabled%s: should send mail from %s to %d ' \
|
2014-11-14 06:19:47 -06:00
|
|
|
'recipient(s): %s.' % (msg, fromAddress, len(to), str(to)))
|
2014-09-18 04:08:29 -05:00
|
|
|
log('subject: %s' % subject)
|
|
|
|
log('body: %s' % body)
|
|
|
|
if attachments and log: log('%d attachment(s).' % len(attachments))
|
2012-05-03 03:51:54 -05:00
|
|
|
return
|
2014-09-18 04:08:29 -05:00
|
|
|
if log: log('sending mail from %s to %s (subject: %s).' % \
|
|
|
|
(fromAddress, str(to), subject))
|
2012-05-03 03:51:54 -05:00
|
|
|
# Create the base MIME message
|
|
|
|
body = MIMEText(body, 'plain', 'utf-8')
|
|
|
|
if attachments:
|
|
|
|
msg = MIMEMultipart()
|
2014-09-17 11:09:30 -05:00
|
|
|
msg.attach(body)
|
2012-05-03 03:51:54 -05:00
|
|
|
else:
|
|
|
|
msg = body
|
|
|
|
# Add the header values
|
|
|
|
msg['Subject'] = Header(subject, 'utf-8')
|
|
|
|
msg['From'] = fromAddress
|
2014-11-14 06:19:47 -06:00
|
|
|
if len(to) == 1:
|
|
|
|
msg['To'] = to[0]
|
2012-05-03 03:51:54 -05:00
|
|
|
else:
|
2014-11-14 06:19:47 -06:00
|
|
|
msg['To'] = fromAddress
|
|
|
|
msg['Bcc'] = ', '.join(to)
|
|
|
|
to = fromAddress
|
2012-05-03 03:51:54 -05:00
|
|
|
# Add attachments
|
|
|
|
if attachments:
|
2014-09-17 11:09:30 -05:00
|
|
|
for attachment in attachments:
|
|
|
|
# 2 possible forms for an attachment
|
|
|
|
if isinstance(attachment, tuple) or isinstance(attachment, list):
|
|
|
|
fileName, fileContent = attachment
|
2012-05-03 03:51:54 -05:00
|
|
|
else:
|
2014-09-17 11:09:30 -05:00
|
|
|
# a FileInfo instance
|
|
|
|
fileName = attachment.uploadName
|
|
|
|
f = file(attachment.fsPath, 'rb')
|
|
|
|
fileContent = f.read()
|
|
|
|
f.close()
|
|
|
|
part = MIMEBase('application', 'octet-stream')
|
|
|
|
part.set_payload(fileContent)
|
2012-05-03 03:51:54 -05:00
|
|
|
Encoders.encode_base64(part)
|
|
|
|
part.add_header('Content-Disposition',
|
|
|
|
'attachment; filename="%s"' % fileName)
|
|
|
|
msg.attach(part)
|
|
|
|
# Send the email
|
|
|
|
try:
|
2014-09-18 04:08:29 -05:00
|
|
|
smtpServer = smtplib.SMTP(config.server, port=config.port)
|
|
|
|
if config.login:
|
|
|
|
smtpServer.login(config.login, config.password)
|
2014-11-14 06:19:47 -06:00
|
|
|
res = smtpServer.sendmail(fromAddress, to, msg.as_string())
|
2012-05-05 10:04:19 -05:00
|
|
|
smtpServer.quit()
|
2014-09-18 04:08:29 -05:00
|
|
|
if res and log:
|
|
|
|
log('could not send mail to some recipients. %s' % str(res),
|
|
|
|
type='warning')
|
2012-05-03 03:51:54 -05:00
|
|
|
except smtplib.SMTPException, e:
|
2015-01-27 05:10:38 -06:00
|
|
|
if log:
|
|
|
|
log('%s: mail sending failed (%s)' % (config, str(e)), type='error')
|
2012-05-08 07:49:45 -05:00
|
|
|
except socket.error, se:
|
2015-01-27 05:10:38 -06:00
|
|
|
if log:
|
2015-01-27 08:45:15 -06:00
|
|
|
log('%s: mail sending failed (%s)' % (config, str(se)),type='error')
|
2012-05-03 03:51:54 -05:00
|
|
|
# ------------------------------------------------------------------------------
|