1"""Mail handling 

2 

3@copyright: 2022 Ansgar <ansgar@debian.org> 

4@license: GNU General Public License version 2 or later 

5""" 

6 

7import daklib.gpg 

8 

9import email 

10import email.message 

11import email.policy 

12 

13from typing import cast 

14 

15 

16def sign_mail(msg: email.message.EmailMessage, *, digest_algorithm: str = "SHA256", **kwargs) -> email.message.EmailMessage: 

17 """sign an email message using GnuPG. 

18 

19 This only handles non-multipart messages. 

20 """ 

21 mime_data = email.message.MIMEPart() 

22 mime_data.set_content(msg.get_payload()) 

23 # Copy Content-Transfer-Encoding from unsigned message 

24 del mime_data["Content-Transfer-Encoding"] 

25 mime_data["Content-Transfer-Encoding"] = msg["Content-Transfer-Encoding"] 

26 data = mime_data.as_bytes(policy=email.policy.SMTP) 

27 

28 sig = daklib.gpg.sign(data, **kwargs, digest_algorithm=digest_algorithm) 

29 mime_sig = email.message.MIMEPart() 

30 mime_sig['Content-Type'] = 'application/pgp-signature' 

31 mime_sig.set_payload(sig) 

32 

33 msg.clear_content() 

34 del msg['Content-Type'] 

35 msg['Content-Type'] = f'multipart/signed; micalg="pgp-{digest_algorithm.lower()}"; protocol="application/pgp-signature"' 

36 msg.set_payload([mime_data, mime_sig]) 

37 return msg 

38 

39 

40# TODO [python3.10, pep604]: 

41# def parse_mail(msg: bytes | str) -> email.message.EmailMessage: 

42def parse_mail(msg) -> email.message.EmailMessage: 

43 if isinstance(msg, str): 43 ↛ 48line 43 didn't jump to line 48, because the condition on line 43 was never false

44 # We need a cast as the return type depends on the `policy` argument. 

45 return cast(email.message.EmailMessage, email.message_from_string(msg, policy=email.policy.SMTPUTF8)) 

46 else: 

47 # We need a cast as the return type depends on the `policy` argument. 

48 return cast(email.message.EmailMessage, email.message_from_bytes(msg, policy=email.policy.SMTPUTF8))