diff lisp/mailcrypt/mailcrypt.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/mailcrypt/mailcrypt.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,537 @@
+;; mailcrypt.el v3.4, mail encryption with PGP
+;; Copyright (C) 1995  Jin Choi <jin@atype.com>
+;;                     Patrick LoPresti <patl@lcs.mit.edu>
+;; Any comments or suggestions welcome.
+;; Inspired by pgp.el, by Gray Watson <gray@antaire.com>.
+
+;;{{{ Licensing
+;; This file is intended to be used with GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;}}}
+
+;;{{{ Load some required packages
+
+(eval-when-compile
+  ;; Quiet warnings
+  (autoload 'start-itimer "itimer")
+  (autoload 'cancel-itimer "itimer")
+  (autoload 'delete-itimer "itimer"))
+
+(require 'easymenu)
+(require 'comint)
+
+(eval-and-compile
+  (condition-case nil (require 'itimer) (error nil))
+  (if (not (featurep 'itimer))
+      (condition-case nil (require 'timer) (error nil)))
+
+  (if (not (fboundp 'buffer-substring-no-properties))
+      (fset 'buffer-substring-no-properties 'buffer-substring)))
+
+(defconst mc-xemacs-p (string-match "XEmacs" emacs-version))
+
+(if (not mc-xemacs-p)
+    (progn
+      (autoload 'mc-decrypt "mc-toplev" nil t)
+      (autoload 'mc-verify "mc-toplev" nil t)
+      (autoload 'mc-snarf "mc-toplev" nil t)
+      (autoload 'mc-pgp-fetch-key "mc-pgp" nil t)
+      (autoload 'mc-encrypt "mc-toplev" nil t)
+      (autoload 'mc-sign "mc-toplev" nil t)
+      (autoload 'mc-insert-public-key "mc-toplev" nil t)
+      (autoload 'mc-remailer-encrypt-for-chain "mc-remail" nil t)
+      (autoload 'mc-remailer-insert-response-block "mc-remail" nil t)
+      (autoload 'mc-remailer-insert-pseudonym "mc-remail" nil t)))
+
+;;}}}
+
+;;{{{ Minor mode variables and functions
+
+(defvar mc-read-mode nil
+  "Non-nil means Mailcrypt read mode key bindings are available.")
+
+(defvar mc-write-mode nil
+  "Non-nil means Mailcrypt write mode key bindings are available.")
+
+(make-variable-buffer-local 'mc-read-mode)
+(make-variable-buffer-local 'mc-write-mode)
+
+(defvar mc-read-mode-string " MC-r"
+  "*String to put in mode line when Mailcrypt read mode is active.")
+
+(defvar mc-write-mode-string " MC-w"
+  "*String to put in mode line when Mailcrypt write mode is active.")
+
+(defvar mc-read-mode-map nil
+  "Keymap for Mailcrypt read mode bindings.")
+
+(defvar mc-write-mode-map nil
+  "Keymap for Mailcrypt write mode bindings.")
+
+(or mc-read-mode-map
+    (progn
+      (setq mc-read-mode-map (make-sparse-keymap))
+      (define-key mc-read-mode-map "\C-c/f" 'mc-deactivate-passwd)
+      (define-key mc-read-mode-map "\C-c/d" 'mc-decrypt)
+      (define-key mc-read-mode-map "\C-c/v" 'mc-verify)
+      (define-key mc-read-mode-map "\C-c/a" 'mc-snarf)
+      (define-key mc-read-mode-map "\C-c/k" 'mc-pgp-fetch-key)))
+
+(or mc-write-mode-map
+    (progn
+      (setq mc-write-mode-map (make-sparse-keymap))
+      (define-key mc-write-mode-map "\C-c/f" 'mc-deactivate-passwd)
+      (define-key mc-write-mode-map "\C-c/e" 'mc-encrypt)
+      (define-key mc-write-mode-map "\C-c/s" 'mc-sign)
+      (define-key mc-write-mode-map "\C-c/x" 'mc-insert-public-key)
+      (define-key mc-write-mode-map "\C-c/k" 'mc-pgp-fetch-key)
+      (define-key mc-write-mode-map "\C-c/r"
+	'mc-remailer-encrypt-for-chain)
+      (define-key mc-write-mode-map "\C-c/b"
+	'mc-remailer-insert-response-block)
+      (define-key mc-write-mode-map "\C-c/p"
+	'mc-remailer-insert-pseudonym)))
+
+(easy-menu-define
+ mc-read-mode-menu (if mc-xemacs-p nil (list mc-read-mode-map))
+ "Mailcrypt read mode menu."
+ '("Mailcrypt"
+   ["Decrypt Message" mc-decrypt t]
+   ["Verify Signature" mc-verify t]
+   ["Snarf Keys" mc-snarf t]
+   ["Fetch Key" mc-pgp-fetch-key t]
+   ["Forget Passphrase(s)" mc-deactivate-passwd t]))
+
+(easy-menu-define
+ mc-write-mode-menu (if mc-xemacs-p nil (list mc-write-mode-map))
+ "Mailcrypt write mode menu."
+ '("Mailcrypt"
+   ["Encrypt Message" mc-encrypt t]
+   ["Sign Message" mc-sign t]
+   ["Insert Public Key" mc-insert-public-key t]
+   ["Fetch Key" mc-pgp-fetch-key t]
+   ["Encrypt for Remailer(s)" mc-remailer-encrypt-for-chain t]
+   ["Insert Pseudonym" mc-remailer-insert-pseudonym t]
+   ["Insert Response Block" mc-remailer-insert-response-block t]
+   ["Forget Passphrase(s)" mc-deactivate-passwd t]))
+
+(or (assq 'mc-read-mode minor-mode-map-alist)
+    (setq minor-mode-map-alist
+	  (cons (cons 'mc-read-mode mc-read-mode-map)
+		minor-mode-map-alist)))
+
+(or (assq 'mc-write-mode minor-mode-map-alist)
+    (setq minor-mode-map-alist
+	  (cons (cons 'mc-write-mode mc-write-mode-map)
+		minor-mode-map-alist)))
+
+(or (assq 'mc-read-mode minor-mode-alist)
+    (setq minor-mode-alist
+	  (cons '(mc-read-mode mc-read-mode-string) minor-mode-alist)))
+
+(or (assq 'mc-write-mode minor-mode-alist)
+    (setq minor-mode-alist
+	  (cons '(mc-write-mode mc-write-mode-string) minor-mode-alist)))
+
+(defun mc-read-mode (&optional arg)
+  "\nMinor mode for interfacing with cryptographic functions.
+\\<mc-read-mode-map>
+\\[mc-decrypt]\t\tDecrypt an encrypted message
+\\[mc-verify]\t\tVerify signature on a clearsigned message
+\\[mc-snarf]\t\tAdd public key(s) to keyring
+\\[mc-pgp-fetch-key]\t\tFetch a PGP key via finger or HTTP
+\\[mc-deactivate-passwd]\t\tForget passphrase(s)\n"
+  (interactive)
+  (setq mc-read-mode
+	(if (null arg) (not mc-read-mode)
+	  (> (prefix-numeric-value arg) 0)))
+  (and mc-read-mode mc-write-mode (mc-write-mode nil))
+  (if mc-read-mode
+      (easy-menu-add mc-read-mode-menu)
+    (easy-menu-remove mc-read-mode-menu)))
+	
+(defun mc-write-mode (&optional arg)
+  "\nMinor mode for interfacing with cryptographic functions.
+\\<mc-write-mode-map>
+\\[mc-encrypt]\t\tEncrypt (and optionally sign) message
+\\[mc-sign]\t\tClearsign message
+\\[mc-insert-public-key]\t\tExtract public key from keyring and insert into message
+\\[mc-pgp-fetch-key]\t\tFetch a PGP key via finger or HTTP
+\\[mc-remailer-encrypt-for-chain]\t\tEncrypt message for remailing
+\\[mc-remailer-insert-pseudonym]\t\tInsert a pseudonym (for remailing)
+\\[mc-remailer-insert-response-block]\t\tInsert a response block (for remailing)
+\\[mc-deactivate-passwd]\t\tForget passphrase(s)\n"
+  (interactive)
+  (setq mc-write-mode
+	(if (null arg) (not mc-write-mode)
+	  (> (prefix-numeric-value arg) 0)))
+  (and mc-write-mode mc-read-mode (mc-read-mode nil))
+  (if mc-write-mode
+      (easy-menu-add mc-write-mode-menu)
+    (easy-menu-remove mc-write-mode-menu)))
+
+;;;###autoload
+(defun mc-install-read-mode ()
+  (interactive)
+  (mc-read-mode 1))
+
+;;;###autoload
+(defun mc-install-write-mode ()
+  (interactive)
+  (mc-write-mode 1))
+
+;;}}}
+
+;;{{{ Note:
+;; The funny triple braces you see are used by `folding-mode', a minor
+;; mode by Jamie Lokier, available from the elisp archive.
+;;}}}
+
+;;{{{ User variables.
+(defconst mc-version "3.4")
+(defvar mc-default-scheme 'mc-scheme-pgp "*Default encryption scheme to use.")
+(defvar mc-passwd-timeout 60
+  "*Time to deactivate password in seconds after a use.
+nil or 0 means deactivate immediately.  If the only timer package available
+is the 'timer' package, then this can be a string in timer format.")
+
+(defvar mc-ripem-user-id (or (getenv "RIPEM_USER_NAME")
+			     (user-full-name) "*Your RIPEM user ID."))
+
+(defvar mc-always-replace nil
+  "*If t, decrypt mail messages in place without prompting.
+
+If 'never, always use a viewer instead of replacing.")
+
+(defvar mc-use-default-recipients nil "*Assume that the message should
+  be encoded for everyone listed in the To, Cc, and Bcc fields.")
+
+(defvar mc-encrypt-for-me nil "*Encrypt all outgoing messages with
+  user's public key.")
+
+(defvar mc-pre-signature-hook nil
+  "*List of hook functions to run immediately before signing.")
+(defvar mc-post-signature-hook nil
+  "*List of hook functions to run immediately after signing.")
+(defvar mc-pre-encryption-hook nil 
+  "*List of hook functions to run immediately before encrypting.")
+(defvar mc-post-encryption-hook nil 
+  "*List of hook functions to run after encrypting.")
+(defvar mc-pre-decryption-hook nil 
+  "*List of hook functions to run immediately before decrypting.")
+(defvar mc-post-decryption-hook nil 
+  "*List of hook functions to run after decrypting.")
+
+(defconst mc-buffer-name "*MailCrypt*"
+  "Name of temporary buffer for mailcrypt")
+
+(defvar mc-modes-alist
+  '((rmail-mode (decrypt . mc-rmail-decrypt-message)
+		(verify . mc-rmail-verify-signature))
+    (rmail-summary-mode (decrypt . mc-rmail-summary-decrypt-message)
+			(verify . mc-rmail-summary-verify-signature)
+			(snarf . mc-rmail-summary-snarf-keys))
+    (vm-mode (decrypt . mc-vm-decrypt-message)
+	     (verify . mc-vm-verify-signature)
+	     (snarf . mc-vm-snarf-keys))
+    (vm-virtual-mode (decrypt . mc-vm-decrypt-message)
+		     (verify . mc-vm-verify-signature)
+		     (snarf . mc-vm-snarf-keys))
+    (vm-summary-mode (decrypt . mc-vm-decrypt-message)
+		     (verify . mc-vm-verify-signature)
+		     (snarf . mc-vm-snarf-keys))
+    (mh-folder-mode (decrypt . mc-mh-decrypt-message)
+		    (verify . mc-mh-verify-signature)
+		    (snarf . mc-mh-snarf-keys))
+    ;; September Gnus (5.2) has a new message editing mode
+    (message-mode (encrypt . mc-encrypt-message)
+		  (sign . mc-sign-message))
+    (gnus-summary-mode (decrypt . mc-gnus-decrypt-message)
+		       (verify . mc-gnus-verify-signature)
+		       (snarf . mc-gnus-snarf-keys))
+    (gnus-article-mode (decrypt . mc-gnus-decrypt-message)
+		       (verify . mc-gnus-verify-signature)
+		       (snarf . mc-gnus-snarf-keys))
+    (mail-mode (encrypt . mc-encrypt-message)
+	       (sign . mc-sign-message))
+    (vm-mail-mode (encrypt . mc-encrypt-message)
+		  (sign . mc-sign-message))
+    (mh-letter-mode (encrypt . mc-encrypt-message)
+		    (sign . mc-sign-message))
+    (news-reply-mode (encrypt . mc-encrypt-message)
+		     (sign . mc-sign-message)))
+
+  "Association list (indexed by major mode) of association lists
+(indexed by operation) of functions to call for each major mode.")
+
+;;}}}
+;;{{{ Program variables and constants.
+
+(defvar mc-timer nil "Timer object for password deactivation.")
+
+(defvar mc-passwd-cache nil "Cache for passphrases.")
+
+(defvar mc-schemes '(("pgp" . mc-scheme-pgp)))
+
+;;}}}
+
+;;{{{ Utility functions.
+
+(defun mc-message-delimiter-positions (start-re end-re &optional begin)
+  ;; Returns pair of integers (START . END) that delimit message marked off
+  ;; by the regular expressions start-re and end-re. Optional argument BEGIN
+  ;; determines where we should start looking from.
+  (setq begin (or begin (point-min)))
+  (let (start)
+    (save-excursion
+      (goto-char begin)
+      (and (re-search-forward start-re nil t)
+	   (setq start (match-beginning 0))
+	   (re-search-forward end-re nil t)
+	   (cons start (point))))))
+
+
+(defun mc-split (regexp str)
+  "Splits STR into a list of elements which were separated by REGEXP,
+stripping initial and trailing whitespace."
+  (let ((data (match-data))
+	(retval '())
+	beg end)
+    (unwind-protect
+	(progn
+	  (string-match "[ \t\n]*" str)	; Will always match at 0
+	  (setq beg (match-end 0))
+	  (setq end (string-match "[ \t\n]*\\'" str))
+	  (while (string-match regexp str beg)
+	    (setq retval
+		  (cons (substring str beg (match-beginning 0)) 
+			retval))
+	    (setq beg (match-end 0)))
+	  (if (not (= (length str) beg)) ; Not end
+	      (setq retval (cons (substring str beg end) retval)))
+	  (nreverse retval))
+      (store-match-data data))))
+
+;;; FIXME - Function never called?
+;(defun mc-temp-display (beg end &optional name)
+;  (let (tmp)
+;    (if (not name)
+;	(setq name mc-buffer-name))
+;    (if (string-match name "*ERROR*")
+;	(progn
+;	  (message "mailcrypt: An error occured!  See *ERROR* buffer.")
+;	  (beep)))
+;    (setq tmp (buffer-substring beg end))
+;    (delete-region beg end)
+;    (save-excursion
+;      (save-window-excursion
+;	(with-output-to-temp-buffer name
+;	  (princ tmp))))))
+
+;; In case I ever decide to do this right.
+(defconst mc-field-name-regexp "^\\(.+\\)")
+(defconst mc-field-body-regexp "\\(.*\\(\n[ \t].*\\)*\n\\)")
+
+(defun mc-get-fields (&optional matching bounds nuke)
+  "Get all header fields within BOUNDS.  Return as an
+alist ((FIELD-NAME . FIELD-BODY) (FIELD-NAME . FIELD-BODY) ...).
+
+Argument MATCHING, if present, is a regexp which each FIELD-NAME
+must match exactly.  Matching is case-insensitive.
+
+Optional arg NUKE, if non-nil, means eliminate all fields returned."
+  (save-excursion
+    (save-restriction
+      (let ((case-fold-search t)
+	    (header-field-regexp
+	     (concat mc-field-name-regexp ":" mc-field-body-regexp))
+	    ret name body field-start field-end)
+	;; Ensure exact match
+	(if matching
+	    (setq matching (concat "^\\(" matching "\\)$")))
+
+	(if bounds
+	    (narrow-to-region (car bounds) (cdr bounds)))
+
+	(goto-char (point-max))
+ 
+	(while (re-search-backward header-field-regexp nil 'move)
+	  (setq field-start (match-beginning 0))
+	  (setq field-end (match-end 0))
+	  (setq name (buffer-substring-no-properties
+		      (match-beginning 1) (match-end 1)))
+	  (setq body (buffer-substring-no-properties
+		      (match-beginning 2) (match-end 2)))
+	  (if (or (null matching) (string-match matching name))
+	      (progn
+		(setq ret (cons (cons name body) ret))
+		(if nuke
+		    (delete-region field-start field-end)))))
+	ret))))
+
+(defsubst mc-strip-address (addr)
+  "Strip everything from ADDR except the basic Email address."
+  (car (cdr (mail-extract-address-components addr))))
+
+(defun mc-strip-addresses (addr-list)
+  "Strip everything from the addresses in ADDR-LIST except the basic
+Email address.  ADDR-LIST may be a single string or a list of strings."
+  (if (not (listp addr-list)) (setq addr-list (list addr-list)))
+  (setq addr-list
+	(mapcar
+	 (function (lambda (s) (mc-split "\\([ \t\n]*,[ \t\n]*\\)" s)))
+	 addr-list))
+  (setq addr-list (apply 'append addr-list))
+  (mapconcat 'mc-strip-address addr-list ", "))
+
+(defun mc-display-buffer (buffer)
+  "Like display-buffer, but always display top of the buffer."
+  (save-excursion
+    (set-buffer buffer)
+    (goto-char (point-min))
+    (display-buffer buffer)))
+
+(defun mc-message (msg &optional buffer default)
+  ;; returns t if we used msg, nil if we used default
+  (let ((retval t))
+    (if buffer
+	(setq msg
+	      (save-excursion
+		(set-buffer buffer)
+		(goto-char (point-min))
+		(if (re-search-forward msg nil t)
+		    (buffer-substring-no-properties
+		     (match-beginning 0) (match-end 0))
+		  (setq retval nil)
+		  default))))
+    (if msg (message "%s" msg))
+    retval))
+
+(defun mc-process-region (beg end passwd program args parser &optional buffer)
+  (let ((obuf (current-buffer))
+	(process-connection-type nil)
+	mybuf result rgn proc)
+    (unwind-protect
+	(progn
+	  (setq mybuf (or buffer (generate-new-buffer " *mailcrypt temp")))
+	  (set-buffer mybuf)
+	  (erase-buffer)
+	  (set-buffer obuf)
+	  (buffer-disable-undo mybuf)
+	  (setq proc
+		(apply 'start-process "*PGP*" mybuf program args))
+	  (if passwd
+	      (progn
+		(process-send-string proc (concat passwd "\n"))
+		(or mc-passwd-timeout (mc-deactivate-passwd t))))
+	  (process-send-region proc beg end)
+	  (process-send-eof proc)
+	  (while (eq 'run (process-status proc))
+	    (accept-process-output proc 5))
+	  (setq result (process-exit-status proc))
+	  ;; Hack to force a status_notify() in Emacs 19.29
+	  (delete-process proc)
+	  (set-buffer mybuf)
+	  (goto-char (point-max))
+	  (if (re-search-backward "\nProcess \\*PGP.*\n\\'" nil t)
+	      (delete-region (match-beginning 0) (match-end 0)))
+	  (goto-char (point-min))
+	  ;; CRNL -> NL
+	  (while (search-forward "\r\n" nil t)
+	    (replace-match "\n"))
+	  ;; Hurm.  FIXME; must get better result codes.
+	  (if (stringp result)
+	      (error "%s exited abnormally: '%s'" program result)
+	    (setq rgn (funcall parser result))
+	    ;; If the parser found something, migrate it
+	    (if (consp rgn)
+		(progn
+		  (set-buffer obuf)
+		  (delete-region beg end)
+		  (goto-char beg)
+		  (insert-buffer-substring mybuf (car rgn) (cdr rgn))
+		  (set-buffer mybuf)
+		  (delete-region (car rgn) (cdr rgn)))))
+	  ;; Return nil on failure and exit code on success
+	  (if rgn result))
+      ;; Cleanup even on nonlocal exit
+      (if (and proc (eq 'run (process-status proc)))
+	  (interrupt-process proc))
+      (set-buffer obuf)
+      (or buffer (null mybuf) (kill-buffer mybuf)))))
+
+;;}}}
+
+;;{{{ Passphrase management
+(defun mc-activate-passwd (id &optional prompt)
+  "Activate the passphrase matching ID, using PROMPT for a prompt.
+Return the passphrase.  If PROMPT is nil, only return value if cached."
+  (cond ((featurep 'itimer)
+	 (if mc-timer (delete-itimer mc-timer))
+	 (setq mc-timer (if mc-passwd-timeout
+			    (start-itimer "mc-itimer"
+					  'mc-deactivate-passwd
+					  mc-passwd-timeout)
+			  nil)))
+	((featurep 'timer)
+	 (let ((string-time (if (integerp mc-passwd-timeout)
+				(format "%d sec" mc-passwd-timeout)
+			      mc-passwd-timeout)))
+	   (if mc-timer (cancel-timer mc-timer))
+	   (setq mc-timer (if string-time
+			      (run-at-time string-time 
+					   nil 'mc-deactivate-passwd)
+			    nil)))))
+  (let ((cell (assoc id mc-passwd-cache))
+	passwd)
+    (setq passwd (cdr-safe cell))
+    (if (and (not passwd) prompt)
+	(setq passwd (comint-read-noecho prompt)))
+    (if cell
+	(setcdr cell passwd)
+      (setq mc-passwd-cache (cons (cons id passwd) mc-passwd-cache)))
+    passwd))
+
+;;;###autoload
+(defun mc-deactivate-passwd (&optional inhibit-message)
+  "*Deactivate the passphrase cache."
+  (interactive)
+  (if mc-timer
+      (cond ((featurep 'itimer) (delete-itimer mc-timer))
+	    ((featurep 'timer) (cancel-timer mc-timer))))
+  (mapcar
+   (function
+    (lambda (cell)
+      (if (stringp (cdr-safe cell)) (fillarray (cdr cell) 0))
+      (setcdr cell nil)))
+   mc-passwd-cache)
+  (or inhibit-message
+      (not (interactive-p))
+      (message "Passphrase%s deactivated"
+	       (if (> (length mc-passwd-cache) 1) "s" ""))))
+
+;;}}}
+
+;;{{{ Define several aliases so that an apropos on `mailcrypt' will
+;; return something.
+(defalias 'mailcrypt-encrypt 'mc-encrypt)
+(defalias 'mailcrypt-decrypt 'mc-decrypt)
+(defalias 'mailcrypt-sign 'mc-sign)
+(defalias 'mailcrypt-verify 'mc-verify)
+(defalias 'mailcrypt-insert-public-key 'mc-insert-public-key)
+(defalias 'mailcrypt-snarf 'mc-snarf)
+;;}}}
+(provide 'mailcrypt)