From: yamaoka Date: Wed, 24 Mar 1999 11:03:10 +0000 (+0000) Subject: * New branch for developing PGP 5.0i and GnuPG support. X-Git-Tag: semi-pgpgpg_00 X-Git-Url: http://git.chise.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d8cd3766567fbe1be08d6b3825a92899d58b537c;p=elisp%2Fsemi.git * New branch for developing PGP 5.0i and GnuPG support. --- diff --git a/ChangeLog b/ChangeLog index 1da428f..17b5ba8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,61 @@ +1999-03-24 Katsumi Yamaoka + + * NEWS, README.en, mime-ui-en.sgml, mime-ui-ja.sgml: Update for + PGP 5.0i and GnuPG support. + + * semi-def.el (TopLevel): Autoload "mc-pgp5" and "mc-gpg". + (pgp-function): Modify for extended `pgp-function-alist'. + (pgp-function-alist): Extend for supporting PGP 5.0i and GnuPG. + (pgp-version): New user option. + + * mime-pgp.el (mime-pgp-good-signature-post-function-pgp50-us): New + function. + + (mime-verify-application/pgp-signature): Support PGP 5.0i and + GnuPG. + (mime-pgp-check-signature): Likewise. + + (mime-pgp-key-expected-regexp, + mime-pgp-good-signature-post-function, + mime-pgp-good-signature-regexp, mime-pgp-default-language, + mime-pgp-command): New macros. + + (mime-pgp-key-expected-regexp-alist): Use `defcustom'; extend for + supporting PGP 5.0i and GnuPG. + (mime-pgp-good-signature-regexp-alist): Likewise. + + (mime-pgp-default-language-alist, mime-pgp-command-alist): New + user options. + (mime-pgp-default-language): Abolish variable. Move to + `mime-pgp-default-language-alist'. + (mime-pgp-command): Abolish variable. Move to + `mime-pgp-command-alist' + + (TopLevel): Require `semi-def' explicitly for referring to the + value of `pgp-version'. + + * mime-mc.el (mime-mc-pgp-encrypt-region): Load "mc-pgp" if the + function `mc-pgp-encrypt-region' does not exist. + (mime-mc-pgp-sign-region): Load "mc-pgp" if the function + `mc-pgp-generic-parser' does not exist. + + (mime-mc-pgp50-encrypt-region, mime-mc-pgp50-sign-region, + mime-mc-pgp50-process-region, mime-mc-gpg-encrypt-region, + mime-mc-gpg-sign-region, mime-mc-gpg-process-region, + mime-mc-snarf-keys, mime-mc-decrypt, mime-mc-verify, + mime-mc-insert-public-key): New functions. + + (mime-mc-pgp-generic-parser): Abolish function. + (TopLevel): Autoload or load some modules if they are needed + instead of to load "mc-pgp" first; define some variables for + avoiding byte-compile warnings; require `semi-def' explicitly for + referring to the value of `pgp-version'. + + * mime-edit.el (mime-edit-encrypt-pgp-mime): Use "gpg" for the + prefix string of boundary if GnuPG is used. + (TopLevel): Require `semi-def' explicitly for referring to the + value of `pgp-version'. + 1999-03-11 MORIOKA Tomohiko * mime-edit.el (mime-charset-type-list): Add `tis-620'. diff --git a/NEWS b/NEWS index ad678eb..28c5a0e 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,14 @@ Copyright (C) 1998,1999 Free Software Foundation, Inc. * Changes in SEMI 1.13 +** PGP 5.0i and GnuPG are now supported for PGP/MIME + +You can select the various PGP or GnuPG commands by the new user +option `pgp-version'. Note that Mailcrypt 3.5.3 or later is needed +for PGP 5.0i or GnuPG. A user interface for editing or viewing has +never changed. + + ** Function `mime-play-entity' Function `mime-play-entity' was renamed from `mime-raw-play-entity' diff --git a/README.en b/README.en index 363cc58..66733ef 100644 --- a/README.en +++ b/README.en @@ -50,7 +50,15 @@ Required environment ftp://ftp.jaist.ac.jp/pub/GNU/elisp/flim/ - PGP/MIME and application/pgp require mailcrypt or tiny-pgp package. + PGP/MIME and application/pgp require Mailcrypt or tiny-pgp package. + In addition, if you want to use PGP 5.0i or GnuPG for PGP/MIME, it + is needed that the version of Mailcrypt package is 3.5.3 or later. + If Mailcrypt is not installed on your system, obtain the latest + version from the Mailcrypt home page at: + + http://www.pobox.com/~lbudney/linux/software/mailcrypt.html + + and follow the instructions in the file `INSTALL'. Installation diff --git a/mime-edit.el b/mime-edit.el index 0c04b97..ab98d43 100644 --- a/mime-edit.el +++ b/mime-edit.el @@ -109,6 +109,7 @@ (require 'sendmail) (require 'mail-utils) (require 'mel) +(require 'semi-def) (require 'mime-view) (require 'signature) (require 'alist) @@ -1797,7 +1798,10 @@ Parameter must be '(PROMPT CHOICE1 (CHOISE2 ...))." (mime-edit-translate-region beg end boundary)) (ctype (car ret)) (encoding (nth 1 ret)) - (pgp-boundary (concat "pgp-" boundary))) + (pgp-boundary (concat (if (eq 'gpg pgp-version) + "pgp-" + "gpg-") + boundary))) (goto-char beg) (insert header) (insert (format "Content-Type: %s\n" ctype)) diff --git a/mime-mc.el b/mime-mc.el index 7e5cb26..7312e48 100644 --- a/mime-mc.el +++ b/mime-mc.el @@ -1,9 +1,10 @@ ;;; mime-mc.el --- Mailcrypt interface for SEMI -;; Copyright (C) 1996,1997,1998 MORIOKA Tomohiko +;; Copyright (C) 1996,1997,1998,1999 MORIOKA Tomohiko ;; Author: MORIOKA Tomohiko -;; Keywords: PGP, security, MIME, multimedia, mail, news +;; Katsumi Yamaoka +;; Keywords: PGP, GnuPG, security, MIME, multimedia, mail, news ;; This file is part of SEMI (Secure Emacs MIME Interface). @@ -24,15 +25,392 @@ ;;; Code: +(require 'semi-def) (require 'mailcrypt) -(eval-and-compile (load "mc-pgp")) -(defun mime-mc-pgp-generic-parser (result) - (let ((ret (mc-pgp-generic-parser result))) - (if (consp ret) - (vector (car ret)(cdr ret)) +(eval-and-compile + (mapcar + (function (lambda (elem) (apply 'autoload elem))) + '( + (mc-gpg-debug-print "mc-gpg") + (mc-gpg-encrypt-region "mc-gpg") + (mc-gpg-lookup-key "mc-gpg") + (mc-pgp50-encrypt-region "mc-pgp5") + (mc-pgp50-lookup-key "mc-pgp5") + (mc-pgp-encrypt-region "mc-pgp") + (mc-pgp-lookup-key "mc-pgp") + (mc-snarf-keys "mc-toplev") + ))) + +(defvar mc-gpg-comment) +(defvar mc-gpg-extra-args) +(defvar mc-gpg-path) +(defvar mc-gpg-user-id) +(defvar mc-pgp50-comment) +(defvar mc-pgp50-pgps-path) +(defvar mc-pgp50-user-id) +(defvar mc-pgp-comment) +(defvar mc-pgp-path) +(defvar mc-pgp-user-id) + + +;;; @ Generic functions +;;; + +(defun mime-mc-insert-public-key (&optional userid scheme) + (mc-insert-public-key + userid + (or scheme (intern (format "mc-scheme-%s" pgp-version))) + )) + +(defun mime-mc-verify () + (let ((mc-default-scheme (intern (format "mc-scheme-%s" pgp-version)))) + (mc-verify) + )) + +(defun mime-mc-decrypt () + (let ((mc-default-scheme (intern (format "mc-scheme-%s" pgp-version)))) + (mc-decrypt) + )) + +(defun mime-mc-snarf-keys () + (let ((mc-default-scheme (intern (format "mc-scheme-%s" pgp-version)))) + (mc-snarf-keys) + )) + + +;;; @ GnuPG functions +;;; + +(defun mime-mc-gpg-process-region + (beg end passwd program args parser bufferdummy boundary) + (let ((obuf (current-buffer)) + (process-connection-type nil) + (shell-file-name "/bin/sh") ;; ??? force? need sh (not tcsh) for "2>" + ; other local vars + mybuf + stderr-tempfilename stderr-buf + status-tempfilename status-buf + proc rc status parser-result + ) + (mc-gpg-debug-print (format + "(mc-gpg-process-region beg=%s end=%s passwd=%s program=%s args=%s parser=%s bufferdummy=%s)" + beg end passwd program args parser bufferdummy)) + (setq stderr-tempfilename + (make-temp-name (expand-file-name "mailcrypt-gpg-stderr-" + mc-temp-directory))) + (setq status-tempfilename + (make-temp-name (expand-file-name "mailcrypt-gpg-status-" + mc-temp-directory))) + (unwind-protect + (progn + ;; get output places ready + (setq mybuf (get-buffer-create " *mailcrypt stdout temp")) + (set-buffer mybuf) + (erase-buffer) + (set-buffer obuf) + (buffer-disable-undo mybuf) + (if passwd + (setq args (append '("--passphrase-fd" "0") args))) + (setq args (append (list (concat "2>" stderr-tempfilename)) args)) + (setq args (append (list (concat "3>" status-tempfilename)) args)) + (setq args (append '("--status-fd" "3") args)) + (if mc-gpg-extra-args + (setq args (append mc-gpg-extra-args args))) + (mc-gpg-debug-print (format "prog is %s, args are %s" + program + (mapconcat '(lambda (x) + (format "'%s'" x)) + args " "))) + (setq proc + (apply 'start-process-shell-command "*GPG*" mybuf + program args)) + ;; send in passwd if necessary + (if passwd + (progn + (process-send-string proc (concat passwd "\n")) + (or mc-passwd-timeout (mc-deactivate-passwd t)))) + ;; send in the region + (process-send-region proc beg end) + ;; finish it off + (process-send-eof proc) + ;; wait for it to finish + (while (eq 'run (process-status proc)) + (accept-process-output proc 5)) + ;; remember result codes + (setq status (process-status proc)) + (setq rc (process-exit-status proc)) + (mc-gpg-debug-print (format "prog finished, rc=%s" rc)) + ;; Hack to force a status_notify() in Emacs 19.29 + (delete-process proc) + ;; remove the annoying "yes your process has finished" message + (set-buffer mybuf) + (goto-char (point-max)) + (if (re-search-backward "\nProcess \\*GPG.*\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")) + ;; ponder process death: signal, not just rc!=0 + (if (or (eq 'stop status) (eq 'signal status)) + ;; process died + (error "%s exited abnormally: '%s'" program rc) ;;is rc a string? + ) + (if (= 127 rc) + (error "%s could not be found" program) ;; at least on my system + ) + ;; fill stderr buf + (setq stderr-buf (get-buffer-create " *mailcrypt stderr temp")) + (buffer-disable-undo stderr-buf) + (set-buffer stderr-buf) + (erase-buffer) + (insert-file-contents stderr-tempfilename) + ;; fill status buf + (setq status-buf (get-buffer-create " *mailcrypt status temp")) + (buffer-disable-undo status-buf) + (set-buffer status-buf) + (erase-buffer) + (insert-file-contents status-tempfilename) + ;; feed the parser + (set-buffer mybuf) + (setq parser-result (funcall parser mybuf stderr-buf status-buf rc)) + (mc-gpg-debug-print (format " parser returned %s" parser-result)) + ;; what did the parser tell us? + (if (car parser-result) + ;; yes, replace region + (progn + (set-buffer obuf) + (if boundary + (save-restriction + (narrow-to-region beg end) + (goto-char beg) + (insert (format "--%s\n" boundary)) + (goto-char (point-max)) + (insert (format "\n--%s +Content-Type: application/pgp-signature +Content-Transfer-Encoding: 7bit + +" boundary)) + (insert-buffer-substring mybuf) + (goto-char (point-max)) + (insert (format "\n--%s--\n" boundary)) + ) + (delete-region beg end) + (goto-char beg) + (insert-buffer-substring mybuf) + ))) + ;; return result + (cdr parser-result) + ) + ;; cleanup forms + (if (and proc (eq 'run (process-status proc))) + ;; it is still running. kill it. + (interrupt-process proc)) + (set-buffer obuf) + (delete-file stderr-tempfilename) + (delete-file status-tempfilename) + ;; kill off temporary buffers (which would be useful for debugging) + (if t ;; nil for easier debugging + (progn + (if (get-buffer " *mailcrypt stdout temp") + (kill-buffer " *mailcrypt stdout temp")) + (if (get-buffer " *mailcrypt stderr temp") + (kill-buffer " *mailcrypt stderr temp")) + (if (get-buffer " *mailcrypt status temp") + (kill-buffer " *mailcrypt status temp")) + )) ))) +(defun mime-mc-gpg-sign-region (start end &optional id unclear boundary) + (if (not (fboundp 'mc-gpg-insert-parser)) + (load "mc-gpg") + ) + (let ((buffer (get-buffer-create mc-buffer-name)) + passwd args key + (parser (function mc-gpg-insert-parser)) + (pgp-path mc-gpg-path) + ) + (setq key (mc-gpg-lookup-key (or id mc-gpg-user-id))) + (setq passwd + (mc-activate-passwd + (cdr key) + (format "GnuPG passphrase for %s (%s): " (car key) (cdr key)))) + (setq args + (cons + (if boundary + "--detach-sign" + (if unclear + "--sign" + "--clearsign")) + (list "--passphrase-fd" "0" + "--armor" "--batch" "--textmode" "--verbose" + "--local-user" (cdr key)))) + (if mc-gpg-comment + (setq args (nconc args + (list "--comment" + (format "\"%s\"" mc-gpg-comment)))) + ) + (if (and boundary + (string-match "^pgp-" boundary)) + (setq boundary + (concat "gpg-" (substring boundary (match-end 0)))) + ) + (message "Signing as %s ..." (car key)) + (if (mime-mc-gpg-process-region + start end passwd pgp-path args parser buffer boundary) + (progn + (if boundary + (progn + (goto-char (point-min)) + (insert + (format "\ +--[[multipart/signed; protocol=\"application/pgp-signature\"; + boundary=\"%s\"; micalg=pgp-sha1][7bit]]\n" boundary)) + )) + (message "Signing as %s ... Done." (car key)) + t) + nil))) + +(defun mime-mc-gpg-encrypt-region (recipients start end &optional id sign) + (if (not (fboundp 'mc-gpg-encrypt-region)) + (load "mc-gpg") + ) + (let ((mc-pgp-always-sign (if (eq sign 'maybe) + mc-pgp-always-sign + 'never))) + (mc-gpg-encrypt-region + (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients) + start end id nil) + )) + + +;;; @ PGP 5.0i functions +;;; + +(defun mime-mc-pgp50-process-region + (beg end passwd program args parser &optional buffer boundary) + (let ((obuf (current-buffer)) + (process-connection-type nil) + (shell-file-name "/bin/sh") + mybuf result rgn proc results) + (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-shell-command "*PGP*" mybuf program + "2>&1" args)) + ;; Now hand the process to the parser, which returns the exit + ;; status of the dead process and the limits of the region + ;; containing the PGP results. + (setq results (funcall parser proc obuf beg end mybuf passwd)) + (setq result (car results)) + (setq rgn (cadr results)) + ;; Hack to force a status_notify() in Emacs 19.29 + (set-buffer mybuf) + ;; Hurm. FIXME; must get better result codes. + (if (stringp result) + (mc-message result)) + ;; If the parser found something, migrate it to the old + ;; buffer. In particular, the parser's job is to return + ;; a cons of the form ( beg . end ) delimited the result + ;; of PGP in the new buffer. + (if (consp rgn) + (progn + (set-buffer obuf) + (if boundary + (save-restriction + (narrow-to-region beg end) + (goto-char beg) + (insert (format "--%s\n" boundary)) + (goto-char (point-max)) + (insert (format "\n--%s +Content-Type: application/pgp-signature +Content-Transfer-Encoding: 7bit + +" boundary)) + (insert-buffer-substring mybuf (car rgn) (cdr rgn)) + (goto-char (point-max)) + (insert (format "\n--%s--\n" boundary)) + ) + (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 nil)) + ;; 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)) + rgn))) + +(defun mime-mc-pgp50-sign-region (start end &optional id unclear boundary) + (if (not (fboundp 'mc-pgp50-sign-parser)) + (load "mc-pgp5") + ) + (let ((process-environment process-environment) + (buffer (get-buffer-create mc-buffer-name)) + passwd args key + (parser (function mc-pgp50-sign-parser)) + (pgp-path mc-pgp50-pgps-path) + ) + (setq key (mc-pgp50-lookup-key (or id mc-pgp50-user-id))) + (setq passwd + (mc-activate-passwd + (cdr key) + (format "PGP passphrase for %s (%s): " (car key) (cdr key)))) + (setenv "PGPPASSFD" "0") + (setq args + (cons + (if boundary + "-fbat" + "-fat") + (list "+verbose=1" "+language=us" + (format "+clearsig=%s" (if unclear "off" "on")) + "+batchmode" "-u" (cdr key)))) + (if mc-pgp50-comment + (setq args (cons (format "+comment=\"%s\"" mc-pgp50-comment) args)) + ) + (message "Signing as %s ..." (car key)) + (if (mime-mc-pgp50-process-region + start end passwd pgp-path args parser buffer boundary) + (progn + (if boundary + (progn + (goto-char (point-min)) + (insert + (format "\ +--[[multipart/signed; protocol=\"application/pgp-signature\"; + boundary=\"%s\"; micalg=pgp-md5][7bit]]\n" boundary)) + )) + (message "Signing as %s ... Done." (car key)) + t) + nil))) + +(defun mime-mc-pgp50-encrypt-region (recipients start end &optional id sign) + (if (not (fboundp 'mc-pgp50-encrypt-region)) + (load "mc-pgp5") + ) + (let ((mc-pgp-always-sign (if (eq sign 'maybe) + mc-pgp-always-sign + 'never))) + (mc-pgp50-encrypt-region + (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients) + start end id nil) + )) + + +;;; @ PGP 2.6 functions +;;; + (defun mime-mc-process-region (beg end passwd program args parser &optional buffer boundary) (let ((obuf (current-buffer)) @@ -104,9 +482,9 @@ Content-Transfer-Encoding: 7bit (or buffer (null mybuf) (kill-buffer mybuf))))) (defun mime-mc-pgp-sign-region (start end &optional id unclear boundary) - ;; (if (not (boundp 'mc-pgp-user-id)) - ;; (load "mc-pgp") - ;; ) + (if (not (fboundp 'mc-pgp-generic-parser)) + (load "mc-pgp") + ) (let ((process-environment process-environment) (buffer (get-buffer-create mc-buffer-name)) passwd args key @@ -147,6 +525,9 @@ Content-Transfer-Encoding: 7bit nil))) (defun mime-mc-pgp-encrypt-region (recipients start end &optional id sign) + (if (not (fboundp 'mc-pgp-encrypt-region)) + (load "mc-pgp") + ) (let ((mc-pgp-always-sign (if (eq sign 'maybe) mc-pgp-always-sign 'never))) @@ -155,7 +536,7 @@ Content-Transfer-Encoding: 7bit start end id nil) )) - + ;;; @ end ;;; diff --git a/mime-pgp.el b/mime-pgp.el index fd0203a..88ffcb3 100644 --- a/mime-pgp.el +++ b/mime-pgp.el @@ -1,11 +1,12 @@ -;;; mime-pgp.el --- mime-view internal methods for PGP. +;;; mime-pgp.el --- mime-view internal methods for either PGP or GnuPG. ;; Copyright (C) 1995,1996,1997,1998,1999 MORIOKA Tomohiko ;; Author: MORIOKA Tomohiko +;; Katsumi Yamaoka ;; Created: 1995/12/7 ;; Renamed: 1997/2/27 from tm-pgp.el -;; Keywords: PGP, security, MIME, multimedia, mail, news +;; Keywords: PGP, GnuPG, security, MIME, multimedia, mail, news ;; This file is part of SEMI (Secure Emacs MIME Interface). @@ -30,9 +31,9 @@ ;; [security-multipart] RFC 1847: "Security Multiparts for MIME: ;; Multipart/Signed and Multipart/Encrypted" by -;; Jim Galvin , Sandy Murphy , +;; Jim Galvin , Sandy Murphy , ;; Steve Crocker and -;; Ned Freed (1995/10) +;; Ned Freed (1995/10) ;; [PGP/MIME] RFC 2015: "MIME Security with Pretty Good Privacy ;; (PGP)" by Michael Elkins (1996/6) @@ -43,6 +44,7 @@ ;;; Code: +(require 'semi-def) (require 'mime-play) @@ -115,42 +117,203 @@ ;;; ;;; It is based on RFC 2015 (PGP/MIME). -(defvar mime-pgp-command "pgp" - "*Name of the PGP command.") - -(defvar mime-pgp-default-language 'en - "*Symbol of language for pgp. -It should be ISO 639 2 letter language code such as en, ja, ...") - -(defvar mime-pgp-good-signature-regexp-alist - '((en . "Good signature from user.*$")) - "Alist of language vs regexp to detect ``Good signature''.") - -(defvar mime-pgp-key-expected-regexp-alist - '((en . "Key matching expected Key ID \\(\\S +\\) not found")) - "Alist of language vs regexp to detect ``Key expected''.") +(defcustom mime-pgp-command-alist '((gpg . "gpg") + (pgp50 . "pgp") + (pgp . "pgp")) + "Alist of the schemes and the name of the commands. Valid SCHEMEs are: + + gpg - GnuPG. + pgp50 - PGP version 5.0i. + pgp - PGP version 2.6. + +COMMAND for `pgp50' must *NOT* have a suffix, like neither \"pgpe\", \"pgpk\", +\"pgps\" nor \"pgpv\"." + :group 'mime + :type '(repeat (cons :format "%v" + (choice (choice-item :tag "GnuPG" gpg) + (choice-item :tag "PGP 5.0i" pgp50) + (choice-item :tag "PGP 2.6" pgp)) + (string :tag "Command")))) + +(defcustom mime-pgp-default-language-alist '((gpg . nil) + (pgp50 . us) + (pgp . en)) + "Alist of the schemes and the symbol of languages. It should be ISO 639 +2 letter language code such as en, ja, ... Each element looks like +\(SCHEME . SYMBOL). See also `mime-pgp-command-alist' for valid SCHEMEs." + :group 'mime + :type '(repeat (cons :format "%v" + (choice (choice-item :tag "GnuPG" gpg) + (choice-item :tag "PGP 5.0i" pgp50) + (choice-item :tag "PGP 2.6" pgp)) + (symbol :tag "Language")))) + +(defcustom mime-pgp-good-signature-regexp-alist + '((gpg + (nil "Good signature from.*$" nil) + ) + (pgp50 + (us "Good signature made .* by key:$" + mime-pgp-good-signature-post-function-pgp50-us) + ) + (pgp + (en "Good signature from user.*$" nil) + )) + "Alist of the schemes and alist of the languages and the regexps for +detecting ``Good signature''. The optional symbol of the post processing +function for arranging the output message can be specified in each element. +It will be called just after re-search is done successfully, and it is +expected that the function returns a string for messaging." + :group 'mime + :type '(repeat (cons :format "%v" + (choice (choice-item :tag "GnuPG" gpg) + (choice-item :tag "PGP 5.0i" pgp50) + (choice-item :tag "PGP 2.6" pgp)) + (repeat (list :format "%v" + (symbol :tag "Language") + (regexp :tag "Regexp") + (function :tag "Post Function")))))) + +(defcustom mime-pgp-key-expected-regexp-alist + '((gpg + (nil + . + "key ID \\(\\S +\\)\ngpg: Can't check signature: public key not found") + ) + (pgp50 + (us . "Signature by unknown keyid: 0x\\(\\S +\\)") + ) + (pgp + (en . "Key matching expected Key ID \\(\\S +\\) not found") + )) + "Alist of the schemes and alist of the languages and regexps for detecting +``Key expected''." + :group 'mime + :type '(repeat (cons :format "%v" + (choice (choice-item :tag "GnuPG" gpg) + (choice-item :tag "PGP 5.0i" pgp50) + (choice-item :tag "PGP 2.6" pgp)) + (repeat (cons :format "%v" + (symbol :tag "Language") + (regexp :tag "Regexp")))))) + +(defmacro mime-pgp-command (&optional suffix) + "Return a suitable command. SUFFIX should be either \"e\", \"k\", \"s\" +or \"v\" for choosing a command of PGP 5.0i." + (` (let ((command (cdr (assq pgp-version mime-pgp-command-alist)))) + (if (and command + (progn + (if (eq 'pgp50 pgp-version) + (setq command (format "%s%s" command (, suffix)))) + (exec-installed-p command))) + command + (error "Please specify the valid command name for `%s'." + (or pgp-version 'pgp-version)))))) + +(defmacro mime-pgp-default-language () + "Return a symbol of language." + '(cond ((eq 'gpg pgp-version) + nil) + ((eq 'pgp50 pgp-version) + (or (cdr (assq pgp-version mime-pgp-default-language-alist)) 'us) + ) + (t + (or (cdr (assq pgp-version mime-pgp-default-language-alist)) 'en) + ))) + +(defmacro mime-pgp-good-signature-regexp () + "Return a regexp to detect ``Good signature''." + '(nth 1 + (assq + (mime-pgp-default-language) + (cdr (assq pgp-version mime-pgp-good-signature-regexp-alist)) + ))) + +(defmacro mime-pgp-good-signature-post-function () + "Return a post processing function for arranging the message for +``Good signature''." + '(nth 2 + (assq + (mime-pgp-default-language) + (cdr (assq pgp-version mime-pgp-good-signature-regexp-alist)) + ))) + +(defmacro mime-pgp-key-expected-regexp () + "Return a regexp to detect ``Key expected''." + '(cdr (assq (mime-pgp-default-language) + (cdr (assq pgp-version mime-pgp-key-expected-regexp-alist)) + ))) (defun mime-pgp-check-signature (output-buffer orig-file) (save-excursion (set-buffer output-buffer) - (erase-buffer)) - (let* ((lang (or mime-pgp-default-language 'en)) - (status (call-process-region (point-min)(point-max) - mime-pgp-command - nil output-buffer nil - orig-file (format "+language=%s" lang))) - (regexp (cdr (assq lang mime-pgp-good-signature-regexp-alist)))) - (if (= status 0) + (erase-buffer) + (setq truncate-lines t)) + (let* ((lang (mime-pgp-default-language)) + (command (mime-pgp-command 'v)) + (args (cond ((eq 'gpg pgp-version) + (list "--batch" "--verify" + (concat orig-file ".sig")) + ) + ((eq 'pgp50 pgp-version) + (list "+batchmode=1" + (format "+language=%s" lang) + (concat orig-file ".sig")) + ) + ((eq 'pgp pgp-version) + (list (format "+language=%s" lang) orig-file)) + )) + (regexp (mime-pgp-good-signature-regexp)) + (post-function (mime-pgp-good-signature-post-function)) + pgp-id) + (if (zerop (apply 'call-process-region + (point-min) (point-max) command nil output-buffer nil + args)) (save-excursion (set-buffer output-buffer) (goto-char (point-min)) - (message - (cond ((not (stringp regexp)) - "Please specify right regexp for specified language") - ((re-search-forward regexp nil t) - (buffer-substring (match-beginning 0) (match-end 0))) - (t "Bad signature"))) - )))) + (cond + ((not (stringp regexp)) + (message "Please specify right regexp for specified language") + ) + ((re-search-forward regexp nil t) + (message (if post-function + (funcall post-function) + (buffer-substring (match-beginning 0) (match-end 0)))) + (goto-char (point-min)) + ) + ;; PGP 5.0i always returns 0, so we should attempt to fetch. + ((eq 'pgp50 pgp-version) + (if (not (stringp (setq regexp (mime-pgp-key-expected-regexp)))) + (message "Please specify right regexp for specified language") + (if (re-search-forward regexp nil t) + (progn + (setq pgp-id + (concat "0x" (buffer-substring-no-properties + (match-beginning 1) + (match-end 1)))) + (if (and + pgp-id + (y-or-n-p + (format "Key %s not found; attempt to fetch? " + pgp-id) + )) + (progn + (funcall (pgp-function 'fetch-key) (cons nil pgp-id)) + (mime-pgp-check-signature mime-echo-buffer-name + orig-file) + ) + (message "Bad signature") + )) + (message "Bad signature") + )) + ) + (t + (message "Bad signature") + )) + ) + (message "Bad signature") + nil))) (defun mime-verify-application/pgp-signature (entity situation) "Internal method to check PGP/MIME signature." @@ -168,39 +331,60 @@ It should be ISO 639 2 letter language code such as en, ja, ...") (mime-write-entity orig-entity orig-file) (save-excursion (mime-show-echo-buffer)) (mime-write-entity-content entity sig-file) - (or (mime-pgp-check-signature mime-echo-buffer-name orig-file) - (let (pgp-id) + (if (mime-pgp-check-signature mime-echo-buffer-name orig-file) + (let ((other-window-scroll-buffer mime-echo-buffer-name)) + (scroll-other-window + (cdr (assq pgp-version '((gpg . 0) (pgp50 . 1) (pgp . 10)))) + )) + (if (eq 'pgp pgp-version) (save-excursion (set-buffer mime-echo-buffer-name) (goto-char (point-min)) - (let ((regexp (cdr (assq (or mime-pgp-default-language 'en) - mime-pgp-key-expected-regexp-alist)))) - (cond ((not (stringp regexp)) - (message - "Please specify right regexp for specified language") - ) - ((re-search-forward regexp nil t) - (setq pgp-id - (concat "0x" (buffer-substring-no-properties + (if (search-forward "\C-g" nil t) + (goto-char (match-beginning 0)) + (forward-line 7)) + (set-window-start + (get-buffer-window mime-echo-buffer-name) + (point)) + ) + (let ((other-window-scroll-buffer mime-echo-buffer-name)) + (scroll-other-window + (cdr (assq pgp-version '((gpg . 0) (pgp50 . 1)))) + ))) + (let (pgp-id) + (save-excursion + (set-buffer mime-echo-buffer-name) + (goto-char (point-min)) + (let ((regexp (mime-pgp-key-expected-regexp))) + (cond + ((not (stringp regexp)) + (message "Please specify right regexp for specified language") + ) + ((re-search-forward regexp nil t) + (setq pgp-id (concat "0x" (buffer-substring-no-properties (match-beginning 1) (match-end 1)))) - )))) - (if (and pgp-id - (y-or-n-p - (format "Key %s not found; attempt to fetch? " pgp-id)) - ) - (progn - (funcall (pgp-function 'fetch-key) (cons nil pgp-id)) - (mime-pgp-check-signature mime-echo-buffer-name orig-file) - )) - )) - (let ((other-window-scroll-buffer mime-echo-buffer-name)) - (scroll-other-window 8) - ) + )))) + (if (and pgp-id + (prog1 + (y-or-n-p + (format "Key %s not found; attempt to fetch? " pgp-id)) + (message "")) + ) + (progn + (funcall (pgp-function 'fetch-key) (cons nil pgp-id)) + (mime-pgp-check-signature mime-echo-buffer-name orig-file) + ) + ))) (delete-file orig-file) (delete-file sig-file) )) +(defun mime-pgp-good-signature-post-function-pgp50-us () + (forward-line 2) + (looking-at "\\s +\\(.+\\)$") + (format "Good signature from %s" (match-string 1))) + ;;; @ Internal method for application/pgp-encrypted ;;; @@ -243,7 +427,7 @@ It should be ISO 639 2 letter language code such as en, ja, ...") (kill-buffer (current-buffer)) )) - + ;;; @ end ;;; diff --git a/mime-ui-en.sgml b/mime-ui-en.sgml index 6f1b876..49f60ca 100644 --- a/mime-ui-en.sgml +++ b/mime-ui-en.sgml @@ -608,23 +608,35 @@ sending. mime-edit provides PGP encryption, signature and inserting public-key features based on PGP/MIME (RFC 2015) or PGP-kazu (draft-kazu-pgp-mime-00.txt). +In current version, PGP 5.0i and GnuPG are supported as well.

