;;; 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 'mailcrypt)
-(eval-and-compile (load "mc-pgp"))
+(eval-and-compile
+ (load "mc-pgp")
+ (load "mc-pgp5" t)
+ (load "mc-gpg" t)
+ )
(defun mime-mc-pgp-generic-parser (result)
(let ((ret (mc-pgp-generic-parser result)))
(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")
- ;; )
(let ((process-environment process-environment)
(buffer (get-buffer-create mc-buffer-name))
- passwd args key
(parser (function mc-pgp-generic-parser))
- (pgp-path mc-pgp-path)
- )
- (setq key (mc-pgp-lookup-key (or id mc-pgp-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
- "-fbast"
- "-fast")
- (list "+verbose=1" "+language=en"
- (format "+clearsig=%s" (if unclear "off" "on"))
- "+batchmode" "-u" (cdr key))))
- (if mc-pgp-comment
- (setq args (cons (format "+comment=%s" mc-pgp-comment) args))
+ pgp-path key args prompt passwd hash-function)
+ (cond
+ ((eq 'mc-scheme-gpg mc-default-scheme)
+ (setq pgp-path mc-gpg-path
+ key (mc-gpg-lookup-key (or id mc-gpg-user-id))
+ args (delq nil
+ (nconc
+ (if mc-gpg-comment
+ (list "--comment" (format "%s" mc-gpg-comment)))
+ (list
+ (if boundary
+ "--detach-sign"
+ (if unclear
+ "--sign"
+ "--clearsign"))
+ "--armor" "--batch" "--textmode" "--verbose"
+ "--passphrase-fd" "0" "--local-user" (cdr key))))
+ prompt (format "GnuPG passphrase for %s (%s): "
+ (car key) (cdr key))
+ hash-function 'sha1)
+ (if (and boundary
+ (string-match "^pgp-" boundary))
+ (setq boundary
+ (concat "gpg-" (substring boundary (match-end 0))))
+ ))
+ ((eq 'mc-scheme-pgp50 mc-default-scheme)
+ (setq pgp-path mc-pgp50-pgps-path
+ key (mc-pgp50-lookup-key (or id mc-pgp50-user-id))
+ args (delq nil
+ (list
+ (if mc-pgp50-comment
+ (format "+comment=%s" mc-pgp50-comment))
+ (if boundary
+ "-fbat"
+ "-fat")
+ "+verbose=1" "+language=us"
+ (format "+clearsig=%s" (if unclear "off" "on"))
+ "+batchmode" "-u" (cdr key)))
+ prompt (format "PGP passphrase for %s (%s): "
+ (car key) (cdr key))
+ hash-function 'md5)
+ (setenv "PGPPASSFD" "0")
)
+ (t
+ (setq pgp-path mc-pgp-path
+ key (mc-pgp-lookup-key (or id mc-pgp-user-id))
+ args (delq nil
+ (list
+ (if mc-pgp-comment
+ (format "+comment=%s" mc-pgp-comment))
+ (if boundary
+ "-fbast"
+ "-fast")
+ "+verbose=1" "+language=en"
+ (format "+clearsig=%s" (if unclear "off" "on"))
+ "+batchmode" "-u" (cdr key)))
+ prompt (format "PGP passphrase for %s (%s): "
+ (car key) (cdr key))
+ hash-function 'md5)
+ (setenv "PGPPASSFD" "0")
+ ))
+ (setq passwd (mc-activate-passwd (cdr key) prompt))
(message "Signing as %s ..." (car key))
(if (mime-mc-process-region
start end passwd pgp-path args parser buffer boundary)
(insert
(format "\
--[[multipart/signed; protocol=\"application/pgp-signature\";
- boundary=\"%s\"; micalg=pgp-md5][7bit]]\n" boundary))
+ boundary=\"%s\"; micalg=pgp-%s][7bit]]\n"
+ boundary hash-function))
))
(message "Signing as %s ... Done." (car key))
t)
(defun mime-mc-pgp-encrypt-region (recipients start end &optional id sign)
(let ((mc-pgp-always-sign (if (eq sign 'maybe)
mc-pgp-always-sign
- 'never)))
- (mc-pgp-encrypt-region
- (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients)
- start end id nil)
+ 'never))
+ (function (cond ((eq 'mc-scheme-gpg mc-default-scheme)
+ 'mc-gpg-encrypt-region)
+ ((eq 'mc-scheme-pgp50 mc-default-scheme)
+ 'mc-pgp50-encrypt-region)
+ (t
+ 'mc-pgp-encrypt-region))))
+ (funcall function
+ (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients)
+ start end id nil)
))
-;;; 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)
;;;
;;; It is based on RFC 2015 (PGP/MIME).
-(defvar mime-pgp-command "pgp"
- "*Name of the PGP command.")
+(defvar mime-pgp-default-scheme 'pgp
+ "*Default encryption scheme to use. See also `mime-pgp-command-alist'
+for valid schemes.")
-(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-command-alist '((gpg . "gpg")
+ (pgp50 . "pgp")
+ (pgp . "pgp"))
+ "*Alist of the corresponding schemes to the name of the commands.
+Each element looks like (SCHEME . COMMAND). 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\".")
+
+(defvar mime-pgp-default-language-alist nil
+ "*Alist of the corresponding schemes to the symbols of languages.
+It should be ISO 639 2 letter language code such as en, ja, ... Each
+element looks like (SCHEME . SYMBOL). Also see `mime-pgp-command-alist'
+for valid SCHEMEs.")
(defvar mime-pgp-good-signature-regexp-alist
- '((en . "Good signature from user.*$"))
- "Alist of language vs regexp to detect ``Good signature''.")
+ '((gpg
+ (nil "Good signature from.*$")
+ )
+ (pgp50
+ (us "Good signature made .* by key:$"
+ mime-pgp-good-signature-post-function-pgp50-us)
+ )
+ (pgp
+ (en "Good signature from user.*$")
+ ))
+ "Alist of the scheme vs corresponding languages to regexps for detecting
+``Good signature''. The optional symbol of the post processing function
+for arranging the output message can be specified. It will be called just
+after re-search is done successfully, and it is expected that the function
+returns a string for messaging.")
(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''.")
+ '((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 scheme vs corresponding languages to regexps for detecting
+``Key expected''.")
+
+(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
+ mime-pgp-default-scheme mime-pgp-command-alist))))
+ (if (and command
+ (progn
+ (if (eq 'pgp50 mime-pgp-default-scheme)
+ (setq command (concat command (, suffix))))
+ (exec-installed-p command)))
+ command
+ (error "Please specify the valid command name for `%s'."
+ (or mime-pgp-default-scheme 'mime-pgp-default-scheme))))))
+
+(defmacro mime-pgp-default-language ()
+ "Return a symbol of language."
+ '(cond ((eq 'gpg mime-pgp-default-scheme)
+ nil)
+ ((eq 'pgp50 mime-pgp-default-scheme)
+ (or (cdr (assq mime-pgp-default-scheme
+ mime-pgp-default-language-alist))
+ 'us))
+ (t
+ (or (cdr (assq mime-pgp-default-scheme
+ 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
+ mime-pgp-default-scheme 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
+ mime-pgp-default-scheme mime-pgp-good-signature-regexp-alist)))))
+
+(defmacro mime-pgp-key-expected-regexp ()
+ "Return a regexp to detect ``Key expected''."
+ '(nth 1
+ (assq
+ (mime-pgp-default-language)
+ (cdr
+ (assq
+ mime-pgp-default-scheme 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 mime-pgp-default-scheme)
+ (list "--verify" (concat orig-file ".sig"))
+ )
+ ((eq 'pgp50 mime-pgp-default-scheme)
+ (list "+batchmode=1"
+ (format "+language=%s" lang)
+ (concat orig-file ".sig"))
+ )
+ ((eq 'pgp mime-pgp-default-scheme)
+ (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 mime-pgp-default-scheme)
+ (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 mime-pgp-default-scheme
+ '((gpg . 5) (pgp50 . 1) (pgp . 10)))))
+ )
+ (if (eq 'pgp mime-pgp-default-scheme)
(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 mime-pgp-default-scheme '((gpg . 5) (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))
))
-
+
+;;; @ Command.
+
+(defvar mime-pgp-setversion-hook nil
+ "*Hook run after the value of `mime-pgp-default-scheme' has been changed.
+It is supposed to be used for working `mc-setversion' together with the
+command `mime-pgp-setversion'. For the purpose, it will be overridden by
+\"semi-setup\" in default.")
+
+(defun mime-pgp-setversion (&optional version)
+ "Select PGP version or GnuPG."
+ (interactive)
+ (let ((table '(("GnuPG" . gpg) ("PGP 2.6" . pgp) ("PGP 5.0i" . pgp50)
+ ("gnupg" . gpg) ("gpg" . gpg)
+ ("pgp" . pgp) ("pgp2" . pgp) ("pgp2.6" . pgp)
+ ("pgp5" . pgp50) ("pgp5.0" . pgp50) ("pgp50" . pgp50)
+ ("2.6" . pgp) ("5.0" . pgp50)))
+ (msg (interactive-p)))
+ (if msg
+ (setq version
+ (completing-read
+ (format "Select PGP version or GnuPG (currently %s): "
+ (car (rassq mime-pgp-default-scheme table)))
+ table nil t)
+ ))
+ (if (zerop (length version))
+ (setq msg (and msg "PGP version is not modified."))
+ (setq mime-pgp-default-scheme (cdr (assoc version table))
+ msg (and msg (format "PGP version set to \"%s\"." version)))
+ (run-hooks 'mime-pgp-setversion-hook)
+ )
+ (if msg (message "%s" msg))
+ )
+ mime-pgp-default-scheme)
+
+(mime-pgp-setversion
+ (cdr (assq mime-pgp-default-scheme
+ '((gpg . "GnuPG")
+ (pgp50 . "PGP 5.0i")
+ (pgp . "PGP 2.6"))
+ )))
+
+
;;; @ end
;;;
(require 'custom)
-(defconst mime-user-interface-product ["SEMI" (1 13 3) "Komaiko"]
+(defconst mime-user-interface-product ["SEMI" (1 13 4) "Terai"]
"Product name, version number and code name of MIME-kernel package.")
(autoload 'mule-caesar-region "mule-caesar"
;; for mime-pgp
(verify mc-verify "mc-toplev")
(decrypt mc-decrypt "mc-toplev")
- (fetch-key mc-pgp-fetch-key "mc-pgp")
+ (fetch-key mc-pgp-fetch-key "mc-pgp"
+ mc-pgp50-fetch-key "mc-pgp5"
+ mc-gpg-fetch-key "mc-gpg")
(snarf-keys mc-snarf-keys "mc-toplev")
;; for mime-edit
(mime-sign mime-mc-pgp-sign-region "mime-mc")
- (traditional-sign mc-pgp-sign-region "mc-pgp")
+ (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")
(insert-key mc-insert-public-key "mc-toplev")
)
"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))))
+ (require 'mailcrypt)
+ (cond ((eq 'mc-scheme-gpg (symbol-value 'mc-default-scheme))
+ (if (> (length elem) 3)
+ (nth 5 elem)
+ (nth 1 elem))
+ )
+ ((eq 'mc-scheme-pgp50 (symbol-value 'mc-default-scheme))
+ (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)