diff options
Diffstat (limited to 'slime-0.11/slime_pgpmime.py')
-rw-r--r-- | slime-0.11/slime_pgpmime.py | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/slime-0.11/slime_pgpmime.py b/slime-0.11/slime_pgpmime.py new file mode 100644 index 0000000..e02d06d --- /dev/null +++ b/slime-0.11/slime_pgpmime.py @@ -0,0 +1,168 @@ +"""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() |