]> code.delx.au - gnu-emacs/blobdiff - lisp/gnus/mml2015.el
-
[gnu-emacs] / lisp / gnus / mml2015.el
index 5104cea5abbb1f37aa7b43dd2c902f824bc32ccc..774821320f16556052259cf6ee669800cfba8e91 100644 (file)
@@ -1,6 +1,6 @@
 ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP)
 
-;; Copyright (C) 2000-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2000-2016 Free Software Foundation, Inc.
 
 ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
 ;; Keywords: PGP MIME MML
 
 ;;; Code:
 
-(eval-and-compile
-  (if (locate-library "password-cache")
-      (require 'password-cache)
-    (require 'password)))
-
 (eval-when-compile (require 'cl))
 (require 'mm-decode)
 (require 'mm-util)
 (require 'mml)
 (require 'mml-sec)
+(require 'epg-config)
 
 (defvar mc-pgp-always-sign)
 
 ;; 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
-                    (progn
-                      (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)
-                                (fboundp 'mc-sign-generic)
-                                (fboundp 'mc-cleanup-recipient-headers)
-                                'mailcrypt)))
+(defvar mml2015-use 'epg
   "The package used for PGP/MIME.
 Valid packages include `epg', `pgg' and `mailcrypt'.")
 
@@ -111,6 +87,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.
@@ -118,6 +97,9 @@ 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(s) which will be used to sign a message.
@@ -481,14 +463,17 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
                        (or (y-or-n-p "Sign the message? ")
                            'not))))
             'never)))
-    (mm-with-unibyte-current-buffer
-      (mc-encrypt-generic
-       (or (message-options-get 'message-recipients)
-          (message-options-set 'message-recipients
-                             (mc-cleanup-recipient-headers
-                              (read-string "Recipients: "))))
-       nil nil nil
-       (message-options-get 'message-sender))))
+    (insert
+     (with-temp-buffer
+       (set-buffer-multibyte nil)
+       (mc-encrypt-generic
+       (or (message-options-get 'message-recipients)
+           (message-options-set 'message-recipients
+                                (mc-cleanup-recipient-headers
+                                 (read-string "Recipients: "))))
+       nil nil nil
+       (message-options-get 'message-sender))
+       (buffer-string))))
   (goto-char (point-min))
   (unless (looking-at "-----BEGIN PGP MESSAGE-----")
     (error "Fail to encrypt the message"))
@@ -613,7 +598,7 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
            (insert "\r"))
          (forward-line)
          (end-of-line))
-       (with-temp-file (setq signature-file (mm-make-temp-file "pgg"))
+       (with-temp-file (setq signature-file (make-temp-file "pgg"))
          (mm-insert-part signature))
        (if (condition-case err
                (prog1
@@ -654,7 +639,7 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
     (if (condition-case err
            (prog1
                (mm-with-unibyte-buffer
-                 (insert (mm-encode-coding-string text coding-system))
+                 (insert (encode-coding-string text coding-system))
                  (pgg-verify-region (point-min) (point-max) nil t))
              (goto-char (point-min))
              (while (search-forward "\r\n" nil t)
@@ -774,105 +759,10 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
 (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-check-user-id (key recipient)
-  (let ((pointer (epg-key-user-id-list key))
-       result)
-    (while pointer
-      (if (and (equal (car (mail-header-parse-address
-                           (epg-user-id-string (car pointer))))
-                     (car (mail-header-parse-address
-                           recipient)))
-              (not (memq (epg-user-id-validity (car pointer))
-                         '(revoked expired))))
-         (setq result t
-               pointer nil)
-       (setq pointer (cdr pointer))))
-    result))
-
-(defun mml2015-epg-check-sub-key (key usage)
-  (let ((pointer (epg-key-sub-key-list key))
-       result)
-    ;; The primary key will be marked as disabled, when the entire
-    ;; key is disabled (see 12 Field, Format of colon listings, in
-    ;; gnupg/doc/DETAILS)
-    (unless (memq 'disabled (epg-sub-key-capability (car pointer)))
-      (while pointer
-       (if (and (memq usage (epg-sub-key-capability (car pointer)))
-                (not (memq (epg-sub-key-validity (car pointer))
-                           '(revoked expired))))
-           (setq result t
-                 pointer nil)
-         (setq pointer (cdr pointer)))))
-    result))
-
-(defun mml2015-epg-find-usable-key (context name usage
-                                           &optional name-is-key-id)
-  (let ((keys (epg-list-keys context name))
-       key)
-    (while keys
-      (if (and (or name-is-key-id
-                  ;; Non email user-id can be supplied through
-                  ;; mml2015-signers if mml2015-encrypt-to-self is set.
-                  ;; Treat it as valid, as it is user's intention.
-                  (not (string-match "\\`<" name))
-                  (mml2015-epg-check-user-id (car keys) name))
-              (mml2015-epg-check-sub-key (car keys) usage))
-         (setq key (car keys)
-               keys nil)
-       (setq keys (cdr keys))))
-    key))
-
-;; XXX: since gpg --list-secret-keys does not return validity of each
-;; key, `mml2015-epg-find-usable-key' defined above is not enough for
-;; secret keys.  The function `mml2015-epg-find-usable-secret-key'
-;; below looks at appropriate public keys to check usability.
-(defun mml2015-epg-find-usable-secret-key (context name usage)
-  (let ((secret-keys (epg-list-keys context name t))
-       secret-key)
-    (while (and (not secret-key) secret-keys)
-      (if (mml2015-epg-find-usable-key
-          context
-          (epg-sub-key-fingerprint
-           (car (epg-key-sub-key-list
-                 (car secret-keys))))
-          usage
-          t)
-         (setq secret-key (car secret-keys)
-               secret-keys nil)
-       (setq secret-keys (cdr secret-keys))))
-    secret-key))
-
-(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)
+    (set-buffer-multibyte nil)
     (let* ((coding-system-for-write 'binary)
            (coding-system-for-read 'binary)
            (data (shell-command-to-string
@@ -921,18 +811,15 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
         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)
@@ -968,18 +855,15 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
   (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)
@@ -1018,7 +902,7 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
        (mm-set-handle-multipart-parameter
         mm-security-handle 'gnus-info "Corrupted")
        (throw 'error handle))
-      (setq part (mm-replace-in-string part "\n" "\r\n")
+      (setq part (replace-regexp-in-string "\n" "\r\n" part)
            signature (mm-get-part signature)
            context (epg-make-context))
       (condition-case error
@@ -1041,8 +925,8 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
 (defun mml2015-epg-clear-verify ()
   (let ((inhibit-redisplay t)
        (context (epg-make-context))
-       (signature (mm-encode-coding-string (buffer-string)
-                                           coding-system-for-write))
+       (signature (encode-coding-string (buffer-string)
+                                        coding-system-for-write))
        plain)
     (condition-case error
        (setq plain (epg-verify-string context signature))
@@ -1061,180 +945,41 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
           (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)))
+         (insert (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-names (or mml2015-signers
-                          (if (and mml2015-sign-with-sender sender)
-                              (list (concat "<" 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.  "
-                                   signer-names
-                                   t)
-                (if (or sender mml2015-signers)
-                    (delq nil
-                          (mapcar
-                           (lambda (signer)
-                             (setq signer-key
-                                   (mml2015-epg-find-usable-secret-key
-                                    context signer '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)
-                           signer-names)))))))
-        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))
+  (let ((inhibit-redisplay t)
+       (boundary (mml-compute-boundary cont)))
     ;; Signed data must end with a newline (RFC 3156, 5).
     (goto-char (point-max))
     (unless (bolp)
       (insert "\n"))
-    (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; name=\"signature.asc\"\n\n")
-    (insert signature)
-    (goto-char (point-max))
-    (insert (format "--%s--\n" boundary))
-    (goto-char (point-max))))
+    (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))
         (boundary (mml-compute-boundary cont))
-        (config (epg-configuration))
-        (recipients (message-options-get 'mml2015-epg-recipients))
-        cipher
-        (sender (message-options-get 'message-sender))
-        (signer-names (or mml2015-signers
-                          (if (and mml2015-sign-with-sender sender)
-                              (list (concat "<" sender ">")))))
-        signers
-        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 signer-names
-         (error "Neither message sender nor mml2015-signers are set"))
-       (setq recipients (nconc recipients signer-names)))
-      (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
-                                           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.  "
-                                     signer-names
-                                     t)
-                  (if (or sender mml2015-signers)
-                      (delq nil
-                            (mapcar
-                             (lambda (signer)
-                               (setq signer-key
-                                     (mml2015-epg-find-usable-secret-key
-                                      context signer '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)
-                             signer-names)))))))
-      (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))))
+        (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"