Mercurial > public > mercurial-scm > hg
comparison mercurial/mail.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | eef9a2d67051 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
42 smtplib.SMTP.__init__(self, **kwargs) | 42 smtplib.SMTP.__init__(self, **kwargs) |
43 self._ui = ui | 43 self._ui = ui |
44 self._host = host | 44 self._host = host |
45 | 45 |
46 def starttls(self, keyfile=None, certfile=None): | 46 def starttls(self, keyfile=None, certfile=None): |
47 if not self.has_extn("starttls"): | 47 if not self.has_extn(b"starttls"): |
48 msg = "STARTTLS extension not supported by server" | 48 msg = b"STARTTLS extension not supported by server" |
49 raise smtplib.SMTPException(msg) | 49 raise smtplib.SMTPException(msg) |
50 (resp, reply) = self.docmd("STARTTLS") | 50 (resp, reply) = self.docmd(b"STARTTLS") |
51 if resp == 220: | 51 if resp == 220: |
52 self.sock = sslutil.wrapsocket( | 52 self.sock = sslutil.wrapsocket( |
53 self.sock, | 53 self.sock, |
54 keyfile, | 54 keyfile, |
55 certfile, | 55 certfile, |
78 self.default_port = smtplib.SMTP_SSL_PORT | 78 self.default_port = smtplib.SMTP_SSL_PORT |
79 self._ui = ui | 79 self._ui = ui |
80 | 80 |
81 def _get_socket(self, host, port, timeout): | 81 def _get_socket(self, host, port, timeout): |
82 if self.debuglevel > 0: | 82 if self.debuglevel > 0: |
83 self._ui.debug('connect: %r\n' % ((host, port),)) | 83 self._ui.debug(b'connect: %r\n' % ((host, port),)) |
84 new_socket = socket.create_connection((host, port), timeout) | 84 new_socket = socket.create_connection((host, port), timeout) |
85 new_socket = sslutil.wrapsocket( | 85 new_socket = sslutil.wrapsocket( |
86 new_socket, | 86 new_socket, |
87 self.keyfile, | 87 self.keyfile, |
88 self.certfile, | 88 self.certfile, |
104 return False | 104 return False |
105 | 105 |
106 | 106 |
107 def _smtp(ui): | 107 def _smtp(ui): |
108 '''build an smtp connection and return a function to send mail''' | 108 '''build an smtp connection and return a function to send mail''' |
109 local_hostname = ui.config('smtp', 'local_hostname') | 109 local_hostname = ui.config(b'smtp', b'local_hostname') |
110 tls = ui.config('smtp', 'tls') | 110 tls = ui.config(b'smtp', b'tls') |
111 # backward compatible: when tls = true, we use starttls. | 111 # backward compatible: when tls = true, we use starttls. |
112 starttls = tls == 'starttls' or stringutil.parsebool(tls) | 112 starttls = tls == b'starttls' or stringutil.parsebool(tls) |
113 smtps = tls == 'smtps' | 113 smtps = tls == b'smtps' |
114 if (starttls or smtps) and not _pyhastls(): | 114 if (starttls or smtps) and not _pyhastls(): |
115 raise error.Abort(_("can't use TLS: Python SSL support not installed")) | 115 raise error.Abort(_(b"can't use TLS: Python SSL support not installed")) |
116 mailhost = ui.config('smtp', 'host') | 116 mailhost = ui.config(b'smtp', b'host') |
117 if not mailhost: | 117 if not mailhost: |
118 raise error.Abort(_('smtp.host not configured - cannot send mail')) | 118 raise error.Abort(_(b'smtp.host not configured - cannot send mail')) |
119 if smtps: | 119 if smtps: |
120 ui.note(_('(using smtps)\n')) | 120 ui.note(_(b'(using smtps)\n')) |
121 s = SMTPS(ui, local_hostname=local_hostname, host=mailhost) | 121 s = SMTPS(ui, local_hostname=local_hostname, host=mailhost) |
122 elif starttls: | 122 elif starttls: |
123 s = STARTTLS(ui, local_hostname=local_hostname, host=mailhost) | 123 s = STARTTLS(ui, local_hostname=local_hostname, host=mailhost) |
124 else: | 124 else: |
125 s = smtplib.SMTP(local_hostname=local_hostname) | 125 s = smtplib.SMTP(local_hostname=local_hostname) |
126 if smtps: | 126 if smtps: |
127 defaultport = 465 | 127 defaultport = 465 |
128 else: | 128 else: |
129 defaultport = 25 | 129 defaultport = 25 |
130 mailport = util.getport(ui.config('smtp', 'port', defaultport)) | 130 mailport = util.getport(ui.config(b'smtp', b'port', defaultport)) |
131 ui.note(_('sending mail: smtp host %s, port %d\n') % (mailhost, mailport)) | 131 ui.note(_(b'sending mail: smtp host %s, port %d\n') % (mailhost, mailport)) |
132 s.connect(host=mailhost, port=mailport) | 132 s.connect(host=mailhost, port=mailport) |
133 if starttls: | 133 if starttls: |
134 ui.note(_('(using starttls)\n')) | 134 ui.note(_(b'(using starttls)\n')) |
135 s.ehlo() | 135 s.ehlo() |
136 s.starttls() | 136 s.starttls() |
137 s.ehlo() | 137 s.ehlo() |
138 if starttls or smtps: | 138 if starttls or smtps: |
139 ui.note(_('(verifying remote certificate)\n')) | 139 ui.note(_(b'(verifying remote certificate)\n')) |
140 sslutil.validatesocket(s.sock) | 140 sslutil.validatesocket(s.sock) |
141 username = ui.config('smtp', 'username') | 141 username = ui.config(b'smtp', b'username') |
142 password = ui.config('smtp', 'password') | 142 password = ui.config(b'smtp', b'password') |
143 if username and not password: | 143 if username and not password: |
144 password = ui.getpass() | 144 password = ui.getpass() |
145 if username and password: | 145 if username and password: |
146 ui.note(_('(authenticating to mail server as %s)\n') % username) | 146 ui.note(_(b'(authenticating to mail server as %s)\n') % username) |
147 try: | 147 try: |
148 s.login(username, password) | 148 s.login(username, password) |
149 except smtplib.SMTPException as inst: | 149 except smtplib.SMTPException as inst: |
150 raise error.Abort(inst) | 150 raise error.Abort(inst) |
151 | 151 |
152 def send(sender, recipients, msg): | 152 def send(sender, recipients, msg): |
153 try: | 153 try: |
154 return s.sendmail(sender, recipients, msg) | 154 return s.sendmail(sender, recipients, msg) |
155 except smtplib.SMTPRecipientsRefused as inst: | 155 except smtplib.SMTPRecipientsRefused as inst: |
156 recipients = [r[1] for r in inst.recipients.values()] | 156 recipients = [r[1] for r in inst.recipients.values()] |
157 raise error.Abort('\n' + '\n'.join(recipients)) | 157 raise error.Abort(b'\n' + b'\n'.join(recipients)) |
158 except smtplib.SMTPException as inst: | 158 except smtplib.SMTPException as inst: |
159 raise error.Abort(inst) | 159 raise error.Abort(inst) |
160 | 160 |
161 return send | 161 return send |
162 | 162 |
163 | 163 |
164 def _sendmail(ui, sender, recipients, msg): | 164 def _sendmail(ui, sender, recipients, msg): |
165 '''send mail using sendmail.''' | 165 '''send mail using sendmail.''' |
166 program = ui.config('email', 'method') | 166 program = ui.config(b'email', b'method') |
167 stremail = lambda x: ( | 167 stremail = lambda x: ( |
168 procutil.quote(stringutil.email(encoding.strtolocal(x))) | 168 procutil.quote(stringutil.email(encoding.strtolocal(x))) |
169 ) | 169 ) |
170 cmdline = '%s -f %s %s' % ( | 170 cmdline = b'%s -f %s %s' % ( |
171 program, | 171 program, |
172 stremail(sender), | 172 stremail(sender), |
173 ' '.join(map(stremail, recipients)), | 173 b' '.join(map(stremail, recipients)), |
174 ) | 174 ) |
175 ui.note(_('sending mail: %s\n') % cmdline) | 175 ui.note(_(b'sending mail: %s\n') % cmdline) |
176 fp = procutil.popen(cmdline, 'wb') | 176 fp = procutil.popen(cmdline, b'wb') |
177 fp.write(util.tonativeeol(msg)) | 177 fp.write(util.tonativeeol(msg)) |
178 ret = fp.close() | 178 ret = fp.close() |
179 if ret: | 179 if ret: |
180 raise error.Abort( | 180 raise error.Abort( |
181 '%s %s' | 181 b'%s %s' |
182 % ( | 182 % ( |
183 os.path.basename(program.split(None, 1)[0]), | 183 os.path.basename(program.split(None, 1)[0]), |
184 procutil.explainexit(ret), | 184 procutil.explainexit(ret), |
185 ) | 185 ) |
186 ) | 186 ) |
187 | 187 |
188 | 188 |
189 def _mbox(mbox, sender, recipients, msg): | 189 def _mbox(mbox, sender, recipients, msg): |
190 '''write mails to mbox''' | 190 '''write mails to mbox''' |
191 fp = open(mbox, 'ab+') | 191 fp = open(mbox, b'ab+') |
192 # Should be time.asctime(), but Windows prints 2-characters day | 192 # Should be time.asctime(), but Windows prints 2-characters day |
193 # of month instead of one. Make them print the same thing. | 193 # of month instead of one. Make them print the same thing. |
194 date = time.strftime(r'%a %b %d %H:%M:%S %Y', time.localtime()) | 194 date = time.strftime(r'%a %b %d %H:%M:%S %Y', time.localtime()) |
195 fp.write( | 195 fp.write( |
196 'From %s %s\n' | 196 b'From %s %s\n' |
197 % (encoding.strtolocal(sender), encoding.strtolocal(date)) | 197 % (encoding.strtolocal(sender), encoding.strtolocal(date)) |
198 ) | 198 ) |
199 fp.write(msg) | 199 fp.write(msg) |
200 fp.write('\n\n') | 200 fp.write(b'\n\n') |
201 fp.close() | 201 fp.close() |
202 | 202 |
203 | 203 |
204 def connect(ui, mbox=None): | 204 def connect(ui, mbox=None): |
205 '''make a mail connection. return a function to send mail. | 205 '''make a mail connection. return a function to send mail. |
206 call as sendmail(sender, list-of-recipients, msg).''' | 206 call as sendmail(sender, list-of-recipients, msg).''' |
207 if mbox: | 207 if mbox: |
208 open(mbox, 'wb').close() | 208 open(mbox, b'wb').close() |
209 return lambda s, r, m: _mbox(mbox, s, r, m) | 209 return lambda s, r, m: _mbox(mbox, s, r, m) |
210 if ui.config('email', 'method') == 'smtp': | 210 if ui.config(b'email', b'method') == b'smtp': |
211 return _smtp(ui) | 211 return _smtp(ui) |
212 return lambda s, r, m: _sendmail(ui, s, r, m) | 212 return lambda s, r, m: _sendmail(ui, s, r, m) |
213 | 213 |
214 | 214 |
215 def sendmail(ui, sender, recipients, msg, mbox=None): | 215 def sendmail(ui, sender, recipients, msg, mbox=None): |
217 return send(sender, recipients, msg) | 217 return send(sender, recipients, msg) |
218 | 218 |
219 | 219 |
220 def validateconfig(ui): | 220 def validateconfig(ui): |
221 '''determine if we have enough config data to try sending email.''' | 221 '''determine if we have enough config data to try sending email.''' |
222 method = ui.config('email', 'method') | 222 method = ui.config(b'email', b'method') |
223 if method == 'smtp': | 223 if method == b'smtp': |
224 if not ui.config('smtp', 'host'): | 224 if not ui.config(b'smtp', b'host'): |
225 raise error.Abort( | 225 raise error.Abort( |
226 _( | 226 _( |
227 'smtp specified as email transport, ' | 227 b'smtp specified as email transport, ' |
228 'but no smtp host configured' | 228 b'but no smtp host configured' |
229 ) | 229 ) |
230 ) | 230 ) |
231 else: | 231 else: |
232 if not procutil.findexe(method): | 232 if not procutil.findexe(method): |
233 raise error.Abort( | 233 raise error.Abort( |
234 _('%r specified as email transport, ' 'but not in PATH') | 234 _(b'%r specified as email transport, ' b'but not in PATH') |
235 % method | 235 % method |
236 ) | 236 ) |
237 | 237 |
238 | 238 |
239 def codec2iana(cs): | 239 def codec2iana(cs): |
240 '''''' | 240 '''''' |
241 cs = pycompat.sysbytes(email.charset.Charset(cs).input_charset.lower()) | 241 cs = pycompat.sysbytes(email.charset.Charset(cs).input_charset.lower()) |
242 | 242 |
243 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1" | 243 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1" |
244 if cs.startswith("iso") and not cs.startswith("iso-"): | 244 if cs.startswith(b"iso") and not cs.startswith(b"iso-"): |
245 return "iso-" + cs[3:] | 245 return b"iso-" + cs[3:] |
246 return cs | 246 return cs |
247 | 247 |
248 | 248 |
249 def mimetextpatch(s, subtype='plain', display=False): | 249 def mimetextpatch(s, subtype=b'plain', display=False): |
250 '''Return MIME message suitable for a patch. | 250 '''Return MIME message suitable for a patch. |
251 Charset will be detected by first trying to decode as us-ascii, then utf-8, | 251 Charset will be detected by first trying to decode as us-ascii, then utf-8, |
252 and finally the global encodings. If all those fail, fall back to | 252 and finally the global encodings. If all those fail, fall back to |
253 ISO-8859-1, an encoding with that allows all byte sequences. | 253 ISO-8859-1, an encoding with that allows all byte sequences. |
254 Transfer encodings will be used if necessary.''' | 254 Transfer encodings will be used if necessary.''' |
255 | 255 |
256 cs = ['us-ascii', 'utf-8', encoding.encoding, encoding.fallbackencoding] | 256 cs = [b'us-ascii', b'utf-8', encoding.encoding, encoding.fallbackencoding] |
257 if display: | 257 if display: |
258 cs = ['us-ascii'] | 258 cs = [b'us-ascii'] |
259 for charset in cs: | 259 for charset in cs: |
260 try: | 260 try: |
261 s.decode(pycompat.sysstr(charset)) | 261 s.decode(pycompat.sysstr(charset)) |
262 return mimetextqp(s, subtype, codec2iana(charset)) | 262 return mimetextqp(s, subtype, codec2iana(charset)) |
263 except UnicodeDecodeError: | 263 except UnicodeDecodeError: |
264 pass | 264 pass |
265 | 265 |
266 return mimetextqp(s, subtype, "iso-8859-1") | 266 return mimetextqp(s, subtype, b"iso-8859-1") |
267 | 267 |
268 | 268 |
269 def mimetextqp(body, subtype, charset): | 269 def mimetextqp(body, subtype, charset): |
270 '''Return MIME message. | 270 '''Return MIME message. |
271 Quoted-printable transfer encoding will be used if necessary. | 271 Quoted-printable transfer encoding will be used if necessary. |
272 ''' | 272 ''' |
273 cs = email.charset.Charset(charset) | 273 cs = email.charset.Charset(charset) |
274 msg = email.message.Message() | 274 msg = email.message.Message() |
275 msg.set_type(pycompat.sysstr('text/' + subtype)) | 275 msg.set_type(pycompat.sysstr(b'text/' + subtype)) |
276 | 276 |
277 for line in body.splitlines(): | 277 for line in body.splitlines(): |
278 if len(line) > 950: | 278 if len(line) > 950: |
279 cs.body_encoding = email.charset.QP | 279 cs.body_encoding = email.charset.QP |
280 break | 280 break |
291 return msg | 291 return msg |
292 | 292 |
293 | 293 |
294 def _charsets(ui): | 294 def _charsets(ui): |
295 '''Obtains charsets to send mail parts not containing patches.''' | 295 '''Obtains charsets to send mail parts not containing patches.''' |
296 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')] | 296 charsets = [cs.lower() for cs in ui.configlist(b'email', b'charsets')] |
297 fallbacks = [ | 297 fallbacks = [ |
298 encoding.fallbackencoding.lower(), | 298 encoding.fallbackencoding.lower(), |
299 encoding.encoding.lower(), | 299 encoding.encoding.lower(), |
300 'utf-8', | 300 b'utf-8', |
301 ] | 301 ] |
302 for cs in fallbacks: # find unique charsets while keeping order | 302 for cs in fallbacks: # find unique charsets while keeping order |
303 if cs not in charsets: | 303 if cs not in charsets: |
304 charsets.append(cs) | 304 charsets.append(cs) |
305 return [cs for cs in charsets if not cs.endswith('ascii')] | 305 return [cs for cs in charsets if not cs.endswith(b'ascii')] |
306 | 306 |
307 | 307 |
308 def _encode(ui, s, charsets): | 308 def _encode(ui, s, charsets): |
309 '''Returns (converted) string, charset tuple. | 309 '''Returns (converted) string, charset tuple. |
310 Finds out best charset by cycling through sendcharsets in descending | 310 Finds out best charset by cycling through sendcharsets in descending |
320 try: | 320 try: |
321 return s.encode(pycompat.sysstr(ocs)), ocs | 321 return s.encode(pycompat.sysstr(ocs)), ocs |
322 except UnicodeEncodeError: | 322 except UnicodeEncodeError: |
323 pass | 323 pass |
324 except LookupError: | 324 except LookupError: |
325 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs) | 325 ui.warn(_(b'ignoring invalid sendcharset: %s\n') % ocs) |
326 else: | 326 else: |
327 # Everything failed, ascii-armor what we've got and send it. | 327 # Everything failed, ascii-armor what we've got and send it. |
328 return s.encode('ascii', 'backslashreplace') | 328 return s.encode('ascii', 'backslashreplace') |
329 # We have a bytes of unknown encoding. We'll try and guess a valid | 329 # We have a bytes of unknown encoding. We'll try and guess a valid |
330 # encoding, falling back to pretending we had ascii even though we | 330 # encoding, falling back to pretending we had ascii even though we |
341 try: | 341 try: |
342 return u.encode(pycompat.sysstr(ocs)), ocs | 342 return u.encode(pycompat.sysstr(ocs)), ocs |
343 except UnicodeEncodeError: | 343 except UnicodeEncodeError: |
344 pass | 344 pass |
345 except LookupError: | 345 except LookupError: |
346 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs) | 346 ui.warn(_(b'ignoring invalid sendcharset: %s\n') % ocs) |
347 # if ascii, or all conversion attempts fail, send (broken) ascii | 347 # if ascii, or all conversion attempts fail, send (broken) ascii |
348 return s, 'us-ascii' | 348 return s, b'us-ascii' |
349 | 349 |
350 | 350 |
351 def headencode(ui, s, charsets=None, display=False): | 351 def headencode(ui, s, charsets=None, display=False): |
352 '''Returns RFC-2047 compliant header from given string.''' | 352 '''Returns RFC-2047 compliant header from given string.''' |
353 if not display: | 353 if not display: |
359 | 359 |
360 def _addressencode(ui, name, addr, charsets=None): | 360 def _addressencode(ui, name, addr, charsets=None): |
361 assert isinstance(addr, bytes) | 361 assert isinstance(addr, bytes) |
362 name = headencode(ui, name, charsets) | 362 name = headencode(ui, name, charsets) |
363 try: | 363 try: |
364 acc, dom = addr.split('@') | 364 acc, dom = addr.split(b'@') |
365 acc.decode('ascii') | 365 acc.decode('ascii') |
366 dom = dom.decode(pycompat.sysstr(encoding.encoding)).encode('idna') | 366 dom = dom.decode(pycompat.sysstr(encoding.encoding)).encode('idna') |
367 addr = '%s@%s' % (acc, dom) | 367 addr = b'%s@%s' % (acc, dom) |
368 except UnicodeDecodeError: | 368 except UnicodeDecodeError: |
369 raise error.Abort(_('invalid email address: %s') % addr) | 369 raise error.Abort(_(b'invalid email address: %s') % addr) |
370 except ValueError: | 370 except ValueError: |
371 try: | 371 try: |
372 # too strict? | 372 # too strict? |
373 addr.decode('ascii') | 373 addr.decode('ascii') |
374 except UnicodeDecodeError: | 374 except UnicodeDecodeError: |
375 raise error.Abort(_('invalid local address: %s') % addr) | 375 raise error.Abort(_(b'invalid local address: %s') % addr) |
376 return pycompat.bytesurl( | 376 return pycompat.bytesurl( |
377 email.utils.formataddr((name, encoding.strfromlocal(addr))) | 377 email.utils.formataddr((name, encoding.strfromlocal(addr))) |
378 ) | 378 ) |
379 | 379 |
380 | 380 |
381 def addressencode(ui, address, charsets=None, display=False): | 381 def addressencode(ui, address, charsets=None, display=False): |
382 '''Turns address into RFC-2047 compliant header.''' | 382 '''Turns address into RFC-2047 compliant header.''' |
383 if display or not address: | 383 if display or not address: |
384 return address or '' | 384 return address or b'' |
385 name, addr = email.utils.parseaddr(encoding.strfromlocal(address)) | 385 name, addr = email.utils.parseaddr(encoding.strfromlocal(address)) |
386 return _addressencode(ui, name, encoding.strtolocal(addr), charsets) | 386 return _addressencode(ui, name, encoding.strtolocal(addr), charsets) |
387 | 387 |
388 | 388 |
389 def addrlistencode(ui, addrs, charsets=None, display=False): | 389 def addrlistencode(ui, addrs, charsets=None, display=False): |
406 | 406 |
407 | 407 |
408 def mimeencode(ui, s, charsets=None, display=False): | 408 def mimeencode(ui, s, charsets=None, display=False): |
409 '''creates mime text object, encodes it if needed, and sets | 409 '''creates mime text object, encodes it if needed, and sets |
410 charset and transfer-encoding accordingly.''' | 410 charset and transfer-encoding accordingly.''' |
411 cs = 'us-ascii' | 411 cs = b'us-ascii' |
412 if not display: | 412 if not display: |
413 s, cs = _encode(ui, s, charsets) | 413 s, cs = _encode(ui, s, charsets) |
414 return mimetextqp(s, 'plain', cs) | 414 return mimetextqp(s, b'plain', cs) |
415 | 415 |
416 | 416 |
417 if pycompat.ispy3: | 417 if pycompat.ispy3: |
418 | 418 |
419 def parse(fp): | 419 def parse(fp): |