Variable name changes. Removed gpg sig. analyzer
[edward.git] / edward-bot
index 39d7f8f4396418e9f8feceaffe279772eb2e5465..80bda246a48b23b6da4b1fcbf281646971092797 100755 (executable)
@@ -35,166 +35,182 @@ import re
 import io
 import time
 
+
 def main ():
 
     handle_args()
 
-    txt = sys.stdin.read()
-    msg = email.parser.Parser().parsestr(txt)
+    email_text = sys.stdin.read()
+    message, keys = decode_simplify_email(email_text)
 
-    print("From: " + msg['From'])
-    print("Subject: " + msg['Subject'])
-    print()
+    print(message)
 
-    msg_walk(msg)
+    for key in keys:
+        print(key.subkeys[0].fpr)
 
 
-def msg_walk (msg):
+def decode_simplify_email (email_text):
 
-    for part in msg.walk():
+    body, keys = email_struct_decode_flatten(email_text)
+    email_from, email_subject = get_from_subject(email_text)
 
-        if part.get_content_type() == 'multipart':
-            continue
+    message  = ""
+    message += "From: " + email_from + "\n"
+    message += "Subject: " + email_subject + "\n\n"
+    message += body
 
-        charset   = part.get_content_charset()
-        payload_b = part.get_payload(decode=True)
+    return message, keys
 
-        filename  = part.get_filename()
-        conttype  = part.get_content_type()
-        descrip_p = part.get_params(header='content-description')
 
-        if charset == None:
-            charset = 'utf-8'
+def email_struct_decode_flatten (email_text):
 
-        if payload_b == None:
-            continue
-        else:
-            payload = payload_b.decode(charset)
+    body = ""
+    keys = []
 
+    email_struct = email.parser.Parser().parsestr(email_text)
 
-        if descrip_p == None:
-            decript = ""
-        else:
-            descript = descrip_p[0][0]
+    for subpart in email_struct.walk():
+
+        payload, description, filename, content_type \
+                = get_email_subpart_info(subpart)
 
+        if payload == "":
+            continue
+
+        if content_type == "multipart":
+            continue
 
-        if conttype == "application/pgp-encrypted":
-            if descript == 'PGP/MIME version identification':
+        if content_type == "application/pgp-encrypted":
+            if description == "PGP/MIME version identification":
                 if payload.strip() != "Version: 1":
