##// END OF EJS Templates
email: fix config default value inconsistency
Matt Mackall -
r25842:fa3f0301 default
parent child Browse files
Show More
@@ -1,328 +1,328 b''
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
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.wrapsocket(self.sock, keyfile, certfile,
48 self.sock = sslutil.wrapsocket(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.wrapsocket(new_socket,
77 new_socket = sslutil.wrapsocket(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 verifycert = False
103 verifycert = False
104 if (starttls or smtps) and verifycert:
104 if (starttls or smtps) and verifycert:
105 sslkwargs = sslutil.sslkwargs(ui, mailhost)
105 sslkwargs = sslutil.sslkwargs(ui, mailhost)
106 else:
106 else:
107 # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
107 # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
108 sslkwargs = {'ui': ui}
108 sslkwargs = {'ui': ui}
109 if smtps:
109 if smtps:
110 ui.note(_('(using smtps)\n'))
110 ui.note(_('(using smtps)\n'))
111 s = SMTPS(sslkwargs, local_hostname=local_hostname)
111 s = SMTPS(sslkwargs, local_hostname=local_hostname)
112 elif starttls:
112 elif starttls:
113 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
113 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
114 else:
114 else:
115 s = smtplib.SMTP(local_hostname=local_hostname)
115 s = smtplib.SMTP(local_hostname=local_hostname)
116 if smtps:
116 if smtps:
117 defaultport = 465
117 defaultport = 465
118 else:
118 else:
119 defaultport = 25
119 defaultport = 25
120 mailport = util.getport(ui.config('smtp', 'port', defaultport))
120 mailport = util.getport(ui.config('smtp', 'port', defaultport))
121 ui.note(_('sending mail: smtp host %s, port %s\n') %
121 ui.note(_('sending mail: smtp host %s, port %s\n') %
122 (mailhost, mailport))
122 (mailhost, mailport))
123 s.connect(host=mailhost, port=mailport)
123 s.connect(host=mailhost, port=mailport)
124 if starttls:
124 if starttls:
125 ui.note(_('(using starttls)\n'))
125 ui.note(_('(using starttls)\n'))
126 s.ehlo()
126 s.ehlo()
127 s.starttls()
127 s.starttls()
128 s.ehlo()
128 s.ehlo()
129 if (starttls or smtps) and verifycert:
129 if (starttls or smtps) and verifycert:
130 ui.note(_('(verifying remote certificate)\n'))
130 ui.note(_('(verifying remote certificate)\n'))
131 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
131 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
132 username = ui.config('smtp', 'username')
132 username = ui.config('smtp', 'username')
133 password = ui.config('smtp', 'password')
133 password = ui.config('smtp', 'password')
134 if username and not password:
134 if username and not password:
135 password = ui.getpass()
135 password = ui.getpass()
136 if username and password:
136 if username and password:
137 ui.note(_('(authenticating to mail server as %s)\n') %
137 ui.note(_('(authenticating to mail server as %s)\n') %
138 (username))
138 (username))
139 try:
139 try:
140 s.login(username, password)
140 s.login(username, password)
141 except smtplib.SMTPException as inst:
141 except smtplib.SMTPException as inst:
142 raise util.Abort(inst)
142 raise util.Abort(inst)
143
143
144 def send(sender, recipients, msg):
144 def send(sender, recipients, msg):
145 try:
145 try:
146 return s.sendmail(sender, recipients, msg)
146 return s.sendmail(sender, recipients, msg)
147 except smtplib.SMTPRecipientsRefused as inst:
147 except smtplib.SMTPRecipientsRefused as inst:
148 recipients = [r[1] for r in inst.recipients.values()]
148 recipients = [r[1] for r in inst.recipients.values()]
149 raise util.Abort('\n' + '\n'.join(recipients))
149 raise util.Abort('\n' + '\n'.join(recipients))
150 except smtplib.SMTPException as inst:
150 except smtplib.SMTPException as inst:
151 raise util.Abort(inst)
151 raise util.Abort(inst)
152
152
153 return send
153 return send
154
154
155 def _sendmail(ui, sender, recipients, msg):
155 def _sendmail(ui, sender, recipients, msg):
156 '''send mail using sendmail.'''
156 '''send mail using sendmail.'''
157 program = ui.config('email', 'method')
157 program = ui.config('email', 'method', 'smtp')
158 cmdline = '%s -f %s %s' % (program, util.email(sender),
158 cmdline = '%s -f %s %s' % (program, util.email(sender),
159 ' '.join(map(util.email, recipients)))
159 ' '.join(map(util.email, recipients)))
160 ui.note(_('sending mail: %s\n') % cmdline)
160 ui.note(_('sending mail: %s\n') % cmdline)
161 fp = util.popen(cmdline, 'w')
161 fp = util.popen(cmdline, 'w')
162 fp.write(msg)
162 fp.write(msg)
163 ret = fp.close()
163 ret = fp.close()
164 if ret:
164 if ret:
165 raise util.Abort('%s %s' % (
165 raise util.Abort('%s %s' % (
166 os.path.basename(program.split(None, 1)[0]),
166 os.path.basename(program.split(None, 1)[0]),
167 util.explainexit(ret)[0]))
167 util.explainexit(ret)[0]))
168
168
169 def _mbox(mbox, sender, recipients, msg):
169 def _mbox(mbox, sender, recipients, msg):
170 '''write mails to mbox'''
170 '''write mails to mbox'''
171 fp = open(mbox, 'ab+')
171 fp = open(mbox, 'ab+')
172 # Should be time.asctime(), but Windows prints 2-characters day
172 # Should be time.asctime(), but Windows prints 2-characters day
173 # of month instead of one. Make them print the same thing.
173 # of month instead of one. Make them print the same thing.
174 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
174 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
175 fp.write('From %s %s\n' % (sender, date))
175 fp.write('From %s %s\n' % (sender, date))
176 fp.write(msg)
176 fp.write(msg)
177 fp.write('\n\n')
177 fp.write('\n\n')
178 fp.close()
178 fp.close()
179
179
180 def connect(ui, mbox=None):
180 def connect(ui, mbox=None):
181 '''make a mail connection. return a function to send mail.
181 '''make a mail connection. return a function to send mail.
182 call as sendmail(sender, list-of-recipients, msg).'''
182 call as sendmail(sender, list-of-recipients, msg).'''
183 if mbox:
183 if mbox:
184 open(mbox, 'wb').close()
184 open(mbox, 'wb').close()
185 return lambda s, r, m: _mbox(mbox, s, r, m)
185 return lambda s, r, m: _mbox(mbox, s, r, m)
186 if ui.config('email', 'method', 'smtp') == 'smtp':
186 if ui.config('email', 'method', 'smtp') == 'smtp':
187 return _smtp(ui)
187 return _smtp(ui)
188 return lambda s, r, m: _sendmail(ui, s, r, m)
188 return lambda s, r, m: _sendmail(ui, s, r, m)
189
189
190 def sendmail(ui, sender, recipients, msg, mbox=None):
190 def sendmail(ui, sender, recipients, msg, mbox=None):
191 send = connect(ui, mbox=mbox)
191 send = connect(ui, mbox=mbox)
192 return send(sender, recipients, msg)
192 return send(sender, recipients, msg)
193
193
194 def validateconfig(ui):
194 def validateconfig(ui):
195 '''determine if we have enough config data to try sending email.'''
195 '''determine if we have enough config data to try sending email.'''
196 method = ui.config('email', 'method', 'smtp')
196 method = ui.config('email', 'method', 'smtp')
197 if method == 'smtp':
197 if method == 'smtp':
198 if not ui.config('smtp', 'host'):
198 if not ui.config('smtp', 'host'):
199 raise util.Abort(_('smtp specified as email transport, '
199 raise util.Abort(_('smtp specified as email transport, '
200 'but no smtp host configured'))
200 'but no smtp host configured'))
201 else:
201 else:
202 if not util.findexe(method):
202 if not util.findexe(method):
203 raise util.Abort(_('%r specified as email transport, '
203 raise util.Abort(_('%r specified as email transport, '
204 'but not in PATH') % method)
204 'but not in PATH') % method)
205
205
206 def mimetextpatch(s, subtype='plain', display=False):
206 def mimetextpatch(s, subtype='plain', display=False):
207 '''Return MIME message suitable for a patch.
207 '''Return MIME message suitable for a patch.
208 Charset will be detected as utf-8 or (possibly fake) us-ascii.
208 Charset will be detected as utf-8 or (possibly fake) us-ascii.
209 Transfer encodings will be used if necessary.'''
209 Transfer encodings will be used if necessary.'''
210
210
211 cs = 'us-ascii'
211 cs = 'us-ascii'
212 if not display:
212 if not display:
213 try:
213 try:
214 s.decode('us-ascii')
214 s.decode('us-ascii')
215 except UnicodeDecodeError:
215 except UnicodeDecodeError:
216 try:
216 try:
217 s.decode('utf-8')
217 s.decode('utf-8')
218 cs = 'utf-8'
218 cs = 'utf-8'
219 except UnicodeDecodeError:
219 except UnicodeDecodeError:
220 # We'll go with us-ascii as a fallback.
220 # We'll go with us-ascii as a fallback.
221 pass
221 pass
222
222
223 return mimetextqp(s, subtype, cs)
223 return mimetextqp(s, subtype, cs)
224
224
225 def mimetextqp(body, subtype, charset):
225 def mimetextqp(body, subtype, charset):
226 '''Return MIME message.
226 '''Return MIME message.
227 Quoted-printable transfer encoding will be used if necessary.
227 Quoted-printable transfer encoding will be used if necessary.
228 '''
228 '''
229 enc = None
229 enc = None
230 for line in body.splitlines():
230 for line in body.splitlines():
231 if len(line) > 950:
231 if len(line) > 950:
232 body = quopri.encodestring(body)
232 body = quopri.encodestring(body)
233 enc = "quoted-printable"
233 enc = "quoted-printable"
234 break
234 break
235
235
236 msg = email.MIMEText.MIMEText(body, subtype, charset)
236 msg = email.MIMEText.MIMEText(body, subtype, charset)
237 if enc:
237 if enc:
238 del msg['Content-Transfer-Encoding']
238 del msg['Content-Transfer-Encoding']
239 msg['Content-Transfer-Encoding'] = enc
239 msg['Content-Transfer-Encoding'] = enc
240 return msg
240 return msg
241
241
242 def _charsets(ui):
242 def _charsets(ui):
243 '''Obtains charsets to send mail parts not containing patches.'''
243 '''Obtains charsets to send mail parts not containing patches.'''
244 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
244 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
245 fallbacks = [encoding.fallbackencoding.lower(),
245 fallbacks = [encoding.fallbackencoding.lower(),
246 encoding.encoding.lower(), 'utf-8']
246 encoding.encoding.lower(), 'utf-8']
247 for cs in fallbacks: # find unique charsets while keeping order
247 for cs in fallbacks: # find unique charsets while keeping order
248 if cs not in charsets:
248 if cs not in charsets:
249 charsets.append(cs)
249 charsets.append(cs)
250 return [cs for cs in charsets if not cs.endswith('ascii')]
250 return [cs for cs in charsets if not cs.endswith('ascii')]
251
251
252 def _encode(ui, s, charsets):
252 def _encode(ui, s, charsets):
253 '''Returns (converted) string, charset tuple.
253 '''Returns (converted) string, charset tuple.
254 Finds out best charset by cycling through sendcharsets in descending
254 Finds out best charset by cycling through sendcharsets in descending
255 order. Tries both encoding and fallbackencoding for input. Only as
255 order. Tries both encoding and fallbackencoding for input. Only as
256 last resort send as is in fake ascii.
256 last resort send as is in fake ascii.
257 Caveat: Do not use for mail parts containing patches!'''
257 Caveat: Do not use for mail parts containing patches!'''
258 try:
258 try:
259 s.decode('ascii')
259 s.decode('ascii')
260 except UnicodeDecodeError:
260 except UnicodeDecodeError:
261 sendcharsets = charsets or _charsets(ui)
261 sendcharsets = charsets or _charsets(ui)
262 for ics in (encoding.encoding, encoding.fallbackencoding):
262 for ics in (encoding.encoding, encoding.fallbackencoding):
263 try:
263 try:
264 u = s.decode(ics)
264 u = s.decode(ics)
265 except UnicodeDecodeError:
265 except UnicodeDecodeError:
266 continue
266 continue
267 for ocs in sendcharsets:
267 for ocs in sendcharsets:
268 try:
268 try:
269 return u.encode(ocs), ocs
269 return u.encode(ocs), ocs
270 except UnicodeEncodeError:
270 except UnicodeEncodeError:
271 pass
271 pass
272 except LookupError:
272 except LookupError:
273 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
273 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
274 # if ascii, or all conversion attempts fail, send (broken) ascii
274 # if ascii, or all conversion attempts fail, send (broken) ascii
275 return s, 'us-ascii'
275 return s, 'us-ascii'
276
276
277 def headencode(ui, s, charsets=None, display=False):
277 def headencode(ui, s, charsets=None, display=False):
278 '''Returns RFC-2047 compliant header from given string.'''
278 '''Returns RFC-2047 compliant header from given string.'''
279 if not display:
279 if not display:
280 # split into words?
280 # split into words?
281 s, cs = _encode(ui, s, charsets)
281 s, cs = _encode(ui, s, charsets)
282 return str(email.Header.Header(s, cs))
282 return str(email.Header.Header(s, cs))
283 return s
283 return s
284
284
285 def _addressencode(ui, name, addr, charsets=None):
285 def _addressencode(ui, name, addr, charsets=None):
286 name = headencode(ui, name, charsets)
286 name = headencode(ui, name, charsets)
287 try:
287 try:
288 acc, dom = addr.split('@')
288 acc, dom = addr.split('@')
289 acc = acc.encode('ascii')
289 acc = acc.encode('ascii')
290 dom = dom.decode(encoding.encoding).encode('idna')
290 dom = dom.decode(encoding.encoding).encode('idna')
291 addr = '%s@%s' % (acc, dom)
291 addr = '%s@%s' % (acc, dom)
292 except UnicodeDecodeError:
292 except UnicodeDecodeError:
293 raise util.Abort(_('invalid email address: %s') % addr)
293 raise util.Abort(_('invalid email address: %s') % addr)
294 except ValueError:
294 except ValueError:
295 try:
295 try:
296 # too strict?
296 # too strict?
297 addr = addr.encode('ascii')
297 addr = addr.encode('ascii')
298 except UnicodeDecodeError:
298 except UnicodeDecodeError:
299 raise util.Abort(_('invalid local address: %s') % addr)
299 raise util.Abort(_('invalid local address: %s') % addr)
300 return email.Utils.formataddr((name, addr))
300 return email.Utils.formataddr((name, addr))
301
301
302 def addressencode(ui, address, charsets=None, display=False):
302 def addressencode(ui, address, charsets=None, display=False):
303 '''Turns address into RFC-2047 compliant header.'''
303 '''Turns address into RFC-2047 compliant header.'''
304 if display or not address:
304 if display or not address:
305 return address or ''
305 return address or ''
306 name, addr = email.Utils.parseaddr(address)
306 name, addr = email.Utils.parseaddr(address)
307 return _addressencode(ui, name, addr, charsets)
307 return _addressencode(ui, name, addr, charsets)
308
308
309 def addrlistencode(ui, addrs, charsets=None, display=False):
309 def addrlistencode(ui, addrs, charsets=None, display=False):
310 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
310 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
311 A single element of input list may contain multiple addresses, but output
311 A single element of input list may contain multiple addresses, but output
312 always has one address per item'''
312 always has one address per item'''
313 if display:
313 if display:
314 return [a.strip() for a in addrs if a.strip()]
314 return [a.strip() for a in addrs if a.strip()]
315
315
316 result = []
316 result = []
317 for name, addr in email.Utils.getaddresses(addrs):
317 for name, addr in email.Utils.getaddresses(addrs):
318 if name or addr:
318 if name or addr:
319 result.append(_addressencode(ui, name, addr, charsets))
319 result.append(_addressencode(ui, name, addr, charsets))
320 return result
320 return result
321
321
322 def mimeencode(ui, s, charsets=None, display=False):
322 def mimeencode(ui, s, charsets=None, display=False):
323 '''creates mime text object, encodes it if needed, and sets
323 '''creates mime text object, encodes it if needed, and sets
324 charset and transfer-encoding accordingly.'''
324 charset and transfer-encoding accordingly.'''
325 cs = 'us-ascii'
325 cs = 'us-ascii'
326 if not display:
326 if not display:
327 s, cs = _encode(ui, s, charsets)
327 s, cs = _encode(ui, s, charsets)
328 return mimetextqp(s, 'plain', cs)
328 return mimetextqp(s, 'plain', cs)
General Comments 0
You need to be logged in to leave comments. Login now