summaryrefslogtreecommitdiff
path: root/slime-0.11/slime_pgpmime.py
diff options
context:
space:
mode:
Diffstat (limited to 'slime-0.11/slime_pgpmime.py')
-rw-r--r--slime-0.11/slime_pgpmime.py168
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()