From 182e1ceb43d5cc315a167c1d4bd8f483d35efb8c Mon Sep 17 00:00:00 2001 From: revsuine Date: Fri, 15 Nov 2024 17:40:22 +0000 Subject: [PATCH 1/2] conform to pEp standards --- gpgmymail | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gpgmymail b/gpgmymail index 633dbb1..c0acc7b 100755 --- a/gpgmymail +++ b/gpgmymail @@ -42,6 +42,7 @@ import gnupg # constants DEFAULT_ENCODING='utf-8' # default is latin-1 which fails w some unicode chars +PEP_SUBJECT='=?utf-8?Q?p=E2=89=A1p?=' def is_message_encrypted(message: email.message.Message) -> bool: """Determines whether or not an email message is encrypted. @@ -169,6 +170,27 @@ def decode_email(message: email.message.Message) -> email.message.Message: return decoded_bytes_to_return_value(decoded_bytes) +def set_email_header( + message: email.message.Message, + name: str, + value: str +) -> None: + """ + Set the header of an email Message. Will either replace the first instance + of the header, or if the header is not present, will add the header. + + Note: Python passes objects as references, so there is no need for a return + value. + + :param message: the Message object to be modified + :param name: the email header to set + :param value: the value to set the header to + """ + if message.get(name): + message.replace_header(name, value) + else: + message.add_header(name, value) + def encrypt( message: email.message.Message, recipients: typing.List[str], @@ -254,6 +276,10 @@ def encrypt( if key.lower() not in headers_not_to_override: encmsg[key] = value + # mark as confirming to pEp: https://blog.jak-linux.org/2019/06/13/encrypted-email-storage/#pretty-easy-privacy-pp + set_email_header(encmsg, 'Subject', PEP_SUBJECT) + set_email_header(encmsg, 'X-pEp-Version', '2.1') + return encmsg.as_string() def decrypt(message: email.message.Message, *, encoding: str = DEFAULT_ENCODING) -> str: From f5a31997cf36aea6d58e9d37dfea723ad2bc1f8f Mon Sep 17 00:00:00 2001 From: revsuine Date: Fri, 15 Nov 2024 17:55:22 +0000 Subject: [PATCH 2/2] add X-gpgmymail-Status header --- README.md | 11 +++++++++-- gpgmymail | 12 ++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a8dfe15..0c893c5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # gpgmymail -Takes an email from stdin and encrypts it using the recipient's PGP key, -provided as an argument when calling the script. +Takes an email from stdin and encrypts it to stdout using the recipient's PGP +key, provided as an argument when calling the script. + +Leaves a `X-gpgmymail-Status` header on the email, which has the following +statuses: + +* `entered` - the email has entered the encryption function, but not been + encrypted +* `encrypted` - the encryption function has encrypted the email Written to be a Sieve filter to be used with `sieve_extprograms`. Can be used in a Sieve filter e.g.: diff --git a/gpgmymail b/gpgmymail index c0acc7b..8622548 100755 --- a/gpgmymail +++ b/gpgmymail @@ -24,6 +24,12 @@ works well for emails created with this tool. When encrypting, the tool preserves all headers in the original email in the encrypted part, and copies relevant headers to the output. When decrypting, any headers are ignored, and only the encrypted headers are restored. + +Emails exiting this script will have the 'X-gpgmymail-Status' header, which has +the following options: + +- entered: the email has entered the encrypt() function +- encrypted: the email has been encrypted """ import argparse @@ -219,6 +225,9 @@ def encrypt( blocks. find ignore_errors to see instances where this occurs :return: The encrypted email as a string""" + + # mark the email as having passed through us + set_email_header(message, 'X-gpgmymail-Status', 'entered') # exclusion criteria: # some mail clients like Thunderbird don't like twice-encrypted emails, @@ -280,6 +289,9 @@ def encrypt( set_email_header(encmsg, 'Subject', PEP_SUBJECT) set_email_header(encmsg, 'X-pEp-Version', '2.1') + # we have encrypted the email, set our gpgmymail header appropriately + set_email_header(encmsg, 'X-gpgmymail-Status', 'encrypted') + return encmsg.as_string() def decrypt(message: email.message.Message, *, encoding: str = DEFAULT_ENCODING) -> str: