##// END OF EJS Templates
made auth type optional in constructor
marcink -
r1583:8e77c75b beta
parent child Browse files
Show More
@@ -1,165 +1,165 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.smtp_mailer
3 rhodecode.lib.smtp_mailer
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
15 # (at your option) any later version.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
24
25 import logging
25 import logging
26 import smtplib
26 import smtplib
27 import mimetypes
27 import mimetypes
28 from socket import sslerror
28 from socket import sslerror
29
29
30 from email.mime.multipart import MIMEMultipart
30 from email.mime.multipart import MIMEMultipart
31 from email.mime.image import MIMEImage
31 from email.mime.image import MIMEImage
32 from email.mime.audio import MIMEAudio
32 from email.mime.audio import MIMEAudio
33 from email.mime.base import MIMEBase
33 from email.mime.base import MIMEBase
34 from email.mime.text import MIMEText
34 from email.mime.text import MIMEText
35 from email.utils import formatdate
35 from email.utils import formatdate
36 from email import encoders
36 from email import encoders
37
37
38
38
39
40 class SmtpMailer(object):
39 class SmtpMailer(object):
41 """SMTP mailer class
40 """SMTP mailer class
42
41
43 mailer = SmtpMailer(mail_from, user, passwd, mail_server,
42 mailer = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth
44 mail_port, ssl, tls)
43 mail_port, ssl, tls)
45 mailer.send(recipients, subject, body, attachment_files)
44 mailer.send(recipients, subject, body, attachment_files)
46
45
47 :param recipients might be a list of string or single string
46 :param recipients might be a list of string or single string
48 :param attachment_files is a dict of {filename:location}
47 :param attachment_files is a dict of {filename:location}
49 it tries to guess the mimetype and attach the file
48 it tries to guess the mimetype and attach the file
50
49
51 """
50 """
52
51
53 def __init__(self, mail_from, user, passwd, mail_server,smtp_auth,
52 def __init__(self, mail_from, user, passwd, mail_server, smtp_auth=None,
54 mail_port=None, ssl=False, tls=False, debug=False):
53 mail_port=None, ssl=False, tls=False, debug=False):
55
54
56 self.mail_from = mail_from
55 self.mail_from = mail_from
57 self.mail_server = mail_server
56 self.mail_server = mail_server
58 self.mail_port = mail_port
57 self.mail_port = mail_port
59 self.user = user
58 self.user = user
60 self.passwd = passwd
59 self.passwd = passwd
61 self.ssl = ssl
60 self.ssl = ssl
62 self.tls = tls
61 self.tls = tls
63 self.debug = debug
62 self.debug = debug
64 self.auth = smtp_auth
63 self.auth = smtp_auth
65
64
66 def send(self, recipients=[], subject='', body='', attachment_files=None):
65 def send(self, recipients=[], subject='', body='', attachment_files=None):
67
66
68 if isinstance(recipients, basestring):
67 if isinstance(recipients, basestring):
69 recipients = [recipients]
68 recipients = [recipients]
70 if self.ssl:
69 if self.ssl:
71 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
70 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
72 else:
71 else:
73 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
72 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
74
73
75 if self.tls:
74 if self.tls:
76 smtp_serv.ehlo()
75 smtp_serv.ehlo()
77 smtp_serv.starttls()
76 smtp_serv.starttls()
78
77
79 if self.debug:
78 if self.debug:
80 smtp_serv.set_debuglevel(1)
79 smtp_serv.set_debuglevel(1)
81
80
82 smtp_serv.ehlo()
81 smtp_serv.ehlo()
83 if self.auth:
82 if self.auth:
84 smtp_serv.esmtp_features["auth"] = self.auth
83 smtp_serv.esmtp_features["auth"] = self.auth
85
84
86 #if server requires authorization you must provide login and password
85 # if server requires authorization you must provide login and password
87 #but only if we have them
86 # but only if we have them
88 if self.user and self.passwd:
87 if self.user and self.passwd:
89 smtp_serv.login(self.user, self.passwd)
88 smtp_serv.login(self.user, self.passwd)
90
89
91 date_ = formatdate(localtime=True)
90 date_ = formatdate(localtime=True)
92 msg = MIMEMultipart()
91 msg = MIMEMultipart()
93 msg.set_type('multipart/alternative')
92 msg.set_type('multipart/alternative')
94 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
93 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
95
94
96 text_msg = MIMEText(body)
95 text_msg = MIMEText(body)
97 text_msg.set_type('text/plain')
96 text_msg.set_type('text/plain')
98 text_msg.set_param('charset', 'UTF-8')
97 text_msg.set_param('charset', 'UTF-8')
99
98
100 msg['From'] = self.mail_from
99 msg['From'] = self.mail_from
101 msg['To'] = ','.join(recipients)
100 msg['To'] = ','.join(recipients)
102 msg['Date'] = date_
101 msg['Date'] = date_
103 msg['Subject'] = subject
102 msg['Subject'] = subject
104
103
105 msg.attach(text_msg)
104 msg.attach(text_msg)
106
105
107 if attachment_files:
106 if attachment_files:
108 self.__atach_files(msg, attachment_files)
107 self.__atach_files(msg, attachment_files)
109
108
110 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
109 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
111 logging.info('MAIL SEND TO: %s' % recipients)
110 logging.info('MAIL SEND TO: %s' % recipients)
112
111
113 try:
112 try:
114 smtp_serv.quit()
113 smtp_serv.quit()
115 except sslerror:
114 except sslerror:
116 # sslerror is raised in tls connections on closing sometimes
115 # sslerror is raised in tls connections on closing sometimes
117 pass
116 pass
118
117
119 def __atach_files(self, msg, attachment_files):
118 def __atach_files(self, msg, attachment_files):
120 if isinstance(attachment_files, dict):
119 if isinstance(attachment_files, dict):
121 for f_name, msg_file in attachment_files.items():
120 for f_name, msg_file in attachment_files.items():
122 ctype, encoding = mimetypes.guess_type(f_name)
121 ctype, encoding = mimetypes.guess_type(f_name)
123 logging.info("guessing file %s type based on %s", ctype,
122 logging.info("guessing file %s type based on %s", ctype,
124 f_name)
123 f_name)
125 if ctype is None or encoding is not None:
124 if ctype is None or encoding is not None:
126 # No guess could be made, or the file is encoded
125 # No guess could be made, or the file is encoded
127 # (compressed), so use a generic bag-of-bits type.
126 # (compressed), so use a generic bag-of-bits type.
128 ctype = 'application/octet-stream'
127 ctype = 'application/octet-stream'
129 maintype, subtype = ctype.split('/', 1)
128 maintype, subtype = ctype.split('/', 1)
130 if maintype == 'text':
129 if maintype == 'text':
131 # Note: we should handle calculating the charset
130 # Note: we should handle calculating the charset
132 file_part = MIMEText(self.get_content(msg_file),
131 file_part = MIMEText(self.get_content(msg_file),
133 _subtype=subtype)
132 _subtype=subtype)
134 elif maintype == 'image':
133 elif maintype == 'image':
135 file_part = MIMEImage(self.get_content(msg_file),
134 file_part = MIMEImage(self.get_content(msg_file),
136 _subtype=subtype)
135 _subtype=subtype)
137 elif maintype == 'audio':
136 elif maintype == 'audio':
138 file_part = MIMEAudio(self.get_content(msg_file),
137 file_part = MIMEAudio(self.get_content(msg_file),
139 _subtype=subtype)
138 _subtype=subtype)
140 else:
139 else:
141 file_part = MIMEBase(maintype, subtype)
140 file_part = MIMEBase(maintype, subtype)
142 file_part.set_payload(self.get_content(msg_file))
141 file_part.set_payload(self.get_content(msg_file))
143 # Encode the payload using Base64
142 # Encode the payload using Base64
144 encoders.encode_base64(msg)
143 encoders.encode_base64(msg)
145 # Set the filename parameter
144 # Set the filename parameter
146 file_part.add_header('Content-Disposition', 'attachment',
145 file_part.add_header('Content-Disposition', 'attachment',
147 filename=f_name)
146 filename=f_name)
148 file_part.add_header('Content-Type', ctype, name=f_name)
147 file_part.add_header('Content-Type', ctype, name=f_name)
149 msg.attach(file_part)
148 msg.attach(file_part)
150 else:
149 else:
151 raise Exception('Attachment files should be'
150 raise Exception('Attachment files should be'
152 'a dict in format {"filename":"filepath"}')
151 'a dict in format {"filename":"filepath"}')
153
152
154 def get_content(self, msg_file):
153 def get_content(self, msg_file):
155 """Get content based on type, if content is a string do open first
154 """Get content based on type, if content is a string do open first
156 else just read because it's a probably open file object
155 else just read because it's a probably open file object
157
156
158 :param msg_file:
157 :param msg_file:
159 """
158 """
160 if isinstance(msg_file, str):
159 if isinstance(msg_file, str):
161 return open(msg_file, "rb").read()
160 return open(msg_file, "rb").read()
162 else:
161 else:
163 #just for safe seek to 0
162 # just for safe seek to 0
164 msg_file.seek(0)
163 msg_file.seek(0)
165 return msg_file.read()
164 return msg_file.read()
165
General Comments 0
You need to be logged in to leave comments. Login now