mail.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. """Mail handling
  2. @copyright: 2022 Ansgar <ansgar@debian.org>
  3. @license: GNU General Public License version 2 or later
  4. """
  5. import email
  6. import email.message
  7. import email.policy
  8. from typing import cast
  9. import daklib.gpg
  10. def sign_mail(
  11. msg: email.message.EmailMessage, *, digest_algorithm: str = "SHA256", **kwargs
  12. ) -> email.message.EmailMessage:
  13. """sign an email message using GnuPG.
  14. This only handles non-multipart messages.
  15. """
  16. mime_data = email.message.MIMEPart()
  17. mime_data.set_content(msg.get_payload())
  18. # Copy Content-Transfer-Encoding from unsigned message
  19. del mime_data["Content-Transfer-Encoding"]
  20. mime_data["Content-Transfer-Encoding"] = msg["Content-Transfer-Encoding"]
  21. data = mime_data.as_bytes(policy=email.policy.SMTP)
  22. sig = daklib.gpg.sign(data, **kwargs, digest_algorithm=digest_algorithm)
  23. mime_sig = email.message.MIMEPart()
  24. mime_sig["Content-Type"] = "application/pgp-signature"
  25. mime_sig.set_payload(sig)
  26. msg.clear_content()
  27. del msg["Content-Type"]
  28. msg["Content-Type"] = (
  29. f'multipart/signed; micalg="pgp-{digest_algorithm.lower()}"; protocol="application/pgp-signature"'
  30. )
  31. msg.set_payload([mime_data, mime_sig])
  32. return msg
  33. # TODO [python3.10, pep604]:
  34. # def parse_mail(msg: bytes | str) -> email.message.EmailMessage:
  35. def parse_mail(msg) -> email.message.EmailMessage:
  36. if isinstance(msg, str):
  37. # We need a cast as the return type depends on the `policy` argument.
  38. return cast(
  39. email.message.EmailMessage,
  40. email.message_from_string(msg, policy=email.policy.SMTPUTF8),
  41. )
  42. else:
  43. # We need a cast as the return type depends on the `policy` argument.
  44. return cast(
  45. email.message.EmailMessage,
  46. email.message_from_bytes(msg, policy=email.policy.SMTPUTF8),
  47. )