smtp_mailer.py
143 lines
| 5.0 KiB
| text/x-python
|
PythonLexer
r981 | # -*- coding: utf-8 -*- | |||
""" | ||||
rhodecode.lib.smtp_mailer | ||||
~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
Simple smtp mailer used in RhodeCode | ||||
r984 | :created_on: Sep 13, 2010 | |||
r981 | :copyright: (c) 2011 by marcink. | |||
:license: LICENSE_NAME, see LICENSE_FILE for more details. | ||||
""" | ||||
r547 | import logging | |||
import smtplib | ||||
import mimetypes | ||||
r981 | from socket import sslerror | |||
r547 | from email.mime.multipart import MIMEMultipart | |||
from email.mime.image import MIMEImage | ||||
from email.mime.audio import MIMEAudio | ||||
from email.mime.base import MIMEBase | ||||
from email.mime.text import MIMEText | ||||
from email.utils import formatdate | ||||
from email import encoders | ||||
class SmtpMailer(object): | ||||
r981 | """SMTP mailer class | |||
r547 | ||||
mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls) | ||||
mailer.send(recipients, subject, body, attachment_files) | ||||
:param recipients might be a list of string or single string | ||||
:param attachment_files is a dict of {filename:location} | ||||
r981 | it tries to guess the mimetype and attach the file | |||
r547 | """ | |||
def __init__(self, mail_from, user, passwd, mail_server, | ||||
mail_port=None, ssl=False, tls=False): | ||||
r689 | ||||
r547 | self.mail_from = mail_from | |||
self.mail_server = mail_server | ||||
self.mail_port = mail_port | ||||
self.user = user | ||||
self.passwd = passwd | ||||
self.ssl = ssl | ||||
self.tls = tls | ||||
self.debug = False | ||||
r689 | ||||
r1029 | def send(self, recipients=[], subject='', body='', attachment_files=None): | |||
r547 | ||||
if isinstance(recipients, basestring): | ||||
recipients = [recipients] | ||||
if self.ssl: | ||||
smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port) | ||||
else: | ||||
smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port) | ||||
if self.tls: | ||||
r981 | smtp_serv.ehlo() | |||
r547 | smtp_serv.starttls() | |||
r689 | ||||
if self.debug: | ||||
r547 | smtp_serv.set_debuglevel(1) | |||
r952 | smtp_serv.ehlo() | |||
r547 | ||||
#if server requires authorization you must provide login and password | ||||
r981 | #but only if we have them | |||
if self.user and self.passwd: | ||||
smtp_serv.login(self.user, self.passwd) | ||||
r547 | ||||
date_ = formatdate(localtime=True) | ||||
msg = MIMEMultipart() | ||||
msg['From'] = self.mail_from | ||||
msg['To'] = ','.join(recipients) | ||||
msg['Date'] = date_ | ||||
msg['Subject'] = subject | ||||
msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' | ||||
msg.attach(MIMEText(body)) | ||||
if attachment_files: | ||||
self.__atach_files(msg, attachment_files) | ||||
smtp_serv.sendmail(self.mail_from, recipients, msg.as_string()) | ||||
logging.info('MAIL SEND TO: %s' % recipients) | ||||
r981 | ||||
try: | ||||
smtp_serv.quit() | ||||
except sslerror: | ||||
# sslerror is raised in tls connections on closing sometimes | ||||
pass | ||||
r547 | ||||
def __atach_files(self, msg, attachment_files): | ||||
if isinstance(attachment_files, dict): | ||||
for f_name, msg_file in attachment_files.items(): | ||||
ctype, encoding = mimetypes.guess_type(f_name) | ||||
logging.info("guessing file %s type based on %s" , ctype, f_name) | ||||
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': | ||||
# Note: we should handle calculating the charset | ||||
r689 | file_part = MIMEText(self.get_content(msg_file), | |||
r547 | _subtype=subtype) | |||
elif maintype == 'image': | ||||
r689 | file_part = MIMEImage(self.get_content(msg_file), | |||
r547 | _subtype=subtype) | |||
elif maintype == 'audio': | ||||
r689 | file_part = MIMEAudio(self.get_content(msg_file), | |||
r547 | _subtype=subtype) | |||
else: | ||||
file_part = MIMEBase(maintype, subtype) | ||||
file_part.set_payload(self.get_content(msg_file)) | ||||
# Encode the payload using Base64 | ||||
encoders.encode_base64(msg) | ||||
# Set the filename parameter | ||||
r689 | file_part.add_header('Content-Disposition', 'attachment', | |||
r547 | filename=f_name) | |||
file_part.add_header('Content-Type', ctype, name=f_name) | ||||
msg.attach(file_part) | ||||
else: | ||||
r689 | raise Exception('Attachment files should be' | |||
'a dict in format {"filename":"filepath"}') | ||||
r547 | ||||
def get_content(self, msg_file): | ||||
r1029 | """Get content based on type, if content is a string do open first | |||
r547 | else just read because it's a probably open file object | |||
r1029 | ||||
r604 | :param msg_file: | |||
r1029 | """ | |||
r547 | if isinstance(msg_file, str): | |||
return open(msg_file, "rb").read() | ||||
else: | ||||
#just for safe seek to 0 | ||||
msg_file.seek(0) | ||||
return msg_file.read() | ||||