search text/plain for keys and encoded messages
[edward.git] / edward
diff --git a/edward b/edward
index 3b828276245bd7339612ac6f6ce28004cbe666d8..baf3059e83d4579b92f8784adb94a4527457f251 100755 (executable)
--- a/edward
+++ b/edward
@@ -1,5 +1,5 @@
 #! /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  *
@@ -36,6 +36,7 @@ import sys
 import gpgme
 import re
 import io
+import os
 
 import email.parser
 import email.message
@@ -45,26 +46,31 @@ from email.mime.multipart       import MIMEMultipart
 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 = []
@@ -92,16 +98,25 @@ def email_decode_flatten (email_text):
 
 
         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
 
@@ -141,25 +156,48 @@ def get_email_subpart_info (part):
     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
@@ -167,46 +205,33 @@ def decrypt_text (gpg_text):
     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)
@@ -224,7 +249,7 @@ def choose_reply_encryption_key (keys):
 
 
 def generate_reply (plaintext, email_from, email_subject, encrypt_to_key,
-                    sign_with_fingerprint):
+                    sign_with_fingerprint, gpgme_ctx):
 
 
     reply  = "To: " + email_from + "\n"
@@ -246,7 +271,8 @@ def generate_reply (plaintext, email_from, email_subject, encrypt_to_key,
 
         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',
@@ -283,10 +309,7 @@ def email_quote_text (text):
     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]
@@ -301,6 +324,12 @@ def encrypt_sign_message (plaintext, encrypt_to_key, sign_with_fingerprint):
     return encrypted_txt
 
 
+def debug (debug_msg):
+
+    if edward_config.debug == True:
+        sys.stderr.write(debug_msg + "\n")
+
+
 def handle_args ():
     if __name__ == "__main__":