annotate mercurial/mail.py @ 29998:e6de6ef3e426

sslutil: remove ui from sslkwargs (API) Arguments to sslutil.wrapsocket() are partially determined by calling sslutil.sslkwargs(). This function receives a ui and a hostname and determines what settings, if any, need to be applied when the socket is wrapped. Both the ui and hostname are passed into wrapsocket(). The other arguments to wrapsocket() provided by sslkwargs() (ca_certs and cert_reqs) are not looked at or modified anywhere outside of sslutil.py. So, sslkwargs() doesn't need to exist as a separate public API called before wrapsocket(). This commit starts the process of removing external consumers of sslkwargs() by removing the "ui" key/argument from its return. All callers now pass the ui argument explicitly.
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 25 May 2016 19:43:22 -0700
parents dffe78d80a6c
children 31acc78c632a
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
28274
10bed65ef209 mail: use print function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27447
diff changeset
8 from __future__ import absolute_import, print_function
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
26541
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
11 import os
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
12 import quopri
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
13 import smtplib
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
14 import socket
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
15 import sys
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
16 import time
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
17
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
18 from .i18n import _
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
19 from . import (
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
20 encoding,
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
21 error,
26541
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
22 sslutil,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
23 util,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 26402
diff changeset
24 )
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
25
11538
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
26 _oldheaderinit = email.Header.Header.__init__
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
27 def _unifiedheaderinit(self, *args, **kw):
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
28 """
17411
72803c8edaa4 avoid using abbreviations that look like spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 17407
diff changeset
29 Python 2.7 introduces a backwards incompatible change
11538
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
30 (Python issue1974, r70772) in email.Generator.Generator code:
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
31 pre-2.7 code passed "continuation_ws='\t'" to the Header
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
32 constructor, and 2.7 removed this parameter.
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
33
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
34 Default argument is continuation_ws=' ', which means that the
26685
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 26541
diff changeset
35 behavior is different in <2.7 and 2.7
11538
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
36
26685
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 26541
diff changeset
37 We consider the 2.7 behavior to be preferable, but need
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 26541
diff changeset
38 to have an unified behavior for versions 2.4 to 2.7
11538
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
39 """
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
40 # override continuation_ws
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
41 kw['continuation_ws'] = ' '
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
42 _oldheaderinit(self, *args, **kw)
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
43
29942
bac14dbbbfab py3: use setattr() to assign new class attribute
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29685
diff changeset
44 setattr(email.header.Header, '__init__', _unifiedheaderinit)
11538
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
45
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
46 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
47 '''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
48
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 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
50 '''
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
51 def __init__(self, ui, sslkwargs, 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
52 smtplib.SMTP.__init__(self, **kwargs)
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
53 self._ui = ui
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
54 self._sslkwargs = sslkwargs
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
55 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
56
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 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
58 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
59 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
60 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
61 (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
62 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
63 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
64 ui=self._ui,
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
65 serverhostname=self._host,
25963
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25640
diff changeset
66 **self._sslkwargs)
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
67 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
68 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
69 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
70 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
71 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
72 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
73
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
74 class SMTPS(smtplib.SMTP):
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
75 '''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
76
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
77 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
78 '''
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
79 def __init__(self, ui, sslkwargs, keyfile=None, certfile=None, host=None,
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
80 **kwargs):
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
81 self.keyfile = keyfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
82 self.certfile = certfile
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
83 smtplib.SMTP.__init__(self, **kwargs)
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
84 self._host = host
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
85 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
86 self._ui = ui
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
87 self._sslkwargs = sslkwargs
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
88
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
89 def _get_socket(self, host, port, timeout):
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
90 if self.debuglevel > 0:
28274
10bed65ef209 mail: use print function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 27447
diff changeset
91 print('connect:', (host, port), file=sys.stderr)
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
92 new_socket = socket.create_connection((host, port), timeout)
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
93 new_socket = sslutil.wrapsocket(new_socket,
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
94 self.keyfile, self.certfile,
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
95 ui=self._ui,
29685
a4c5c23de1d3 mail: retain hostname for sslutil.wrapsocket (issue5203)
timeless <timeless@mozdev.org>
parents: 29012
diff changeset
96 serverhostname=self._host,
27313
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
97 **self._sslkwargs)
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
98 self.file = smtplib.SSLFakeFile(new_socket)
ab1af5e7d734 mail: drop python 2.5 support
timeless@mozdev.org
parents: 27227
diff changeset
99 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
100
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
101 def _smtp(ui):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
102 '''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
103 local_hostname = ui.config('smtp', 'local_hostname')
13244
d8f92c3a17d6 mail: fix regression when parsing unset smtp.tls option
Patrick Mezard <pmezard@gmail.com>
parents: 13201
diff changeset
104 tls = ui.config('smtp', 'tls', 'none')
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
105 # backward compatible: when tls = true, we use starttls.
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
106 starttls = tls == 'starttls' or util.parsebool(tls)
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
107 smtps = tls == 'smtps'
14966
194b043dfa51 mail: use safehasattr instead of hasattr
Augie Fackler <durin42@gmail.com>
parents: 14271
diff changeset
108 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
109 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
110 mailhost = ui.config('smtp', 'host')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
111 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
112 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
113 verifycert = ui.config('smtp', 'verifycert', 'strict')
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
114 if verifycert not in ['strict', 'loose']:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
115 if util.parsebool(verifycert) is not False:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
116 raise error.Abort(_('invalid smtp.verifycert configuration: %s')
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
117 % (verifycert))
23223
a4af6fd99fb0 mail: actually use the verifycert config value
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 19810
diff changeset
118 verifycert = False
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
119 if (starttls or smtps) and verifycert:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
120 sslkwargs = sslutil.sslkwargs(ui, mailhost)
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
121 else:
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
122 sslkwargs = {}
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
123
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
124 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
125 ui.note(_('(using smtps)\n'))
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
126 s = SMTPS(ui, sslkwargs, 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
127 elif starttls:
29998
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
128 s = STARTTLS(ui, sslkwargs, local_hostname=local_hostname,
e6de6ef3e426 sslutil: remove ui from sslkwargs (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29977
diff changeset
129 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
130 else:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
131 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
132 if smtps:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
133 defaultport = 465
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
134 else:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
135 defaultport = 25
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
136 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
137 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
138 (mailhost, mailport))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
139 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
140 if starttls:
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
141 ui.note(_('(using starttls)\n'))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
142 s.ehlo()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
143 s.starttls()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
144 s.ehlo()
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
145 if (starttls or smtps) and verifycert:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
146 ui.note(_('(verifying remote certificate)\n'))
29977
dffe78d80a6c sslutil: convert socket validation from a class to a function (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29942
diff changeset
147 sslutil.validatesocket(s.sock, verifycert == 'strict')
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
148 username = ui.config('smtp', 'username')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
149 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
150 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
151 password = ui.getpass()
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
152 if username and password:
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
153 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
154 (username))
9279
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
155 try:
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
156 s.login(username, password)
26214
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25999
diff changeset
157 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
158 raise error.Abort(inst)
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
159
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
160 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
161 try:
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
162 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
163 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
164 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
165 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
166 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
167 raise error.Abort(inst)
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
168
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
169 return send
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
170
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
171 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
172 '''send mail using sendmail.'''
26402
fa3f0301cf91 email: fix config default value inconsistency
Matt Mackall <mpm@selenic.com>
parents: 26214
diff changeset
173 program = ui.config('email', 'method', 'smtp')
5975
75d9fe70c654 templater: move email function to util
Matt Mackall <mpm@selenic.com>
parents: 5973
diff changeset
174 cmdline = '%s -f %s %s' % (program, util.email(sender),
75d9fe70c654 templater: move email function to util
Matt Mackall <mpm@selenic.com>
parents: 5973
diff changeset
175 ' '.join(map(util.email, recipients)))
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
176 ui.note(_('sending mail: %s\n') % cmdline)
6534
962eb403165b replace usage of os.popen() with util.popen()
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5975
diff changeset
177 fp = util.popen(cmdline, 'w')
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
178 fp.write(msg)
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
179 ret = fp.close()
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
180 if ret:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
181 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
182 os.path.basename(program.split(None, 1)[0]),
14234
600e64004eb5 rename explain_exit to explainexit
Adrian Buehlmann <adrian@cadifra.com>
parents: 13244
diff changeset
183 util.explainexit(ret)[0]))
2909
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
184
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
185 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
186 '''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
187 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
188 # 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
189 # of month instead of one. Make them print the same thing.
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
190 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
191 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
192 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
193 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
194 fp.close()
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
195
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
196 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
197 '''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
198 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
199 if mbox:
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14966
diff changeset
200 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
201 return lambda s, r, m: _mbox(mbox, s, r, m)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
202 if ui.config('email', 'method', 'smtp') == 'smtp':
5945
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5873
diff changeset
203 return _smtp(ui)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5945
diff changeset
204 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
205
15561
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
206 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
207 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
208 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
209
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
210 def validateconfig(ui):
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
211 '''determine if we have enough config data to try sending email.'''
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
212 method = ui.config('email', 'method', 'smtp')
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
213 if method == 'smtp':
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
214 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
215 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
216 '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
217 else:
14271
4030630fb59c rename util.find_exe to findexe
Adrian Buehlmann <adrian@cadifra.com>
parents: 14234
diff changeset
218 if not util.findexe(method):
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
219 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
220 'but not in PATH') % method)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
221
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
222 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
223 '''Return MIME message suitable for a patch.
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
224 Charset will be detected as utf-8 or (possibly fake) us-ascii.
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
225 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
226
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
227 cs = 'us-ascii'
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
228 if not display:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
229 try:
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
230 s.decode('us-ascii')
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
231 except UnicodeDecodeError:
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
232 try:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
233 s.decode('utf-8')
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
234 cs = 'utf-8'
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
235 except UnicodeDecodeError:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
236 # We'll go with us-ascii as a fallback.
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
237 pass
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
238
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
239 return mimetextqp(s, subtype, cs)
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
240
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
241 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
242 '''Return MIME message.
17407
e7cfe3587ea4 fix trivial spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 15562
diff changeset
243 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
244 '''
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
245 enc = None
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
246 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
247 if len(line) > 950:
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
248 body = quopri.encodestring(body)
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
249 enc = "quoted-printable"
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
250 break
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
251
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
252 msg = email.MIMEText.MIMEText(body, subtype, charset)
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
253 if enc:
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
254 del msg['Content-Transfer-Encoding']
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
255 msg['Content-Transfer-Encoding'] = enc
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
256 return msg
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
257
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
258 def _charsets(ui):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
259 '''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
260 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
261 fallbacks = [encoding.fallbackencoding.lower(),
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
262 encoding.encoding.lower(), 'utf-8']
8343
6fbbb90261b1 mail: updated comment
Martin Geisler <mg@lazybytes.net>
parents: 8332
diff changeset
263 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
264 if cs not in charsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
265 charsets.append(cs)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
266 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
267
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
268 def _encode(ui, s, charsets):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
269 '''Returns (converted) string, charset tuple.
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
270 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
271 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
272 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
273 Caveat: Do not use for mail parts containing patches!'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
274 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
275 s.decode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
276 except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
277 sendcharsets = charsets or _charsets(ui)
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
278 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
279 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
280 u = s.decode(ics)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
281 except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
282 continue
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
283 for ocs in sendcharsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
284 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
285 return u.encode(ocs), ocs
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
286 except UnicodeEncodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
287 pass
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
288 except LookupError:
7195
9fabcb1fe68d mail: correct typo in variable name
Christian Ebert <blacktrash@gmx.net>
parents: 7191
diff changeset
289 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
290 # 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
291 return s, 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
292
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
293 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
294 '''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
295 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
296 # split into words?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
297 s, cs = _encode(ui, s, charsets)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
298 return str(email.Header.Header(s, cs))
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
299 return s
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
300
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
301 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
302 name = headencode(ui, name, charsets)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
303 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
304 acc, dom = addr.split('@')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
305 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
306 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
307 addr = '%s@%s' % (acc, dom)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
308 except UnicodeDecodeError:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
309 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
310 except ValueError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
311 try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
312 # too strict?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
313 addr = addr.encode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
314 except UnicodeDecodeError:
27227
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26685
diff changeset
315 raise error.Abort(_('invalid local address: %s') % addr)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
316 return email.Utils.formataddr((name, addr))
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
317
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
318 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
319 '''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
320 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
321 return address or ''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
322 name, addr = email.Utils.parseaddr(address)
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
323 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
324
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
325 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
326 '''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
327 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
328 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
329 if display:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
330 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
331
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
332 result = []
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
333 for name, addr in email.Utils.getaddresses(addrs):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
334 if name or addr:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
335 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
336 return result
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
337
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
338 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
339 '''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
340 charset and transfer-encoding accordingly.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
341 cs = 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
342 if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6534
diff changeset
343 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
344 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
345
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
346 def headdecode(s):
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
347 '''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
348 uparts = []
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
349 for part, charset in email.Header.decode_header(s):
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
350 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
351 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
352 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
353 continue
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
354 except UnicodeDecodeError:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
355 pass
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
356 try:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
357 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
358 continue
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
359 except UnicodeDecodeError:
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
360 pass
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
361 uparts.append(part.decode('ISO-8859-1'))
8286f551b7ee patch: when importing from email, RFC2047-decode From/Subject headers
Julien Cristau <julien.cristau@logilab.fr>
parents: 28274
diff changeset
362 return encoding.tolocal(u' '.join(uparts).encode('UTF-8'))