##// END OF EJS Templates
mail: correct import of email module
Augie Fackler -
r19790:53f16f4a default
parent child Browse files
Show More
@@ -1,326 +1,326
1 # mail.py - mail sending bits for mercurial
1 # mail.py - mail sending bits for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import util, encoding, sslutil
9 import util, encoding, sslutil
10 import os, smtplib, socket, quopri, time, sys
10 import os, smtplib, socket, quopri, time, sys
11 import email.Header, email.MIMEText, email.Utils
11 import email
12
12
13 _oldheaderinit = email.Header.Header.__init__
13 _oldheaderinit = email.Header.Header.__init__
14 def _unifiedheaderinit(self, *args, **kw):
14 def _unifiedheaderinit(self, *args, **kw):
15 """
15 """
16 Python 2.7 introduces a backwards incompatible change
16 Python 2.7 introduces a backwards incompatible change
17 (Python issue1974, r70772) in email.Generator.Generator code:
17 (Python issue1974, r70772) in email.Generator.Generator code:
18 pre-2.7 code passed "continuation_ws='\t'" to the Header
18 pre-2.7 code passed "continuation_ws='\t'" to the Header
19 constructor, and 2.7 removed this parameter.
19 constructor, and 2.7 removed this parameter.
20
20
21 Default argument is continuation_ws=' ', which means that the
21 Default argument is continuation_ws=' ', which means that the
22 behaviour is different in <2.7 and 2.7
22 behaviour is different in <2.7 and 2.7
23
23
24 We consider the 2.7 behaviour to be preferable, but need
24 We consider the 2.7 behaviour to be preferable, but need
25 to have an unified behaviour for versions 2.4 to 2.7
25 to have an unified behaviour for versions 2.4 to 2.7
26 """
26 """
27 # override continuation_ws
27 # override continuation_ws
28 kw['continuation_ws'] = ' '
28 kw['continuation_ws'] = ' '
29 _oldheaderinit(self, *args, **kw)
29 _oldheaderinit(self, *args, **kw)
30
30
31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
32
32
33 class STARTTLS(smtplib.SMTP):
33 class STARTTLS(smtplib.SMTP):
34 '''Derived class to verify the peer certificate for STARTTLS.
34 '''Derived class to verify the peer certificate for STARTTLS.
35
35
36 This class allows to pass any keyword arguments to SSL socket creation.
36 This class allows to pass any keyword arguments to SSL socket creation.
37 '''
37 '''
38 def __init__(self, sslkwargs, **kwargs):
38 def __init__(self, sslkwargs, **kwargs):
39 smtplib.SMTP.__init__(self, **kwargs)
39 smtplib.SMTP.__init__(self, **kwargs)
40 self._sslkwargs = sslkwargs
40 self._sslkwargs = sslkwargs
41
41
42 def starttls(self, keyfile=None, certfile=None):
42 def starttls(self, keyfile=None, certfile=None):
43 if not self.has_extn("starttls"):
43 if not self.has_extn("starttls"):
44 msg = "STARTTLS extension not supported by server"
44 msg = "STARTTLS extension not supported by server"
45 raise smtplib.SMTPException(msg)
45 raise smtplib.SMTPException(msg)
46 (resp, reply) = self.docmd("STARTTLS")
46 (resp, reply) = self.docmd("STARTTLS")
47 if resp == 220:
47 if resp == 220:
48 self.sock = sslutil.ssl_wrap_socket(self.sock, keyfile, certfile,
48 self.sock = sslutil.ssl_wrap_socket(self.sock, keyfile, certfile,
49 **self._sslkwargs)
49 **self._sslkwargs)
50 if not util.safehasattr(self.sock, "read"):
50 if not util.safehasattr(self.sock, "read"):
51 # using httplib.FakeSocket with Python 2.5.x or earlier
51 # using httplib.FakeSocket with Python 2.5.x or earlier
52 self.sock.read = self.sock.recv
52 self.sock.read = self.sock.recv
53 self.file = smtplib.SSLFakeFile(self.sock)
53 self.file = smtplib.SSLFakeFile(self.sock)
54 self.helo_resp = None
54 self.helo_resp = None
55 self.ehlo_resp = None
55 self.ehlo_resp = None
56 self.esmtp_features = {}
56 self.esmtp_features = {}
57 self.does_esmtp = 0
57 self.does_esmtp = 0
58 return (resp, reply)
58 return (resp, reply)
59
59
60 if util.safehasattr(smtplib.SMTP, '_get_socket'):
60 if util.safehasattr(smtplib.SMTP, '_get_socket'):
61 class SMTPS(smtplib.SMTP):
61 class SMTPS(smtplib.SMTP):
62 '''Derived class to verify the peer certificate for SMTPS.
62 '''Derived class to verify the peer certificate for SMTPS.
63
63
64 This class allows to pass any keyword arguments to SSL socket creation.
64 This class allows to pass any keyword arguments to SSL socket creation.
65 '''
65 '''
66 def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
66 def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
67 self.keyfile = keyfile
67 self.keyfile = keyfile
68 self.certfile = certfile
68 self.certfile = certfile
69 smtplib.SMTP.__init__(self, **kwargs)
69 smtplib.SMTP.__init__(self, **kwargs)
70 self.default_port = smtplib.SMTP_SSL_PORT
70 self.default_port = smtplib.SMTP_SSL_PORT
71 self._sslkwargs = sslkwargs
71 self._sslkwargs = sslkwargs
72
72
73 def _get_socket(self, host, port, timeout):
73 def _get_socket(self, host, port, timeout):
74 if self.debuglevel > 0:
74 if self.debuglevel > 0:
75 print >> sys.stderr, 'connect:', (host, port)
75 print >> sys.stderr, 'connect:', (host, port)
76 new_socket = socket.create_connection((host, port), timeout)
76 new_socket = socket.create_connection((host, port), timeout)
77 new_socket = sslutil.ssl_wrap_socket(new_socket,
77 new_socket = sslutil.ssl_wrap_socket(new_socket,
78 self.keyfile, self.certfile,
78 self.keyfile, self.certfile,
79 **self._sslkwargs)
79 **self._sslkwargs)
80 self.file = smtplib.SSLFakeFile(new_socket)
80 self.file = smtplib.SSLFakeFile(new_socket)
81 return new_socket
81 return new_socket
82 else:
82 else:
83 def SMTPS(sslkwargs, keyfile=None, certfile=None, **kwargs):
83 def SMTPS(sslkwargs, keyfile=None, certfile=None, **kwargs):
84 raise util.Abort(_('SMTPS requires Python 2.6 or later'))
84 raise util.Abort(_('SMTPS requires Python 2.6 or later'))
85
85
86 def _smtp(ui):
86 def _smtp(ui):
87 '''build an smtp connection and return a function to send mail'''
87 '''build an smtp connection and return a function to send mail'''
88 local_hostname = ui.config('smtp', 'local_hostname')
88 local_hostname = ui.config('smtp', 'local_hostname')
89 tls = ui.config('smtp', 'tls', 'none')
89 tls = ui.config('smtp', 'tls', 'none')
90 # backward compatible: when tls = true, we use starttls.
90 # backward compatible: when tls = true, we use starttls.
91 starttls = tls == 'starttls' or util.parsebool(tls)
91 starttls = tls == 'starttls' or util.parsebool(tls)
92 smtps = tls == 'smtps'
92 smtps = tls == 'smtps'
93 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
93 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
94 raise util.Abort(_("can't use TLS: Python SSL support not installed"))
94 raise util.Abort(_("can't use TLS: Python SSL support not installed"))
95 mailhost = ui.config('smtp', 'host')
95 mailhost = ui.config('smtp', 'host')
96 if not mailhost:
96 if not mailhost:
97 raise util.Abort(_('smtp.host not configured - cannot send mail'))
97 raise util.Abort(_('smtp.host not configured - cannot send mail'))
98 verifycert = ui.config('smtp', 'verifycert', 'strict')
98 verifycert = ui.config('smtp', 'verifycert', 'strict')
99 if verifycert not in ['strict', 'loose']:
99 if verifycert not in ['strict', 'loose']:
100 if util.parsebool(verifycert) is not False:
100 if util.parsebool(verifycert) is not False:
101 raise util.Abort(_('invalid smtp.verifycert configuration: %s')
101 raise util.Abort(_('invalid smtp.verifycert configuration: %s')
102 % (verifycert))
102 % (verifycert))
103 if (starttls or smtps) and verifycert:
103 if (starttls or smtps) and verifycert:
104 sslkwargs = sslutil.sslkwargs(ui, mailhost)
104 sslkwargs = sslutil.sslkwargs(ui, mailhost)
105 else:
105 else:
106 sslkwargs = {}
106 sslkwargs = {}
107 if smtps:
107 if smtps:
108 ui.note(_('(using smtps)\n'))
108 ui.note(_('(using smtps)\n'))
109 s = SMTPS(sslkwargs, local_hostname=local_hostname)
109 s = SMTPS(sslkwargs, local_hostname=local_hostname)
110 elif starttls:
110 elif starttls:
111 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
111 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
112 else:
112 else:
113 s = smtplib.SMTP(local_hostname=local_hostname)
113 s = smtplib.SMTP(local_hostname=local_hostname)
114 if smtps:
114 if smtps:
115 defaultport = 465
115 defaultport = 465
116 else:
116 else:
117 defaultport = 25
117 defaultport = 25
118 mailport = util.getport(ui.config('smtp', 'port', defaultport))
118 mailport = util.getport(ui.config('smtp', 'port', defaultport))
119 ui.note(_('sending mail: smtp host %s, port %s\n') %
119 ui.note(_('sending mail: smtp host %s, port %s\n') %
120 (mailhost, mailport))
120 (mailhost, mailport))
121 s.connect(host=mailhost, port=mailport)
121 s.connect(host=mailhost, port=mailport)
122 if starttls:
122 if starttls:
123 ui.note(_('(using starttls)\n'))
123 ui.note(_('(using starttls)\n'))
124 s.ehlo()
124 s.ehlo()
125 s.starttls()
125 s.starttls()
126 s.ehlo()
126 s.ehlo()
127 if (starttls or smtps) and verifycert:
127 if (starttls or smtps) and verifycert:
128 ui.note(_('(verifying remote certificate)\n'))
128 ui.note(_('(verifying remote certificate)\n'))
129 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
129 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
130 username = ui.config('smtp', 'username')
130 username = ui.config('smtp', 'username')
131 password = ui.config('smtp', 'password')
131 password = ui.config('smtp', 'password')
132 if username and not password:
132 if username and not password:
133 password = ui.getpass()
133 password = ui.getpass()
134 if username and password:
134 if username and password:
135 ui.note(_('(authenticating to mail server as %s)\n') %
135 ui.note(_('(authenticating to mail server as %s)\n') %
136 (username))
136 (username))
137 try:
137 try:
138 s.login(username, password)
138 s.login(username, password)
139 except smtplib.SMTPException, inst:
139 except smtplib.SMTPException, inst:
140 raise util.Abort(inst)
140 raise util.Abort(inst)
141
141
142 def send(sender, recipients, msg):
142 def send(sender, recipients, msg):
143 try:
143 try:
144 return s.sendmail(sender, recipients, msg)
144 return s.sendmail(sender, recipients, msg)
145 except smtplib.SMTPRecipientsRefused, inst:
145 except smtplib.SMTPRecipientsRefused, inst:
146 recipients = [r[1] for r in inst.recipients.values()]
146 recipients = [r[1] for r in inst.recipients.values()]
147 raise util.Abort('\n' + '\n'.join(recipients))
147 raise util.Abort('\n' + '\n'.join(recipients))
148 except smtplib.SMTPException, inst:
148 except smtplib.SMTPException, inst:
149 raise util.Abort(inst)
149 raise util.Abort(inst)
150
150
151 return send
151 return send
152
152
153 def _sendmail(ui, sender, recipients, msg):
153 def _sendmail(ui, sender, recipients, msg):
154 '''send mail using sendmail.'''
154 '''send mail using sendmail.'''
155 program = ui.config('email', 'method')
155 program = ui.config('email', 'method')
156 cmdline = '%s -f %s %s' % (program, util.email(sender),
156 cmdline = '%s -f %s %s' % (program, util.email(sender),
157 ' '.join(map(util.email, recipients)))
157 ' '.join(map(util.email, recipients)))
158 ui.note(_('sending mail: %s\n') % cmdline)
158 ui.note(_('sending mail: %s\n') % cmdline)
159 fp = util.popen(cmdline, 'w')
159 fp = util.popen(cmdline, 'w')
160 fp.write(msg)
160 fp.write(msg)
161 ret = fp.close()
161 ret = fp.close()
162 if ret:
162 if ret:
163 raise util.Abort('%s %s' % (
163 raise util.Abort('%s %s' % (
164 os.path.basename(program.split(None, 1)[0]),
164 os.path.basename(program.split(None, 1)[0]),
165 util.explainexit(ret)[0]))
165 util.explainexit(ret)[0]))
166
166
167 def _mbox(mbox, sender, recipients, msg):
167 def _mbox(mbox, sender, recipients, msg):
168 '''write mails to mbox'''
168 '''write mails to mbox'''
169 fp = open(mbox, 'ab+')
169 fp = open(mbox, 'ab+')
170 # Should be time.asctime(), but Windows prints 2-characters day
170 # Should be time.asctime(), but Windows prints 2-characters day
171 # of month instead of one. Make them print the same thing.
171 # of month instead of one. Make them print the same thing.
172 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
172 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
173 fp.write('From %s %s\n' % (sender, date))
173 fp.write('From %s %s\n' % (sender, date))
174 fp.write(msg)
174 fp.write(msg)
175 fp.write('\n\n')
175 fp.write('\n\n')
176 fp.close()
176 fp.close()
177
177
178 def connect(ui, mbox=None):
178 def connect(ui, mbox=None):
179 '''make a mail connection. return a function to send mail.
179 '''make a mail connection. return a function to send mail.
180 call as sendmail(sender, list-of-recipients, msg).'''
180 call as sendmail(sender, list-of-recipients, msg).'''
181 if mbox:
181 if mbox:
182 open(mbox, 'wb').close()
182 open(mbox, 'wb').close()
183 return lambda s, r, m: _mbox(mbox, s, r, m)
183 return lambda s, r, m: _mbox(mbox, s, r, m)
184 if ui.config('email', 'method', 'smtp') == 'smtp':
184 if ui.config('email', 'method', 'smtp') == 'smtp':
185 return _smtp(ui)
185 return _smtp(ui)
186 return lambda s, r, m: _sendmail(ui, s, r, m)
186 return lambda s, r, m: _sendmail(ui, s, r, m)
187
187
188 def sendmail(ui, sender, recipients, msg, mbox=None):
188 def sendmail(ui, sender, recipients, msg, mbox=None):
189 send = connect(ui, mbox=mbox)
189 send = connect(ui, mbox=mbox)
190 return send(sender, recipients, msg)
190 return send(sender, recipients, msg)
191
191
192 def validateconfig(ui):
192 def validateconfig(ui):
193 '''determine if we have enough config data to try sending email.'''
193 '''determine if we have enough config data to try sending email.'''
194 method = ui.config('email', 'method', 'smtp')
194 method = ui.config('email', 'method', 'smtp')
195 if method == 'smtp':
195 if method == 'smtp':
196 if not ui.config('smtp', 'host'):
196 if not ui.config('smtp', 'host'):
197 raise util.Abort(_('smtp specified as email transport, '
197 raise util.Abort(_('smtp specified as email transport, '
198 'but no smtp host configured'))
198 'but no smtp host configured'))
199 else:
199 else:
200 if not util.findexe(method):
200 if not util.findexe(method):
201 raise util.Abort(_('%r specified as email transport, '
201 raise util.Abort(_('%r specified as email transport, '
202 'but not in PATH') % method)
202 'but not in PATH') % method)
203
203
204 def mimetextpatch(s, subtype='plain', display=False):
204 def mimetextpatch(s, subtype='plain', display=False):
205 '''Return MIME message suitable for a patch.
205 '''Return MIME message suitable for a patch.
206 Charset will be detected as utf-8 or (possibly fake) us-ascii.
206 Charset will be detected as utf-8 or (possibly fake) us-ascii.
207 Transfer encodings will be used if necessary.'''
207 Transfer encodings will be used if necessary.'''
208
208
209 cs = 'us-ascii'
209 cs = 'us-ascii'
210 if not display:
210 if not display:
211 try:
211 try:
212 s.decode('us-ascii')
212 s.decode('us-ascii')
213 except UnicodeDecodeError:
213 except UnicodeDecodeError:
214 try:
214 try:
215 s.decode('utf-8')
215 s.decode('utf-8')
216 cs = 'utf-8'
216 cs = 'utf-8'
217 except UnicodeDecodeError:
217 except UnicodeDecodeError:
218 # We'll go with us-ascii as a fallback.
218 # We'll go with us-ascii as a fallback.
219 pass
219 pass
220
220
221 return mimetextqp(s, subtype, cs)
221 return mimetextqp(s, subtype, cs)
222
222
223 def mimetextqp(body, subtype, charset):
223 def mimetextqp(body, subtype, charset):
224 '''Return MIME message.
224 '''Return MIME message.
225 Quoted-printable transfer encoding will be used if necessary.
225 Quoted-printable transfer encoding will be used if necessary.
226 '''
226 '''
227 enc = None
227 enc = None
228 for line in body.splitlines():
228 for line in body.splitlines():
229 if len(line) > 950:
229 if len(line) > 950:
230 body = quopri.encodestring(body)
230 body = quopri.encodestring(body)
231 enc = "quoted-printable"
231 enc = "quoted-printable"
232 break
232 break
233
233
234 msg = email.MIMEText.MIMEText(body, subtype, charset)
234 msg = email.MIMEText.MIMEText(body, subtype, charset)
235 if enc:
235 if enc:
236 del msg['Content-Transfer-Encoding']
236 del msg['Content-Transfer-Encoding']
237 msg['Content-Transfer-Encoding'] = enc
237 msg['Content-Transfer-Encoding'] = enc
238 return msg
238 return msg
239
239
240 def _charsets(ui):
240 def _charsets(ui):
241 '''Obtains charsets to send mail parts not containing patches.'''
241 '''Obtains charsets to send mail parts not containing patches.'''
242 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
242 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
243 fallbacks = [encoding.fallbackencoding.lower(),
243 fallbacks = [encoding.fallbackencoding.lower(),
244 encoding.encoding.lower(), 'utf-8']
244 encoding.encoding.lower(), 'utf-8']
245 for cs in fallbacks: # find unique charsets while keeping order
245 for cs in fallbacks: # find unique charsets while keeping order
246 if cs not in charsets:
246 if cs not in charsets:
247 charsets.append(cs)
247 charsets.append(cs)
248 return [cs for cs in charsets if not cs.endswith('ascii')]
248 return [cs for cs in charsets if not cs.endswith('ascii')]
249
249
250 def _encode(ui, s, charsets):
250 def _encode(ui, s, charsets):
251 '''Returns (converted) string, charset tuple.
251 '''Returns (converted) string, charset tuple.
252 Finds out best charset by cycling through sendcharsets in descending
252 Finds out best charset by cycling through sendcharsets in descending
253 order. Tries both encoding and fallbackencoding for input. Only as
253 order. Tries both encoding and fallbackencoding for input. Only as
254 last resort send as is in fake ascii.
254 last resort send as is in fake ascii.
255 Caveat: Do not use for mail parts containing patches!'''
255 Caveat: Do not use for mail parts containing patches!'''
256 try:
256 try:
257 s.decode('ascii')
257 s.decode('ascii')
258 except UnicodeDecodeError:
258 except UnicodeDecodeError:
259 sendcharsets = charsets or _charsets(ui)
259 sendcharsets = charsets or _charsets(ui)
260 for ics in (encoding.encoding, encoding.fallbackencoding):
260 for ics in (encoding.encoding, encoding.fallbackencoding):
261 try:
261 try:
262 u = s.decode(ics)
262 u = s.decode(ics)
263 except UnicodeDecodeError:
263 except UnicodeDecodeError:
264 continue
264 continue
265 for ocs in sendcharsets:
265 for ocs in sendcharsets:
266 try:
266 try:
267 return u.encode(ocs), ocs
267 return u.encode(ocs), ocs
268 except UnicodeEncodeError:
268 except UnicodeEncodeError:
269 pass
269 pass
270 except LookupError:
270 except LookupError:
271 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
271 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
272 # if ascii, or all conversion attempts fail, send (broken) ascii
272 # if ascii, or all conversion attempts fail, send (broken) ascii
273 return s, 'us-ascii'
273 return s, 'us-ascii'
274
274
275 def headencode(ui, s, charsets=None, display=False):
275 def headencode(ui, s, charsets=None, display=False):
276 '''Returns RFC-2047 compliant header from given string.'''
276 '''Returns RFC-2047 compliant header from given string.'''
277 if not display:
277 if not display:
278 # split into words?
278 # split into words?
279 s, cs = _encode(ui, s, charsets)
279 s, cs = _encode(ui, s, charsets)
280 return str(email.Header.Header(s, cs))
280 return str(email.Header.Header(s, cs))
281 return s
281 return s
282
282
283 def _addressencode(ui, name, addr, charsets=None):
283 def _addressencode(ui, name, addr, charsets=None):
284 name = headencode(ui, name, charsets)
284 name = headencode(ui, name, charsets)
285 try:
285 try:
286 acc, dom = addr.split('@')
286 acc, dom = addr.split('@')
287 acc = acc.encode('ascii')
287 acc = acc.encode('ascii')
288 dom = dom.decode(encoding.encoding).encode('idna')
288 dom = dom.decode(encoding.encoding).encode('idna')
289 addr = '%s@%s' % (acc, dom)
289 addr = '%s@%s' % (acc, dom)
290 except UnicodeDecodeError:
290 except UnicodeDecodeError:
291 raise util.Abort(_('invalid email address: %s') % addr)
291 raise util.Abort(_('invalid email address: %s') % addr)
292 except ValueError:
292 except ValueError:
293 try:
293 try:
294 # too strict?
294 # too strict?
295 addr = addr.encode('ascii')
295 addr = addr.encode('ascii')
296 except UnicodeDecodeError:
296 except UnicodeDecodeError:
297 raise util.Abort(_('invalid local address: %s') % addr)
297 raise util.Abort(_('invalid local address: %s') % addr)
298 return email.Utils.formataddr((name, addr))
298 return email.Utils.formataddr((name, addr))
299
299
300 def addressencode(ui, address, charsets=None, display=False):
300 def addressencode(ui, address, charsets=None, display=False):
301 '''Turns address into RFC-2047 compliant header.'''
301 '''Turns address into RFC-2047 compliant header.'''
302 if display or not address:
302 if display or not address:
303 return address or ''
303 return address or ''
304 name, addr = email.Utils.parseaddr(address)
304 name, addr = email.Utils.parseaddr(address)
305 return _addressencode(ui, name, addr, charsets)
305 return _addressencode(ui, name, addr, charsets)
306
306
307 def addrlistencode(ui, addrs, charsets=None, display=False):
307 def addrlistencode(ui, addrs, charsets=None, display=False):
308 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
308 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
309 A single element of input list may contain multiple addresses, but output
309 A single element of input list may contain multiple addresses, but output
310 always has one address per item'''
310 always has one address per item'''
311 if display:
311 if display:
312 return [a.strip() for a in addrs if a.strip()]
312 return [a.strip() for a in addrs if a.strip()]
313
313
314 result = []
314 result = []
315 for name, addr in email.Utils.getaddresses(addrs):
315 for name, addr in email.Utils.getaddresses(addrs):
316 if name or addr:
316 if name or addr:
317 result.append(_addressencode(ui, name, addr, charsets))
317 result.append(_addressencode(ui, name, addr, charsets))
318 return result
318 return result
319
319
320 def mimeencode(ui, s, charsets=None, display=False):
320 def mimeencode(ui, s, charsets=None, display=False):
321 '''creates mime text object, encodes it if needed, and sets
321 '''creates mime text object, encodes it if needed, and sets
322 charset and transfer-encoding accordingly.'''
322 charset and transfer-encoding accordingly.'''
323 cs = 'us-ascii'
323 cs = 'us-ascii'
324 if not display:
324 if not display:
325 s, cs = _encode(ui, s, charsets)
325 s, cs = _encode(ui, s, charsets)
326 return mimetextqp(s, 'plain', cs)
326 return mimetextqp(s, 'plain', cs)
General Comments 0
You need to be logged in to leave comments. Login now