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