smtp_mailer.py
165 lines
| 5.9 KiB
| text/x-python
|
PythonLexer
r981 | # -*- coding: utf-8 -*- | |||
""" | ||||
rhodecode.lib.smtp_mailer | ||||
~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
r1203 | ||||
r981 | Simple smtp mailer used in RhodeCode | |||
r1203 | ||||
r984 | :created_on: Sep 13, 2010 | |||
r1532 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |||
:license: GPLv3, see COPYING for more details. | ||||
r981 | """ | |||
r1532 | # This program is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation, either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
r981 | ||||
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 | ||||
r1307 | ||||
|
r1581 | |||
r547 | class SmtpMailer(object): | |||
r981 | """SMTP mailer class | |||
r1203 | ||||
r1307 | mailer = SmtpMailer(mail_from, user, passwd, mail_server, | |||
mail_port, ssl, tls) | ||||
r1203 | mailer.send(recipients, subject, body, attachment_files) | |||
r547 | :param recipients might be a list of string or single string | |||
r1203 | :param attachment_files is a dict of {filename:location} | |||
it tries to guess the mimetype and attach the file | ||||
r547 | """ | |||
|
r1581 | def __init__(self, mail_from, user, passwd, mail_server,smtp_auth, | ||
r1169 | mail_port=None, ssl=False, tls=False, debug=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 | ||||
r1169 | self.debug = debug | |||
|
r1581 | self.auth = smtp_auth | ||
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() | |||
|
r1581 | if self.auth: | ||
smtp_serv.esmtp_features["auth"] = self.auth | ||||
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() | ||||
r1417 | msg.set_type('multipart/alternative') | |||
msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' | ||||
text_msg = MIMEText(body) | ||||
text_msg.set_type('text/plain') | ||||
text_msg.set_param('charset', 'UTF-8') | ||||
r547 | msg['From'] = self.mail_from | |||
msg['To'] = ','.join(recipients) | ||||
msg['Date'] = date_ | ||||
msg['Subject'] = subject | ||||
r1417 | msg.attach(text_msg) | |||
r547 | ||||
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) | ||||
r1307 | logging.info("guessing file %s type based on %s", ctype, | |||
f_name) | ||||
r547 | if ctype is None or encoding is not None: | |||
r1307 | # No guess could be made, or the file is encoded | |||
# (compressed), so use a generic bag-of-bits type. | ||||
r547 | 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 | |||
r1203 | ||||
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() | ||||