From 394a1476b84c4cdbd2fc9c672eb71f1f92a12dc3 Mon Sep 17 00:00:00 2001 From: Andrew Engelbrecht Date: Sat, 18 Jul 2015 16:17:17 -0400 Subject: [PATCH] Variable name changes. Removed gpg sig. analyzer Many variable names were changed to make the code easier to understand. Signature analysis was removed and replaced with collection of keys used in gpg signing. This will be useful for choosing the public key to use when encrypting the reply message. These two commits should have been separate but the edits got mixed together. --- edward-bot | 186 ++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/edward-bot b/edward-bot index 7e68f9a..80bda24 100755 --- a/edward-bot +++ b/edward-bot @@ -41,180 +41,176 @@ def main (): handle_args() email_text = sys.stdin.read() - print(decode_email(email_text)) + message, keys = decode_simplify_email(email_text) + print(message) -def decode_email (text): + for key in keys: + print(key.subkeys[0].fpr) - msg = email.parser.Parser().parsestr(text) - message = "" - message += "From: " + msg['From'] + "\n" - message += "Subject: " + msg['Subject'] + "\n\n" +def decode_simplify_email (email_text): + + body, keys = email_struct_decode_flatten(email_text) + email_from, email_subject = get_from_subject(email_text) - message += msg_walk(msg) + message = "" + message += "From: " + email_from + "\n" + message += "Subject: " + email_subject + "\n\n" + message += body - return message + return message, keys -def msg_walk (msg): +def email_struct_decode_flatten (email_text): body = "" - for part in msg.walk(): + keys = [] + + email_struct = email.parser.Parser().parsestr(email_text) + + for subpart in email_struct.walk(): - payload, descript, filename, conttype = get_part_info(part) + payload, description, filename, content_type \ + = get_email_subpart_info(subpart) - if payload == None: + if payload == "": continue - if conttype == 'multipart': + 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 - if (filename == "encrypted.asc") or (conttype == "pgp/mime"): - body += decrypt_payload(payload) + if (filename == "encrypted.asc") or (content_type == "pgp/mime"): + plaintext, more_keys = decrypt_text(payload) - elif conttype == "text/plain": + body += plaintext + keys += more_keys + + elif content_type == "text/plain": body += payload + "\n" else: body += payload + "\n" - return body + return body, keys -def get_part_info (part): +def get_from_subject (email_text): - charset = part.get_content_charset() - payload_b = part.get_payload(decode=True) + email_struct = email.parser.Parser().parsestr(email_text) - filename = part.get_filename() - conttype = part.get_content_type() - descrip_p = part.get_params(header='content-description') + email_from = email_struct['From'] + email_subject = email_struct['Subject'] - if charset == None: - charset = 'utf-8' + return email_from, email_subject - if payload_b == None: - payload = None - else: - payload = payload_b.decode(charset) - if descrip_p == None: - descript = None - else: - descript = descrip_p[0][0] +def get_email_subpart_info (part): + charset = part.get_content_charset() + payload_bytes = part.get_payload(decode=True) - return payload, descript, filename, conttype + filename = part.get_filename() + content_type = part.get_content_type() + description_list = part.get_params(header='content-description') + if charset == None: + charset = 'utf-8' -def decrypt_payload (payload): + if payload_bytes != None: + payload = payload_bytes.decode(charset) + else: + payload = "" - blocks = split_message(payload) - decrypted_tree = decrypt_blocks(blocks) + if description_list != None: + description = description_list[0][0] + else: + description = "" - if decrypted_tree == None: - return + return payload, description, filename, content_type - body = "" - for node in decrypted_tree: - msg = email.parser.Parser().parsestr(node[0]) - sigs = node[1] - body += msg_walk(msg) - for sig in sigs: - body += format_sig(sig) +def decrypt_text (gpg_text): - return body + body = "" + keys = [] + gpg_chunks = split_message(gpg_text) -def format_sig (sig): + plaintext_and_sigs_chunks = decrypt_chunks(gpg_chunks) - fprint = sig.fpr - fprint_short = re.search("[0-9A-Fa-f]{32}([0-9A-Fa-f]{8})", fprint).groups()[0] + for chunk in plaintext_and_sigs_chunks: + plaintext = chunk[0] + sigs = chunk[1] - timestamp = time.localtime(sig.timestamp) - date = time.strftime("%a %d %b %Y %I:%M:%S %p %Z", timestamp) + for sig in sigs: + key = get_pub_key(sig) + keys += [key] - g = gpgme.Context() - key = g.get_key(fprint) + # recursive for nested layers of mime and/or gpg + plaintext, more_keys = email_struct_decode_flatten(plaintext) - # 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. + body += plaintext + keys += more_keys - name = key.uids[0].name - e_addr = key.uids[0].email - comment = key.uids[0].comment + return body, keys - # 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. - validity = sig.validity - if validity == gpgme.VALIDITY_ULTIMATE \ - or validity == gpgme.VALIDITY_FULL: - status = "MAYBE-Good Signature " - elif validity == gpgme.VALIDITY_MARGINAL: - status = "MAYBE-Marginal Signature " - else: - status = "MAYBE-BAD Signature " +def get_pub_key (sig): + gpgme_ctx = gpgme.Context() - sig_str = "Signature Made " + date + " using key ID " + fprint_short + "\n" - sig_str += status + "from " + name + " (" + comment + ") <" + e_addr + ">" + finger_print = sig.fpr + key = gpgme_ctx.get_key(finger_print) - return sig_str + 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 (): -- 2.25.1