X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=edward;h=c06751da7ebf1845c719180c1088704ef0b3d7f3;hb=ad07fe462f49ae6e33aa543231a8265f86d2b8ac;hp=df1506e96a16b35039ef4f24aca7f36350b6896a;hpb=d873ff48f5f8c96e2fdc806261a8f402bac72d29;p=edward.git diff --git a/edward b/edward index df1506e..c06751d 100755 --- a/edward +++ b/edward @@ -92,6 +92,9 @@ class GPGData (object): class ReplyInfo (object): def __init__(self): self.replies = None + + self.target_key = None + self.fallback_target_key = None self.msg_to_quote = "" self.success_decrypt = False @@ -119,15 +122,15 @@ def main (): replyinfo_obj.replies = lang.replies prepare_for_reply(email_struct, replyinfo_obj) + encrypt_to_key = get_key_from_fp(replyinfo_obj, gpgme_ctx) reply_plaintext = write_reply(replyinfo_obj) - print(reply_plaintext) + # TODO error handling for missing keys and setting .no_public_key + reply_mime = generate_encrypted_mime(reply_plaintext, email_from, \ + email_subject, encrypt_to_key, + gpgme_ctx) -# encrypt_to_key = choose_reply_encryption_key(gpgme_ctx, fingerprints) -# -# reply_mime = generate_encrypted_mime(plaintext, email_from, \ -# email_subject, encrypt_to_key, -# gpgme_ctx) + print(reply_mime) def get_gpg_context (gnupghome, sign_with_key_fp): @@ -153,27 +156,27 @@ def parse_pgp_mime (email_text, gpgme_ctx): email_struct = email.parser.Parser().parsestr(email_text) - eddy_obj = parse_mime(email_struct) - split_payloads(eddy_obj) - gpg_on_payloads(eddy_obj, gpgme_ctx) + eddymsg_obj = parse_mime(email_struct) + split_payloads(eddymsg_obj) + gpg_on_payloads(eddymsg_obj, gpgme_ctx) - return eddy_obj + return eddymsg_obj def parse_mime(msg_struct): - eddy_obj = EddyMsg() + eddymsg_obj = EddyMsg() if msg_struct.is_multipart() == True: payloads = msg_struct.get_payload() - eddy_obj.multipart = True - eddy_obj.subparts = list(map(parse_mime, payloads)) + eddymsg_obj.multipart = True + eddymsg_obj.subparts = list(map(parse_mime, payloads)) else: - eddy_obj = get_subpart_data(msg_struct) + eddymsg_obj = get_subpart_data(msg_struct) - return eddy_obj + return eddymsg_obj def scan_and_split (payload_piece, match_type, pattern): @@ -237,53 +240,54 @@ def get_subpart_data (part): return obj -def do_to_eddys_pieces (function_to_do, eddy_obj, data): +def do_to_eddys_pieces (function_to_do, eddymsg_obj, data): - if eddy_obj.multipart == True: - for sub in eddy_obj.subparts: + if eddymsg_obj.multipart == True: + for sub in eddymsg_obj.subparts: do_to_eddys_pieces(function_to_do, sub, data) else: - function_to_do(eddy_obj, data) + function_to_do(eddymsg_obj, data) -def split_payloads (eddy_obj): +def split_payloads (eddymsg_obj): for match_type in match_types: - do_to_eddys_pieces(split_payload_pieces, eddy_obj, match_type) + do_to_eddys_pieces(split_payload_pieces, eddymsg_obj, match_type) -def split_payload_pieces (eddy_obj, match_type): +def split_payload_pieces (eddymsg_obj, match_type): (match_name, pattern) = match_type new_pieces_list = [] - for piece in eddy_obj.payload_pieces: + for piece in eddymsg_obj.payload_pieces: new_pieces_list += scan_and_split(piece, match_name, pattern) - eddy_obj.payload_pieces = new_pieces_list + eddymsg_obj.payload_pieces = new_pieces_list -def gpg_on_payloads (eddy_obj, gpgme_ctx, prev_parts=[]): +def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): - if eddy_obj.multipart == True: + if eddymsg_obj.multipart == True: prev_parts=[] - for sub in eddy_obj.subparts: + for sub in eddymsg_obj.subparts: gpg_on_payloads (sub, gpgme_ctx, prev_parts) prev_parts += [sub] return - for piece in eddy_obj.payload_pieces: + for piece in eddymsg_obj.payload_pieces: if piece.piece_type == "text": # don't transform the plaintext. pass elif piece.piece_type == "message": - (plaintext, sigs) = decrypt_block (piece.string, gpgme_ctx) + (plaintext, sigs) = decrypt_block(piece.string, gpgme_ctx) if plaintext: piece.gpg_data = GPGData() + piece.gpg_data.decrypted = True piece.gpg_data.sigs = sigs # recurse! piece.gpg_data.plainobj = parse_pgp_mime(plaintext, gpgme_ctx) @@ -313,18 +317,18 @@ def gpg_on_payloads (eddy_obj, gpgme_ctx, prev_parts=[]): piece.gpg_data.sigs = sig_fps piece.gpg_data.plainobj = prev break + else: pass -def prepare_for_reply (eddy_obj, replyinfo_obj): +def prepare_for_reply (eddymsg_obj, replyinfo_obj): - do_to_eddys_pieces(prepare_for_reply_pieces, eddy_obj, replyinfo_obj) + do_to_eddys_pieces(prepare_for_reply_pieces, eddymsg_obj, replyinfo_obj) +def prepare_for_reply_pieces (eddymsg_obj, replyinfo_obj): -def prepare_for_reply_pieces (eddy_obj, replyinfo_obj): - - for piece in eddy_obj.payload_pieces: + for piece in eddymsg_obj.payload_pieces: if piece.piece_type == "text": # don't quote the plaintext part. pass @@ -334,9 +338,14 @@ def prepare_for_reply_pieces (eddy_obj, replyinfo_obj): replyinfo_obj.failed_decrypt = True else: replyinfo_obj.success_decrypt = True - # TODO: only quote it if it is also signed by the encrypter. - replyinfo_obj.msg_to_quote += flatten_payloads(piece.gpg_data.plainobj) + if replyinfo_obj.target_key != None: + continue + if piece.gpg_data.sigs != []: + replyinfo_obj.target_key = piece.gpg_data.sigs[0] + replyinfo_obj.msg_to_quote = flatten_payloads(piece.gpg_data.plainobj) + + # to catch public keys in encrypted blocks prepare_for_reply(piece.gpg_data.plainobj, replyinfo_obj) elif piece.piece_type == "pubkey": @@ -352,32 +361,72 @@ def prepare_for_reply_pieces (eddy_obj, replyinfo_obj): else: replyinfo_obj.sig_success = True + if replyinfo_obj.fallback_target_key == None: + replyinfo_obj.fallback_target_key = piece.gpg_data.sigs[0] -def flatten_payloads (eddy_obj): + + +def flatten_payloads (eddymsg_obj): flat_string = "" - if eddy_obj.multipart == True: - for sub in eddy_obj.subparts: + if eddymsg_obj == None: + return "" + + if eddymsg_obj.multipart == True: + for sub in eddymsg_obj.subparts: flat_string += flatten_payloads (sub) return flat_string - for piece in eddy_obj.payload_pieces: + # todo: don't include nested decrypted messages. + for piece in eddymsg_obj.payload_pieces: if piece.piece_type == "text": flat_string += piece.string + elif piece.piece_type == "message": + flat_string += flatten_payloads(piece.plainobj) + elif ((piece.piece_type == "clearsign") \ + or (piece.piece_type == "detachedsig")) \ + and (piece.gpg_data != None): + flat_string += flatten_payloads (piece.gpg_data.plainobj) + return flat_string +def get_key_from_fp (replyinfo_obj, gpgme_ctx): + + if replyinfo_obj.target_key == None: + replyinfo_obj.target_key = replyinfo_obj.fallback_target_key + + if replyinfo_obj.target_key != None: + try: + encrypt_to_key = gpgme_ctx.get_key(replyinfo_obj.target_key) + return encrypt_to_key + + except: + pass + + # no available key to use + replyinfo_obj.target_key = None + replyinfo_obj.fallback_target_key = None + + replyinfo_obj.no_public_key = True + replyinfo_obj.public_key_received = False + + return None + + def write_reply (replyinfo_obj): reply_plain = "" if replyinfo_obj.success_decrypt == True: - quoted_text = email_quote_text(replyinfo_obj.msg_to_quote) reply_plain += replyinfo_obj.replies['success_decrypt'] - reply_plain += quoted_text + + if replyinfo_obj.no_public_key == False: + quoted_text = email_quote_text(replyinfo_obj.msg_to_quote) + reply_plain += quoted_text elif replyinfo_obj.failed_decrypt == True: reply_plain += replyinfo_obj.replies['failed_decrypt'] @@ -471,7 +520,11 @@ def decrypt_block (msg_block, gpgme_ctx): return ("",[]) plaintext = plain_b.getvalue().decode('utf-8') - return (plaintext, sigs) + + fingerprints = [] + for sig in sigs: + fingerprints += [sig.fpr] + return (plaintext, fingerprints) def choose_reply_encryption_key (gpgme_ctx, fingerprints): @@ -518,23 +571,17 @@ def import_lang(email_to): def generate_encrypted_mime (plaintext, email_from, email_subject, encrypt_to_key, gpgme_ctx): + # quoted printable encoding lets most ascii characters look normal + # before the decrypted mime message is decoded. + char_set = email.charset.Charset("utf-8") + char_set.body_encoding = email.charset.QP - reply = "To: " + email_from + "\n" - reply += "Subject: " + email_subject + "\n" + # MIMEText doesn't allow setting the text encoding + # so we use MIMENonMultipart. + plaintext_mime = MIMENonMultipart('text', 'plain') + plaintext_mime.set_payload(plaintext, charset=char_set) if (encrypt_to_key != None): - plaintext_reply = "thanks for the message!\n\n\n" - plaintext_reply += email_quote_text(plaintext) - - # quoted printable encoding lets most ascii characters look normal - # before the decrypted mime message is decoded. - char_set = email.charset.Charset("utf-8") - char_set.body_encoding = email.charset.QP - - # MIMEText doesn't allow setting the text encoding - # so we use MIMENonMultipart. - plaintext_mime = MIMENonMultipart('text', 'plain') - plaintext_mime.set_payload(plaintext_reply, charset=char_set) encrypted_text = encrypt_sign_message(plaintext_mime.as_string(), encrypt_to_key, @@ -558,12 +605,13 @@ def generate_encrypted_mime (plaintext, email_from, email_subject, encrypt_to_ke message_mime.attach(encoded_mime) message_mime['Content-Disposition'] = 'inline' - reply += message_mime.as_string() - else: - reply += "\n" - reply += "Sorry, i couldn't find your key.\n" - reply += "I'll need that to encrypt a message to you." + message_mime = plaintext_mime + + message_mime['To'] = email_from + message_mime['Subject'] = email_subject + + reply = message_mime.as_string() return reply