#! /usr/bin/env python3
-
+# -*- coding: utf-8 -*-
"""*********************************************************************
* Edward is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero Public License as published by *
import gpgme
import re
import io
+import os
import email.parser
import email.message
from email.mime.application import MIMEApplication
from email.mime.nonmultipart import MIMENonMultipart
+import edward_config
def main ():
handle_args()
email_text = sys.stdin.read()
-
email_from, email_subject = email_from_subject(email_text)
- plaintext, keys = email_decode_flatten(email_text)
+ os.environ['GNUPGHOME'] = edward_config.gnupghome
+ gpgme_ctx = gpgme.Context()
+ gpgme_ctx.armor = True
+
+ plaintext, keys = email_decode_flatten(email_text, gpgme_ctx, False)
encrypt_to_key = choose_reply_encryption_key(keys)
reply_message = generate_reply(plaintext, email_from, \
email_subject, encrypt_to_key,
- "DAB4F989E2788B8DF058E0EFEF1EC52039B36E58")
+ edward_config.sign_with_key,
+ gpgme_ctx)
print(reply_message)
-def email_decode_flatten (email_text):
+def email_decode_flatten (email_text, gpgme_ctx, from_decryption):
body = ""
keys = []
if (filename == "encrypted.asc") or (content_type == "pgp/mime"):
- plaintext, more_keys = decrypt_text(payload)
+ plaintext, more_keys = decrypt_text(payload, gpgme_ctx)
body += plaintext
keys += more_keys
+ elif content_type == "application/pgp-keys":
+ keys += add_gpg_keys(payload, gpgme_ctx)
+
elif content_type == "text/plain":
- body += payload + "\n"
+ if from_decryption == True:
+ body += payload + "\n"
+
+ else:
+ plaintext, more_keys = decrypt_text(payload, gpgme_ctx)
+
+ body += plaintext
+ keys += more_keys
- else:
- body += payload + "\n"
+ keys += add_gpg_keys(payload, gpgme_ctx)
return body, keys
return payload, description, filename, content_type
-def decrypt_text (gpg_text):
+def add_gpg_keys (text, gpgme_ctx):
+
+ key_blocks = scan_and_grab(text,
+ '-----BEGIN PGP PUBLIC KEY BLOCK-----',
+ '-----END PGP PUBLIC KEY BLOCK-----')
+
+ keys = []
+ for key_block in key_blocks:
+ fp = io.BytesIO(key_block.encode('ascii'))
+
+ result = gpgme_ctx.import_(fp)
+ imports = result.imports
+
+ if imports != []:
+ fingerprint = imports[0][0]
+ keys += [gpgme_ctx.get_key(fingerprint)]
+
+ debug("added gpg key: " + fingerprint)
+
+ return keys
+
+
+def decrypt_text (gpg_text, gpgme_ctx):
body = ""
keys = []
- gpg_chunks = split_message(gpg_text)
+ msg_blocks = scan_and_grab(gpg_text,
+ '-----BEGIN PGP MESSAGE-----',
+ '-----END PGP MESSAGE-----')
- plaintext_and_sigs_chunks = decrypt_chunks(gpg_chunks)
+ plaintexts_and_sigs = decrypt_blocks(msg_blocks, gpgme_ctx)
- for chunk in plaintext_and_sigs_chunks:
- plaintext = chunk[0]
- sigs = chunk[1]
+ for pair in plaintexts_and_sigs:
+ plaintext = pair[0]
+ sigs = pair[1]
for sig in sigs:
- key = get_pub_key(sig)
- keys += [key]
+ keys += [gpgme_ctx.get_key(sig.fpr)]
# recursive for nested layers of mime and/or gpg
- plaintext, more_keys = email_decode_flatten(plaintext)
+ plaintext, more_keys = email_decode_flatten(plaintext, gpgme_ctx, True)
body += plaintext
keys += more_keys
return body, keys
-def get_pub_key (sig):
-
- gpgme_ctx = gpgme.Context()
-
- fingerprint = sig.fpr
- key = gpgme_ctx.get_key(fingerprint)
-
- return key
-
+def scan_and_grab (text, start_text, end_text):
-def split_message (text):
+ matches = re.search('(' + start_text + '.*' + end_text + ')',
+ text, flags=re.DOTALL)
- gpg_matches = re.search( \
- '(-----BEGIN PGP MESSAGE-----' + \
- '.*' + \
- '-----END PGP MESSAGE-----)', \
- text, \
- flags=re.DOTALL)
-
- if gpg_matches != None:
- gpg_chunks = gpg_matches.groups()
+ if matches != None:
+ match_tuple = matches.groups()
else:
- gpg_chunks = ()
+ match_tuple = ()
- return gpg_chunks
+ return match_tuple
-def decrypt_chunks (gpg_chunks):
+def decrypt_blocks (msg_blocks, gpgme_ctx):
- return map(decrypt_chunk, gpg_chunks)
+ return [decrypt_block(block, gpgme_ctx) for block in msg_blocks]
-def decrypt_chunk (gpg_chunk):
+def decrypt_block (msg_block, gpgme_ctx):
- gpgme_ctx = gpgme.Context()
-
- chunk_b = io.BytesIO(gpg_chunk.encode('ascii'))
+ block_b = io.BytesIO(msg_block.encode('ascii'))
plain_b = io.BytesIO()
- sigs = gpgme_ctx.decrypt_verify(chunk_b, plain_b)
+ try:
+ sigs = gpgme_ctx.decrypt_verify(block_b, plain_b)
+ except:
+ return ("",[])
plaintext = plain_b.getvalue().decode('utf-8')
return (plaintext, sigs)
def generate_reply (plaintext, email_from, email_subject, encrypt_to_key,
- sign_with_fingerprint):
+ sign_with_fingerprint, gpgme_ctx):
reply = "To: " + email_from + "\n"
encrypted_text = encrypt_sign_message(plaintext_mime.as_string(),
encrypt_to_key,
- sign_with_fingerprint)
+ sign_with_fingerprint,
+ gpgme_ctx)
control_mime = MIMEApplication("Version: 1",
_subtype='pgp-encrypted',
return quoted_message
-def encrypt_sign_message (plaintext, encrypt_to_key, sign_with_fingerprint):
-
- gpgme_ctx = gpgme.Context()
- gpgme_ctx.armor = True
+def encrypt_sign_message (plaintext, encrypt_to_key, sign_with_fingerprint, gpgme_ctx):
sign_with_key = gpgme_ctx.get_key(sign_with_fingerprint)
gpgme_ctx.signers = [sign_with_key]
return encrypted_txt
+def debug (debug_msg):
+
+ if edward_config.debug == True:
+ sys.stderr.write(debug_msg + "\n")
+
+
def handle_args ():
if __name__ == "__main__":