##// END OF EJS Templates
mail: let all charset values be native strings...
Denis Laxalde -
r44025:bdb0ddab default
parent child Browse files
Show More
@@ -253,17 +253,13 b' def validateconfig(ui):'
253
253
254
254
255 def codec2iana(cs):
255 def codec2iana(cs):
256 # type: (bytes) -> bytes
256 # type: (str) -> str
257 ''''''
257 ''''''
258 cs = pycompat.sysbytes(
258 cs = email.charset.Charset(cs).input_charset.lower()
259 email.charset.Charset(
260 cs # pytype: disable=wrong-arg-types
261 ).input_charset.lower()
262 )
263
259
264 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1"
260 # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1"
265 if cs.startswith(b"iso") and not cs.startswith(b"iso-"):
261 if cs.startswith("iso") and not cs.startswith("iso-"):
266 return b"iso-" + cs[3:]
262 return "iso-" + cs[3:]
267 return cs
263 return cs
268
264
269
265
@@ -275,27 +271,30 b" def mimetextpatch(s, subtype=b'plain', d"
275 ISO-8859-1, an encoding with that allows all byte sequences.
271 ISO-8859-1, an encoding with that allows all byte sequences.
276 Transfer encodings will be used if necessary.'''
272 Transfer encodings will be used if necessary.'''
277
273
278 cs = [b'us-ascii', b'utf-8', encoding.encoding, encoding.fallbackencoding]
274 cs = [
275 'us-ascii',
276 'utf-8',
277 pycompat.sysstr(encoding.encoding),
278 pycompat.sysstr(encoding.fallbackencoding),
279 ]
279 if display:
280 if display:
280 cs = [b'us-ascii']
281 cs = ['us-ascii']
281 for charset in cs:
282 for charset in cs:
282 try:
283 try:
283 s.decode(pycompat.sysstr(charset))
284 s.decode(charset)
284 return mimetextqp(s, subtype, codec2iana(charset))
285 return mimetextqp(s, subtype, codec2iana(charset))
285 except UnicodeDecodeError:
286 except UnicodeDecodeError:
286 pass
287 pass
287
288
288 return mimetextqp(s, subtype, b"iso-8859-1")
289 return mimetextqp(s, subtype, "iso-8859-1")
289
290
290
291
291 def mimetextqp(body, subtype, charset):
292 def mimetextqp(body, subtype, charset):
292 # type: (bytes, bytes, bytes) -> email.message.Message
293 # type: (bytes, bytes, str) -> email.message.Message
293 '''Return MIME message.
294 '''Return MIME message.
294 Quoted-printable transfer encoding will be used if necessary.
295 Quoted-printable transfer encoding will be used if necessary.
295 '''
296 '''
296 # Experimentally charset is okay as a bytes even if the type
297 cs = email.charset.Charset(charset)
297 # stubs disagree.
298 cs = email.charset.Charset(charset) # pytype: disable=wrong-arg-types
299 msg = email.message.Message()
298 msg = email.message.Message()
300 msg.set_type(pycompat.sysstr(b'text/' + subtype))
299 msg.set_type(pycompat.sysstr(b'text/' + subtype))
301
300
@@ -317,24 +316,25 b' def mimetextqp(body, subtype, charset):'
317
316
318
317
319 def _charsets(ui):
318 def _charsets(ui):
320 # type: (Any) -> List[bytes]
319 # type: (Any) -> List[str]
321 '''Obtains charsets to send mail parts not containing patches.'''
320 '''Obtains charsets to send mail parts not containing patches.'''
322 charsets = [
321 charsets = [
323 cs.lower() for cs in ui.configlist(b'email', b'charsets')
322 pycompat.sysstr(cs.lower())
324 ] # type: List[bytes]
323 for cs in ui.configlist(b'email', b'charsets')
324 ]
325 fallbacks = [
325 fallbacks = [
326 encoding.fallbackencoding.lower(),
326 pycompat.sysstr(encoding.fallbackencoding.lower()),
327 encoding.encoding.lower(),
327 pycompat.sysstr(encoding.encoding.lower()),
328 b'utf-8',
328 'utf-8',
329 ] # type: List[bytes]
329 ]
330 for cs in fallbacks: # find unique charsets while keeping order
330 for cs in fallbacks: # find unique charsets while keeping order
331 if cs not in charsets:
331 if cs not in charsets:
332 charsets.append(cs)
332 charsets.append(cs)
333 return [cs for cs in charsets if not cs.endswith(b'ascii')]
333 return [cs for cs in charsets if not cs.endswith('ascii')]
334
334
335
335
336 def _encode(ui, s, charsets):
336 def _encode(ui, s, charsets):
337 # type: (Any, bytes, List[bytes]) -> Tuple[bytes, bytes]
337 # type: (Any, bytes, List[str]) -> Tuple[bytes, str]
338 '''Returns (converted) string, charset tuple.
338 '''Returns (converted) string, charset tuple.
339 Finds out best charset by cycling through sendcharsets in descending
339 Finds out best charset by cycling through sendcharsets in descending
340 order. Tries both encoding and fallbackencoding for input. Only as
340 order. Tries both encoding and fallbackencoding for input. Only as
@@ -347,14 +347,17 b' def _encode(ui, s, charsets):'
347 # wants, and fall back to garbage-in-ascii.
347 # wants, and fall back to garbage-in-ascii.
348 for ocs in sendcharsets:
348 for ocs in sendcharsets:
349 try:
349 try:
350 return s.encode(pycompat.sysstr(ocs)), ocs
350 return s.encode(ocs), ocs
351 except UnicodeEncodeError:
351 except UnicodeEncodeError:
352 pass
352 pass
353 except LookupError:
353 except LookupError:
354 ui.warn(_(b'ignoring invalid sendcharset: %s\n') % ocs)
354 ui.warn(
355 _(b'ignoring invalid sendcharset: %s\n')
356 % pycompat.sysbytes(ocs)
357 )
355 else:
358 else:
356 # Everything failed, ascii-armor what we've got and send it.
359 # Everything failed, ascii-armor what we've got and send it.
357 return s.encode('ascii', 'backslashreplace'), b'us-ascii'
360 return s.encode('ascii', 'backslashreplace'), 'us-ascii'
358 # We have a bytes of unknown encoding. We'll try and guess a valid
361 # We have a bytes of unknown encoding. We'll try and guess a valid
359 # encoding, falling back to pretending we had ascii even though we
362 # encoding, falling back to pretending we had ascii even though we
360 # know that's wrong.
363 # know that's wrong.
@@ -369,29 +372,30 b' def _encode(ui, s, charsets):'
369 continue
372 continue
370 for ocs in sendcharsets:
373 for ocs in sendcharsets:
371 try:
374 try:
372 return u.encode(pycompat.sysstr(ocs)), ocs
375 return u.encode(ocs), ocs
373 except UnicodeEncodeError:
376 except UnicodeEncodeError:
374 pass
377 pass
375 except LookupError:
378 except LookupError:
376 ui.warn(_(b'ignoring invalid sendcharset: %s\n') % ocs)
379 ui.warn(
380 _(b'ignoring invalid sendcharset: %s\n')
381 % pycompat.sysbytes(ocs)
382 )
377 # if ascii, or all conversion attempts fail, send (broken) ascii
383 # if ascii, or all conversion attempts fail, send (broken) ascii
378 return s, b'us-ascii'
384 return s, 'us-ascii'
379
385
380
386
381 def headencode(ui, s, charsets=None, display=False):
387 def headencode(ui, s, charsets=None, display=False):
382 # type: (Any, Union[bytes, str], List[bytes], bool) -> str
388 # type: (Any, Union[bytes, str], List[str], bool) -> str
383 '''Returns RFC-2047 compliant header from given string.'''
389 '''Returns RFC-2047 compliant header from given string.'''
384 if not display:
390 if not display:
385 # split into words?
391 # split into words?
386 s, cs = _encode(ui, s, charsets)
392 s, cs = _encode(ui, s, charsets)
387 return email.header.Header(
393 return email.header.Header(s, cs).encode()
388 s, cs # pytype: disable=wrong-arg-types
389 ).encode()
390 return encoding.strfromlocal(s)
394 return encoding.strfromlocal(s)
391
395
392
396
393 def _addressencode(ui, name, addr, charsets=None):
397 def _addressencode(ui, name, addr, charsets=None):
394 # type: (Any, str, bytes, List[bytes]) -> str
398 # type: (Any, str, bytes, List[str]) -> str
395 assert isinstance(addr, bytes)
399 assert isinstance(addr, bytes)
396 name = headencode(ui, name, charsets)
400 name = headencode(ui, name, charsets)
397 try:
401 try:
@@ -411,7 +415,7 b' def _addressencode(ui, name, addr, chars'
411
415
412
416
413 def addressencode(ui, address, charsets=None, display=False):
417 def addressencode(ui, address, charsets=None, display=False):
414 # type: (Any, bytes, List[bytes], bool) -> str
418 # type: (Any, bytes, List[str], bool) -> str
415 '''Turns address into RFC-2047 compliant header.'''
419 '''Turns address into RFC-2047 compliant header.'''
416 if display or not address:
420 if display or not address:
417 return encoding.strfromlocal(address or b'')
421 return encoding.strfromlocal(address or b'')
@@ -420,7 +424,7 b' def addressencode(ui, address, charsets='
420
424
421
425
422 def addrlistencode(ui, addrs, charsets=None, display=False):
426 def addrlistencode(ui, addrs, charsets=None, display=False):
423 # type: (Any, List[bytes], List[bytes], bool) -> List[str]
427 # type: (Any, List[bytes], List[str], bool) -> List[str]
424 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
428 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
425 A single element of input list may contain multiple addresses, but output
429 A single element of input list may contain multiple addresses, but output
426 always has one address per item'''
430 always has one address per item'''
@@ -440,10 +444,10 b' def addrlistencode(ui, addrs, charsets=N'
440
444
441
445
442 def mimeencode(ui, s, charsets=None, display=False):
446 def mimeencode(ui, s, charsets=None, display=False):
443 # type: (Any, bytes, List[bytes], bool) -> email.message.Message
447 # type: (Any, bytes, List[str], bool) -> email.message.Message
444 '''creates mime text object, encodes it if needed, and sets
448 '''creates mime text object, encodes it if needed, and sets
445 charset and transfer-encoding accordingly.'''
449 charset and transfer-encoding accordingly.'''
446 cs = b'us-ascii'
450 cs = 'us-ascii'
447 if not display:
451 if not display:
448 s, cs = _encode(ui, s, charsets)
452 s, cs = _encode(ui, s, charsets)
449 return mimetextqp(s, b'plain', cs)
453 return mimetextqp(s, b'plain', cs)
General Comments 0
You need to be logged in to leave comments. Login now