+1999-03-24 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * 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 <morioka@jaist.ac.jp>
* mime-edit.el (mime-charset-type-list): Add `tis-620'.
* 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'
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
(require 'sendmail)
(require 'mail-utils)
(require 'mel)
+(require 'semi-def)
(require 'mime-view)
(require 'signature)
(require 'alist)
(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))
;;; 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 <morioka@jaist.ac.jp>
-;; Keywords: PGP, security, MIME, multimedia, mail, news
+;; Katsumi Yamaoka <yamaoka@jpl.org>
+;; Keywords: PGP, GnuPG, security, MIME, multimedia, mail, news
;; This file is part of SEMI (Secure Emacs MIME Interface).
;;; 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))
(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
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)))
start end id nil)
))
-
+
;;; @ end
;;;
-;;; 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 <morioka@jaist.ac.jp>
+;; Katsumi Yamaoka <yamaoka@jpl.org>
;; 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).
;; [security-multipart] RFC 1847: "Security Multiparts for MIME:
;; Multipart/Signed and Multipart/Encrypted" by
-;; Jim Galvin <galvin@tis.com>, Sandy Murphy <sandy@tis.com>,
+;; Jim Galvin <galvin@tis.com>, Sandy Murphy <sandy@tis.com>,
;; Steve Crocker <crocker@cybercash.com> and
-;; Ned Freed <ned@innosoft.com> (1995/10)
+;; Ned Freed <ned@innosoft.com> (1995/10)
;; [PGP/MIME] RFC 2015: "MIME Security with Pretty Good Privacy
;; (PGP)" by Michael Elkins <elkins@aero.org> (1996/6)
;;; Code:
+(require 'semi-def)
(require 'mime-play)
;;;
;;; 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."
(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
;;;
(kill-buffer (current-buffer))
))
-
+
;;; @ end
;;;
mime-edit provides PGP encryption, signature and inserting public-key
features based on <concept>PGP/MIME</concept> (RFC 2015) or
<concept>PGP-kazu</concept> (draft-kazu-pgp-mime-00.txt).
+In current version, PGP 5.0i and GnuPG are supported as well.
<p>
-This feature requires pgp command and pgp interface package, such as
-<a file="mailcrypt">Mailcrypt package</a>.
+This feature requires PGP or GnuPG commands and its interface package,
+such as <a file="mailcrypt">Mailcrypt package</a>.
+
+<defvar name="pgp-version">
+<p>
+Version of PGP or GnuPG command to be used for encryption or sign.
+The value should be a symbol. Allowed versions are <code>gpg</code>,
+<code>pgp50</code> or <code>pgp</code>.
+</defvar>
<defvar name="pgp-function-alist">
<p>
Alist of service names vs. corresponding functions and its filenames.
-Each element looks like <code>(SERVICE FUNCTION FILE)</code>.
+Each element looks like
+<code>(SERVICE FUNCTION FILE [PGP5_FUNCTION PGP5_FILE [GPG_FUNCTION GPG_FILE]])</code>.
<p>
-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'.
<p>
Function is a symbol of function to do specified SERVICE.
<p>
FILE is string of filename which has definition of corresponding
FUNCTION.
+<p>
+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.
</defvar>
<defun name="pgp-function">
mime-edit \e$B$G$O\e(B <concept>PGP/MIME</concept> (RFC 2015) \e$B$*$h$S\e(B
<concept>PGP-kazu</concept> (draft-kazu-pgp-mime-00.txt) \e$B$K$h$k0E9f2=!&\e(B
\e$BEE;R=pL>!&8x3+80$NA^F~5!G=$rMxMQ$9$k$3$H$,$G$-$^$9!#\e(B
+\e$B8=:_$NHG$G$O\e(B PGP 5.0i \e$B$H\e(B GnuPG \e$B$b%5%]!<%H$5$l$F$$$^$9!#\e(B
<p>
\e$BC"$7!"$3$N5!G=$rMxMQ$9$k$K$O\e(B <a file="mailcrypt">Mailcrypt package</a>
\e$B$H\e(B pgp command \e$B$,I,MW$G$9!#\e(B
+<defvar name="pgp-version">
+<p>
+Version of PGP or GnuPG command to be used for encryption or sign.
+The value should be a symbol. Allowed versions are <code>gpg</code>,
+<code>pgp50</code> or <code>pgp</code>.
+</defvar>
+
<defvar name="pgp-function-alist">
<p>
Alist of service names vs. corresponding functions and its filenames.
-Each element looks like <code>(SERVICE FUNCTION FILE)</code>.
+Each element looks like
+<code>(SERVICE FUNCTION FILE [PGP5_FUNCTION PGP5_FILE [GPG_FUNCTION GPG_FILE]])</code>.
<p>
-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'.
<p>
Function is a symbol of function to do specified SERVICE.
<p>
FILE is string of filename which has definition of corresponding
FUNCTION.
+<p>
+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.
</defvar>
<defun name="pgp-function">
;;; @ 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)
;;; 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 <morioka@jaist.ac.jp>
;; Keywords: mail, news, MIME, multimedia, multilingual, encoded-word
;; 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"