;; Keywords: crypto
;; Created: 2000-04-15
-;; $Id: gpg.el,v 6.2 2000/10/31 22:16:42 zsh Exp $
+;; $Id: gpg.el,v 1.17 2001/12/26 15:23:03 fw Exp $
;; This file is NOT (yet?) part of GNU Emacs.
;; * Customization for all flavors of PGP is possible.
;; * The main operations (verify, decrypt, sign, encrypt, sign &
;; encrypt) are implemented.
-;; * Gero Treuner's gpg-2comp script is supported, and data which is is
-;; compatible with PGP 2.6.3 is generated.
+;; * Optionally, Gero Treuner's gpg-2comp script is supported,
+;; to generate data which is compatible with PGP 2.6.3.
;; Customizing external programs
;; =============================
;; function (bound to `C-h l' by default).
\f
-;;;; Code:
+;;; Code:
(require 'timer)
-(eval-when-compile
- (require 'cl))
+(eval-when-compile (require 'cl))
+
+(eval-and-compile
+ (defalias 'gpg-point-at-eol
+ (if (fboundp 'point-at-eol)
+ 'point-at-eol
+ 'line-end-position)))
;;;; Customization:
;;; Customization: Widgets:
-(define-widget 'gpg-command-alist 'alist
- "An association list for GnuPG command names."
- :key-type '(symbol :tag "Abbreviation")
- :value-type '(string :tag "Program name")
- :convert-widget 'widget-alist-convert-widget
- :tag "Alist")
+(if (get 'alist 'widget-type)
+ (define-widget 'gpg-command-alist 'alist
+ "An association list for GnuPG command names."
+ :key-type '(symbol :tag "Abbreviation")
+ :value-type '(string :tag "Program name")
+ :convert-widget 'widget-alist-convert-widget
+ :tag "Alist")
+ (define-widget 'gpg-command-alist 'repeat
+ "An association list for GnuPG command names."
+ :args '((cons :format "%v"
+ (symbol :tag "Abbreviation")
+ (string :tag "Program name")))
+ :tag "Alist"))
(define-widget 'gpg-command-program 'choice
"Widget for entering the name of a program (mostly the GnuPG binary)."
(defcustom gpg-command-default-alist
'((gpg . "gpg")
- (gpg-2comp . "gpg-2comp"))
+ (gpg-2comp . "gpg"))
"Default paths for some GnuPG-related programs.
Modify this variable if you have to change the paths to the
-executables required by the GnuPG interface. You can enter \"gpg\"
-for `gpg-2comp' if you don't have this script, but you'll lose PGP
-2.6.x compatibility."
+executables required by the GnuPG interface. You can enter \"gpg-2comp\"
+for `gpg-2comp' if you have obtained this script, in order to gain
+PGP 2.6.x compatibility."
:tag "GnuPG programs"
:type 'gpg-command-alist
:group 'gpg-options)
+(defcustom gpg-command-all-arglist
+ nil
+ "List of arguments to add to all GPG commands."
+ :tag "All command args"
+ :group 'gpg-options)
+
(defcustom gpg-command-flag-textmode "--textmode"
"The flag to indicate canonical text mode to GnuPG."
:tag "Text mode flag"
;;; Customization: Variables: GnuPG Commands:
(defcustom gpg-command-verify
- '(gpg . ("--batch" "--verbose" "--verify" signature-file message-file))
+ '(gpg . ("--status-fd" "1" "--batch" "--verbose" "--verify" signature-file message-file))
"Command to verify a detached signature.
The invoked program has to read the signed message and the signature
from the given files. It should write human-readable information to
(string :format "%v"))))
:group 'gpg-commands)
+(defcustom gpg-command-verify-cleartext
+ '(gpg . ("--status-fd" "1" "--batch" "--verbose" "--verify" message-file))
+ "Command to verify a message.
+The invoked program has to read the signed message from the given
+file. It should write human-readable information to standard output
+and/or standard error. The program shall not convert charsets or line
+endings; the input data shall be treated as binary."
+ :tag "Cleartext Verify Command"
+ :type '(cons
+ gpg-command-program
+ (repeat
+ :tag "Arguments"
+ (choice
+ :format "%[Type%] %v"
+ (const :tag "Insert name of file containing the message here."
+ :value message-file)
+ (string :format "%v"))))
+ :group 'gpg-commands)
+
(defcustom gpg-command-decrypt
- '(gpg . ("--decrypt" "--batch" "--passphrase-fd=0"))
+ '(gpg . ("--status-fd" "2" "--decrypt" "--batch" "--passphrase-fd=0"))
"Command to decrypt a message.
The invoked program has to read the passphrase from standard
input, followed by the encrypted message. It writes the decrypted
'(gpg-2comp . ("--batch" "--passphrase-fd=0" "--output=-"
armor textmode "--clearsign"
sign-with-key))
- "Command to create a create a \"clearsign\" text file.
+ "Command to create a \"clearsign\" text file.
The invoked program has to read the passphrase from standard input,
followed by the message to sign. It should write the ASCII-amored
signed text message to standard output, and diagnostic messages to
'(gpg-2comp . ("--batch" "--passphrase-fd=0" "--output=-"
armor textmode "--detach-sign"
sign-with-key))
- "Command to create a create a detached signature.
+ "Command to create a detached signature.
The invoked program has to read the passphrase from standard input,
followed by the message to sign. It should write the ASCII-amored
detached signature to standard output, and diagnostic messages to
SUBSTITIONS is a list of (SYMBOL . SEXP) pairs, where SEXP is either a
string (which is inserted literally), a list of strings (which are
inserted as well), or nil, which means to insert nothing."
- (let (arglist)
+ (let ((arglist (copy-sequence gpg-command-all-arglist)))
(while template
(let* ((templ (pop template))
(repl (assoc templ substitutions))
(when gpg-command-passphrase-env
;; This will clear the variable if it wasn't set before.
(setenv (car gpg-command-passphrase-env) ,env-value))))))
+(put 'gpg-with-passphrase-env 'lisp-indent-function 0)
+(put 'gpg-with-passphrase-env 'edebug-form-spec '(body))
;;; Temporary files:
(defun gpg-make-temp-file ()
"Create a temporary file in a safe way"
- (let ((name (concat gpg-temp-directory "/gnupg")))
+ (let ((name ;; User may use "~/"
+ (expand-file-name "gnupg" gpg-temp-directory)))
(if (fboundp 'make-temp-file)
;; If we've got make-temp-file, we are on the save side.
(make-temp-file name)
;; make-temp-name doesn't create the file, and an ordinary
;; write-file operation is prone to nasty symlink attacks if the
;; temporary file resides in a world-writable directory.
- (unless (eq (file-modes gpg-temp-directory) 448) ; mode 0700
- (error "Directory for temporary files must have mode 0700."))
+ (unless (or (memq system-type '(windows-nt cygwin32 win32 w32 mswindows))
+ (eq (file-modes gpg-temp-directory) 448)) ; mode 0700
+ (error "Directory for temporary files (%s) must have mode 0700" gpg-temp-directory))
(setq name (make-temp-name name))
(let ((mode (default-file-modes)))
(unwind-protect
(gpg-with-temp-files-create ,count)
,@body)
(gpg-with-temp-files-delete))))
+(put 'gpg-with-temp-files 'lisp-indent-function 1)
+(put 'gpg-with-temp-files 'edebug-form-spec '(body))
;;; Making subprocesses:
(apply 'call-process-region (point-min) (point-max) cpr-args)
;; Wipe out passphrase.
(goto-char (point-min))
- (translate-region (point) (line-end-position)
+ (translate-region (point) (gpg-point-at-eol)
(make-string 256 ? )))
(if (listp stdin)
(with-current-buffer (car stdin)
(save-window-excursion
(display-buffer (current-buffer))
(unless (y-or-n-p "Continue? ")
- (error "GnuPG operation aborted."))))))
+ (error "GnuPG operation aborted"))))))
(defmacro gpg-show-result (always-show &rest body)
"Show GnuPG result to user for confirmation.
(unwind-protect
(gpg-show-result-buffer ,always-show (progn ,@body))
(kill-buffer gpg-result-buffer))))
+(put 'gpg-show-result 'lisp-indent-function 1)
+(put 'gpg-show-result 'edebug-form-spec '(body))
;;; Passphrase handling:
"Forget stored passphrase."
(interactive)
(cancel-timer gpg-passphrase-timer)
+ (setq gpg-passphrase-timer nil)
(gpg-passphrase-clear-string gpg-passphrase)
(setq gpg-passphrase nil))
"Store PASSPHRASE in cache.
Updates the timeout for clearing the cache to `gpg-passphrase-timeout'."
(unless (equal gpg-passphrase-timeout 0)
+ (if (null gpg-passphrase-timer)
+ (setq gpg-passphrase-timer (timer-create)))
(timer-set-time gpg-passphrase-timer
(timer-relative-time (current-time)
gpg-passphrase-timeout))
(timer-set-function gpg-passphrase-timer 'gpg-passphrase-forget)
- (timer-activate gpg-passphrase-timer)
+ (unless (and (fboundp 'itimer-live-p)
+ (itimer-live-p gpg-passphrase-timer))
+ (timer-activate gpg-passphrase-timer))
(setq gpg-passphrase passphrase))
passphrase)
-
+
(defun gpg-passphrase-read ()
"Read a passphrase and remember it for some time."
(interactive)
nil)
t))))
+;;;###autoload
+(defun gpg-verify-cleartext (message result)
+ "Verify message in buffer MESSAGE.
+Returns t if everything worked out well, nil otherwise. Consult
+buffer RESULT for details.
+
+NOTE: Use of this function is deprecated."
+ (interactive "bBuffer containing message: \nbBuffor for result: ")
+ (gpg-with-temp-files 1
+ (let* ((msg-file (nth 0 gpg-temp-files))
+ (cmd (gpg-exec-path gpg-command-verify-cleartext))
+ (args (gpg-build-arg-list (cdr gpg-command-verify-cleartext)
+ `((message-file . ,msg-file))))
+ res)
+ (with-temp-file msg-file
+ (buffer-disable-undo)
+ (apply 'insert-buffer-substring (if (listp message)
+ message
+ (list message))))
+ (setq res (apply 'call-process-region
+ (point-min) (point-min) ; no data
+ cmd
+ nil ; don't delete
+ result
+ nil ; don't display
+ args))
+ (if (or (stringp res) (> res 0))
+ ;; Signal or abnormal exit.
+ (with-current-buffer result
+ (insert (format "\nCommand exit status: %s\n" res))
+ nil)
+ t))))
+
;;;###autoload
(defun gpg-decrypt (ciphertext plaintext result &optional passphrase)
"Decrypt buffer CIPHERTEXT to buffer PLAINTEXT.
(defun gpg-key-lessp (a b)
"Returns t if primary user ID of A is less than B."
- (let ((res (compare-strings (gpg-key-primary-user-id a) 0 nil
- (gpg-key-primary-user-id b) 0 nil
- t)))
- (if (eq res t)
- nil
- (< res 0))))
+ (string-lessp (gpg-key-primary-user-id a) (gpg-key-primary-user-id b) ))
;;; Accessing the key database:
(defun gpg-key-list-keys-parse-line ()
"Parse the line in the current buffer and return a vector of fields."
- (let* ((eol (line-end-position))
+ (let* ((eol (gpg-point-at-eol))
(v (if (eolp)
nil
(vector
(?u . trust-ultimate))
"Alist mapping GnuPG trust value short forms to long symbols.")
+(defconst gpg-unabbrev-trust-alist
+ '(("TRUST_UNDEFINED" . trust-undefined)
+ ("TRUST_NEVER" . trust-none)
+ ("TRUST_MARGINAL" . trust-marginal)
+ ("TRUST_FULLY" . trust-full)
+ ("TRUST_ULTIMATE" . trust-ultimate))
+ "Alist mapping capitalized GnuPG trust values to long symbols.")
+
(defmacro gpg-key-list-keys-in-buffer-store ()
'(when primary-user-id
(sort user-id 'string-lessp)