##// END OF EJS Templates
mail: use print function...
Gregory Szorc -
r27619:10bed65e default
parent child Browse files
Show More
@@ -1,334 +1,334
1 1 # mail.py - mail sending bits for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import
8 from __future__ import absolute_import, print_function
9 9
10 10 import email
11 11 import os
12 12 import quopri
13 13 import smtplib
14 14 import socket
15 15 import sys
16 16 import time
17 17
18 18 from .i18n import _
19 19 from . import (
20 20 encoding,
21 21 error,
22 22 sslutil,
23 23 util,
24 24 )
25 25
26 26 _oldheaderinit = email.Header.Header.__init__
27 27 def _unifiedheaderinit(self, *args, **kw):
28 28 """
29 29 Python 2.7 introduces a backwards incompatible change
30 30 (Python issue1974, r70772) in email.Generator.Generator code:
31 31 pre-2.7 code passed "continuation_ws='\t'" to the Header
32 32 constructor, and 2.7 removed this parameter.
33 33
34 34 Default argument is continuation_ws=' ', which means that the
35 35 behavior is different in <2.7 and 2.7
36 36
37 37 We consider the 2.7 behavior to be preferable, but need
38 38 to have an unified behavior for versions 2.4 to 2.7
39 39 """
40 40 # override continuation_ws
41 41 kw['continuation_ws'] = ' '
42 42 _oldheaderinit(self, *args, **kw)
43 43
44 44 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
45 45
46 46 class STARTTLS(smtplib.SMTP):
47 47 '''Derived class to verify the peer certificate for STARTTLS.
48 48
49 49 This class allows to pass any keyword arguments to SSL socket creation.
50 50 '''
51 51 def __init__(self, sslkwargs, **kwargs):
52 52 smtplib.SMTP.__init__(self, **kwargs)
53 53 self._sslkwargs = sslkwargs
54 54
55 55 def starttls(self, keyfile=None, certfile=None):
56 56 if not self.has_extn("starttls"):
57 57 msg = "STARTTLS extension not supported by server"
58 58 raise smtplib.SMTPException(msg)
59 59 (resp, reply) = self.docmd("STARTTLS")
60 60 if resp == 220:
61 61 self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile,
62 62 **self._sslkwargs)
63 63 self.file = smtplib.SSLFakeFile(self.sock)
64 64 self.helo_resp = None
65 65 self.ehlo_resp = None
66 66 self.esmtp_features = {}
67 67 self.does_esmtp = 0
68 68 return (resp, reply)
69 69
70 70 class SMTPS(smtplib.SMTP):
71 71 '''Derived class to verify the peer certificate for SMTPS.
72 72
73 73 This class allows to pass any keyword arguments to SSL socket creation.
74 74 '''
75 75 def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
76 76 self.keyfile = keyfile
77 77 self.certfile = certfile
78 78 smtplib.SMTP.__init__(self, **kwargs)
79 79 self.default_port = smtplib.SMTP_SSL_PORT
80 80 self._sslkwargs = sslkwargs
81 81
82 82 def _get_socket(self, host, port, timeout):
83 83 if self.debuglevel > 0:
84 print >> sys.stderr, 'connect:', (host, port)
84 print('connect:', (host, port), file=sys.stderr)
85 85 new_socket = socket.create_connection((host, port), timeout)
86 86 new_socket = sslutil.wrapsocket(new_socket,
87 87 self.keyfile, self.certfile,
88 88 **self._sslkwargs)
89 89 self.file = smtplib.SSLFakeFile(new_socket)
90 90 return new_socket
91 91
92 92 def _smtp(ui):
93 93 '''build an smtp connection and return a function to send mail'''
94 94 local_hostname = ui.config('smtp', 'local_hostname')
95 95 tls = ui.config('smtp', 'tls', 'none')
96 96 # backward compatible: when tls = true, we use starttls.
97 97 starttls = tls == 'starttls' or util.parsebool(tls)
98 98 smtps = tls == 'smtps'
99 99 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
100 100 raise error.Abort(_("can't use TLS: Python SSL support not installed"))
101 101 mailhost = ui.config('smtp', 'host')
102 102 if not mailhost:
103 103 raise error.Abort(_('smtp.host not configured - cannot send mail'))
104 104 verifycert = ui.config('smtp', 'verifycert', 'strict')
105 105 if verifycert not in ['strict', 'loose']:
106 106 if util.parsebool(verifycert) is not False:
107 107 raise error.Abort(_('invalid smtp.verifycert configuration: %s')
108 108 % (verifycert))
109 109 verifycert = False
110 110 if (starttls or smtps) and verifycert:
111 111 sslkwargs = sslutil.sslkwargs(ui, mailhost)
112 112 else:
113 113 # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
114 114 sslkwargs = {'ui': ui}
115 115 if smtps:
116 116 ui.note(_('(using smtps)\n'))
117 117 s = SMTPS(sslkwargs, local_hostname=local_hostname)
118 118 elif starttls:
119 119 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
120 120 else:
121 121 s = smtplib.SMTP(local_hostname=local_hostname)
122 122 if smtps:
123 123 defaultport = 465
124 124 else:
125 125 defaultport = 25
126 126 mailport = util.getport(ui.config('smtp', 'port', defaultport))
127 127 ui.note(_('sending mail: smtp host %s, port %d\n') %
128 128 (mailhost, mailport))
129 129 s.connect(host=mailhost, port=mailport)
130 130 if starttls:
131 131 ui.note(_('(using starttls)\n'))
132 132 s.ehlo()
133 133 s.starttls()
134 134 s.ehlo()
135 135 if (starttls or smtps) and verifycert:
136 136 ui.note(_('(verifying remote certificate)\n'))
137 137 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
138 138 username = ui.config('smtp', 'username')
139 139 password = ui.config('smtp', 'password')
140 140 if username and not password:
141 141 password = ui.getpass()
142 142 if username and password:
143 143 ui.note(_('(authenticating to mail server as %s)\n') %
144 144 (username))
145 145 try:
146 146 s.login(username, password)
147 147 except smtplib.SMTPException as inst:
148 148 raise error.Abort(inst)
149 149
150 150 def send(sender, recipients, msg):
151 151 try:
152 152 return s.sendmail(sender, recipients, msg)
153 153 except smtplib.SMTPRecipientsRefused as inst:
154 154 recipients = [r[1] for r in inst.recipients.values()]
155 155 raise error.Abort('\n' + '\n'.join(recipients))
156 156 except smtplib.SMTPException as inst:
157 157 raise error.Abort(inst)
158 158
159 159 return send
160 160
161 161 def _sendmail(ui, sender, recipients, msg):
162 162 '''send mail using sendmail.'''
163 163 program = ui.config('email', 'method', 'smtp')
164 164 cmdline = '%s -f %s %s' % (program, util.email(sender),
165 165 ' '.join(map(util.email, recipients)))
166 166 ui.note(_('sending mail: %s\n') % cmdline)
167 167 fp = util.popen(cmdline, 'w')
168 168 fp.write(msg)
169 169 ret = fp.close()
170 170 if ret:
171 171 raise error.Abort('%s %s' % (
172 172 os.path.basename(program.split(None, 1)[0]),
173 173 util.explainexit(ret)[0]))
174 174
175 175 def _mbox(mbox, sender, recipients, msg):
176 176 '''write mails to mbox'''
177 177 fp = open(mbox, 'ab+')
178 178 # Should be time.asctime(), but Windows prints 2-characters day
179 179 # of month instead of one. Make them print the same thing.
180 180 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
181 181 fp.write('From %s %s\n' % (sender, date))
182 182 fp.write(msg)
183 183 fp.write('\n\n')
184 184 fp.close()
185 185
186 186 def connect(ui, mbox=None):
187 187 '''make a mail connection. return a function to send mail.
188 188 call as sendmail(sender, list-of-recipients, msg).'''
189 189 if mbox:
190 190 open(mbox, 'wb').close()
191 191 return lambda s, r, m: _mbox(mbox, s, r, m)
192 192 if ui.config('email', 'method', 'smtp') == 'smtp':
193 193 return _smtp(ui)
194 194 return lambda s, r, m: _sendmail(ui, s, r, m)
195 195
196 196 def sendmail(ui, sender, recipients, msg, mbox=None):
197 197 send = connect(ui, mbox=mbox)
198 198 return send(sender, recipients, msg)
199 199
200 200 def validateconfig(ui):
201 201 '''determine if we have enough config data to try sending email.'''
202 202 method = ui.config('email', 'method', 'smtp')
203 203 if method == 'smtp':
204 204 if not ui.config('smtp', 'host'):
205 205 raise error.Abort(_('smtp specified as email transport, '
206 206 'but no smtp host configured'))
207 207 else:
208 208 if not util.findexe(method):
209 209 raise error.Abort(_('%r specified as email transport, '
210 210 'but not in PATH') % method)
211 211
212 212 def mimetextpatch(s, subtype='plain', display=False):
213 213 '''Return MIME message suitable for a patch.
214 214 Charset will be detected as utf-8 or (possibly fake) us-ascii.
215 215 Transfer encodings will be used if necessary.'''
216 216
217 217 cs = 'us-ascii'
218 218 if not display:
219 219 try:
220 220 s.decode('us-ascii')
221 221 except UnicodeDecodeError:
222 222 try:
223 223 s.decode('utf-8')
224 224 cs = 'utf-8'
225 225 except UnicodeDecodeError:
226 226 # We'll go with us-ascii as a fallback.
227 227 pass
228 228
229 229 return mimetextqp(s, subtype, cs)
230 230
231 231 def mimetextqp(body, subtype, charset):
232 232 '''Return MIME message.
233 233 Quoted-printable transfer encoding will be used if necessary.
234 234 '''
235 235 enc = None
236 236 for line in body.splitlines():
237 237 if len(line) > 950:
238 238 body = quopri.encodestring(body)
239 239 enc = "quoted-printable"
240 240 break
241 241
242 242 msg = email.MIMEText.MIMEText(body, subtype, charset)
243 243 if enc:
244 244 del msg['Content-Transfer-Encoding']
245 245 msg['Content-Transfer-Encoding'] = enc
246 246 return msg
247 247
248 248 def _charsets(ui):
249 249 '''Obtains charsets to send mail parts not containing patches.'''
250 250 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
251 251 fallbacks = [encoding.fallbackencoding.lower(),
252 252 encoding.encoding.lower(), 'utf-8']
253 253 for cs in fallbacks: # find unique charsets while keeping order
254 254 if cs not in charsets:
255 255 charsets.append(cs)
256 256 return [cs for cs in charsets if not cs.endswith('ascii')]
257 257
258 258 def _encode(ui, s, charsets):
259 259 '''Returns (converted) string, charset tuple.
260 260 Finds out best charset by cycling through sendcharsets in descending
261 261 order. Tries both encoding and fallbackencoding for input. Only as
262 262 last resort send as is in fake ascii.
263 263 Caveat: Do not use for mail parts containing patches!'''
264 264 try:
265 265 s.decode('ascii')
266 266 except UnicodeDecodeError:
267 267 sendcharsets = charsets or _charsets(ui)
268 268 for ics in (encoding.encoding, encoding.fallbackencoding):
269 269 try:
270 270 u = s.decode(ics)
271 271 except UnicodeDecodeError:
272 272 continue
273 273 for ocs in sendcharsets:
274 274 try:
275 275 return u.encode(ocs), ocs
276 276 except UnicodeEncodeError:
277 277 pass
278 278 except LookupError:
279 279 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
280 280 # if ascii, or all conversion attempts fail, send (broken) ascii
281 281 return s, 'us-ascii'
282 282
283 283 def headencode(ui, s, charsets=None, display=False):
284 284 '''Returns RFC-2047 compliant header from given string.'''
285 285 if not display:
286 286 # split into words?
287 287 s, cs = _encode(ui, s, charsets)
288 288 return str(email.Header.Header(s, cs))
289 289 return s
290 290
291 291 def _addressencode(ui, name, addr, charsets=None):
292 292 name = headencode(ui, name, charsets)
293 293 try:
294 294 acc, dom = addr.split('@')
295 295 acc = acc.encode('ascii')
296 296 dom = dom.decode(encoding.encoding).encode('idna')
297 297 addr = '%s@%s' % (acc, dom)
298 298 except UnicodeDecodeError:
299 299 raise error.Abort(_('invalid email address: %s') % addr)
300 300 except ValueError:
301 301 try:
302 302 # too strict?
303 303 addr = addr.encode('ascii')
304 304 except UnicodeDecodeError:
305 305 raise error.Abort(_('invalid local address: %s') % addr)
306 306 return email.Utils.formataddr((name, addr))
307 307
308 308 def addressencode(ui, address, charsets=None, display=False):
309 309 '''Turns address into RFC-2047 compliant header.'''
310 310 if display or not address:
311 311 return address or ''
312 312 name, addr = email.Utils.parseaddr(address)
313 313 return _addressencode(ui, name, addr, charsets)
314 314
315 315 def addrlistencode(ui, addrs, charsets=None, display=False):
316 316 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
317 317 A single element of input list may contain multiple addresses, but output
318 318 always has one address per item'''
319 319 if display:
320 320 return [a.strip() for a in addrs if a.strip()]
321 321
322 322 result = []
323 323 for name, addr in email.Utils.getaddresses(addrs):
324 324 if name or addr:
325 325 result.append(_addressencode(ui, name, addr, charsets))
326 326 return result
327 327
328 328 def mimeencode(ui, s, charsets=None, display=False):
329 329 '''creates mime text object, encodes it if needed, and sets
330 330 charset and transfer-encoding accordingly.'''
331 331 cs = 'us-ascii'
332 332 if not display:
333 333 s, cs = _encode(ui, s, charsets)
334 334 return mimetextqp(s, 'plain', cs)
@@ -1,182 +1,181
1 1 #require test-repo
2 2
3 3 $ cd "$TESTDIR"/..
4 4
5 5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 6 contrib/casesmash.py not using absolute_import
7 7 contrib/check-code.py not using absolute_import
8 8 contrib/check-code.py requires print_function
9 9 contrib/check-config.py not using absolute_import
10 10 contrib/check-config.py requires print_function
11 11 contrib/debugcmdserver.py not using absolute_import
12 12 contrib/debugcmdserver.py requires print_function
13 13 contrib/debugshell.py not using absolute_import
14 14 contrib/fixpax.py not using absolute_import
15 15 contrib/fixpax.py requires print_function
16 16 contrib/hgclient.py not using absolute_import
17 17 contrib/hgclient.py requires print_function
18 18 contrib/hgfixes/fix_bytes.py not using absolute_import
19 19 contrib/hgfixes/fix_bytesmod.py not using absolute_import
20 20 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
21 21 contrib/import-checker.py not using absolute_import
22 22 contrib/import-checker.py requires print_function
23 23 contrib/memory.py not using absolute_import
24 24 contrib/perf.py not using absolute_import
25 25 contrib/python-hook-examples.py not using absolute_import
26 26 contrib/revsetbenchmarks.py not using absolute_import
27 27 contrib/revsetbenchmarks.py requires print_function
28 28 contrib/showstack.py not using absolute_import
29 29 contrib/synthrepo.py not using absolute_import
30 30 contrib/win32/hgwebdir_wsgi.py not using absolute_import
31 31 doc/check-seclevel.py not using absolute_import
32 32 doc/gendoc.py not using absolute_import
33 33 doc/hgmanpage.py not using absolute_import
34 34 hgext/__init__.py not using absolute_import
35 35 hgext/acl.py not using absolute_import
36 36 hgext/blackbox.py not using absolute_import
37 37 hgext/bugzilla.py not using absolute_import
38 38 hgext/censor.py not using absolute_import
39 39 hgext/children.py not using absolute_import
40 40 hgext/churn.py not using absolute_import
41 41 hgext/clonebundles.py not using absolute_import
42 42 hgext/color.py not using absolute_import
43 43 hgext/convert/__init__.py not using absolute_import
44 44 hgext/convert/bzr.py not using absolute_import
45 45 hgext/convert/common.py not using absolute_import
46 46 hgext/convert/convcmd.py not using absolute_import
47 47 hgext/convert/cvs.py not using absolute_import
48 48 hgext/convert/cvsps.py not using absolute_import
49 49 hgext/convert/darcs.py not using absolute_import
50 50 hgext/convert/filemap.py not using absolute_import
51 51 hgext/convert/git.py not using absolute_import
52 52 hgext/convert/gnuarch.py not using absolute_import
53 53 hgext/convert/hg.py not using absolute_import
54 54 hgext/convert/monotone.py not using absolute_import
55 55 hgext/convert/p4.py not using absolute_import
56 56 hgext/convert/subversion.py not using absolute_import
57 57 hgext/convert/transport.py not using absolute_import
58 58 hgext/eol.py not using absolute_import
59 59 hgext/extdiff.py not using absolute_import
60 60 hgext/factotum.py not using absolute_import
61 61 hgext/fetch.py not using absolute_import
62 62 hgext/gpg.py not using absolute_import
63 63 hgext/graphlog.py not using absolute_import
64 64 hgext/hgcia.py not using absolute_import
65 65 hgext/hgk.py not using absolute_import
66 66 hgext/highlight/__init__.py not using absolute_import
67 67 hgext/highlight/highlight.py not using absolute_import
68 68 hgext/histedit.py not using absolute_import
69 69 hgext/keyword.py not using absolute_import
70 70 hgext/largefiles/__init__.py not using absolute_import
71 71 hgext/largefiles/basestore.py not using absolute_import
72 72 hgext/largefiles/lfcommands.py not using absolute_import
73 73 hgext/largefiles/lfutil.py not using absolute_import
74 74 hgext/largefiles/localstore.py not using absolute_import
75 75 hgext/largefiles/overrides.py not using absolute_import
76 76 hgext/largefiles/proto.py not using absolute_import
77 77 hgext/largefiles/remotestore.py not using absolute_import
78 78 hgext/largefiles/reposetup.py not using absolute_import
79 79 hgext/largefiles/uisetup.py not using absolute_import
80 80 hgext/largefiles/wirestore.py not using absolute_import
81 81 hgext/mq.py not using absolute_import
82 82 hgext/notify.py not using absolute_import
83 83 hgext/pager.py not using absolute_import
84 84 hgext/patchbomb.py not using absolute_import
85 85 hgext/purge.py not using absolute_import
86 86 hgext/rebase.py not using absolute_import
87 87 hgext/record.py not using absolute_import
88 88 hgext/relink.py not using absolute_import
89 89 hgext/schemes.py not using absolute_import
90 90 hgext/share.py not using absolute_import
91 91 hgext/shelve.py not using absolute_import
92 92 hgext/strip.py not using absolute_import
93 93 hgext/transplant.py not using absolute_import
94 94 hgext/win32mbcs.py not using absolute_import
95 95 hgext/win32text.py not using absolute_import
96 96 hgext/zeroconf/Zeroconf.py not using absolute_import
97 97 hgext/zeroconf/Zeroconf.py requires print_function
98 98 hgext/zeroconf/__init__.py not using absolute_import
99 99 i18n/check-translation.py not using absolute_import
100 100 i18n/polib.py not using absolute_import
101 101 mercurial/cmdutil.py not using absolute_import
102 102 mercurial/commands.py not using absolute_import
103 mercurial/mail.py requires print_function
104 103 setup.py not using absolute_import
105 104 tests/filterpyflakes.py requires print_function
106 105 tests/generate-working-copy-states.py requires print_function
107 106 tests/get-with-headers.py requires print_function
108 107 tests/heredoctest.py requires print_function
109 108 tests/hypothesishelpers.py not using absolute_import
110 109 tests/hypothesishelpers.py requires print_function
111 110 tests/killdaemons.py not using absolute_import
112 111 tests/md5sum.py not using absolute_import
113 112 tests/mockblackbox.py not using absolute_import
114 113 tests/printenv.py not using absolute_import
115 114 tests/readlink.py not using absolute_import
116 115 tests/readlink.py requires print_function
117 116 tests/revlog-formatv0.py not using absolute_import
118 117 tests/run-tests.py not using absolute_import
119 118 tests/seq.py not using absolute_import
120 119 tests/seq.py requires print_function
121 120 tests/silenttestrunner.py not using absolute_import
122 121 tests/silenttestrunner.py requires print_function
123 122 tests/sitecustomize.py not using absolute_import
124 123 tests/svn-safe-append.py not using absolute_import
125 124 tests/svnxml.py not using absolute_import
126 125 tests/test-ancestor.py requires print_function
127 126 tests/test-atomictempfile.py not using absolute_import
128 127 tests/test-batching.py not using absolute_import
129 128 tests/test-batching.py requires print_function
130 129 tests/test-bdiff.py not using absolute_import
131 130 tests/test-bdiff.py requires print_function
132 131 tests/test-context.py not using absolute_import
133 132 tests/test-context.py requires print_function
134 133 tests/test-demandimport.py not using absolute_import
135 134 tests/test-demandimport.py requires print_function
136 135 tests/test-dispatch.py not using absolute_import
137 136 tests/test-dispatch.py requires print_function
138 137 tests/test-doctest.py not using absolute_import
139 138 tests/test-duplicateoptions.py not using absolute_import
140 139 tests/test-duplicateoptions.py requires print_function
141 140 tests/test-filecache.py not using absolute_import
142 141 tests/test-filecache.py requires print_function
143 142 tests/test-filelog.py not using absolute_import
144 143 tests/test-filelog.py requires print_function
145 144 tests/test-hg-parseurl.py not using absolute_import
146 145 tests/test-hg-parseurl.py requires print_function
147 146 tests/test-hgweb-auth.py not using absolute_import
148 147 tests/test-hgweb-auth.py requires print_function
149 148 tests/test-hgwebdir-paths.py not using absolute_import
150 149 tests/test-hybridencode.py not using absolute_import
151 150 tests/test-hybridencode.py requires print_function
152 151 tests/test-lrucachedict.py not using absolute_import
153 152 tests/test-lrucachedict.py requires print_function
154 153 tests/test-manifest.py not using absolute_import
155 154 tests/test-minirst.py not using absolute_import
156 155 tests/test-minirst.py requires print_function
157 156 tests/test-parseindex2.py not using absolute_import
158 157 tests/test-parseindex2.py requires print_function
159 158 tests/test-pathencode.py not using absolute_import
160 159 tests/test-pathencode.py requires print_function
161 160 tests/test-propertycache.py not using absolute_import
162 161 tests/test-propertycache.py requires print_function
163 162 tests/test-revlog-ancestry.py not using absolute_import
164 163 tests/test-revlog-ancestry.py requires print_function
165 164 tests/test-run-tests.py not using absolute_import
166 165 tests/test-simplemerge.py not using absolute_import
167 166 tests/test-status-inprocess.py not using absolute_import
168 167 tests/test-status-inprocess.py requires print_function
169 168 tests/test-symlink-os-yes-fs-no.py not using absolute_import
170 169 tests/test-trusted.py not using absolute_import
171 170 tests/test-trusted.py requires print_function
172 171 tests/test-ui-color.py not using absolute_import
173 172 tests/test-ui-color.py requires print_function
174 173 tests/test-ui-config.py not using absolute_import
175 174 tests/test-ui-config.py requires print_function
176 175 tests/test-ui-verbosity.py not using absolute_import
177 176 tests/test-ui-verbosity.py requires print_function
178 177 tests/test-url.py not using absolute_import
179 178 tests/test-url.py requires print_function
180 179 tests/test-walkrepo.py requires print_function
181 180 tests/test-wireproto.py requires print_function
182 181 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now