"""PGP/MIME interface for Slime. Parts of this module is based on the algorithms in pgpExec.tcl in Exmh 2.0gamma. """ from slime_abstract import * from slime_mh import * from slime_unix import * from ui_config import config import slime_send, helpers import tempfile, regex, mimetools, os, FCNTL # Exception names for results of PGP stuff SecretMissing = "slime_pgp.SecretMissing" PublicMissing = "slime_pgp.PublicMissing" GoodUntrustedSignature = "slime_pgp.GoodUntrustedSignature" BadUntrustedSignature = "slime_pgp.BadUntrustedSignature" BadTrustedSignature = "slime_pgp.BadTrustedSignature" OtherResult = "slime_pgp.OtherResult" # Regular expressions for interpreting the PGP results secret_missing_pat = \ regex.compile("This.*do not have the secret key.*file", regex.casefold) public_missing_pat = \ regex.compile("Can't.*can't check signature integrity", regex.casefold) good_signature_pat = \ regex.compile("Good signature", regex.casefold) confidence_pat = \ regex.compile("WARNING:.*confidence", regex.casefold) doesnt_match_pat = \ regex.compile("WARNING:.*doesn't match", regex.casefold) def interpret_pgp(exit_code, stdout, stderr): """Interpret the output of PGP, raise exception for errors.""" tuple = (exit_code, stdout, stderr) if secret_missing_pat.search(stdout) >= 0: raise SecretMissing, tuple elif public_missing_pat.search(stdout) >= 0: raise PublicMissing, tuple elif good_signature_pat.search(stdout) >= 0: if confidence_pat.search(stdout) >= 0: raise GoodUntrustedSignature, tuple else: return # Yay! It's OK! Way to go, man! elif doesnt_match_pat.search(stdout) >= 0: if confidence_pat.search(stdout) >= 0: raise BadUntrustedSignature, tuple else: raise BadTrustedSignature, tuple else: raise OtherResult, tuple def write_to_named_file(filename, msg): helpers.write_text_to_named_file(filename, msg.getheadertext() + "\n" + msg.getbodytext()[:-1]) # XXX note that the [:-1] part is a kludge -- exmh # creates an extra newline or something def check_signature(msg): """Check signature of a PGP/MIME message, raise exception for errors.""" if msg.gettype() != "multipart/signed" or \ not "protocol" in msg.getparamnames() or \ msg.getparam("protocol") != "application/pgp-signature" or \ not "micalg" in msg.getparamnames() or \ msg.getparam("micalg") != "pgp-md5": return None parts = msg.getbodyparts() msgtxt = tempfile.mktemp() msgsig = tempfile.mktemp() write_to_named_file(msgtxt, parts[0]) write_to_named_file(msgsig, parts[1]) exit_code, stdout, stderr = \ helpers.run_pgp(config["pgp-path"], "", "", [], [msgsig, msgtxt]) os.remove(msgtxt) os.remove(msgsig) interpret_pgp(exit_code, stdout, stderr) def filter_content_headers(headername): headername = string.lower(headername) return not headername in ["content-type", "content-transfer-encoding"] def make_signature(msg, username, password): """Return string with text of signed message.""" body_type = msg["content-type"] body_xfer = msg["content-transfer-encoding"] if not body_type or body_type == "text/plain": charset = slime_send.deduce_charset(msg.getbodytext()) body_type = "text/plain; charset=%s" % charset if charset == "us-ascii": body_xfer = "7bit" else: body_xfer = "8bit" msg_headers = msg.getheadertext(filter_content_headers) msg_body = msg.getbodytext(decode=0) boundary = mimetools.choose_boundary() msg_type = 'multipart/signed; boundary=%s;\n' + \ ' micalg=pgp-md5; protocol="application/pgp-signature"' msg_type = msg_type % boundary part1_headers = "Content-Type: %s\nContent-Transfer-Encoding: %s\n" % \ (body_type, body_xfer) part1 = part1_headers + "\n" + msg_body exit_code, stdout, stderr = \ helpers.run_pgp(config["pgp-path"], username, password, ["-sbatf"], [], stdin=part1) if exit_code != 0: raise OtherResult, (exit_code, stdout, stderr) part2_headers = "Content-Type: application/pgp\n" part2 = part2_headers + "\n" + stdout return "%sContent-Type: %s\n\n--%s\n%s\n--%s\n%s\n--%s--\n" % \ (msg_headers, msg_type, boundary, part1, boundary, part2, boundary) def test_check_signature(): mhroot = mh_top_folder() mhroot.open() mhroot.rescan_subfolders() for sub in mhroot.list_all_subfolders(): if sub.get_nice_name() == "sent-mail": sub.open() sub.rescan_messages() list = sub.list_all_messages()[:1] if list: msg = list[0] result = check_signature(msg) print "Check result:" print result sub.close() mhroot.close() def test_make_signature(): import sys mhroot = mh_top_folder() mhroot.open() mhroot.rescan_subfolders() for sub in mhroot.list_all_subfolders(): if sub.get_nice_name() == "drafts": sub.open() sub.rescan_messages() list = sub.list_all_messages()[:1] if list: msg = list[0] print "Enter username:" username = sys.stdin.readline()[:-1] print "Enter password:" password = sys.stdin.readline()[:-1] result = make_signature(msg, username, password) print result sub.close() mhroot.close() if __name__ == "__main__": test_check_signature()