8
8
import sys
9
9
from tempfile import NamedTemporaryFile
10
10
from unittest import mock
11
- import warnings
12
- from base64 import b64decode , b64encode
11
+ from base64 import b64decode
13
12
from configparser import ConfigParser
14
13
from copy import deepcopy
15
14
from email import message_from_bytes
16
15
from email .header import decode_header
17
16
from email .generator import Generator
18
17
from email .message import EmailMessage , Message
19
18
from email .parser import BytesParser
20
- from email .utils import make_msgid , formatdate , getaddresses
21
- from getpass import getpass
19
+ from email .utils import make_msgid , formatdate
22
20
from itertools import chain
23
- from os import environ , urandom
21
+ from os import environ
24
22
from pathlib import Path
25
23
from quopri import decodestring
26
24
from types import GeneratorType
27
- from typing import Literal , Union , List , Set , Optional , Any
25
+ from typing import Literal , Union , Optional , Any
28
26
29
- from .address import Address
27
+ from .address import Address , _getaddresses
30
28
from .attachment import Attachment
31
29
from .constants import ISSUE_LINK , smime_import_error , gnupg , CRLF , AUTO , PLAIN , HTML , SIMULATION , SAFE_LOCALE
32
30
from .message import _Message
35
33
from .utils import AutoSubmittedHeader , Fetched , is_gpg_importable_key , assure_list , assure_fetched , get_mimetype
36
34
37
35
38
-
39
36
__doc__ = """Quick layer over python-gnupg, M2Crypto, smtplib, magic and email handling packages.
40
37
Their common use cases merged into a single function. Want to sign a text and tired of forgetting how to do it right?
41
38
You do not need to know everything about GPG or S/MIME, you do not have to bother with importing keys.
@@ -320,14 +317,14 @@ def __init__(self, message=None, from_=None, to=None, subject=None, headers=None
320
317
# that explicitly states we have no from header
321
318
self ._from : Union [Address , False , None ] = None # e-mail From header
322
319
self ._from_addr : Optional [Address ] = None # SMTP envelope MAIL FROM address
323
- self ._to : List [Address ] = []
324
- self ._cc : List [Address ] = []
325
- self ._bcc : List [Address ] = []
326
- self ._reply_to : List [Address ] = []
320
+ self ._to : list [Address ] = []
321
+ self ._cc : list [Address ] = []
322
+ self ._bcc : list [Address ] = []
323
+ self ._reply_to : list [Address ] = []
327
324
self ._subject : Union [str , None ] = None
328
325
self ._subject_encrypted : Union [str , bool ] = True
329
326
self ._smtp = None
330
- self ._attachments : List [Attachment ] = []
327
+ self ._attachments : list [Attachment ] = []
331
328
self ._mime = AUTO
332
329
self ._nl2br = AUTO
333
330
self ._headers = EmailMessage () # object for storing headers the most standard way possible
@@ -336,7 +333,7 @@ def __init__(self, message=None, from_=None, to=None, subject=None, headers=None
336
333
# variables defined while processing
337
334
self ._status : bool = False # whether we successfully encrypted/signed/send
338
335
self ._processed : bool = False # prevent the user from mistakenly call .sign().send() instead of .signature().send()
339
- self ._result : List [Union [str , EmailMessage , Message ]] = [] # text output for str() conversion
336
+ self ._result : list [Union [str , EmailMessage , Message ]] = [] # text output for str() conversion
340
337
self ._result_cache : Optional [str ] = None
341
338
self ._result_cache_hash : Optional [int ] = None
342
339
self ._smtp = SMTPHandler ()
@@ -407,32 +404,32 @@ def _parse_addresses(registry, email_or_more):
407
404
if addresses :
408
405
registry += (a for a in Address .parse (addresses ) if a not in registry )
409
406
410
- def to (self , email_or_more = None ) -> Union ["Envelope" , List [Address ]]:
407
+ def to (self , email_or_more = None ) -> Union ["Envelope" , list [Address ]]:
411
408
""" Multiple addresses may be given in a string, delimited by comma (or semicolon).
412
409
(The same is valid for `to`, `cc`, `bcc` and `reply-to`.)
413
410
414
- :param email_or_more: str|Tuple[str]|List [str]|Generator[str]|Set[str]|Frozenset[str]
411
+ :param email_or_more: str|Tuple[str]|list [str]|Generator[str]|Set[str]|Frozenset[str]
415
412
Set e-mail address/es. If None, we are reading.
416
- return: Envelope if `email_or_more` set or List [Address] if not set
413
+ return: Envelope if `email_or_more` set or list [Address] if not set
417
414
"""
418
415
if email_or_more is None :
419
416
return self ._to
420
417
self ._parse_addresses (self ._to , email_or_more )
421
418
return self
422
419
423
- def cc (self , email_or_more = None ) -> Union ["Envelope" , List [Address ]]:
420
+ def cc (self , email_or_more = None ) -> Union ["Envelope" , list [Address ]]:
424
421
if email_or_more is None :
425
422
return self ._cc
426
423
self ._parse_addresses (self ._cc , email_or_more )
427
424
return self
428
425
429
- def bcc (self , email_or_more = None ) -> Union ["Envelope" , List [Address ]]:
426
+ def bcc (self , email_or_more = None ) -> Union ["Envelope" , list [Address ]]:
430
427
if email_or_more is None :
431
428
return self ._bcc
432
429
self ._parse_addresses (self ._bcc , email_or_more )
433
430
return self
434
431
435
- def reply_to (self , email_or_more = None ) -> Union ["Envelope" , List [Address ]]:
432
+ def reply_to (self , email_or_more = None ) -> Union ["Envelope" , list [Address ]]:
436
433
if email_or_more is None :
437
434
return self ._reply_to
438
435
self ._parse_addresses (self ._reply_to , email_or_more )
@@ -586,7 +583,7 @@ def subject(self, subject=None, encrypted: Union[str, bool] = None) -> Union["En
586
583
self ._subject_encrypted = encrypted
587
584
return self
588
585
589
- def mime (self , subtype = AUTO , nl2br : Literal ["auto" ] | bool = AUTO ):
586
+ def mime (self , subtype = AUTO , nl2br : Literal ["auto" ] | bool = AUTO ):
590
587
"""
591
588
Ignored if `Content-Type` header put to the message.
592
589
@type subtype: str Set contents mime subtype: "auto" (default), "html" or "plain" for plain text.
@@ -1084,7 +1081,6 @@ def _gpg_import_or_fail(self, key):
1084
1081
else :
1085
1082
raise ValueError (f"Could not import key starting: { key [:80 ]} ..." )
1086
1083
1087
-
1088
1084
def _get_gnupg_home (self , for_help = False ):
1089
1085
s = self ._gpg if type (self ._gpg ) is str else None
1090
1086
if for_help :
@@ -1236,7 +1232,7 @@ def _encrypt_gpg_now(self, message, encrypt, sign_fingerprint):
1236
1232
return False
1237
1233
1238
1234
def _gpg_list_keys (self , secret = False ):
1239
- return ((key , address ) for key in self ._gnupg .list_keys (secret ) for _ , address in getaddresses (key ["uids" ]))
1235
+ return ((key , address ) for key in self ._gnupg .list_keys (secret ) for _ , address in _getaddresses (key ["uids" ]))
1240
1236
1241
1237
def _gpg_verify (self , signature : bytes , data : bytes ):
1242
1238
""" Allows verifying detached GPG signature.
@@ -1250,7 +1246,7 @@ def _gpg_verify(self, signature: bytes, data: bytes):
1250
1246
fp .seek (0 )
1251
1247
return bool (self ._gnupg .verify_data (fp .name , data ))
1252
1248
1253
- def _get_decipherers (self ) -> Set [str ]:
1249
+ def _get_decipherers (self ) -> set [str ]:
1254
1250
"""
1255
1251
:return: Set of e-mail addresses
1256
1252
"""
@@ -1299,7 +1295,6 @@ def smime_sign_only(self, email, sign):
1299
1295
1300
1296
return signed_email
1301
1297
1302
-
1303
1298
def smime_sign_encrypt (self , email , sign , encrypt ):
1304
1299
from cryptography .hazmat .primitives .serialization import load_pem_private_key , pkcs7
1305
1300
from cryptography .x509 import load_pem_x509_certificate
@@ -1347,7 +1342,7 @@ def smime_sign_encrypt(self, email, sign, encrypt):
1347
1342
raise ValueError ("failed to load certificate from file" )
1348
1343
1349
1344
recipient_certs .append (c )
1350
-
1345
+
1351
1346
try :
1352
1347
pubkey = load_pem_x509_certificate (pubkey )
1353
1348
except ValueError as e :
@@ -1360,7 +1355,6 @@ def smime_sign_encrypt(self, email, sign, encrypt):
1360
1355
for recip in recipient_certs :
1361
1356
envelope_builder = envelope_builder .add_recipient (recip )
1362
1357
1363
-
1364
1358
options = [pkcs7 .PKCS7Options .Binary ]
1365
1359
encrypted_email = envelope_builder .encrypt (serialization .Encoding .SMIME , options )
1366
1360
return encrypted_email
@@ -1371,7 +1365,6 @@ def smime_encrypt_only(self, email, encrypt):
1371
1365
from cryptography .x509 import load_pem_x509_certificate
1372
1366
from cryptography .hazmat .primitives import serialization
1373
1367
1374
-
1375
1368
if self ._cert :
1376
1369
certificates = [self ._cert ]
1377
1370
else :
@@ -1396,16 +1389,15 @@ def smime_encrypt_only(self, email, encrypt):
1396
1389
1397
1390
return encrypted_email
1398
1391
1399
-
1400
- def _encrypt_smime_now (self , email , sign , encrypt : Union [None , bool , bytes , List [bytes ]]):
1392
+ def _encrypt_smime_now (self , email , sign , encrypt : Union [None , bool , bytes , list [bytes ]]):
1401
1393
"""
1402
1394
1403
1395
:type encrypt: Can be None, False, bytes or list[bytes]
1404
1396
"""
1405
1397
1406
1398
# passphrase has to be bytes
1407
1399
if (self ._passphrase is not None ):
1408
- self ._passphrase = self ._passphrase .encode ('utf-8' )
1400
+ self ._passphrase = self ._passphrase .encode ('utf-8' )
1409
1401
1410
1402
if sign is not None and type (sign ) != bool :
1411
1403
sign = assure_fetched (sign , bytes )
@@ -1421,8 +1413,6 @@ def _encrypt_smime_now(self, email, sign, encrypt: Union[None, bool, bytes, List
1421
1413
1422
1414
return output
1423
1415
1424
-
1425
-
1426
1416
def _compose_gpg_signed (self , email , text , micalg = None ):
1427
1417
msg_payload = email
1428
1418
email = EmailMessage ()
@@ -1600,7 +1590,7 @@ def _prepare_email(self, plain: bytes, html: bytes, encrypt_gpg: bool, sign_gpg:
1600
1590
email ["Subject" ] = self ._subject
1601
1591
return email
1602
1592
1603
- def recipients (self , * , clear = False ) -> Union [Set [Address ], 'Envelope' ]:
1593
+ def recipients (self , * , clear = False ) -> Union [set [Address ], 'Envelope' ]:
1604
1594
""" Return set of all recipients – To, Cc, Bcc
1605
1595
:param: clear If true, all To, Cc and Bcc recipients are removed and the object is returned.
1606
1596
@@ -1632,7 +1622,7 @@ def _report(self) -> Union[dict, None]:
1632
1622
raise NotImplemented ("Current multipart/report has not been impemented."
1633
1623
f"Please post current message as a new issue at { ISSUE_LINK } " )
1634
1624
1635
- def attachments (self , name = None , inline = None ) -> Union [Attachment , List [Attachment ], bool ]:
1625
+ def attachments (self , name = None , inline = None ) -> Union [Attachment , list [Attachment ], bool ]:
1636
1626
""" Access the attachments.
1637
1627
XX make available from CLI too
1638
1628
--attachments(-inline)(-enclosed) [name]
0 commit comments