-This feature requires pgp command and pgp interface package, such as -Mailcrypt package. +This feature requires PGP or GnuPG commands and its interface package, +such as Mailcrypt package. + + +

+Version of PGP or GnuPG command to be used for encryption or sign. +The value should be a symbol. Allowed versions are gpg, +pgp50 or pgp. +

Alist of service names vs. corresponding functions and its filenames. -Each element looks like (SERVICE FUNCTION FILE). +Each element looks like +(SERVICE FUNCTION FILE [PGP5_FUNCTION PGP5_FILE [GPG_FUNCTION GPG_FILE]]).

-SERVICE is a symbol of PGP processing. It allows `verify', `decrypt', -`fetch-key', `snarf-keys', `mime-sign', `traditional-sign', `encrypt' -or `insert-key'. +SERVICE is a symbol of PGP2, PGP5 or GnuPG processing. It allows +`verify', `decrypt', `fetch-key', `snarf-keys', `mime-sign', +`traditional-sign', `encrypt' or `insert-key'.

Function is a symbol of function to do specified SERVICE.

FILE is string of filename which has definition of corresponding FUNCTION. +

+PGP5_FUNCTION, PGP5_FILE, GPG_FUNCTION and GPG_FILE are similar to +FUNCTION and FILE, but they will be used for PGP 5.0i or GnuPG. diff --git a/mime-ui-ja.sgml b/mime-ui-ja.sgml index 2e1094a..bda633d 100644 --- a/mime-ui-ja.sgml +++ b/mime-ui-ja.sgml @@ -623,23 +623,35 @@ sending. mime-edit では PGP/MIME (RFC 2015) および PGP-kazu (draft-kazu-pgp-mime-00.txt) による暗号化・ 電子署名・公開鍵の挿入機能を利用することができます。 +現在の版では PGP 5.0i と GnuPG もサポートされています。

但し、この機能を利用するには Mailcrypt package と pgp command が必要です。 + +

+Version of PGP or GnuPG command to be used for encryption or sign. +The value should be a symbol. Allowed versions are gpg, +pgp50 or pgp. + +

Alist of service names vs. corresponding functions and its filenames. -Each element looks like (SERVICE FUNCTION FILE). +Each element looks like +(SERVICE FUNCTION FILE [PGP5_FUNCTION PGP5_FILE [GPG_FUNCTION GPG_FILE]]).

-SERVICE is a symbol of PGP processing. It allows `verify', `decrypt', -`fetch-key', `snarf-keys', `mime-sign', `traditional-sign', `encrypt' -or `insert-key'. +SERVICE is a symbol of PGP2, PGP5 or GnuPG processing. It allows +`verify', `decrypt', `fetch-key', `snarf-keys', `mime-sign', +`traditional-sign', `encrypt' or `insert-key'.

