Mercurial > hg > xemacs-beta
diff lisp/mailcrypt/mc-toplev.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/mailcrypt/mc-toplev.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,654 @@ +;; mc-toplev.el, entry point functions for Mailcrypt +;; Copyright (C) 1995 Jin Choi <jsc@mit.edu> +;; Patrick LoPresti <patl@lcs.mit.edu> + +;;{{{ 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 +(require 'mailcrypt) +(require 'mail-utils) + +(eval-when-compile + ;; RMAIL + (condition-case nil (require 'rmail) (error nil)) + (autoload 'rmail-abort-edit "rmailedit") + (autoload 'rmail-cease-edit "rmailedit") + ;; Is this a good idea? + (defvar rmail-buffer nil) + + ;; VM + (condition-case nil (require 'vm) (error nil)) + + ;; GNUS + (condition-case nil (require 'gnus) (error nil)) + + ;; MH-E + (condition-case nil (require 'mh-e) (error nil))) + +(eval-and-compile + (condition-case nil (require 'mailalias) (error nil))) + +(if (not mc-xemacs-p) + (autoload 'mc-scheme-pgp "mc-pgp" nil t)) + +;;}}} + +;;{{{ Encryption + +;;;###autoload +(defun mc-cleanup-recipient-headers (str) + ;; Takes a comma separated string of recipients to encrypt for and, + ;; assuming they were possibly extracted from the headers of a reply, + ;; returns a list of the address components. + (mapcar 'mc-strip-address + (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" str))) + +(defun mc-find-headers-end () + (save-excursion + (goto-char (point-min)) + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "\n")) + (if (looking-at "^::\n") + (re-search-forward "^\n" nil t)) + (if (looking-at "^##\n") + (re-search-forward "^\n" nil t)) + (point-marker))) + +;;;###autoload +(defun mc-encrypt (arg) + "*Encrypt the current buffer. + +Exact behavior depends on current major mode. + +With \\[universal-argument], prompt for User ID to sign as. + +With \\[universal-argument] \\[universal-argument], prompt for encryption scheme to use." + (interactive "p") + (mc-encrypt-region arg nil nil)) + +(defun mc-encrypt-region (arg start end) + "*Encrypt the current region." + (interactive "p\nr") + (let* ((mode-alist (cdr-safe (assq major-mode mc-modes-alist))) + (func (or (cdr-safe (assq 'encrypt mode-alist)) + 'mc-encrypt-generic)) + sign scheme from) + (if (>= arg 4) + (setq from (read-string "User ID: ") + sign t)) + (if (>= arg 16) + (setq scheme + (cdr (assoc + (completing-read "Encryption Scheme: " mc-schemes) + mc-schemes)))) + (funcall func nil scheme start end from sign))) + +(defun mc-encrypt-generic (&optional recipients scheme start end from sign) + "*Generic function to encrypt a region of data." + (save-excursion + (or start (setq start (point-min-marker))) + (or (markerp start) (setq start (copy-marker start))) + (or end (setq end (point-max-marker))) + (or (markerp end) (setq end (copy-marker end))) + (run-hooks 'mc-pre-encryption-hook) + (cond ((stringp recipients) + (setq recipients + (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients))) + ((null recipients) + (setq recipients + (mc-cleanup-recipient-headers (read-string "Recipients: ")))) + (t (error "mc-encrypt-generic: recipients not string or nil"))) + (or scheme (setq scheme mc-default-scheme)) + (if (funcall (cdr (assoc 'encryption-func (funcall scheme))) + recipients start end from sign) + (progn + (run-hooks 'mc-post-encryption-hook) + t)))) + +;;;###autoload +(defun mc-encrypt-message (&optional recipients scheme start end from sign) + "*Encrypt a message for RECIPIENTS using the given encryption SCHEME. +RECIPIENTS is a comma separated string. If SCHEME is nil, use the value +of `mc-default-scheme'. Returns t on success, nil otherwise." + (save-excursion + (let ((headers-end (mc-find-headers-end)) + default-recipients) + + (setq default-recipients + (save-restriction + (goto-char (point-min)) + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "$")) + (narrow-to-region (point-min) (point)) + (and (featurep 'mailalias) + (not (featurep 'mail-abbrevs)) + mail-aliases + (expand-mail-aliases (point-min) (point-max))) + (mc-strip-addresses + (mapcar 'cdr + (mc-get-fields "to\\|cc\\|bcc"))))) + + (if (not from) + (save-restriction + (goto-char (point-min)) + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "\n")) + (narrow-to-region (point) headers-end) + (setq from (mail-fetch-field "From")))) + + (if (not recipients) + (setq recipients + (if mc-use-default-recipients + default-recipients + (read-from-minibuffer "Recipients: " default-recipients)))) + + (or start (setq start headers-end)) + (or end (setq end (point-max-marker))) + + (mc-encrypt-generic recipients scheme start end from sign)))) + + +;;}}} +;;{{{ Decryption + +;;;###autoload +(defun mc-decrypt () + "*Decrypt a message in the current buffer. + +Exact behavior depends on current major mode." + (interactive) + (let* ((mode-alist (cdr-safe (assq major-mode mc-modes-alist))) + (func (or (cdr-safe (assq 'decrypt mode-alist)) + 'mc-decrypt-message))) + (funcall func))) + +;;;###autoload +(defun mc-decrypt-message () + "Decrypt whatever message is in the current buffer. +Returns a pair (SUCCEEDED . VERIFIED) where SUCCEEDED is t if the encryption +succeeded and VERIFIED is t if it had a valid signature." + (save-excursion + (let ((schemes mc-schemes) + limits scheme) + (while (and schemes + (setq scheme (cdr (car schemes))) + (not (setq + limits + (mc-message-delimiter-positions + (cdr (assoc 'msg-begin-line (funcall scheme))) + (cdr (assoc 'msg-end-line (funcall scheme))))))) + (setq schemes (cdr schemes))) + + (if (null limits) + (error "Found no encrypted message in this buffer.") + (run-hooks 'mc-pre-decryption-hook) + (let ((resultval (funcall (cdr (assoc 'decryption-func + (funcall scheme))) + (car limits) (cdr limits)))) + (goto-char (point-min)) + (if (car resultval) ; decryption succeeded + (run-hooks 'mc-post-decryption-hook)) + resultval))))) +;;}}} +;;{{{ Signing +;;;###autoload +(defun mc-sign (arg) + "*Sign a message in the current buffer. + +Exact behavior depends on current major mode. + +With one prefix arg, prompts for private key to use, with two prefix args, +also prompts for encryption scheme to use. With negative prefix arg, +inhibits clearsigning (pgp)." + (interactive "p") + (mc-sign-region arg nil nil)) + +(defun mc-sign-region (arg start end) + "*Sign the current region." + (interactive "p\nr") + (let* ((mode-alist (cdr-safe (assq major-mode mc-modes-alist))) + (func (or (cdr-safe (assq 'sign mode-alist)) + 'mc-sign-generic)) + from scheme) + (if (>= arg 16) + (setq scheme + (cdr (assoc + (completing-read "Encryption Scheme: " mc-schemes) + mc-schemes)))) + (if (>= arg 4) + (setq from (read-string "User ID: "))) + + (funcall func from scheme start end (< arg 0)))) + +(defun mc-sign-generic (withkey scheme start end unclearsig) + (or scheme (setq scheme mc-default-scheme)) + (or start (setq start (point-min-marker))) + (or (markerp start) (setq start (copy-marker start))) + (or end (setq end (point-max-marker))) + (or (markerp end) (setq end (copy-marker end))) + (run-hooks 'mc-pre-signature-hook) + (if (funcall (cdr (assoc 'signing-func (funcall scheme))) + start end withkey unclearsig) + (progn + (run-hooks 'mc-post-signature-hook) + t))) + +;;;###autoload +(defun mc-sign-message (&optional withkey scheme start end unclearsig) + "Clear sign the message." + (save-excursion + (let ((headers-end (mc-find-headers-end))) + (or withkey + (progn + (goto-char (point-min)) + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "\n")) + (save-restriction + (narrow-to-region (point) headers-end) + (setq withkey (mail-fetch-field "From"))))) + (or start (setq start headers-end)) + (or end (setq end (point-max-marker))) + (mc-sign-generic withkey scheme start end unclearsig)))) + +;;}}} +;;{{{ Signature verification + +;;;###autoload +(defun mc-verify () + "*Verify a message in the current buffer. + +Exact behavior depends on current major mode." + (interactive) + (let* ((mode-alist (cdr-safe (assq major-mode mc-modes-alist))) + (func (or (cdr-safe (assq 'verify mode-alist)) + 'mc-verify-signature))) + (funcall func))) + +;;;###autoload +(defun mc-verify-signature () + "*Verify the signature of the signed message in the current buffer. +Show the result as a message in the minibuffer. Returns t if the signature +is verified." + (save-excursion + (let ((schemes mc-schemes) + limits scheme) + (while (and schemes + (setq scheme (cdr (car schemes))) + (not + (setq + limits + (mc-message-delimiter-positions + (cdr (assoc 'signed-begin-line (funcall scheme))) + (cdr (assoc 'signed-end-line (funcall scheme))))))) + (setq schemes (cdr schemes))) + + (if (null limits) + (error "Found no signed message in this buffer.") + (funcall (cdr (assoc 'verification-func (funcall scheme))) + (car limits) (cdr limits)))))) + + +;;}}} +;;{{{ Key management + +;;{{{ mc-insert-public-key + +;;;###autoload +(defun mc-insert-public-key (&optional userid scheme) + "*Insert your public key at point. +With one prefix arg, prompts for user id to use. With two prefix +args, prompts for encryption scheme." + (interactive + (let (arglist) + (if (not (and (listp current-prefix-arg) + (numberp (car current-prefix-arg)))) + nil + (if (>= (car current-prefix-arg) 16) + (setq arglist + (cons (cdr (assoc (completing-read "Encryption Scheme: " + mc-schemes) + mc-schemes)) + arglist))) + (if (>= (car current-prefix-arg) 4) + (setq arglist (cons (read-string "User ID: ") arglist)))) + arglist)) + +; (if (< (point) (mc-find-headers-end)) +; (error "Can't insert key inside message header")) + (or scheme (setq scheme mc-default-scheme)) + (or userid (setq userid (cdr (assoc 'user-id (funcall scheme))))) + + ;; (goto-char (point-max)) + (if (not (bolp)) + (insert "\n")) + (funcall (cdr (assoc 'key-insertion-func (funcall scheme))) userid)) + +;;}}} +;;{{{ mc-snarf-keys + +;;;###autoload +(defun mc-snarf () + "*Add all public keys in the buffer to your keyring. + +Exact behavior depends on current major mode." + (interactive) + (let* ((mode-alist (cdr-safe (assq major-mode mc-modes-alist))) + (func (or (cdr-safe (assq 'snarf mode-alist)) + 'mc-snarf-keys))) + (funcall func))) + +;;;###autoload +(defun mc-snarf-keys () + "*Add all public keys in the buffer to your keyring." + (interactive) + (let ((schemes mc-schemes) + (start (point-min)) + (found 0) + limits scheme) + (save-excursion + (catch 'done + (while t + (while (and schemes + (setq scheme (cdr (car schemes))) + (not + (setq + limits + (mc-message-delimiter-positions + (cdr (assoc 'key-begin-line (funcall scheme))) + (cdr (assoc 'key-end-line (funcall scheme))) + start)))) + (setq schemes (cdr schemes))) + (if (null limits) + (throw 'done found) + (setq start (cdr limits)) + (setq found (+ found (funcall (cdr (assoc 'snarf-func + (funcall scheme))) + (car limits) (cdr limits))))))) + (message (format "%d new key%s found" found + (if (eq 1 found) "" "s")))))) +;;}}} +;;}}} +;;{{{ Mode specific functions + +;;{{{ RMAIL +;;;###autoload +(defun mc-rmail-summary-verify-signature () + "*Verify the signature in the current message." + (interactive) + (if (not (eq major-mode 'rmail-summary-mode)) + (error + "mc-rmail-summary-verify-signature called in inappropriate buffer")) + (save-excursion + (set-buffer rmail-buffer) + (mc-verify))) + +;;;###autoload +(defun mc-rmail-summary-decrypt-message () + "*Decrypt the contents of this message" + (interactive) + (if (not (eq major-mode 'rmail-summary-mode)) + (error + "mc-rmail-summary-decrypt-message called in inappropriate buffer")) + (save-excursion + (set-buffer rmail-buffer) + (mc-decrypt))) + +;;;###autoload +(defun mc-rmail-summary-snarf-keys () + "*Adds keys from current message to public key ring" + (interactive) + (if (not (eq major-mode 'rmail-summary-mode)) + (error + "mc-rmail-summary-snarf-keys called in inappropriate buffer")) + (save-excursion + (set-buffer rmail-buffer) + (mc-snarf))) + +;;;###autoload +(defun mc-rmail-verify-signature () + "*Verify the signature in the current message." + (interactive) + (if (not (equal mode-name "RMAIL")) + (error "mc-rmail-verify-signature called in a non-RMAIL buffer")) + ;; Hack to load rmailkwd before verifying sig + (rmail-add-label "verified") + (rmail-kill-label "verified") + (if (mc-verify-signature) + (rmail-add-label "verified"))) + +;;;###autoload +(defun mc-rmail-decrypt-message () + "*Decrypt the contents of this message" + (interactive) + (let (decryption-result) + (if (not (equal mode-name "RMAIL")) + (error "mc-rmail-decrypt-message called in a non-RMAIL buffer")) + (unwind-protect + (progn + (rmail-edit-current-message) + (setq decryption-result (mc-decrypt-message)) + (cond ((not (car decryption-result)) + (rmail-abort-edit)) + ((and (not (eq mc-always-replace 'never)) + (or mc-always-replace + (y-or-n-p + "Replace encrypted message with decrypted? "))) + (rmail-cease-edit) + (rmail-kill-label "edited") + (rmail-add-label "decrypted") + (if (cdr decryption-result) + (rmail-add-label "verified"))) + (t + (let ((tmp (generate-new-buffer "*Mailcrypt Viewing*"))) + (copy-to-buffer tmp (point-min) (point-max)) + (rmail-abort-edit) + (switch-to-buffer tmp t) + (goto-char (point-min)) + (insert "From Mailcrypt-" mc-version " " + (current-time-string) "\n") + (rmail-convert-file) + (rmail-mode) + (use-local-map (copy-keymap (current-local-map))) + (local-set-key "q" 'mc-rmail-view-quit) + (set-buffer-modified-p nil))))) + (if (eq major-mode 'rmail-edit-mode) + (rmail-abort-edit))))) + +(defun mc-rmail-view-quit () + (interactive) + (let ((buf (current-buffer))) + (set-buffer-modified-p nil) + (rmail-quit) + (kill-buffer buf))) + +;;}}} +;;{{{ VM +;;;###autoload +(defun mc-vm-verify-signature () + "*Verify the signature in the current VM message" + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (save-restriction + (vm-widen-page) + (mc-verify-signature))) + +;;;###autoload +(defun mc-vm-decrypt-message () + "*Decrypt the contents of the current VM message" + (interactive) + (let ((vm-frame-per-edit nil) + from-line) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-read-only) + (vm-error-if-folder-empty) + + ;; store away a valid "From " line for possible later use. + (setq from-line (vm-leading-message-separator)) + (vm-edit-message) + (cond ((not (condition-case condition-data + (car (mc-decrypt-message)) + (error + (vm-edit-message-abort) + (error (message "Decryption failed: %s" + (car (cdr condition-data))))))) + (vm-edit-message-abort) + (error "Decryption failed.")) + ((and (not (eq mc-always-replace 'never)) + (or mc-always-replace + (y-or-n-p "Replace encrypted message with decrypted? "))) + (let ((this-command 'vm-edit-message-end)) + (vm-edit-message-end))) + (t + (let ((tmp (generate-new-buffer "*Mailcrypt Viewing*"))) + (copy-to-buffer tmp (point-min) (point-max)) + (vm-edit-message-abort) + (switch-to-buffer tmp t) + (goto-char (point-min)) + (insert from-line) + (set-buffer-modified-p nil) + (vm-mode t)))))) + +;;;###autoload +(defun mc-vm-snarf-keys () + "*Snarf public key from the contents of the current VM message" + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (save-restriction + (vm-widen-page) + (mc-snarf-keys))) + +;;}}} +;;{{{ GNUS + +;;;###autoload +(defun mc-gnus-verify-signature () + (interactive) + (gnus-summary-select-article) + (gnus-eval-in-buffer-window gnus-article-buffer + (save-restriction (widen) (mc-verify-signature)))) + +;;;###autoload +(defun mc-gnus-snarf-keys () + (interactive) + (gnus-summary-select-article) + (gnus-eval-in-buffer-window gnus-article-buffer + (save-restriction (widen) (mc-snarf-keys)))) + +;;;###autoload +(defun mc-gnus-decrypt-message () + (interactive) + (gnus-summary-select-article) + ;; Gnus 5 has the string "Gnus" instead of "GNUS" in gnus-version. + (if (not (let ((case-fold-search nil)) + (string-match "Gnus" gnus-version))) + (gnus-eval-in-buffer-window + gnus-article-buffer + (save-restriction (widen) (mc-decrypt-message))) + ;; Gnus 5 allows editing of articles. (Actually, it makes a great + ;; mail reader.) + (gnus-eval-in-buffer-window gnus-article-buffer + (gnus-summary-edit-article t) + (save-restriction + (widen) + (cond ((not (car (mc-decrypt-message))) + (gnus-summary-edit-article-postpone)) + ((and (not (gnus-group-read-only-p)) + (not (eq mc-always-replace 'never)) + (or mc-always-replace + (y-or-n-p + "Replace encrypted message on disk? "))) + (gnus-summary-edit-article-done)) + (t + (gnus-summary-edit-article-postpone))))))) + +;;}}} +;;{{{ MH + +;;;###autoload +(defun mc-mh-decrypt-message () + "Decrypt the contents of the current MH message in the show buffer." + (interactive "P") + (let* ((msg (mh-get-msg-num t)) + (msg-filename (mh-msg-filename msg)) + (show-buffer (get-buffer mh-show-buffer)) + decrypt-okay decrypt-on-disk) + (setq + decrypt-on-disk + (and (not (eq mc-always-replace 'never)) + (or mc-always-replace + (y-or-n-p "Replace encrypted message on disk? ")))) + (if decrypt-on-disk + (progn + (save-excursion + (set-buffer (create-file-buffer msg-filename)) + (insert-file-contents msg-filename t) + (if (setq decrypt-okay (car (mc-decrypt-message))) + (save-buffer) + (message "Decryption failed.") + (set-buffer-modified-p nil)) + (kill-buffer nil)) + (if decrypt-okay + (if (and show-buffer + (equal msg-filename (buffer-file-name show-buffer))) + (save-excursion + (save-window-excursion + (mh-invalidate-show-buffer))))) + (mh-show msg)) + (mh-show msg) + (save-excursion + (set-buffer mh-show-buffer) + (if (setq decrypt-okay (car (mc-decrypt-message))) + (progn + (goto-char (point-min)) + (set-buffer-modified-p nil)) + (message "Decryption failed."))) + (if (not decrypt-okay) + (progn + (mh-invalidate-show-buffer) + (mh-show msg)))))) + +;;;###autoload +(defun mc-mh-verify-signature () + "*Verify the signature in the current MH message." + (interactive) + (mh-show) + (mh-in-show-buffer (mh-show-buffer) + (mc-verify-signature))) + + +;;;###autoload +(defun mc-mh-snarf-keys () + (interactive) + (mh-show) + (mh-in-show-buffer (mh-show-buffer) + (mc-snarf-keys))) + +;;}}} + +;;}}}