* mml2015.el (mml2015-epg-passphrase-callback): Display key ID on the passphrase...
[gnus] / lisp / mml2015.el
index 234d2df..5429a27 100644 (file)
 (require 'mm-decode)
 (require 'mm-util)
 (require 'mml)
-(require 'password)
+(require 'mml-sec)
 
 (defvar mc-pgp-always-sign)
 
 (defvar mml2015-use (or
+                    (condition-case nil
+                        (progn
+                          (require 'epg-config)
+                          (epg-check-configuration (epg-configuration))
+                          'epg)
+                      (error))
                     (progn
                       (ignore-errors
                        ;; Avoid the "Recursive load suspected" error
@@ -59,7 +65,7 @@
                                 (fboundp 'mc-cleanup-recipient-headers)
                                 'mailcrypt)))
   "The package used for PGP/MIME.
-Valid packages include `pgg', `gpg' and `mailcrypt'.")
+Valid packages include `epg', `pgg', `gpg' and `mailcrypt'.")
 
 ;; Something is not RFC2015.
 (defvar mml2015-function-alist
@@ -103,23 +109,38 @@ Valid packages include `pgg', `gpg' and `mailcrypt'.")
   :type '(repeat (cons (regexp :tag "GnuPG output regexp")
                       (boolean :tag "Trust key"))))
 
-(defcustom mml2015-verbose nil
+(defcustom mml2015-verbose mml-secure-verbose
   "If non-nil, ask the user about the current operation more verbosely."
   :group 'mime-security
   :type 'boolean)
 
-(defcustom mml2015-cache-passphrase t
+(defcustom mml2015-cache-passphrase mml-secure-cache-passphrase
   "If t, cache passphrase."
   :group 'mime-security
   :type 'boolean)
 
-(defcustom mml2015-passphrase-cache-expiry 16
+(defcustom mml2015-passphrase-cache-expiry mml-secure-passphrase-cache-expiry
   "How many seconds the passphrase is cached.
 Whether the passphrase is cached at all is controlled by
 `mml2015-cache-passphrase'."
   :group 'mime-security
   :type 'integer)
 
+(defcustom mml2015-signers nil
+  "A list of your own key ID which will be used to sign a message."
+  :group 'mime-security
+  :type '(repeat (string :tag "Key ID")))
+
+(defcustom mml2015-encrypt-to-self t
+  "If t, add your own key ID to recipient list when encryption."
+  :group 'mime-security
+  :type 'boolean)
+
+(defcustom mml2015-always-trust t
+  "If t, GnuPG skip key validation on encryption."
+  :group 'mime-security
+  :type 'boolean)
+
 ;;; mailcrypt wrapper
 
 (eval-and-compile
@@ -902,8 +923,7 @@ Whether the passphrase is cached at all is controlled by
 ;;; epg wrapper
 
 (eval-and-compile
-  (autoload 'epg-make-context "epg")
-  (autoload 'epa-select-keys "epa"))
+  (autoload 'epg-make-context "epg"))
 
 (eval-when-compile
   (defvar epg-user-id-alist)
@@ -921,23 +941,29 @@ Whether the passphrase is cached at all is controlled by
   (autoload 'epg-sign-string "epg")
   (autoload 'epg-encrypt-string "epg")
   (autoload 'epg-passphrase-callback-function "epg")
-  (autoload 'epg-context-set-passphrase-callback "epg"))
+  (autoload 'epg-context-set-passphrase-callback "epg")
+  (autoload 'epg-configuration "epg-config")
+  (autoload 'epg-expand-group "epg-config"))
+
+(eval-when-compile
+  (defvar password-cache-expiry)
+  (autoload 'password-read "password")
+  (autoload 'password-cache-add "password")
+  (autoload 'password-cache-remove "password"))
 
 (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* ((entry (assoc key-id epg-user-id-alist))
+    (let* (entry
           (passphrase
            (password-read
-            (format "GnuPG passphrase for %s: "
-                    (if entry
-                        (cdr entry)
-                      key-id))
             (if (eq key-id 'PIN)
-                "PIN"
-              key-id))))
+                "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))))))
       (when passphrase
        (let ((password-cache-expiry mml2015-passphrase-cache-expiry))
          (password-cache-add key-id passphrase))
@@ -956,9 +982,10 @@ Whether the passphrase is cached at all is controlled by
         mm-security-handle 'gnus-info "Corrupted")
        (throw 'error handle))
       (setq context (epg-make-context))
-      (epg-context-set-passphrase-callback
-       context
-       #'mml2015-epg-passphrase-callback)
+      (if mml2015-cache-passphrase
+         (epg-context-set-passphrase-callback
+          context
+          #'mml2015-epg-passphrase-callback))
       (condition-case error
          (setq plain (epg-decrypt-string context (mm-get-part child))
                mml2015-epg-secret-key-id-list nil)
@@ -1002,9 +1029,10 @@ Whether the passphrase is cached at all is controlled by
   (let ((inhibit-redisplay t)
        (context (epg-make-context))
        plain)
-    (epg-context-set-passphrase-callback
-     context
-     #'mml2015-epg-passphrase-callback)
+    (if mml2015-cache-passphrase
+       (epg-context-set-passphrase-callback
+        context
+        #'mml2015-epg-passphrase-callback))
     (condition-case error
        (setq plain (epg-decrypt-string context (buffer-string))
              mml2015-epg-secret-key-id-list nil)
@@ -1028,14 +1056,13 @@ Whether the passphrase is cached at all is controlled by
       (goto-char (point-min))
       (while (search-forward "\r\n" nil t)
        (replace-match "\n" t t))
+      (mm-set-handle-multipart-parameter
+       mm-security-handle 'gnus-info "OK")
       (if (epg-context-result-for context 'verify)
          (mm-set-handle-multipart-parameter
-          mm-security-handle 'gnus-info
-          (concat "OK\n"
-                  (epg-verify-result-to-string
-                   (epg-context-result-for context 'verify))))
-       (mm-set-handle-multipart-parameter
-        mm-security-handle 'gnus-info "OK")))))
+          mm-security-handle 'gnus-details
+          (epg-verify-result-to-string
+           (epg-context-result-for context 'verify)))))))
 
 (defun mml2015-epg-verify (handle ctl)
   (catch 'error
@@ -1099,16 +1126,18 @@ Whether the passphrase is cached at all is controlled by
     (if mml2015-verbose
        (setq signers (epa-select-keys context "Select keys for signing.
 If no one is selected, default secret key is used.  "
-                                      nil t))
-      (setq signers (list (car (epg-list-keys
-                               context
-                               (message-options-get 'mml-sender) t)))))
+                                      mml2015-signers t))
+      (if mml2015-signers
+         (setq signers (mapcar (lambda (name)
+                                 (car (epg-list-keys context name t)))
+                               mml2015-signers))))
     (epg-context-set-armor context t)
     (epg-context-set-textmode context t)
     (epg-context-set-signers context signers)
-    (epg-context-set-passphrase-callback
-     context
-     #'mml2015-epg-passphrase-callback)
+    (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)
@@ -1142,30 +1171,57 @@ If no one is selected, default secret key is used.  "
 (defun mml2015-epg-encrypt (cont &optional sign)
   (let ((inhibit-redisplay t)
        (context (epg-make-context))
-       recipients cipher
+       (config (epg-configuration))
+       (recipients (split-string
+                    (or (message-options-get 'message-recipients)
+                        (message-options-set 'message-recipients
+                                             (read-string "Recipients: ")))
+                    "[ \f\t\n\r\v,]+"))
+       cipher signers
        (boundary (mml-compute-boundary cont)))
-    (if (or mml2015-verbose
-           (null (message-options-get 'message-recipients)))
+    (setq recipients (apply #'nconc
+                           (mapcar
+                            (lambda (recipient)
+                              (or (epg-expand-group config recipient)
+                                  (list recipient)))
+                            recipients)))
+    (if mml2015-verbose
        (setq recipients
              (epa-select-keys context "Select recipients for encryption.
 If no one is selected, symmetric encryption will be performed.  "
-                              (if (message-options-get 'message-recipients)
-                                  (split-string
-                                   (message-options-get 'message-recipients)
-                                   "[ \f\t\n\r\v,]+"))))
+                              recipients))
       (setq recipients
-           (epg-list-keys context
-                          (split-string
-                           (message-options-get 'message-recipients)
-                           "[ \f\t\n\r\v,]+"))))
+           (delq nil (mapcar (lambda (name)
+                               (car (epg-list-keys context name)))
+                             recipients))))
+    (if mml2015-encrypt-to-self
+       (if mml2015-signers
+           (setq recipients
+                 (nconc recipients
+                        (mapcar (lambda (name)
+                                  (car (epg-list-keys context name)))
+                                mml2015-signers)))
+         (error "mml2015-signers not set")))
+    (when sign
+      (if mml2015-verbose
+         (setq signers (epa-select-keys context "Select keys for signing.
+If no one is selected, default secret key is used.  "
+                                        mml2015-signers t))
+       (if mml2015-signers
+           (setq signers (mapcar (lambda (name)
+                                   (car (epg-list-keys context name t)))
+                                 mml2015-signers))))
+      (epg-context-set-signers context signers))
     (epg-context-set-armor context t)
     (epg-context-set-textmode context t)
-    (epg-context-set-passphrase-callback
-     context
-     #'mml2015-epg-passphrase-callback)
+    (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)
+             (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