Function is a symbol of function to do specified SERVICE.

FILE is string of filename which has definition of corresponding FUNCTION. +

+PGP5_FUNCTION, PGP5_FILE, GPG_FUNCTION and GPG_FILE are similar to +FUNCTION and FILE, but they will be used for PGP 5.0i or GnuPG. diff --git a/semi-def.el b/semi-def.el index d5e28a7..2f432ce 100644 --- a/semi-def.el +++ b/semi-def.el @@ -168,39 +168,79 @@ ;;; @ PGP ;;; +(defcustom pgp-version 'pgp + "Version of PGP or GnuPG command to be used for encryption or sign. +The value should be a symbol. Allowed versions are: + + gpg - GnuPG. + pgp50 - PGP version 5.0i. + pgp - PGP version 2.6." + :group 'mime + :type '(radio (choice-item :tag "GnuPG" gpg) + (choice-item :tag "PGP 5.0i" pgp50) + (choice-item :tag "PGP 2.6" pgp))) + (defvar pgp-function-alist '( ;; for mime-pgp - (verify mc-verify "mc-toplev") - (decrypt mc-decrypt "mc-toplev") - (fetch-key mc-pgp-fetch-key "mc-pgp") - (snarf-keys mc-snarf-keys "mc-toplev") + (verify mime-mc-verify "mime-mc") + (decrypt mime-mc-decrypt "mime-mc") + (fetch-key mc-pgp-fetch-key "mc-pgp" + mc-pgp50-fetch-key "mc-pgp5" + mc-gpg-fetch-key "mc-gpg") + (snarf-keys mime-mc-snarf-keys "mime-mc") ;; for mime-edit - (mime-sign mime-mc-pgp-sign-region "mime-mc") - (traditional-sign mc-pgp-sign-region "mc-pgp") - (encrypt mime-mc-pgp-encrypt-region "mime-mc") - (insert-key mc-insert-public-key "mc-toplev") + (mime-sign mime-mc-pgp-sign-region "mime-mc" + mime-mc-pgp50-sign-region "mime-mc" + mime-mc-gpg-sign-region "mime-mc") + (traditional-sign mc-pgp-sign-region "mc-pgp" + mc-pgp50-sign-region "mc-pgp5" + mc-gpg-sign-region "mc-gpg") + (encrypt mime-mc-pgp-encrypt-region "mime-mc" + mime-mc-pgp50-encrypt-region "mime-mc" + mime-mc-gpg-encrypt-region "mime-mc") + (insert-key mime-mc-insert-public-key "mime-mc") ) "Alist of service names vs. corresponding functions and its filenames. -Each element looks like (SERVICE FUNCTION FILE). +Each element looks like: -SERVICE is a symbol of PGP processing. It allows `verify', `decrypt', -`fetch-key', `snarf-keys', `mime-sign', `traditional-sign', `encrypt' -or `insert-key'. +\(SERVICE FUNCTION FILE [PGP5_FUNCTION PGP5_FILE [GPG_FUNCTION GPG_FILE]]). -Function is a symbol of function to do specified SERVICE. +SERVICE is a symbol of PGP2, PGP5 or GnuPG processing. It allows `verify', +`decrypt', `fetch-key', `snarf-keys', `mime-sign', `traditional-sign', +`encrypt' or `insert-key'. + +FUNCTION is a symbol of function to do specified SERVICE. FILE is string of filename which has definition of corresponding -FUNCTION.") +FUNCTION. + +PGP5_FUNCTION, PGP5_FILE, GPG_FUNCTION and GPG_FILE are similar to +FUNCTION and FILE, but they will be used for PGP 5.0i or GnuPG.") (defmacro pgp-function (method) "Return function to do service METHOD." - `(cadr (assq ,method (symbol-value 'pgp-function-alist)))) + `(let ((elem (assq ,method (symbol-value 'pgp-function-alist)))) + (cond ((eq 'gpg pgp-version) + (if (> (length elem) 3) + (nth 5 elem) + (nth 1 elem)) + ) + ((eq 'pgp50 pgp-version) + (if (> (length elem) 3) + (nth 3 elem) + (nth 1 elem)) + ) + (t + (nth 1 elem) + )))) (mapcar (function - (lambda (method) - (autoload (cadr method)(nth 2 method)) - )) + (lambda (elem) + (setq elem (cdr elem)) + (while elem + (autoload (car elem) (nth 1 elem)) + (setq elem (nthcdr 2 elem))))) pgp-function-alist) diff --git a/semi-setup.el b/semi-setup.el index 9928d1e..b1493a6 100644 --- a/semi-setup.el +++ b/semi-setup.el @@ -1,6 +1,6 @@ ;;; semi-setup.el --- setup file for MIME-View. -;; Copyright (C) 1994,1995,1996,1997,1998 Free Software Foundation, Inc. +;; Copyright (C) 1994,1995,1996,1997,1998,1999 Free Software Foundation, Inc. ;; Author: MORIOKA Tomohiko ;; Keywords: mail, news, MIME, multimedia, multilingual, encoded-word @@ -85,7 +85,7 @@ it is used as hook to set." ;; for PGP (defvar mime-setup-enable-pgp (module-installed-p 'mailcrypt) - "*If it is non-nil, semi-setup sets uf to use mime-pgp.") + "*If it is non-nil, semi-setup sets up to use mime-pgp.") (if mime-setup-enable-pgp (eval-after-load "mime-view"