annotate mercurial/mail.py @ 51492:2e8a88e5809f

branchcache: stop writing more branchcache file on disk than needed Before this change, we were unconditionally writing a branchmap file for the filter level passed to `update_disk`. This is actually counter productive if no update were needed for this filter level. In many case, the branch cache for a filter level is identical to its parent "subset" and it is better to simply keep the subset update and reuse it every time instead of having to do identical work for similar subset. So we change the `update_disk` method to only write a file when that filter level differ from its parent. This removes many cases where identical files were written, requiring multiple boring update in the test suite. The only notable changes is the change to `test-strip-branch-cache.t`, this case was checking a scenario that no longer reproduce the bug as writing less branchmap file result in less stalled cache on disk. Strictly speaking, we could create a more convoluted scenario that create a similar issue. However the next changeset would also cover that scenario so we directly updated that test case to a "no longer buggy" state.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Sun, 10 Mar 2024 04:53:17 +0100
parents f4a0806081f2
children f4733654f144
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2889
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 #
46819
d4ba4d51f85f contributor: change mentions of mpm to olivia
Raphaël Gomès <rgomes@octobus.net>
parents: 46683
diff changeset
3 # Copyright 2006 Olivia Mackall <olivia@selenic.com>
2889
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.
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
7
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
8
19790
53f16f4aff33 mail: correct import of email module
Augie Fackler <raf@durin42.com>
parents: 19050
diff changeset
9 import email
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
10 import email.charset
43156
0e6a7ce81dde py3: use email.generator.BytesGenerator in patch.split()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43117
diff changeset
11 import email.generator
30072
87b8e40eb812 mail: handle renamed email.Header
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29285
diff changeset
12 import email.header
34310
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
13 import email.message
38332
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 37469
diff changeset
14 import email.parser
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 37469
diff changeset
15 import io
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
16 import os
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
17 import smtplib
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
18 import socket
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
19 import time
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
20
51285
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
21 from typing import (
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
22 Any,
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
23 List,
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
24 Optional,
51285
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
25 Tuple,
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
26 Union,
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
27 )
9d3721552b6c pytype: import typing directly
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
28
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
29 from .i18n import _
43089
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
30 from .pycompat import (
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
31 open,
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
32 )
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
33 from . import (
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
34 encoding,
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26098
diff changeset
35 error,
36047
1407c42b302c py3: pass system string to email.message.Message.set_type()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35151
diff changeset
36 pycompat,
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
37 sslutil,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
38 util,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
39 )
37084
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36120
diff changeset
40 from .utils import (
37120
a8a902d7176e procutil: bulk-replace function calls to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 37084
diff changeset
41 procutil,
37084
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36120
diff changeset
42 stringutil,
46907
ffd3e823a7e5 urlutil: extract `url` related code from `util` into the new module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 46819
diff changeset
43 urlutil,
37084
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36120
diff changeset
44 )
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
45
43625
8d9e2c2b6058 mail: add type hints for pytype
Denis Laxalde <denis@laxalde.org>
parents: 43624
diff changeset
46
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
47 class STARTTLS(smtplib.SMTP):
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
48 """Derived class to verify the peer certificate for STARTTLS.
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
49
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
50 This class allows to pass any keyword arguments to SSL socket creation.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
51 """
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
52
29251
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29248
diff changeset
53 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: 17428
diff changeset
54 smtplib.SMTP.__init__(self, **kwargs)
29248
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29227
diff changeset
55 self._ui = ui
28935
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 28341
diff changeset
56 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: 17428
diff changeset
57
50376
1c7453f2bb57 mail: add a missing argument to properly override starttls
Matt Harbison <matt_harbison@yahoo.com>
parents: 49051
diff changeset
58 def starttls(self, keyfile=None, certfile=None, context=None):
43169
3941e7063d03 py3: call SMTP.has_extn() with an str
Denis Laxalde <denis@laxalde.org>
parents: 43156
diff changeset
59 if not self.has_extn("starttls"):
50377
174290adfc4a mail: unbyteify the SMTPException message
Matt Harbison <matt_harbison@yahoo.com>
parents: 50376
diff changeset
60 msg = "STARTTLS extension not supported by server"
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
61 raise smtplib.SMTPException(msg)
43170
a1801ee97840 py3: call SMTP.docmd() with an str
Denis Laxalde <denis@laxalde.org>
parents: 43169
diff changeset
62 (resp, reply) = self.docmd("STARTTLS")
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
63 if resp == 220:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
64 self.sock = sslutil.wrapsocket(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
65 self.sock,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
66 keyfile,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
67 certfile,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
68 ui=self._ui,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
69 serverhostname=self._host,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
70 )
43171
54b06bec8914 py3: use socket.makefile() instead of dropped smtplib.SSLFakeFile
Denis Laxalde <denis@laxalde.org>
parents: 43170
diff changeset
71 self.file = self.sock.makefile("rb")
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
72 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: 17428
diff changeset
73 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: 17428
diff changeset
74 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: 17428
diff changeset
75 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: 17428
diff changeset
76 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: 17428
diff changeset
77
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
78
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
79 class SMTPS(smtplib.SMTP):
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
80 """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
81
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
82 This class allows to pass any keyword arguments to SSL socket creation.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
83 """
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
84
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
85 def __init__(self, ui, keyfile=None, certfile=None, host=None, **kwargs):
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
86 self.keyfile = keyfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
87 self.certfile = certfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
88 smtplib.SMTP.__init__(self, **kwargs)
28935
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 28341
diff changeset
89 self._host = host
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
90 self.default_port = smtplib.SMTP_SSL_PORT
29248
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29227
diff changeset
91 self._ui = ui
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
92
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
93 def _get_socket(self, host, port, timeout):
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
94 if self.debuglevel > 0:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
95 self._ui.debug(b'connect: %r\n' % ((host, port),))
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
96 new_socket = socket.create_connection((host, port), timeout)
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
97 new_socket = sslutil.wrapsocket(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
98 new_socket,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
99 self.keyfile,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
100 self.certfile,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
101 ui=self._ui,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
102 serverhostname=self._host,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
103 )
43506
9f70512ae2cf cleanup: remove pointless r-prefixes on single-quoted strings
Augie Fackler <augie@google.com>
parents: 43498
diff changeset
104 self.file = new_socket.makefile('rb')
26673
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 26587
diff changeset
105 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
106
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
107
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
108 def _pyhastls() -> bool:
39025
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
109 """Returns true iff Python has TLS support, false otherwise."""
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
110 try:
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
111 import ssl
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
112
39025
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
113 getattr(ssl, 'HAS_TLS', False)
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
114 return True
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
115 except ImportError:
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
116 return False
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
117
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
118
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
119 def _smtp(ui):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
120 '''build an smtp connection and return a function to send mail'''
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
121 local_hostname = ui.config(b'smtp', b'local_hostname')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
122 tls = ui.config(b'smtp', b'tls')
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
123 # backward compatible: when tls = true, we use starttls.
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
124 starttls = tls == b'starttls' or stringutil.parsebool(tls)
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
125 smtps = tls == b'smtps'
39025
569d662816de mail: modernize check for Python-with-TLS
Augie Fackler <augie@google.com>
parents: 39024
diff changeset
126 if (starttls or smtps) and not _pyhastls():
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
127 raise error.Abort(_(b"can't use TLS: Python SSL support not installed"))
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
128 mailhost = ui.config(b'smtp', b'host')
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
129 if not mailhost:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
130 raise error.Abort(_(b'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
131 if smtps:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
132 ui.note(_(b'(using smtps)\n'))
29251
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29248
diff changeset
133 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
134 elif starttls:
29251
31acc78c632a mail: remove use of sslkwargs
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29248
diff changeset
135 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
136 else:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
137 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
138 if smtps:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
139 defaultport = 465
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
140 else:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
141 defaultport = 25
46907
ffd3e823a7e5 urlutil: extract `url` related code from `util` into the new module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 46819
diff changeset
142 mailport = urlutil.getport(ui.config(b'smtp', b'port', defaultport))
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
143 ui.note(_(b'sending mail: smtp host %s, port %d\n') % (mailhost, mailport))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
144 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
145 if starttls:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
146 ui.note(_(b'(using starttls)\n'))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
147 s.ehlo()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
148 s.starttls()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
149 s.ehlo()
29285
63a3749147af mail: unsupport smtp.verifycert (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29251
diff changeset
150 if starttls or smtps:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
151 ui.note(_(b'(verifying remote certificate)\n'))
29285
63a3749147af mail: unsupport smtp.verifycert (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29251
diff changeset
152 sslutil.validatesocket(s.sock)
46991
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
153
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
154 try:
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
155 _smtp_login(ui, s, mailhost, mailport)
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
156 except smtplib.SMTPException as inst:
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
157 raise error.Abort(stringutil.forcebytestr(inst))
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
158
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
159 def send(sender, recipients, msg):
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
160 try:
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
161 return s.sendmail(sender, recipients, msg)
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
162 except smtplib.SMTPRecipientsRefused as inst:
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
163 recipients = [r[1] for r in inst.recipients.values()]
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
164 raise error.Abort(b'\n' + b'\n'.join(recipients))
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
165 except smtplib.SMTPException as inst:
46992
5fa019ceb499 mail: force SMTPException to bytes before wrapping in error.Abort
Matt Harbison <matt_harbison@yahoo.com>
parents: 46991
diff changeset
166 raise error.Abort(stringutil.forcebytestr(inst))
46991
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
167
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
168 return send
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
169
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
170
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
171 def _smtp_login(ui, smtp, mailhost, mailport):
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
172 """A hook for the keyring extension to perform the actual SMTP login.
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
173
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
174 An already connected SMTP object of the proper type is provided, based on
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
175 the current configuration. The host and port to which the connection was
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
176 established are provided for accessibility, since the SMTP object doesn't
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
177 provide an accessor. ``smtplib.SMTPException`` is raised on error.
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
178 """
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
179 username = ui.config(b'smtp', b'username')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
180 password = ui.config(b'smtp', b'password')
43172
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
181 if username:
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
182 if password:
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
183 password = encoding.strfromlocal(password)
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
184 else:
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
185 password = ui.getpass()
45929
87e7dd8e7734 ui: ensure `getpass()` returns bytes
Matt Harbison <matt_harbison@yahoo.com>
parents: 43825
diff changeset
186 if password is not None:
87e7dd8e7734 ui: ensure `getpass()` returns bytes
Matt Harbison <matt_harbison@yahoo.com>
parents: 43825
diff changeset
187 password = encoding.strfromlocal(password)
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
188 if username and password:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
189 ui.note(_(b'(authenticating to mail server as %s)\n') % username)
43172
9145abd8b96d py3: decode username and password before SMTP login
Denis Laxalde <denis@laxalde.org>
parents: 43171
diff changeset
190 username = encoding.strfromlocal(username)
46991
83c0d144ef8d mail: split out the SMTP login to allow the keyring extension to wrap it
Matt Harbison <matt_harbison@yahoo.com>
parents: 46982
diff changeset
191 smtp.login(username, password)
5947
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5866
diff changeset
192
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
193
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
194 def _sendmail(ui, sender, recipients, msg):
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
195 '''send mail using sendmail.'''
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
196 program = ui.config(b'email', b'method')
43096
813aa8cc55d4 patchbomb: use procutil.shellquote() instead of shlex to escape email address
Yuya Nishihara <yuya@tcha.org>
parents: 43089
diff changeset
197
813aa8cc55d4 patchbomb: use procutil.shellquote() instead of shlex to escape email address
Yuya Nishihara <yuya@tcha.org>
parents: 43089
diff changeset
198 def stremail(x):
813aa8cc55d4 patchbomb: use procutil.shellquote() instead of shlex to escape email address
Yuya Nishihara <yuya@tcha.org>
parents: 43089
diff changeset
199 return procutil.shellquote(stringutil.email(encoding.strtolocal(x)))
813aa8cc55d4 patchbomb: use procutil.shellquote() instead of shlex to escape email address
Yuya Nishihara <yuya@tcha.org>
parents: 43089
diff changeset
200
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
201 cmdline = b'%s -f %s %s' % (
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
202 program,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
203 stremail(sender),
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
204 b' '.join(map(stremail, recipients)),
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
205 )
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
206 ui.note(_(b'sending mail: %s\n') % cmdline)
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
207 fp = procutil.popen(cmdline, b'wb')
37458
00e4bd97b095 procutil: always popen() in binary mode
Yuya Nishihara <yuya@tcha.org>
parents: 37120
diff changeset
208 fp.write(util.tonativeeol(msg))
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
209 ret = fp.close()
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
210 if ret:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
211 raise error.Abort(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
212 b'%s %s'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
213 % (
43825
8f26dd09aa78 mail: use procutil.shellsplit instead of bytes.split to parse command
Julien Cristau <jcristau@debian.org>
parents: 43773
diff changeset
214 os.path.basename(procutil.shellsplit(program)[0]),
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
215 procutil.explainexit(ret),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
216 )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
217 )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
218
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
219
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
220 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: 14965
diff changeset
221 '''write mails to mbox'''
46997
106402245301 mail: add a TODO about proper mbox locking
Matt Harbison <matt_harbison@yahoo.com>
parents: 46992
diff changeset
222 # TODO: use python mbox library for proper locking
46982
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
223 with open(mbox, b'ab+') as fp:
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
224 # Should be time.asctime(), but Windows prints 2-characters day
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
225 # of month instead of one. Make them print the same thing.
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
226 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
227 fp.write(
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
228 b'From %s %s\n'
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
229 % (encoding.strtolocal(sender), encoding.strtolocal(date))
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
230 )
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
231 fp.write(msg)
d467bae86b2d mail: use a context manager when writing to mbox
Matt Harbison <matt_harbison@yahoo.com>
parents: 46907
diff changeset
232 fp.write(b'\n\n')
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
233
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
234
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
235 def connect(ui, mbox=None):
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
236 """make a mail connection. return a function to send mail.
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
237 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: 14965
diff changeset
238 if mbox:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
239 open(mbox, b'wb').close()
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
240 return lambda s, r, m: _mbox(mbox, s, r, m)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
241 if ui.config(b'email', b'method') == b'smtp':
5947
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5866
diff changeset
242 return _smtp(ui)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
243 return lambda s, r, m: _sendmail(ui, s, r, m)
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
244
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
245
15561
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
246 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
247 send = connect(ui, mbox=mbox)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
248 return send(sender, recipients, msg)
4489
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
249
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
250
4489
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
251 def validateconfig(ui):
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
252 '''determine if we have enough config data to try sending email.'''
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
253 method = ui.config(b'email', b'method')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
254 if method == b'smtp':
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
255 if not ui.config(b'smtp', b'host'):
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
256 raise error.Abort(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
257 _(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
258 b'smtp specified as email transport, '
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
259 b'but no smtp host configured'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
260 )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
261 )
4489
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
262 else:
49051
1d6c6ad645e1 mail: don't complain about a multi-word email.method
Julien Cristau <jcristau@debian.org>
parents: 48883
diff changeset
263 command = procutil.shellsplit(method)
1d6c6ad645e1 mail: don't complain about a multi-word email.method
Julien Cristau <jcristau@debian.org>
parents: 48883
diff changeset
264 command = command[0] if command else b''
1d6c6ad645e1 mail: don't complain about a multi-word email.method
Julien Cristau <jcristau@debian.org>
parents: 48883
diff changeset
265 if not (command and procutil.findexe(command)):
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
266 raise error.Abort(
49051
1d6c6ad645e1 mail: don't complain about a multi-word email.method
Julien Cristau <jcristau@debian.org>
parents: 48883
diff changeset
267 _(b'%r specified as email transport, but not in PATH') % command
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
268 )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
269
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
270
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
271 def codec2iana(cs: str) -> str:
47062
f38bf44e077f black: make codebase compatible with black v21.4b2 and v20.8b1
Kyle Lippincott <spectral@google.com>
parents: 46997
diff changeset
272 ''' '''
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
273 cs = email.charset.Charset(cs).input_charset.lower()
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
274
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
275 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1"
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
276 if cs.startswith("iso") and not cs.startswith("iso-"):
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
277 return "iso-" + cs[3:]
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
278 return cs
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
279
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
280
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
281 def mimetextpatch(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
282 s: bytes,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
283 subtype: str = 'plain',
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
284 display: bool = False,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
285 ) -> email.message.Message:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
286 """Return MIME message suitable for a patch.
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
287 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: 30072
diff changeset
288 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: 30072
diff changeset
289 ISO-8859-1, an encoding with that allows all byte sequences.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
290 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
291
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
292 cs = [
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
293 'us-ascii',
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
294 'utf-8',
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
295 pycompat.sysstr(encoding.encoding),
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
296 pycompat.sysstr(encoding.fallbackencoding),
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
297 ]
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
298 if display:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
299 cs = ['us-ascii']
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
300 for charset in cs:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
301 try:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
302 s.decode(charset)
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
303 return mimetextqp(s, subtype, codec2iana(charset))
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
304 except UnicodeDecodeError:
30089
040f23ed6963 mail: take --encoding and HGENCODING into account
Gábor Stefanik <gabor.stefanik@nng.com>
parents: 30072
diff changeset
305 pass
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
306
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
307 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
308
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
309
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
310 def mimetextqp(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
311 body: bytes, subtype: str, charset: str
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
312 ) -> email.message.Message:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
313 """Return MIME message.
17424
e7cfe3587ea4 fix trivial spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 15562
diff changeset
314 Quoted-printable transfer encoding will be used if necessary.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
315 """
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
316 cs = email.charset.Charset(charset)
34310
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
317 msg = email.message.Message()
43627
af3e341dbf03 mail: use a native string for "subtype" value
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43626
diff changeset
318 msg.set_type('text/' + subtype)
34310
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
319
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
320 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
321 if len(line) > 950:
34310
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
322 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
323 break
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
324
41405
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
325 # On Python 2, this simply assigns a value. Python 3 inspects
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
326 # body and does different things depending on whether it has
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
327 # encode() or decode() attributes. We can get the old behavior
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
328 # if we pass a str and charset is None and we call set_charset().
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
329 # But we may get into trouble later due to Python attempting to
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
330 # encode/decode using the registered charset (or attempting to
9b3be572ff0c mail: document behavior of Python 3
Gregory Szorc <gregory.szorc@gmail.com>
parents: 39108
diff changeset
331 # use ascii in the absence of a charset).
34310
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
332 msg.set_payload(body, cs)
2d0c306a88c2 mail: encode long unicode lines in emails properly (issue5687)
Igor Ippolitov <iippolitov@gmail.com>
parents: 33499
diff changeset
333
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
334 return msg
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
335
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
336
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
337 def _charsets(ui: Any) -> List[str]:
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
338 '''Obtains charsets to send mail parts not containing patches.'''
43625
8d9e2c2b6058 mail: add type hints for pytype
Denis Laxalde <denis@laxalde.org>
parents: 43624
diff changeset
339 charsets = [
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
340 pycompat.sysstr(cs.lower())
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
341 for cs in ui.configlist(b'email', b'charsets')
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
342 ]
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
343 fallbacks = [
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
344 pycompat.sysstr(encoding.fallbackencoding.lower()),
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
345 pycompat.sysstr(encoding.encoding.lower()),
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
346 'utf-8',
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
347 ]
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
348 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: 6548
diff changeset
349 if cs not in charsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
350 charsets.append(cs)
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
351 return [cs for cs in charsets if not cs.endswith('ascii')]
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
352
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
353
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
354 def _encode(ui: Any, s: bytes, charsets: List[str]) -> Tuple[bytes, str]:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
355 """Returns (converted) string, charset tuple.
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
356 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
357 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: 6548
diff changeset
358 last resort send as is in fake ascii.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
359 Caveat: Do not use for mail parts containing patches!"""
39023
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
360 sendcharsets = charsets or _charsets(ui)
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
361 if not isinstance(s, bytes):
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
362 # 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: 38332
diff changeset
363 # 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: 38332
diff changeset
364 # 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: 38332
diff changeset
365 for ocs in sendcharsets:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
366 try:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
367 return s.encode(ocs), ocs
39023
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
368 except UnicodeEncodeError:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
369 pass
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
370 except LookupError:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
371 ui.warn(
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
372 _(b'ignoring invalid sendcharset: %s\n')
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
373 % pycompat.sysbytes(ocs)
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
374 )
39023
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
375 else:
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
376 # Everything failed, ascii-armor what we've got and send it.
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
377 return s.encode('ascii', 'backslashreplace'), 'us-ascii'
39023
858fe9625dab mail: fix _encode to be more correct on Python 3
Augie Fackler <augie@google.com>
parents: 38332
diff changeset
378 # 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: 38332
diff changeset
379 # 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: 38332
diff changeset
380 # know that's wrong.
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
381 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
382 s.decode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
383 except UnicodeDecodeError:
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
384 for ics in (encoding.encoding, encoding.fallbackencoding):
43330
910827a2cb20 py3: decode encoding literal before passing to .decode()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43326
diff changeset
385 ics = pycompat.sysstr(ics)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
386 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
387 u = s.decode(ics)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
388 except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
389 continue
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
390 for ocs in sendcharsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
391 try:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
392 return u.encode(ocs), ocs
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
393 except UnicodeEncodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
394 pass
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
395 except LookupError:
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
396 ui.warn(
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
397 _(b'ignoring invalid sendcharset: %s\n')
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
398 % pycompat.sysbytes(ocs)
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
399 )
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
400 # if ascii, or all conversion attempts fail, send (broken) ascii
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
401 return s, 'us-ascii'
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
402
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
403
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
404 def headencode(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
405 ui: Any,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
406 s: Union[bytes, str],
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
407 charsets: Optional[List[str]] = None,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
408 display: bool = False,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
409 ) -> str:
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
410 '''Returns RFC-2047 compliant header from given string.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
411 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
412 # split into words?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
413 s, cs = _encode(ui, s, charsets)
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
414 return email.header.Header(s, cs).encode()
43575
67b4439c09b2 mail: let headencode() return a native string
Denis Laxalde <denis@laxalde.org>
parents: 43506
diff changeset
415 return encoding.strfromlocal(s)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
416
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
417
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
418 def _addressencode(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
419 ui: Any, name: str, addr: str, charsets: Optional[List[str]] = None
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
420 ) -> str:
43628
ddb5d097d561 mail: move strtolocal call in _addressencode()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43627
diff changeset
421 addr = encoding.strtolocal(addr)
43575
67b4439c09b2 mail: let headencode() return a native string
Denis Laxalde <denis@laxalde.org>
parents: 43506
diff changeset
422 name = headencode(ui, name, charsets)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
423 try:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
424 acc, dom = addr.split(b'@')
39107
c2327bb3505d mail: call s.decode('ascii') explicitly to see if s is an ascii bytes
Yuya Nishihara <yuya@tcha.org>
parents: 39106
diff changeset
425 acc.decode('ascii')
39108
d2d89d31cbb5 mail: convert encoding.encoding to sysstr
Yuya Nishihara <yuya@tcha.org>
parents: 39107
diff changeset
426 dom = dom.decode(pycompat.sysstr(encoding.encoding)).encode('idna')
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
427 addr = b'%s@%s' % (acc, dom)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
428 except UnicodeDecodeError:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
429 raise error.Abort(_(b'invalid email address: %s') % addr)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
430 except ValueError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
431 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
432 # too strict?
39107
c2327bb3505d mail: call s.decode('ascii') explicitly to see if s is an ascii bytes
Yuya Nishihara <yuya@tcha.org>
parents: 39106
diff changeset
433 addr.decode('ascii')
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
434 except UnicodeDecodeError:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
435 raise error.Abort(_(b'invalid local address: %s') % addr)
43576
14b96072797d mail: let addressencode() / addrlistencode() return native strings
Denis Laxalde <denis@laxalde.org>
parents: 43575
diff changeset
436 return email.utils.formataddr((name, encoding.strfromlocal(addr)))
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
437
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
438
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
439 def addressencode(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
440 ui: Any,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
441 address: bytes,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
442 charsets: Optional[List[str]] = None,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
443 display: bool = False,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
444 ) -> str:
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
445 '''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
446 if display or not address:
43576
14b96072797d mail: let addressencode() / addrlistencode() return native strings
Denis Laxalde <denis@laxalde.org>
parents: 43575
diff changeset
447 return encoding.strfromlocal(address or b'')
39024
eabdf3c25b8b mail: cope with Py3 unicode antics on email addresses
Augie Fackler <augie@google.com>
parents: 39023
diff changeset
448 name, addr = email.utils.parseaddr(encoding.strfromlocal(address))
43628
ddb5d097d561 mail: move strtolocal call in _addressencode()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43627
diff changeset
449 return _addressencode(ui, name, addr, charsets)
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
450
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
451
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
452 def addrlistencode(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
453 ui: Any,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
454 addrs: List[bytes],
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
455 charsets: Optional[List[str]] = None,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
456 display: bool = False,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
457 ) -> List[str]:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
458 """Turns a list of addresses into a list of RFC-2047 compliant headers.
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
459 A single element of input list may contain multiple addresses, but output
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
460 always has one address per item"""
43577
599e25add437 mail: convert addr to str early in addrlistencode()
Denis Laxalde <denis@laxalde.org>
parents: 43576
diff changeset
461 straddrs = []
39024
eabdf3c25b8b mail: cope with Py3 unicode antics on email addresses
Augie Fackler <augie@google.com>
parents: 39023
diff changeset
462 for a in addrs:
43506
9f70512ae2cf cleanup: remove pointless r-prefixes on single-quoted strings
Augie Fackler <augie@google.com>
parents: 43498
diff changeset
463 assert isinstance(a, bytes), '%r unexpectedly not a bytestr' % a
43577
599e25add437 mail: convert addr to str early in addrlistencode()
Denis Laxalde <denis@laxalde.org>
parents: 43576
diff changeset
464 straddrs.append(encoding.strfromlocal(a))
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
465 if display:
43577
599e25add437 mail: convert addr to str early in addrlistencode()
Denis Laxalde <denis@laxalde.org>
parents: 43576
diff changeset
466 return [a.strip() for a in straddrs if a.strip()]
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
467
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
468 result = []
43577
599e25add437 mail: convert addr to str early in addrlistencode()
Denis Laxalde <denis@laxalde.org>
parents: 43576
diff changeset
469 for name, addr in email.utils.getaddresses(straddrs):
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
470 if name or addr:
43628
ddb5d097d561 mail: move strtolocal call in _addressencode()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43627
diff changeset
471 r = _addressencode(ui, name, addr, charsets)
39106
ebf54a34b7b7 mail: pass in addr to _addressencode() in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 39105
diff changeset
472 result.append(r)
39105
f68ad9b4a43b mail: remove redundant bytesurl() from addrlistencode()
Yuya Nishihara <yuya@tcha.org>
parents: 39039
diff changeset
473 return result
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
474
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
475
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
476 def mimeencode(
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
477 ui: Any,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
478 s: bytes,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
479 charsets: Optional[List[str]] = None,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
480 display: bool = False,
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
481 ) -> email.message.Message:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
482 """creates mime text object, encodes it if needed, and sets
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 45929
diff changeset
483 charset and transfer-encoding accordingly."""
43626
bdb0ddab7bb3 mail: let all charset values be native strings
Denis Laxalde <denis@laxalde.org>
parents: 43625
diff changeset
484 cs = 'us-ascii'
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
485 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
486 s, cs = _encode(ui, s, charsets)
43627
af3e341dbf03 mail: use a native string for "subtype" value
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43626
diff changeset
487 return mimetextqp(s, 'plain', cs)
28341
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
488
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
489
48883
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
490 Generator = email.generator.BytesGenerator
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
491
43353
fdc3af52305b mail: black wants to add this blank line
Augie Fackler <augie@google.com>
parents: 43330
diff changeset
492
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
493 def parse(fp: Any) -> email.message.Message:
48883
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
494 ep = email.parser.Parser()
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
495 # disable the "universal newlines" mode, which isn't binary safe.
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
496 # I have no idea if ascii/surrogateescape is correct, but that's
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
497 # what the standard Python email parser does.
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
498 fp = io.TextIOWrapper(
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
499 fp, encoding='ascii', errors='surrogateescape', newline=chr(10)
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
500 )
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
501 try:
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
502 return ep.parse(fp)
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
503 finally:
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
504 fp.detach()
43156
0e6a7ce81dde py3: use email.generator.BytesGenerator in patch.split()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43117
diff changeset
505
38332
7b12a2d2eedc py3: ditch email.parser.BytesParser which appears to be plain crap
Yuya Nishihara <yuya@tcha.org>
parents: 37469
diff changeset
506
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
507 def parsebytes(data: bytes) -> email.message.Message:
48883
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
508 ep = email.parser.BytesParser()
f0c445a8e324 mail: delete conditional code for Python 2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
509 return ep.parsebytes(data)
43326
ef81de93143e py3: use a BytesParser in notify extension
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43325
diff changeset
510
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 43019
diff changeset
511
51287
f15cb5111a1e pytype: move some type comment to proper annotation
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51285
diff changeset
512 def headdecode(s: Union[email.header.Header, bytes]) -> bytes:
28341
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
513 '''Decodes RFC-2047 header'''
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
514 uparts = []
30072
87b8e40eb812 mail: handle renamed email.Header
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29285
diff changeset
515 for part, charset in email.header.decode_header(s):
28341
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
516 if charset is not None:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
517 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
518 uparts.append(part.decode(charset))
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
519 continue
43324
866bd2cf764b mail: catch LookupError in headdecode()
Denis Laxalde <denis.laxalde@logilab.fr>
parents: 43172
diff changeset
520 except (UnicodeDecodeError, LookupError):
28341
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
521 pass
37469
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 37463
diff changeset
522 # 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: 37463
diff changeset
523 # 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: 37463
diff changeset
524 if isinstance(part, type(u'')):
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 37463
diff changeset
525 uparts.append(part)
7edf68862fe3 py3: work around weird handling of bytes/unicode in decode_header()
Yuya Nishihara <yuya@tcha.org>
parents: 37463
diff changeset
526 continue
28341
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
527 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
528 uparts.append(part.decode('UTF-8'))
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
529 continue
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
530 except UnicodeDecodeError:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
531 pass
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 27619
diff changeset
532 uparts.append(part.decode('ISO-8859-1'))
31447
067add650129 encoding: factor out unicode variants of from/tolocal()
Yuya Nishihara <yuya@tcha.org>
parents: 30325
diff changeset
533 return encoding.unitolocal(u' '.join(uparts))