-                    print(progname + ": Warning: unknown " + descript + ": " \
+                    print(progname + ": Warning: unknown " \
+                            + description + ": " \
                             + payload.strip(), file=sys.stderr)
             continue
 
-        elif (filename == "encrypted.asc") or (conttype == "pgp/mime"):
-            dec_msg = decrypt_payload(payload)
-            print_decrypted(dec_msg)
 
-        elif conttype == "text/plain":
-            print(payload)
+        if (filename == "encrypted.asc") or (content_type == "pgp/mime"):
+            plaintext, more_keys = decrypt_text(payload)
+
+            body += plaintext
+            keys += more_keys
+
+        elif content_type == "text/plain":
+            body += payload + "\n"
 
         else:
-            print(payload)
+            body += payload + "\n"
 
+    return body, keys
 
-def print_decrypted (message):
 
-    if message == None:
-        return
+def get_from_subject (email_text):
 
-    for chunk in message:
-        msg = email.parser.Parser().parsestr(chunk[0])
-        sigs = chunk[1]
+    email_struct = email.parser.Parser().parsestr(email_text)
 
-        msg_walk(msg)
-        for sig in sigs:
-            print_sig(sig)
+    email_from      = email_struct['From']
+    email_subject   = email_struct['Subject']
 
-def print_sig (sig):
+    return email_from, email_subject
 
-    fprint = sig.fpr
-    fprint_short = re.search("[0-9A-Fa-f]{32}([0-9A-Fa-f]{8})", fprint).groups()[0]
 
-    timestamp = time.localtime(sig.timestamp)
-    date = time.strftime("%a %d %b %Y %I:%M:%S %p %Z", timestamp)
+def get_email_subpart_info (part):
 
-    g = gpgme.Context()
-    key = g.get_key(fprint)
+    charset             = part.get_content_charset()
+    payload_bytes       = part.get_payload(decode=True)
 
-    # right now i'm just choosing the first user id, even if that id isn't
-    # signed by the user yet another is. if a user id is printed, it should
-    # at least be one that is signed, and/or correspond to the From:
-    # field's email address and full name.
+    filename            = part.get_filename()
+    content_type        = part.get_content_type()
+    description_list    = part.get_params(header='content-description')
 
-    name = key.uids[0].name
-    e_addr = key.uids[0].email
-    comment = key.uids[0].comment
+    if charset == None:
+        charset = 'utf-8'
 
-    # this section needs some work. signature summary, validity, status,
-    # and wrong_key_usage all complicate the picture. their enum/#define
-    # values overlap, which makes things more complicated.
+    if payload_bytes != None:
+        payload = payload_bytes.decode(charset)
+    else:
+        payload = ""
 
-    validity = sig.validity
-    if validity == gpgme.VALIDITY_ULTIMATE \
-            or validity == gpgme.VALIDITY_FULL:
-        status = "Good Signature "
-    elif validity == gpgme.VALIDITY_MARGINAL:
-        status = "Marginal Signature "
+    if description_list != None:
+        description = description_list[0][0]
     else:
-        status = "BAD Signature "
+        description = ""
+
+    return payload, description, filename, content_type
+
+
+def decrypt_text (gpg_text):
+
+    body = ""
+    keys = []
+
+    gpg_chunks = split_message(gpg_text)
+
+    plaintext_and_sigs_chunks = decrypt_chunks(gpg_chunks)
+
+    for chunk in plaintext_and_sigs_chunks:
+        plaintext   = chunk[0]
+        sigs        = chunk[1]
+
+        for sig in sigs:
+            key = get_pub_key(sig)
+            keys += [key]
+
+        # recursive for nested layers of mime and/or gpg
+        plaintext, more_keys = email_struct_decode_flatten(plaintext)
+
+        body += plaintext
+        keys += more_keys
 
+    return body, keys
 
-    print("Signature Made " + date + " using key ID " + fprint_short)
-    print(status + "from " + name + " (" + comment + ") <" \
-            + e_addr + ">")
 
+def get_pub_key (sig):
 
-def decrypt_payload (payload):
+    gpgme_ctx = gpgme.Context()
 
-    blocks = split_message(payload)
-    message = decrypt_blocks(blocks)
+    finger_print = sig.fpr
+    key = gpgme_ctx.get_key(finger_print)
 
-    return message
+    return key
 
 
 def split_message (text):
 
     pgp_matches = re.search( \
-            '(-----BEGIN PGP MESSAGE-----' \
-            '.*' \
+            '(-----BEGIN PGP MESSAGE-----' \
+            '.*' \
             '-----END PGP MESSAGE-----)', \
             text, \
             re.DOTALL)
 
     if pgp_matches == None:
-        return None
+        return ()
     else:
         return pgp_matches.groups()
 
 
-def decrypt_blocks (blocks):
+def decrypt_chunks (gpg_chunks):
 
-    if blocks == None:
-        return None
+    plaintext_and_sigs_chunks = []
 
-    message = []
-    for block in blocks:
-        plain, sigs = decrypt_block(block)
+    for gpg_chunk in gpg_chunks:
+        plaintext_and_sigs_chunks += [decrypt_chunk(gpg_chunk)]
 
-        message = message + [(plain, sigs)]
+    return plaintext_and_sigs_chunks
 
-    return message
 
+def decrypt_chunk (gpg_chunk):
 
-def decrypt_block (block):
+    gpgme_ctx = gpgme.Context()
 
-    block_b = io.BytesIO(block.encode('ASCII'))
+    chunk_b = io.BytesIO(gpg_chunk.encode('ASCII'))
     plain_b = io.BytesIO()
 
-    g = gpgme.Context()
-    sigs = g.decrypt_verify(block_b, plain_b)
+    sigs = gpgme_ctx.decrypt_verify(chunk_b, plain_b)
 
-    plain = plain_b.getvalue().decode('ASCII')
-    return (plain, sigs)
+    plaintext = plain_b.getvalue().decode('ASCII')
+    return (plaintext, sigs)
 
 
 def handle_args ():