annotate mercurial/mail.py @ 39809:858fe9625dab

mail: fix _encode to be more correct on Python 3 This code appears to be on the wrong side of the law in Python 2, at least some of the time. In Python 3, it's definitely wrong in places, but fortunately that's easy to fix. Differential Revision: https://phab.mercurial-scm.org/D3953
author Augie Fackler <augie@google.com>
date Mon, 16 Jul 2018 17:48:03 -0400 (2018-07-16)
parents 7b12a2d2eedc
children eabdf3c25b8b
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
1 # mail.py - mail sending bits for mercurial
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
2 #
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
4 #
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 7948
diff changeset
5 # This software may be used and distributed according to the terms of the
10263
25e572394f5c Update license to GPLv2+
Matt Mackall <mpm@selenic.com>
parents: 9715
diff changeset
6 # GNU General Public License version 2 or any later version.
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
7
31104
f6369544bf85 mail: do not print(), use ui.debug() instead
Yuya Nishihara <yuya@tcha.org>
parents: 30868
diff changeset
8 from __future__ import absolute_import
26541
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
9
19790
53f16f4aff33 mail: correct import of email module
Augie Fackler <raf@durin42.com>
parents: 19050
diff changeset
10 import email
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
11 import email.charset
30851
87b8e40eb812 mail: handle renamed email.Header
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
12 import email.header
35093
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
13 import email.message
39118
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
14 import email.parser
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
15 import io
26541
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
16 import os
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
17 import smtplib
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
18 import socket
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
19 import time
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
20
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
21 from .i18n import _
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
22 from . import (
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
23 encoding,
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
24 error,
36833
1407c42b302c py3: pass system string to email.message.Message.set_type()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35934
diff changeset
25 pycompat,
26541
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
26 sslutil,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
27 util,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
28 )
37870
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
29 from .utils import (
37906
a8a902d7176e procutil: bulk-replace function calls to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 37870
diff changeset
30 procutil,
37870
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
31 stringutil,
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
32 )
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
33
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
34 class STARTTLS(smtplib.SMTP):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
35 '''Derived class to verify the peer certificate for STARTTLS.
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
36
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
37 This class allows to pass any keyword arguments to SSL socket creation.
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
38 '''
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
39 def __init__(self, ui, host=None, **kwargs):
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
40 smtplib.SMTP.__init__(self, **kwargs)
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
41 self._ui = ui
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
42 self._host = host
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
43
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
44 def starttls(self, keyfile=None, certfile=None):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
45 if not self.has_extn("starttls"):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
46 msg = "STARTTLS extension not supported by server"
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
47 raise smtplib.SMTPException(msg)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
48 (resp, reply) = self.docmd("STARTTLS")
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
49 if resp == 220:
25963
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25640
diff changeset
50 self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile,
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
51 ui=self._ui,
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
52 serverhostname=self._host)
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
53 self.file = smtplib.SSLFakeFile(self.sock)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
54 self.helo_resp = None
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
55 self.ehlo_resp = None
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
56 self.esmtp_features = {}
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
57 self.does_esmtp = 0
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
58 return (resp, reply)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17411
diff changeset
59
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
60 class SMTPS(smtplib.SMTP):
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
61 '''Derived class to verify the peer certificate for SMTPS.
18886
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
62
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
63 This class allows to pass any keyword arguments to SSL socket creation.
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
64 '''
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
65 def __init__(self, ui, keyfile=None, certfile=None, host=None,
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
66 **kwargs):
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
67 self.keyfile = keyfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
68 self.certfile = certfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
69 smtplib.SMTP.__init__(self, **kwargs)
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
70 self._host = host
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
71 self.default_port = smtplib.SMTP_SSL_PORT
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
72 self._ui = ui
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
73
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
74 def _get_socket(self, host, port, timeout):
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
75 if self.debuglevel > 0:
31104
f6369544bf85 mail: do not print(), use ui.debug() instead
Yuya Nishihara <yuya@tcha.org>
parents: 30868
diff changeset
76 self._ui.debug('connect: %r\n' % (host, port))
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
77 new_socket = socket.create_connection((host, port), timeout)
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
78 new_socket = sslutil.wrapsocket(new_socket,
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
79 self.keyfile, self.certfile,
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
80 ui=self._ui,
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
81 serverhostname=self._host)
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
82 self.file = smtplib.SSLFakeFile(new_socket)
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
83 return new_socket
18886
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
84
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
85 def _smtp(ui):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
86 '''build an smtp connection and return a function to send mail'''
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
87 local_hostname = ui.config('smtp', 'local_hostname')
34282
0407a51b9d8c codemod: register core configitems using a script
Jun Wu <quark@fb.com>
parents: 33058
diff changeset
88 tls = ui.config('smtp', 'tls')
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
89 # backward compatible: when tls = true, we use starttls.
37870
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
90 starttls = tls == 'starttls' or stringutil.parsebool(tls)
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
91 smtps = tls == 'smtps'
14966
194b043dfa51 mail: use safehasattr instead of hasattr
Augie Fackler <durin42@gmail.com>
parents: 14271
diff changeset
92 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
93 raise error.Abort(_("can't use TLS: Python SSL support not installed"))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
94 mailhost = ui.config('smtp', 'host')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
95 if not mailhost:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
96 raise error.Abort(_('smtp.host not configured - cannot send mail'))
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
97 if smtps:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
98 ui.note(_('(using smtps)\n'))
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
99 s = SMTPS(ui, local_hostname=local_hostname, host=mailhost)
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
100 elif starttls:
30001
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29998
diff changeset
101 s = STARTTLS(ui, local_hostname=local_hostname, host=mailhost)
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
102 else:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
103 s = smtplib.SMTP(local_hostname=local_hostname)
19050
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
104 if smtps:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
105 defaultport = 465
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
106 else:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
107 defaultport = 25
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
108 mailport = util.getport(ui.config('smtp', 'port', defaultport))
27418
a95c975f42e3 l10n: use %d instead of %s for numbers
timeless@mozdev.org
parents: 27313
diff changeset
109 ui.note(_('sending mail: smtp host %s, port %d\n') %
2964
26c8d37496c2 fix typo in mail.py
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 2929
diff changeset
110 (mailhost, mailport))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
111 s.connect(host=mailhost, port=mailport)
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
112 if starttls:
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
113 ui.note(_('(using starttls)\n'))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
114 s.ehlo()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
115 s.starttls()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
116 s.ehlo()
30035
63a3749147af mail: unsupport smtp.verifycert (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30001
diff changeset
117 if starttls or smtps:
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
118 ui.note(_('(verifying remote certificate)\n'))
30035
63a3749147af mail: unsupport smtp.verifycert (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30001
diff changeset
119 sslutil.validatesocket(s.sock)
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
120 username = ui.config('smtp', 'username')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
121 password = ui.config('smtp', 'password')
5753
4fba4fee0718 Patchbomb: Prompt password when using SMTP/TLS and no password in .hgrc.
Arun Thomas <arun.thomas@gmail.com>
parents: 5472
diff changeset
122 if username and not password:
4fba4fee0718 Patchbomb: Prompt password when using SMTP/TLS and no password in .hgrc.
Arun Thomas <arun.thomas@gmail.com>
parents: 5472
diff changeset
123 password = ui.getpass()
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
124 if username and password:
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
125 ui.note(_('(authenticating to mail server as %s)\n') %
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
126 (username))
9279
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
127 try:
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
128 s.login(username, password)
26214
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25999
diff changeset
129 except smtplib.SMTPException as inst:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
130 raise error.Abort(inst)
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
131
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
132 def send(sender, recipients, msg):
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
133 try:
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
134 return s.sendmail(sender, recipients, msg)
26214
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25999
diff changeset
135 except smtplib.SMTPRecipientsRefused as inst:
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
136 recipients = [r[1] for r in inst.recipients.values()]
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
137 raise error.Abort('\n' + '\n'.join(recipients))
26214
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25999
diff changeset
138 except smtplib.SMTPException as inst:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
139 raise error.Abort(inst)
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
140
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
141 return send
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
142
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
143 def _sendmail(ui, sender, recipients, msg):
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
144 '''send mail using sendmail.'''
34282
0407a51b9d8c codemod: register core configitems using a script
Jun Wu <quark@fb.com>
parents: 33058
diff changeset
145 program = ui.config('email', 'method')
37870
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
146 cmdline = '%s -f %s %s' % (program, stringutil.email(sender),
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36906
diff changeset
147 ' '.join(map(stringutil.email, recipients)))
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
148 ui.note(_('sending mail: %s\n') % cmdline)
38244
00e4bd97b095 procutil: always popen() in binary mode
Yuya Nishihara <yuya@tcha.org>
parents: 37906
diff changeset
149 fp = procutil.popen(cmdline, 'wb')
00e4bd97b095 procutil: always popen() in binary mode
Yuya Nishihara <yuya@tcha.org>
parents: 37906
diff changeset
150 fp.write(util.tonativeeol(msg))
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
151 ret = fp.close()
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
152 if ret:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
153 raise error.Abort('%s %s' % (
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
154 os.path.basename(program.split(None, 1)[0]),
38249
bbd240f81ac5 procutil: make explainexit() simply return a message (API)
Yuya Nishihara <yuya@tcha.org>
parents: 38244
diff changeset
155 procutil.explainexit(ret)))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
156
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
157 def _mbox(mbox, sender, recipients, msg):
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
158 '''write mails to mbox'''
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
159 fp = open(mbox, 'ab+')
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
160 # Should be time.asctime(), but Windows prints 2-characters day
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
161 # of month instead of one. Make them print the same thing.
35934
b45a9d121b53 py3: make sure the first argument of time.strftime() is str
Pulkit Goyal <7895pulkit@gmail.com>
parents: 35093
diff changeset
162 date = time.strftime(r'%a %b %d %H:%M:%S %Y', time.localtime())
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
163 fp.write('From %s %s\n' % (sender, date))
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
164 fp.write(msg)
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
165 fp.write('\n\n')
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
166 fp.close()
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
167
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
168 def connect(ui, mbox=None):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
169 '''make a mail connection. return a function to send mail.
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
170 call as sendmail(sender, list-of-recipients, msg).'''
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
171 if mbox:
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
172 open(mbox, 'wb').close()
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
173 return lambda s, r, m: _mbox(mbox, s, r, m)
34282
0407a51b9d8c codemod: register core configitems using a script
Jun Wu <quark@fb.com>
parents: 33058
diff changeset
174 if ui.config('email', 'method') == 'smtp':
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
175 return _smtp(ui)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
176 return lambda s, r, m: _sendmail(ui, s, r, m)
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
177
15561
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
178 def sendmail(ui, sender, recipients, msg, mbox=None):
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
179 send = connect(ui, mbox=mbox)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
180 return send(sender, recipients, msg)
4483
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
181
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
182 def validateconfig(ui):
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
183 '''determine if we have enough config data to try sending email.'''
34282
0407a51b9d8c codemod: register core configitems using a script
Jun Wu <quark@fb.com>
parents: 33058
diff changeset
184 method = ui.config('email', 'method')
4483
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
185 if method == 'smtp':
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
186 if not ui.config('smtp', 'host'):
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
187 raise error.Abort(_('smtp specified as email transport, '
4483
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
188 'but no smtp host configured'))
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
189 else:
37906
a8a902d7176e procutil: bulk-replace function calls to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 37870
diff changeset
190 if not procutil.findexe(method):
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
191 raise error.Abort(_('%r specified as email transport, '
4483
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
192 'but not in PATH') % method)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
193
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
194 def codec2iana(cs):
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
195 ''''''
36905
6ea7f1c10c81 py3: cast character set to bytes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36904
diff changeset
196 cs = pycompat.sysbytes(email.charset.Charset(cs).input_charset.lower())
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
197
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
198 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1"
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
199 if cs.startswith("iso") and not cs.startswith("iso-"):
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
200 return "iso-" + cs[3:]
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
201 return cs
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
202
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
203 def mimetextpatch(s, subtype='plain', display=False):
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
204 '''Return MIME message suitable for a patch.
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
205 Charset will be detected by first trying to decode as us-ascii, then utf-8,
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
206 and finally the global encodings. If all those fail, fall back to
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
207 ISO-8859-1, an encoding with that allows all byte sequences.
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
208 Transfer encodings will be used if necessary.'''
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
209
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
210 cs = ['us-ascii', 'utf-8', encoding.encoding, encoding.fallbackencoding]
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
211 if display:
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
212 return mimetextqp(s, subtype, 'us-ascii')
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
213 for charset in cs:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
214 try:
36904
9e47bfbeb723 py3: cast decode() argument to system string
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36833
diff changeset
215 s.decode(pycompat.sysstr(charset))
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
216 return mimetextqp(s, subtype, codec2iana(charset))
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
217 except UnicodeDecodeError:
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
218 pass
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
219
30868
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30851
diff changeset
220 return mimetextqp(s, subtype, "iso-8859-1")
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
221
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
222 def mimetextqp(body, subtype, charset):
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
223 '''Return MIME message.
17407
e7cfe3587ea4 fix trivial spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 15562
diff changeset
224 Quoted-printable transfer encoding will be used if necessary.
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
225 '''
35093
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
226 cs = email.charset.Charset(charset)
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
227 msg = email.message.Message()
36833
1407c42b302c py3: pass system string to email.message.Message.set_type()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35934
diff changeset
228 msg.set_type(pycompat.sysstr('text/' + subtype))
35093
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
229
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
230 for line in body.splitlines():
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
231 if len(line) > 950:
35093
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
232 cs.body_encoding = email.charset.QP
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
233 break
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
234
35093
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
235 msg.set_payload(body, cs)
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 34282
diff changeset
236
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
237 return msg
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
238
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
239 def _charsets(ui):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
240 '''Obtains charsets to send mail parts not containing patches.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
241 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
242 fallbacks = [encoding.fallbackencoding.lower(),
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
243 encoding.encoding.lower(), 'utf-8']
8343
6fbbb90261b1 mail: updated comment
Martin Geisler <mg@lazybytes.net>
parents: 8332
diff changeset
244 for cs in fallbacks: # find unique charsets while keeping order
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
245 if cs not in charsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
246 charsets.append(cs)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
247 return [cs for cs in charsets if not cs.endswith('ascii')]
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
248
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
249 def _encode(ui, s, charsets):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
250 '''Returns (converted) string, charset tuple.
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
251 Finds out best charset by cycling through sendcharsets in descending
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
252 order. Tries both encoding and fallbackencoding for input. Only as
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
253 last resort send as is in fake ascii.
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
254 Caveat: Do not use for mail parts containing patches!'''
39809
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
255 sendcharsets = charsets or _charsets(ui)
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
256 if not isinstance(s, bytes):
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
257 # We have unicode data, which we need to try and encode to
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
258 # some reasonable-ish encoding. Try the encodings the user
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
259 # wants, and fall back to garbage-in-ascii.
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
260 for ocs in sendcharsets:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
261 try:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
262 return s.encode(pycompat.sysstr(ocs)), ocs
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
263 except UnicodeEncodeError:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
264 pass
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
265 except LookupError:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
266 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
267 else:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
268 # Everything failed, ascii-armor what we've got and send it.
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
269 return s.encode('ascii', 'backslashreplace')
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
270 # We have a bytes of unknown encoding. We'll try and guess a valid
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
271 # encoding, falling back to pretending we had ascii even though we
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
272 # know that's wrong.
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
273 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
274 s.decode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
275 except UnicodeDecodeError:
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
276 for ics in (encoding.encoding, encoding.fallbackencoding):
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
277 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
278 u = s.decode(ics)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
279 except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
280 continue
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
281 for ocs in sendcharsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
282 try:
39809
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 39118
diff changeset
283 return u.encode(pycompat.sysstr(ocs)), ocs
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
284 except UnicodeEncodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
285 pass
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
286 except LookupError:
7195
9fabcb1fe68d mail: correct typo in variable name
Christian Ebert <blacktrash@gmx.net>
parents: 7191
diff changeset
287 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
288 # if ascii, or all conversion attempts fail, send (broken) ascii
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
289 return s, 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
290
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
291 def headencode(ui, s, charsets=None, display=False):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
292 '''Returns RFC-2047 compliant header from given string.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
293 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
294 # split into words?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
295 s, cs = _encode(ui, s, charsets)
30851
87b8e40eb812 mail: handle renamed email.Header
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
296 return str(email.header.Header(s, cs))
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
297 return s
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
298
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
299 def _addressencode(ui, name, addr, charsets=None):
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
300 name = headencode(ui, name, charsets)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
301 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
302 acc, dom = addr.split('@')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
303 acc = acc.encode('ascii')
9715
f0e99a2eac76 patchbomb: fix handling of email addresses with Unicode domains (IDNA)
Marti Raudsepp <marti@juffo.org>
parents: 9279
diff changeset
304 dom = dom.decode(encoding.encoding).encode('idna')
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
305 addr = '%s@%s' % (acc, dom)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
306 except UnicodeDecodeError:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
307 raise error.Abort(_('invalid email address: %s') % addr)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
308 except ValueError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
309 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
310 # too strict?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
311 addr = addr.encode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
312 except UnicodeDecodeError:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
313 raise error.Abort(_('invalid local address: %s') % addr)
36906
54dfb65e2f82 mail: import email.utils not email.Utils
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36905
diff changeset
314 return email.utils.formataddr((name, addr))
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
315
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
316 def addressencode(ui, address, charsets=None, display=False):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
317 '''Turns address into RFC-2047 compliant header.'''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
318 if display or not address:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
319 return address or ''
36906
54dfb65e2f82 mail: import email.utils not email.Utils
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36905
diff changeset
320 name, addr = email.utils.parseaddr(address)
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
321 return _addressencode(ui, name, addr, charsets)
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
322
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
323 def addrlistencode(ui, addrs, charsets=None, display=False):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
324 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
325 A single element of input list may contain multiple addresses, but output
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
326 always has one address per item'''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
327 if display:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
328 return [a.strip() for a in addrs if a.strip()]
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
329
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
330 result = []
36906
54dfb65e2f82 mail: import email.utils not email.Utils
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36905
diff changeset
331 for name, addr in email.utils.getaddresses(addrs):
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
332 if name or addr:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
333 result.append(_addressencode(ui, name, addr, charsets))
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
334 return result
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
335
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
336 def mimeencode(ui, s, charsets=None, display=False):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
337 '''creates mime text object, encodes it if needed, and sets
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
338 charset and transfer-encoding accordingly.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
339 cs = 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
340 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
341 s, cs = _encode(ui, s, charsets)
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
342 return mimetextqp(s, 'plain', cs)
29012
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
343
39118
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
344 if pycompat.ispy3:
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
345 def parse(fp):
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
346 ep = email.parser.Parser()
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
347 # disable the "universal newlines" mode, which isn't binary safe.
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
348 # I have no idea if ascii/surrogateescape is correct, but that's
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
349 # what the standard Python email parser does.
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
350 fp = io.TextIOWrapper(fp, encoding=r'ascii',
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
351 errors=r'surrogateescape', newline=chr(10))
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
352 try:
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
353 return ep.parse(fp)
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
354 finally:
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
355 fp.detach()
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
356 else:
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
357 def parse(fp):
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
358 ep = email.parser.Parser()
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
359 return ep.parse(fp)
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 38255
diff changeset
360
29012
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
361 def headdecode(s):
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
362 '''Decodes RFC-2047 header'''
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
363 uparts = []
30851
87b8e40eb812 mail: handle renamed email.Header
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
364 for part, charset in email.header.decode_header(s):
29012
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
365 if charset is not None:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
366 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
367 uparts.append(part.decode(charset))
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
368 continue
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
369 except UnicodeDecodeError:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
370 pass
38255
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 38249
diff changeset
371 # On Python 3, decode_header() may return either bytes or unicode
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 38249
diff changeset
372 # depending on whether the header has =?<charset>? or not
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 38249
diff changeset
373 if isinstance(part, type(u'')):
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 38249
diff changeset
374 uparts.append(part)
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 38249
diff changeset
375 continue
29012
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
376 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
377 uparts.append(part.decode('UTF-8'))
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
378 continue
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
379 except UnicodeDecodeError:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
380 pass
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
381 uparts.append(part.decode('ISO-8859-1'))
32226
067add650129 encoding: factor out unicode variants of from/tolocal()
Yuya Nishihara <yuya@tcha.org>
parents: 31104
diff changeset
382 return encoding.unitolocal(u' '.join(uparts))