X-Git-Url: http://cgit.sxemacs.org/?p=gnus;a=blobdiff_plain;f=lisp%2Fmml2015.el;h=e2e99771801c1031904c3e039b4dd17f1bb4e3ad;hp=5a515fa282a208447e62d4b6856028704b3e94ac;hb=7067d9be35e2177e6b891ede1db93f9af6c7e234;hpb=3de8a285bb6de5a80f6ff5138858c57a48d26de8 diff --git a/lisp/mml2015.el b/lisp/mml2015.el index 5a515fa28..e2e997718 100644 --- a/lisp/mml2015.el +++ b/lisp/mml2015.el @@ -1,7 +1,6 @@ ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP) -;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, -;; 2008, 2009, 2010 Free Software Foundation, Inc. +;; Copyright (C) 2000-2016 Free Software Foundation, Inc. ;; Author: Shenghuo Zhu ;; Keywords: PGP MIME MML @@ -29,9 +28,6 @@ ;;; Code: (eval-and-compile - ;; For Emacs <22.2 and XEmacs. - (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))) - (if (locate-library "password-cache") (require 'password-cache) (require 'password))) @@ -48,17 +44,24 @@ (config &optional minimum-version)) (declare-function epg-configuration "ext:epg-config" ()) +;; Maybe this should be in eg mml-sec.el (and have a different name). +;; Then mml1991 would not need to require mml2015, and mml1991-use +;; could be removed. (defvar mml2015-use (or - (condition-case nil - (progn - (require 'epg-config) - (epg-check-configuration (epg-configuration)) - 'epg) - (error)) (progn - (ignore-errors (require 'pgg)) - (and (fboundp 'pgg-sign-region) - 'pgg)) + (ignore-errors (require 'epg-config)) + (and (fboundp 'epg-check-configuration) + 'epg)) + (progn + (let ((abs-file (locate-library "pgg"))) + ;; Don't load PGG if it is marked as obsolete + ;; (Emacs 24). + (when (and abs-file + (not (string-match "/obsolete/[^/]*\\'" + abs-file))) + (ignore-errors (require 'pgg)) + (and (fboundp 'pgg-sign-region) + 'pgg)))) (progn (ignore-errors (load "mc-toplev")) (and (fboundp 'mc-encrypt-generic) @@ -108,6 +111,9 @@ Valid packages include `epg', `pgg' and `mailcrypt'.") "If t, cache passphrase." :group 'mime-security :type 'boolean) +(make-obsolete-variable 'mml2015-cache-passphrase + 'mml-secure-cache-passphrase + "25.1") (defcustom mml2015-passphrase-cache-expiry mml-secure-passphrase-cache-expiry "How many seconds the passphrase is cached. @@ -115,12 +121,22 @@ Whether the passphrase is cached at all is controlled by `mml2015-cache-passphrase'." :group 'mime-security :type 'integer) +(make-obsolete-variable 'mml2015-passphrase-cache-expiry + 'mml-secure-passphrase-cache-expiry + "25.1") (defcustom mml2015-signers nil - "A list of your own key ID which will be used to sign a message." + "A list of your own key ID(s) which will be used to sign a message. +If set, it overrides the setting of `mml2015-sign-with-sender'." :group 'mime-security :type '(repeat (string :tag "Key ID"))) +(defcustom mml2015-sign-with-sender nil + "If t, use message sender so find a key to sign with." + :group 'mime-security + :type 'boolean + :version "24.1") + (defcustom mml2015-encrypt-to-self nil "If t, add your own key ID to recipient list when encryption." :group 'mime-security @@ -131,6 +147,18 @@ Whether the passphrase is cached at all is controlled by :group 'mime-security :type 'boolean) +(defcustom mml2015-maximum-key-image-dimension 64 + "The maximum dimension (width or height) of any key images." + :version "24.4" + :group 'mime-security + :type 'integer) + +(defcustom mml2015-display-key-image t + "If t, try to display key images." + :version "24.5" + :group 'mime-security + :type 'boolean) + ;; Extract plaintext from cleartext signature. IMO, this kind of task ;; should be done by GnuPG rather than Elisp, but older PGP backends ;; (such as Mailcrypt, and PGG) discard the output from GnuPG. @@ -723,6 +751,7 @@ Whether the passphrase is cached at all is controlled by (defvar epg-user-id-alist) (defvar epg-digest-algorithm-alist) +(defvar epg-gpg-program) (defvar inhibit-redisplay) (autoload 'epg-make-context "epg") @@ -731,7 +760,6 @@ Whether the passphrase is cached at all is controlled by (autoload 'epg-context-set-signers "epg") (autoload 'epg-context-result-for "epg") (autoload 'epg-new-signature-digest-algorithm "epg") -(autoload 'epg-verify-result-to-string "epg") (autoload 'epg-list-keys "epg") (autoload 'epg-decrypt-string "epg") (autoload 'epg-verify-string "epg") @@ -742,47 +770,58 @@ Whether the passphrase is cached at all is controlled by (autoload 'epg-key-sub-key-list "epg") (autoload 'epg-sub-key-capability "epg") (autoload 'epg-sub-key-validity "epg") +(autoload 'epg-sub-key-fingerprint "epg") +(autoload 'epg-signature-key-id "epg") +(autoload 'epg-signature-to-string "epg") +(autoload 'epg-key-user-id-list "epg") +(autoload 'epg-user-id-string "epg") +(autoload 'epg-user-id-validity "epg") (autoload 'epg-configuration "epg-config") (autoload 'epg-expand-group "epg-config") (autoload 'epa-select-keys "epa") -(defvar mml2015-epg-secret-key-id-list nil) - -(defun mml2015-epg-passphrase-callback (context key-id ignore) - (if (eq key-id 'SYM) - (epg-passphrase-callback-function context key-id nil) - (let* ((password-cache-key-id - (if (eq key-id 'PIN) - "PIN" - key-id)) - entry - (passphrase - (password-read - (if (eq key-id 'PIN) - "Passphrase for PIN: " - (if (setq entry (assoc key-id epg-user-id-alist)) - (format "Passphrase for %s %s: " key-id (cdr entry)) - (format "Passphrase for %s: " key-id))) - password-cache-key-id))) - (when passphrase - (let ((password-cache-expiry mml2015-passphrase-cache-expiry)) - (password-cache-add password-cache-key-id passphrase)) - (setq mml2015-epg-secret-key-id-list - (cons password-cache-key-id mml2015-epg-secret-key-id-list)) - (copy-sequence passphrase))))) - -(defun mml2015-epg-find-usable-key (keys usage) - (catch 'found - (while keys - (let ((pointer (epg-key-sub-key-list (car keys)))) - (while pointer - (if (and (memq usage (epg-sub-key-capability (car pointer))) - (not (memq 'disabled (epg-sub-key-capability (car pointer)))) - (not (memq (epg-sub-key-validity (car pointer)) - '(revoked expired)))) - (throw 'found (car keys))) - (setq pointer (cdr pointer)))) - (setq keys (cdr keys))))) +(autoload 'gnus-create-image "gnus-ems") + +(defun mml2015-epg-key-image (key-id) + "Return the image of a key, if any" + (with-temp-buffer + (mm-set-buffer-multibyte nil) + (let* ((coding-system-for-write 'binary) + (coding-system-for-read 'binary) + (data (shell-command-to-string + (format "%s --list-options no-show-photos --attribute-fd 3 --list-keys %s 3>&1 >/dev/null 2>&1" + (shell-quote-argument epg-gpg-program) key-id)))) + (when (> (length data) 0) + (insert (substring data 16)) + (condition-case nil + (gnus-create-image (buffer-string) nil t) + (error)))))) + +(autoload 'gnus-rescale-image "gnus-util") + +(defun mml2015-epg-key-image-to-string (key-id) + "Return a string with the image of a key, if any" + (let ((key-image (mml2015-epg-key-image key-id))) + (if (not key-image) + "" + (condition-case error + (let ((result " ")) + (put-text-property + 1 2 'display + (gnus-rescale-image key-image + (cons mml2015-maximum-key-image-dimension + mml2015-maximum-key-image-dimension)) + result) + result) + (error ""))))) + +(defun mml2015-epg-signature-to-string (signature) + (concat (epg-signature-to-string signature) + (when mml2015-display-key-image + (mml2015-epg-key-image-to-string (epg-signature-key-id signature))))) + +(defun mml2015-epg-verify-result-to-string (verify-result) + (mapconcat #'mml2015-epg-signature-to-string verify-result "\n")) (defun mml2015-epg-decrypt (handle ctl) (catch 'error @@ -795,18 +834,15 @@ Whether the passphrase is cached at all is controlled by mm-security-handle 'gnus-info "Corrupted") (throw 'error handle)) (setq context (epg-make-context)) - (if mml2015-cache-passphrase + (if (or mml2015-cache-passphrase mml-secure-cache-passphrase) (epg-context-set-passphrase-callback context - #'mml2015-epg-passphrase-callback)) + (cons 'mml-secure-passphrase-callback 'OpenPGP))) (condition-case error (setq plain (epg-decrypt-string context (mm-get-part child)) - mml2015-epg-secret-key-id-list nil) + mml-secure-secret-key-id-list nil) (error - (while mml2015-epg-secret-key-id-list - (password-cache-remove (car mml2015-epg-secret-key-id-list)) - (setq mml2015-epg-secret-key-id-list - (cdr mml2015-epg-secret-key-id-list))) + (mml-secure-clear-secret-key-id-list) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Failed") (if (eq (car error) 'quit) @@ -826,7 +862,7 @@ Whether the passphrase is cached at all is controlled by (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info (concat "OK\n" - (epg-verify-result-to-string + (mml2015-epg-verify-result-to-string (epg-context-result-for context 'verify)))) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "OK")) @@ -842,18 +878,15 @@ Whether the passphrase is cached at all is controlled by (let ((inhibit-redisplay t) (context (epg-make-context)) plain) - (if mml2015-cache-passphrase + (if (or mml2015-cache-passphrase mml-secure-cache-passphrase) (epg-context-set-passphrase-callback context - #'mml2015-epg-passphrase-callback)) + (cons 'mml-secure-passphrase-callback 'OpenPGP))) (condition-case error (setq plain (epg-decrypt-string context (buffer-string)) - mml2015-epg-secret-key-id-list nil) + mml-secure-secret-key-id-list nil) (error - (while mml2015-epg-secret-key-id-list - (password-cache-remove (car mml2015-epg-secret-key-id-list)) - (setq mml2015-epg-secret-key-id-list - (cdr mml2015-epg-secret-key-id-list))) + (mml-secure-clear-secret-key-id-list) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Failed") (if (eq (car error) 'quit) @@ -874,7 +907,7 @@ Whether the passphrase is cached at all is controlled by (if (epg-context-result-for context 'verify) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-details - (epg-verify-result-to-string + (mml2015-epg-verify-result-to-string (epg-context-result-for context 'verify))))))) (defun mml2015-epg-verify (handle ctl) @@ -892,7 +925,7 @@ Whether the passphrase is cached at all is controlled by (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Corrupted") (throw 'error handle)) - (setq part (mm-replace-in-string part "\n" "\r\n" t) + (setq part (mm-replace-in-string part "\n" "\r\n") signature (mm-get-part signature) context (epg-make-context)) (condition-case error @@ -908,7 +941,8 @@ Whether the passphrase is cached at all is controlled by (throw 'error handle))) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info - (epg-verify-result-to-string (epg-context-result-for context 'verify))) + (mml2015-epg-verify-result-to-string + (epg-context-result-for context 'verify))) handle))) (defun mml2015-epg-clear-verify () @@ -931,171 +965,44 @@ Whether the passphrase is cached at all is controlled by (progn (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info - (epg-verify-result-to-string + (mml2015-epg-verify-result-to-string (epg-context-result-for context 'verify))) (delete-region (point-min) (point-max)) (insert (mm-decode-coding-string plain coding-system-for-read))) (mml2015-extract-cleartext-signature)))) (defun mml2015-epg-sign (cont) - (let* ((inhibit-redisplay t) - (context (epg-make-context)) - (boundary (mml-compute-boundary cont)) - (sender (message-options-get 'message-sender)) - signer-key - (signers - (or (message-options-get 'mml2015-epg-signers) - (message-options-set - 'mml2015-epg-signers - (if (eq mm-sign-option 'guided) - (epa-select-keys context "\ -Select keys for signing. -If no one is selected, default secret key is used. " - (cons sender mml2015-signers) t) - (if (or sender mml2015-signers) - (delq nil - (mapcar - (lambda (signer) - (setq signer-key (mml2015-epg-find-usable-key - (epg-list-keys context signer t) - 'sign)) - (unless (or signer-key - (y-or-n-p - (format - "No secret key for %s; skip it? " - signer))) - (error "No secret key for %s" signer)) - signer-key) - (cons sender mml2015-signers)))))))) - signature micalg) - (epg-context-set-armor context t) - (epg-context-set-textmode context t) - (epg-context-set-signers context signers) - (if mml2015-cache-passphrase - (epg-context-set-passphrase-callback - context - #'mml2015-epg-passphrase-callback)) - (condition-case error - (setq signature (epg-sign-string context (buffer-string) t) - mml2015-epg-secret-key-id-list nil) - (error - (while mml2015-epg-secret-key-id-list - (password-cache-remove (car mml2015-epg-secret-key-id-list)) - (setq mml2015-epg-secret-key-id-list - (cdr mml2015-epg-secret-key-id-list))) - (signal (car error) (cdr error)))) - (if (epg-context-result-for context 'sign) - (setq micalg (epg-new-signature-digest-algorithm - (car (epg-context-result-for context 'sign))))) - (goto-char (point-min)) - (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n" - boundary)) - (if micalg - (insert (format "\tmicalg=pgp-%s; " - (downcase - (cdr (assq micalg - epg-digest-algorithm-alist)))))) - (insert "protocol=\"application/pgp-signature\"\n") - (insert (format "\n--%s\n" boundary)) - (goto-char (point-max)) - (insert (format "\n--%s\n" boundary)) - (insert "Content-Type: application/pgp-signature\n\n") - (insert signature) + (let ((inhibit-redisplay t) + (boundary (mml-compute-boundary cont))) + ;; Signed data must end with a newline (RFC 3156, 5). (goto-char (point-max)) - (insert (format "--%s--\n" boundary)) - (goto-char (point-max)))) + (unless (bolp) + (insert "\n")) + (let* ((pair (mml-secure-epg-sign 'OpenPGP t)) + (signature (car pair)) + (micalg (cdr pair))) + (goto-char (point-min)) + (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n" + boundary)) + (if micalg + (insert (format "\tmicalg=pgp-%s; " + (downcase + (cdr (assq micalg + epg-digest-algorithm-alist)))))) + (insert "protocol=\"application/pgp-signature\"\n") + (insert (format "\n--%s\n" boundary)) + (goto-char (point-max)) + (insert (format "\n--%s\n" boundary)) + (insert "Content-Type: application/pgp-signature; name=\"signature.asc\"\n\n") + (insert signature) + (goto-char (point-max)) + (insert (format "--%s--\n" boundary)) + (goto-char (point-max))))) (defun mml2015-epg-encrypt (cont &optional sign) - (let ((inhibit-redisplay t) - (context (epg-make-context)) - (config (epg-configuration)) - (sender (message-options-get 'message-sender)) - (recipients (message-options-get 'mml2015-epg-recipients)) - cipher signers - (boundary (mml-compute-boundary cont)) - recipient-key signer-key) - (unless recipients - (setq recipients - (apply #'nconc - (mapcar - (lambda (recipient) - (or (epg-expand-group config recipient) - (list (concat "<" recipient ">")))) - (split-string - (or (message-options-get 'message-recipients) - (message-options-set 'message-recipients - (read-string "Recipients: "))) - "[ \f\t\n\r\v,]+")))) - (when mml2015-encrypt-to-self - (unless (or sender mml2015-signers) - (error "Message sender and mml2015-signers not set")) - (setq recipients (nconc recipients (cons sender mml2015-signers)))) - (if (eq mm-encrypt-option 'guided) - (setq recipients - (epa-select-keys context "\ -Select recipients for encryption. -If no one is selected, symmetric encryption will be performed. " - recipients)) - (setq recipients - (delq nil - (mapcar - (lambda (recipient) - (setq recipient-key (mml2015-epg-find-usable-key - (epg-list-keys context recipient) - 'encrypt)) - (unless (or recipient-key - (y-or-n-p - (format "No public key for %s; skip it? " - recipient))) - (error "No public key for %s" recipient)) - recipient-key) - recipients))) - (unless recipients - (error "No recipient specified"))) - (message-options-set 'mml2015-epg-recipients recipients)) - (when sign - (setq signers - (or (message-options-get 'mml2015-epg-signers) - (message-options-set - 'mml2015-epg-signers - (if (eq mm-sign-option 'guided) - (epa-select-keys context "\ -Select keys for signing. -If no one is selected, default secret key is used. " - (cons mml2015-signers) t) - (if (or sender mml2015-signers) - (delq nil - (mapcar - (lambda (signer) - (setq signer-key (mml2015-epg-find-usable-key - (epg-list-keys context signer t) - 'sign)) - (unless (or signer-key - (y-or-n-p - (format - "No secret key for %s; skip it? " - signer))) - (error "No secret key for %s" signer)) - signer-key) - (cons mml2015-signers)))))))) - (epg-context-set-signers context signers)) - (epg-context-set-armor context t) - (epg-context-set-textmode context t) - (if mml2015-cache-passphrase - (epg-context-set-passphrase-callback - context - #'mml2015-epg-passphrase-callback)) - (condition-case error - (setq cipher - (epg-encrypt-string context (buffer-string) recipients sign - mml2015-always-trust) - mml2015-epg-secret-key-id-list nil) - (error - (while mml2015-epg-secret-key-id-list - (password-cache-remove (car mml2015-epg-secret-key-id-list)) - (setq mml2015-epg-secret-key-id-list - (cdr mml2015-epg-secret-key-id-list))) - (signal (car error) (cdr error)))) + (let* ((inhibit-redisplay t) + (boundary (mml-compute-boundary cont)) + (cipher (mml-secure-epg-encrypt 'OpenPGP cont sign))) (delete-region (point-min) (point-max)) (goto-char (point-min)) (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"