(gnus-summary-goto-unread): Change default to nil.
[gnus] / lisp / mml2015.el
1 ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP)
2
3 ;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 ;;   2008, 2009, 2010 Free Software Foundation, Inc.
5
6 ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
7 ;; Keywords: PGP MIME MML
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; RFC 2015 is updated by RFC 3156, this file should be compatible
27 ;; with both.
28
29 ;;; Code:
30
31 ;; For Emacs < 22.2.
32 (eval-and-compile
33   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))
34
35   (if (locate-library "password-cache")
36       (require 'password-cache)
37     (require 'password)))
38
39 (eval-when-compile (require 'cl))
40 (require 'mm-decode)
41 (require 'mm-util)
42 (require 'mml)
43 (require 'mml-sec)
44
45 (defvar mc-pgp-always-sign)
46
47 (declare-function epg-check-configuration "ext:epg-config"
48                   (config &optional minimum-version))
49 (declare-function epg-configuration "ext:epg-config" ())
50
51 (defvar mml2015-use (or
52                      (condition-case nil
53                          (progn
54                            (require 'epg-config)
55                            (epg-check-configuration (epg-configuration))
56                            'epg)
57                        (error))
58                      (progn
59                        (ignore-errors
60                         ;; Avoid the "Recursive load suspected" error
61                         ;; in Emacs 21.1.
62                         (let ((recursive-load-depth-limit 100))
63                           (require 'pgg)))
64                        (and (fboundp 'pgg-sign-region)
65                             'pgg))
66                      (progn
67                        (ignore-errors
68                          (require 'gpg))
69                        (and (fboundp 'gpg-sign-detached)
70                             'gpg))
71                      (progn (ignore-errors
72                               (load "mc-toplev"))
73                             (and (fboundp 'mc-encrypt-generic)
74                                  (fboundp 'mc-sign-generic)
75                                  (fboundp 'mc-cleanup-recipient-headers)
76                                  'mailcrypt)))
77   "The package used for PGP/MIME.
78 Valid packages include `epg', `pgg', `gpg' and `mailcrypt'.")
79
80 ;; Something is not RFC2015.
81 (defvar mml2015-function-alist
82   '((mailcrypt mml2015-mailcrypt-sign
83                mml2015-mailcrypt-encrypt
84                mml2015-mailcrypt-verify
85                mml2015-mailcrypt-decrypt
86                mml2015-mailcrypt-clear-verify
87                mml2015-mailcrypt-clear-decrypt)
88     (gpg mml2015-gpg-sign
89          mml2015-gpg-encrypt
90          mml2015-gpg-verify
91          mml2015-gpg-decrypt
92          mml2015-gpg-clear-verify
93          mml2015-gpg-clear-decrypt)
94   (pgg mml2015-pgg-sign
95        mml2015-pgg-encrypt
96        mml2015-pgg-verify
97        mml2015-pgg-decrypt
98        mml2015-pgg-clear-verify
99        mml2015-pgg-clear-decrypt)
100   (epg mml2015-epg-sign
101        mml2015-epg-encrypt
102        mml2015-epg-verify
103        mml2015-epg-decrypt
104        mml2015-epg-clear-verify
105        mml2015-epg-clear-decrypt))
106   "Alist of PGP/MIME functions.")
107
108 (defvar mml2015-result-buffer nil)
109
110 (defcustom mml2015-unabbrev-trust-alist
111   '(("TRUST_UNDEFINED" . nil)
112     ("TRUST_NEVER"     . nil)
113     ("TRUST_MARGINAL"  . t)
114     ("TRUST_FULLY"     . t)
115     ("TRUST_ULTIMATE"  . t))
116   "Map GnuPG trust output values to a boolean saying if you trust the key."
117   :version "22.1"
118   :group 'mime-security
119   :type '(repeat (cons (regexp :tag "GnuPG output regexp")
120                        (boolean :tag "Trust key"))))
121
122 (defcustom mml2015-verbose mml-secure-verbose
123   "If non-nil, ask the user about the current operation more verbosely."
124   :group 'mime-security
125   :type 'boolean)
126
127 (defcustom mml2015-cache-passphrase mml-secure-cache-passphrase
128   "If t, cache passphrase."
129   :group 'mime-security
130   :type 'boolean)
131
132 (defcustom mml2015-passphrase-cache-expiry mml-secure-passphrase-cache-expiry
133   "How many seconds the passphrase is cached.
134 Whether the passphrase is cached at all is controlled by
135 `mml2015-cache-passphrase'."
136   :group 'mime-security
137   :type 'integer)
138
139 (defcustom mml2015-signers nil
140   "A list of your own key ID which will be used to sign a message."
141   :group 'mime-security
142   :type '(repeat (string :tag "Key ID")))
143
144 (defcustom mml2015-encrypt-to-self nil
145   "If t, add your own key ID to recipient list when encryption."
146   :group 'mime-security
147   :type 'boolean)
148
149 (defcustom mml2015-always-trust t
150   "If t, GnuPG skip key validation on encryption."
151   :group 'mime-security
152   :type 'boolean)
153
154 ;; Extract plaintext from cleartext signature.  IMO, this kind of task
155 ;; should be done by GnuPG rather than Elisp, but older PGP backends
156 ;; (such as Mailcrypt, PGG, and gpg.el) discard the output from GnuPG.
157 (defun mml2015-extract-cleartext-signature ()
158   ;; Daiki Ueno in
159   ;; <54a15d860801080142l70b95d7dkac4bf51a86196011@mail.gmail.com>: ``I still
160   ;; believe that the right way is to use the plaintext output from GnuPG as
161   ;; it is, and mml2015-extract-cleartext-signature is just a kludge for
162   ;; misdesigned libraries like PGG, which have no ability to do that.  So, I
163   ;; think it should not have descriptive documentation.''
164   ;;
165   ;; This function doesn't handle NotDashEscaped correctly.  EasyPG handles it
166   ;; correctly.
167   ;; http://thread.gmane.org/gmane.emacs.gnus.general/66062/focus=66082
168   ;; http://thread.gmane.org/gmane.emacs.gnus.general/65087/focus=65109
169   (goto-char (point-min))
170   (forward-line)
171   ;; We need to be careful not to strip beyond the armor headers.
172   ;; Previously, an attacker could replace the text inside our
173   ;; markup with trailing garbage by injecting whitespace into the
174   ;; message.
175   (while (looking-at "Hash:")           ; The only header allowed in cleartext
176     (forward-line))                     ; signatures according to RFC2440.
177   (when (looking-at "[\t ]*$")
178     (forward-line))
179   (delete-region (point-min) (point))
180   (if (re-search-forward "^-----BEGIN PGP SIGNATURE-----" nil t)
181       (delete-region (match-beginning 0) (point-max)))
182   (goto-char (point-min))
183   (while (re-search-forward "^- " nil t)
184     (replace-match "" t t)
185     (forward-line 1)))
186
187 ;;; mailcrypt wrapper
188
189 (autoload 'mailcrypt-decrypt "mailcrypt")
190 (autoload 'mailcrypt-verify "mailcrypt")
191 (autoload 'mc-pgp-always-sign "mailcrypt")
192 (autoload 'mc-encrypt-generic "mc-toplev")
193 (autoload 'mc-cleanup-recipient-headers "mc-toplev")
194 (autoload 'mc-sign-generic "mc-toplev")
195
196 (defvar mc-default-scheme)
197 (defvar mc-schemes)
198
199 (defvar mml2015-decrypt-function 'mailcrypt-decrypt)
200 (defvar mml2015-verify-function 'mailcrypt-verify)
201
202 (defun mml2015-format-error (err)
203   (if (stringp (cadr err))
204       (cadr err)
205     (format "%S" (cdr err))))
206
207 (defun mml2015-mailcrypt-decrypt (handle ctl)
208   (catch 'error
209     (let (child handles result)
210       (unless (setq child (mm-find-part-by-type
211                            (cdr handle)
212                            "application/octet-stream" nil t))
213         (mm-set-handle-multipart-parameter
214          mm-security-handle 'gnus-info "Corrupted")
215         (throw 'error handle))
216       (with-temp-buffer
217         (mm-insert-part child)
218         (setq result
219               (condition-case err
220                   (funcall mml2015-decrypt-function)
221                 (error
222                  (mm-set-handle-multipart-parameter
223                   mm-security-handle 'gnus-details (mml2015-format-error err))
224                  nil)
225                 (quit
226                  (mm-set-handle-multipart-parameter
227                   mm-security-handle 'gnus-details "Quit.")
228                  nil)))
229         (unless (car result)
230           (mm-set-handle-multipart-parameter
231            mm-security-handle 'gnus-info "Failed")
232           (throw 'error handle))
233         (setq handles (mm-dissect-buffer t)))
234       (mm-destroy-parts handle)
235       (mm-set-handle-multipart-parameter
236        mm-security-handle 'gnus-info
237        (concat "OK"
238                (let ((sig (with-current-buffer mml2015-result-buffer
239                             (mml2015-gpg-extract-signature-details))))
240                  (concat ", Signer: " sig))))
241       (if (listp (car handles))
242           handles
243         (list handles)))))
244
245 (defun mml2015-mailcrypt-clear-decrypt ()
246   (let (result)
247     (setq result
248           (condition-case err
249               (funcall mml2015-decrypt-function)
250             (error
251              (mm-set-handle-multipart-parameter
252               mm-security-handle 'gnus-details (mml2015-format-error err))
253              nil)
254             (quit
255              (mm-set-handle-multipart-parameter
256               mm-security-handle 'gnus-details "Quit.")
257              nil)))
258     (if (car result)
259         (mm-set-handle-multipart-parameter
260          mm-security-handle 'gnus-info "OK")
261       (mm-set-handle-multipart-parameter
262        mm-security-handle 'gnus-info "Failed"))))
263
264 (defun mml2015-fix-micalg (alg)
265   (and alg
266        ;; Mutt/1.2.5i has seen sending micalg=php-sha1
267        (upcase (if (string-match "^p[gh]p-" alg)
268                    (substring alg (match-end 0))
269                  alg))))
270
271 (defun mml2015-mailcrypt-verify (handle ctl)
272   (catch 'error
273     (let (part)
274       (unless (setq part (mm-find-raw-part-by-type
275                           ctl (or (mm-handle-multipart-ctl-parameter
276                                    ctl 'protocol)
277                                   "application/pgp-signature")
278                           t))
279         (mm-set-handle-multipart-parameter
280          mm-security-handle 'gnus-info "Corrupted")
281         (throw 'error handle))
282       (with-temp-buffer
283         (insert "-----BEGIN PGP SIGNED MESSAGE-----\n")
284         (insert (format "Hash: %s\n\n"
285                         (or (mml2015-fix-micalg
286                              (mm-handle-multipart-ctl-parameter
287                               ctl 'micalg))
288                             "SHA1")))
289         (save-restriction
290           (narrow-to-region (point) (point))
291           (insert part "\n")
292           (goto-char (point-min))
293           (while (not (eobp))
294             (if (looking-at "^-")
295                 (insert "- "))
296             (forward-line)))
297         (unless (setq part (mm-find-part-by-type
298                             (cdr handle) "application/pgp-signature" nil t))
299           (mm-set-handle-multipart-parameter
300            mm-security-handle 'gnus-info "Corrupted")
301           (throw 'error handle))
302         (save-restriction
303           (narrow-to-region (point) (point))
304           (mm-insert-part part)
305           (goto-char (point-min))
306           (if (re-search-forward "^-----BEGIN PGP [^-]+-----\r?$" nil t)
307               (replace-match "-----BEGIN PGP SIGNATURE-----" t t))
308           (if (re-search-forward "^-----END PGP [^-]+-----\r?$" nil t)
309               (replace-match "-----END PGP SIGNATURE-----" t t)))
310         (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
311           (unless (condition-case err
312                       (prog1
313                           (funcall mml2015-verify-function)
314                         (if (get-buffer " *mailcrypt stderr temp")
315                             (mm-set-handle-multipart-parameter
316                              mm-security-handle 'gnus-details
317                              (with-current-buffer " *mailcrypt stderr temp"
318                                (buffer-string))))
319                         (if (get-buffer " *mailcrypt stdout temp")
320                             (kill-buffer " *mailcrypt stdout temp"))
321                         (if (get-buffer " *mailcrypt stderr temp")
322                             (kill-buffer " *mailcrypt stderr temp"))
323                         (if (get-buffer " *mailcrypt status temp")
324                             (kill-buffer " *mailcrypt status temp"))
325                         (if (get-buffer mc-gpg-debug-buffer)
326                             (kill-buffer mc-gpg-debug-buffer)))
327                     (error
328                      (mm-set-handle-multipart-parameter
329                       mm-security-handle 'gnus-details (mml2015-format-error err))
330                      nil)
331                     (quit
332                      (mm-set-handle-multipart-parameter
333                       mm-security-handle 'gnus-details "Quit.")
334                      nil))
335             (mm-set-handle-multipart-parameter
336              mm-security-handle 'gnus-info "Failed")
337             (throw 'error handle))))
338       (mm-set-handle-multipart-parameter
339        mm-security-handle 'gnus-info "OK")
340       handle)))
341
342 (defun mml2015-mailcrypt-clear-verify ()
343   (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
344     (if (condition-case err
345             (prog1
346                 (funcall mml2015-verify-function)
347               (if (get-buffer " *mailcrypt stderr temp")
348                   (mm-set-handle-multipart-parameter
349                    mm-security-handle 'gnus-details
350                    (with-current-buffer " *mailcrypt stderr temp"
351                      (buffer-string))))
352               (if (get-buffer " *mailcrypt stdout temp")
353                   (kill-buffer " *mailcrypt stdout temp"))
354               (if (get-buffer " *mailcrypt stderr temp")
355                   (kill-buffer " *mailcrypt stderr temp"))
356               (if (get-buffer " *mailcrypt status temp")
357                   (kill-buffer " *mailcrypt status temp"))
358               (if (get-buffer mc-gpg-debug-buffer)
359                   (kill-buffer mc-gpg-debug-buffer)))
360           (error
361            (mm-set-handle-multipart-parameter
362             mm-security-handle 'gnus-details (mml2015-format-error err))
363            nil)
364           (quit
365            (mm-set-handle-multipart-parameter
366             mm-security-handle 'gnus-details "Quit.")
367            nil))
368         (mm-set-handle-multipart-parameter
369          mm-security-handle 'gnus-info "OK")
370       (mm-set-handle-multipart-parameter
371        mm-security-handle 'gnus-info "Failed")))
372   (mml2015-extract-cleartext-signature))
373
374 (defun mml2015-mailcrypt-sign (cont)
375   (mc-sign-generic (message-options-get 'message-sender)
376                    nil nil nil nil)
377   (let ((boundary (mml-compute-boundary cont))
378         hash point)
379     (goto-char (point-min))
380     (unless (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\r?$" nil t)
381       (error "Cannot find signed begin line"))
382     (goto-char (match-beginning 0))
383     (forward-line 1)
384     (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
385       (error "Cannot not find PGP hash"))
386     (setq hash (match-string 1))
387     (unless (re-search-forward "^$" nil t)
388       (error "Cannot not find PGP message"))
389     (forward-line 1)
390     (delete-region (point-min) (point))
391     (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
392                     boundary))
393     (insert (format "\tmicalg=pgp-%s; protocol=\"application/pgp-signature\"\n"
394                     (downcase hash)))
395     (insert (format "\n--%s\n" boundary))
396     (setq point (point))
397     (goto-char (point-max))
398     (unless (re-search-backward "^-----END PGP SIGNATURE-----\r?$" nil t)
399       (error "Cannot find signature part"))
400     (replace-match "-----END PGP MESSAGE-----" t t)
401     (goto-char (match-beginning 0))
402     (unless (re-search-backward "^-----BEGIN PGP SIGNATURE-----\r?$"
403                                 nil t)
404       (error "Cannot find signature part"))
405     (replace-match "-----BEGIN PGP MESSAGE-----" t t)
406     (goto-char (match-beginning 0))
407     (save-restriction
408       (narrow-to-region point (point))
409       (goto-char point)
410       (while (re-search-forward "^- -" nil t)
411         (replace-match "-" t t))
412       (goto-char (point-max)))
413     (insert (format "--%s\n" boundary))
414     (insert "Content-Type: application/pgp-signature\n\n")
415     (goto-char (point-max))
416     (insert (format "--%s--\n" boundary))
417     (goto-char (point-max))))
418
419 ;; We require mm-decode, which requires mm-bodies, which autoloads
420 ;; message-options-get (!).
421 (declare-function message-options-set "message" (symbol value))
422
423 (defun mml2015-mailcrypt-encrypt (cont &optional sign)
424   (let ((mc-pgp-always-sign
425          (or mc-pgp-always-sign
426              sign
427              (eq t (or (message-options-get 'message-sign-encrypt)
428                        (message-options-set
429                         'message-sign-encrypt
430                         (or (y-or-n-p "Sign the message? ")
431                             'not))))
432              'never)))
433     (mm-with-unibyte-current-buffer
434       (mc-encrypt-generic
435        (or (message-options-get 'message-recipients)
436            (message-options-set 'message-recipients
437                               (mc-cleanup-recipient-headers
438                                (read-string "Recipients: "))))
439        nil nil nil
440        (message-options-get 'message-sender))))
441   (goto-char (point-min))
442   (unless (looking-at "-----BEGIN PGP MESSAGE-----")
443     (error "Fail to encrypt the message"))
444   (let ((boundary (mml-compute-boundary cont)))
445     (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
446                     boundary))
447     (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
448     (insert (format "--%s\n" boundary))
449     (insert "Content-Type: application/pgp-encrypted\n\n")
450     (insert "Version: 1\n\n")
451     (insert (format "--%s\n" boundary))
452     (insert "Content-Type: application/octet-stream\n\n")
453     (goto-char (point-max))
454     (insert (format "--%s--\n" boundary))
455     (goto-char (point-max))))
456
457 ;;; gpg wrapper
458
459 (autoload 'gpg-decrypt "gpg")
460 (autoload 'gpg-verify "gpg")
461 (autoload 'gpg-verify-cleartext "gpg")
462 (autoload 'gpg-sign-detached "gpg")
463 (autoload 'gpg-sign-encrypt "gpg")
464 (autoload 'gpg-encrypt "gpg")
465 (autoload 'gpg-passphrase-read "gpg")
466
467 (defun mml2015-gpg-passphrase ()
468   (or (message-options-get 'gpg-passphrase)
469       (message-options-set 'gpg-passphrase (gpg-passphrase-read))))
470
471 (defun mml2015-gpg-decrypt-1 ()
472   (let ((cipher (current-buffer)) plain result)
473     (if (with-temp-buffer
474           (prog1
475               (gpg-decrypt cipher (setq plain (current-buffer))
476                            mml2015-result-buffer nil)
477             (mm-set-handle-multipart-parameter
478              mm-security-handle 'gnus-details
479              (with-current-buffer mml2015-result-buffer
480                (buffer-string)))
481             (set-buffer cipher)
482             (erase-buffer)
483             (insert-buffer-substring plain)
484             (goto-char (point-min))
485             (while (search-forward "\r\n" nil t)
486               (replace-match "\n" t t))))
487         '(t)
488       ;; Some wrong with the return value, check plain text buffer.
489       (if (> (point-max) (point-min))
490           '(t)
491         nil))))
492
493 (defun mml2015-gpg-decrypt (handle ctl)
494   (let ((mml2015-decrypt-function 'mml2015-gpg-decrypt-1))
495     (mml2015-mailcrypt-decrypt handle ctl)))
496
497 (defun mml2015-gpg-clear-decrypt ()
498   (let (result)
499     (setq result (mml2015-gpg-decrypt-1))
500     (if (car result)
501         (mm-set-handle-multipart-parameter
502          mm-security-handle 'gnus-info "OK")
503       (mm-set-handle-multipart-parameter
504        mm-security-handle 'gnus-info "Failed"))))
505
506 (defun mml2015-gpg-pretty-print-fpr (fingerprint)
507   (let* ((result "")
508          (fpr-length (string-width fingerprint))
509          (n-slice 0)
510          slice)
511     (setq fingerprint (string-to-list fingerprint))
512     (while fingerprint
513       (setq fpr-length (- fpr-length 4))
514       (setq slice (butlast fingerprint fpr-length))
515       (setq fingerprint (nthcdr 4 fingerprint))
516       (setq n-slice (1+ n-slice))
517       (setq result
518             (concat
519              result
520              (case n-slice
521                (1  slice)
522                (otherwise (concat " " slice))))))
523     result))
524
525 (defun mml2015-gpg-extract-signature-details ()
526   (goto-char (point-min))
527   (let* ((expired (re-search-forward
528                    "^\\[GNUPG:\\] SIGEXPIRED$"
529                    nil t))
530          (signer (and (re-search-forward
531                        "^\\[GNUPG:\\] GOODSIG \\([0-9A-Za-z]*\\) \\(.*\\)$"
532                        nil t)
533                       (cons (match-string 1) (match-string 2))))
534          (fprint (and (re-search-forward
535                        "^\\[GNUPG:\\] VALIDSIG \\([0-9a-zA-Z]*\\) "
536                        nil t)
537                       (match-string 1)))
538          (trust  (and (re-search-forward
539                        "^\\[GNUPG:\\] \\(TRUST_.*\\)$"
540                        nil t)
541                       (match-string 1)))
542          (trust-good-enough-p
543           (cdr (assoc trust mml2015-unabbrev-trust-alist))))
544     (cond ((and signer fprint)
545            (concat (cdr signer)
546                    (unless trust-good-enough-p
547                      (concat "\nUntrusted, Fingerprint: "
548                              (mml2015-gpg-pretty-print-fpr fprint)))
549                    (when expired
550                      (format "\nWARNING: Signature from expired key (%s)"
551                              (car signer)))))
552           ((re-search-forward
553             "^\\(gpg: \\)?Good signature from \"\\(.*\\)\"$" nil t)
554            (match-string 2))
555           (t
556            "From unknown user"))))
557
558 (defun mml2015-gpg-verify (handle ctl)
559   (catch 'error
560     (let (part message signature info-is-set-p)
561       (unless (setq part (mm-find-raw-part-by-type
562                           ctl (or (mm-handle-multipart-ctl-parameter
563                                    ctl 'protocol)
564                                   "application/pgp-signature")
565                           t))
566         (mm-set-handle-multipart-parameter
567          mm-security-handle 'gnus-info "Corrupted")
568         (throw 'error handle))
569       (with-temp-buffer
570         (setq message (current-buffer))
571         (insert part)
572         ;; Convert <LF> to <CR><LF> in signed text.  If --textmode is
573         ;; specified when signing, the conversion is not necessary.
574         (goto-char (point-min))
575         (end-of-line)
576         (while (not (eobp))
577           (unless (eq (char-before) ?\r)
578             (insert "\r"))
579           (forward-line)
580           (end-of-line))
581         (with-temp-buffer
582           (setq signature (current-buffer))
583           (unless (setq part (mm-find-part-by-type
584                               (cdr handle) "application/pgp-signature" nil t))
585             (mm-set-handle-multipart-parameter
586              mm-security-handle 'gnus-info "Corrupted")
587             (throw 'error handle))
588           (mm-insert-part part)
589           (unless (condition-case err
590                       (prog1
591                           (gpg-verify message signature mml2015-result-buffer)
592                         (mm-set-handle-multipart-parameter
593                          mm-security-handle 'gnus-details
594                          (with-current-buffer mml2015-result-buffer
595                            (buffer-string))))
596                     (error
597                      (mm-set-handle-multipart-parameter
598                       mm-security-handle 'gnus-details (mml2015-format-error err))
599                      (mm-set-handle-multipart-parameter
600                       mm-security-handle 'gnus-info "Error.")
601                      (setq info-is-set-p t)
602                      nil)
603                     (quit
604                      (mm-set-handle-multipart-parameter
605                       mm-security-handle 'gnus-details "Quit.")
606                      (mm-set-handle-multipart-parameter
607                       mm-security-handle 'gnus-info "Quit.")
608                      (setq info-is-set-p t)
609                      nil))
610             (unless info-is-set-p
611               (mm-set-handle-multipart-parameter
612                mm-security-handle 'gnus-info "Failed"))
613             (throw 'error handle)))
614         (mm-set-handle-multipart-parameter
615          mm-security-handle 'gnus-info
616          (with-current-buffer mml2015-result-buffer
617            (mml2015-gpg-extract-signature-details))))
618       handle)))
619
620 (defun mml2015-gpg-clear-verify ()
621   (if (condition-case err
622           (prog1
623               (gpg-verify-cleartext (current-buffer) mml2015-result-buffer)
624             (mm-set-handle-multipart-parameter
625              mm-security-handle 'gnus-details
626              (with-current-buffer mml2015-result-buffer
627                (buffer-string))))
628         (error
629          (mm-set-handle-multipart-parameter
630           mm-security-handle 'gnus-details (mml2015-format-error err))
631          nil)
632         (quit
633          (mm-set-handle-multipart-parameter
634           mm-security-handle 'gnus-details "Quit.")
635          nil))
636       (mm-set-handle-multipart-parameter
637        mm-security-handle 'gnus-info
638        (with-current-buffer mml2015-result-buffer
639          (mml2015-gpg-extract-signature-details)))
640     (mm-set-handle-multipart-parameter
641      mm-security-handle 'gnus-info "Failed"))
642   (mml2015-extract-cleartext-signature))
643
644 (defun mml2015-gpg-sign (cont)
645   (let ((boundary (mml-compute-boundary cont))
646         (text (current-buffer)) signature)
647     (goto-char (point-max))
648     (unless (bolp)
649       (insert "\n"))
650     (with-temp-buffer
651       (unless (gpg-sign-detached text (setq signature (current-buffer))
652                                  mml2015-result-buffer
653                                  nil
654                                  (message-options-get 'message-sender)
655                                  t t) ; armor & textmode
656         (unless (> (point-max) (point-min))
657           (pop-to-buffer mml2015-result-buffer)
658           (error "Sign error")))
659       (goto-char (point-min))
660       (while (re-search-forward "\r+$" nil t)
661         (replace-match "" t t))
662       (set-buffer text)
663       (goto-char (point-min))
664       (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
665                       boundary))
666       ;;; FIXME: what is the micalg?
667       (insert "\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"\n")
668       (insert (format "\n--%s\n" boundary))
669       (goto-char (point-max))
670       (insert (format "\n--%s\n" boundary))
671       (insert "Content-Type: application/pgp-signature\n\n")
672       (insert-buffer-substring signature)
673       (goto-char (point-max))
674       (insert (format "--%s--\n" boundary))
675       (goto-char (point-max)))))
676
677 (defun mml2015-gpg-encrypt (cont &optional sign)
678   (let ((boundary (mml-compute-boundary cont))
679         (text (current-buffer))
680         cipher)
681     (mm-with-unibyte-current-buffer
682       (with-temp-buffer
683         (mm-disable-multibyte)
684         ;; set up a function to call the correct gpg encrypt routine
685         ;; with the right arguments. (FIXME: this should be done
686         ;; differently.)
687         (flet ((gpg-encrypt-func
688                  (sign plaintext ciphertext result recipients &optional
689                        passphrase sign-with-key armor textmode)
690                  (if sign
691                      (gpg-sign-encrypt
692                       plaintext ciphertext result recipients passphrase
693                       sign-with-key armor textmode)
694                    (gpg-encrypt
695                     plaintext ciphertext result recipients passphrase
696                     armor textmode))))
697           (unless (gpg-encrypt-func
698                     sign ; passed in when using signencrypt
699                     text (setq cipher (current-buffer))
700                     mml2015-result-buffer
701                     (split-string
702                      (or
703                       (message-options-get 'message-recipients)
704                       (message-options-set 'message-recipients
705                                            (read-string "Recipients: ")))
706                      "[ \f\t\n\r\v,]+")
707                     nil
708                     (message-options-get 'message-sender)
709                     t t) ; armor & textmode
710             (unless (> (point-max) (point-min))
711               (pop-to-buffer mml2015-result-buffer)
712               (error "Encrypt error"))))
713         (goto-char (point-min))
714         (while (re-search-forward "\r+$" nil t)
715           (replace-match "" t t))
716         (set-buffer text)
717         (delete-region (point-min) (point-max))
718         (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
719                         boundary))
720         (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
721         (insert (format "--%s\n" boundary))
722         (insert "Content-Type: application/pgp-encrypted\n\n")
723         (insert "Version: 1\n\n")
724         (insert (format "--%s\n" boundary))
725         (insert "Content-Type: application/octet-stream\n\n")
726         (insert-buffer-substring cipher)
727         (goto-char (point-max))
728         (insert (format "--%s--\n" boundary))
729         (goto-char (point-max))))))
730
731 ;;; pgg wrapper
732
733 (defvar pgg-default-user-id)
734 (defvar pgg-errors-buffer)
735 (defvar pgg-output-buffer)
736
737 (autoload 'pgg-decrypt-region "pgg")
738 (autoload 'pgg-verify-region "pgg")
739 (autoload 'pgg-sign-region "pgg")
740 (autoload 'pgg-encrypt-region "pgg")
741 (autoload 'pgg-parse-armor "pgg-parse")
742
743 (defun mml2015-pgg-decrypt (handle ctl)
744   (catch 'error
745     (let ((pgg-errors-buffer mml2015-result-buffer)
746           child handles result decrypt-status)
747       (unless (setq child (mm-find-part-by-type
748                            (cdr handle)
749                            "application/octet-stream" nil t))
750         (mm-set-handle-multipart-parameter
751          mm-security-handle 'gnus-info "Corrupted")
752         (throw 'error handle))
753       (with-temp-buffer
754         (mm-insert-part child)
755         (if (condition-case err
756                 (prog1
757                     (pgg-decrypt-region (point-min) (point-max))
758                   (setq decrypt-status
759                         (with-current-buffer mml2015-result-buffer
760                           (buffer-string)))
761                   (mm-set-handle-multipart-parameter
762                    mm-security-handle 'gnus-details
763                    decrypt-status))
764               (error
765                (mm-set-handle-multipart-parameter
766                 mm-security-handle 'gnus-details (mml2015-format-error err))
767                nil)
768               (quit
769                (mm-set-handle-multipart-parameter
770                 mm-security-handle 'gnus-details "Quit.")
771                nil))
772             (with-current-buffer pgg-output-buffer
773               (goto-char (point-min))
774               (while (search-forward "\r\n" nil t)
775                 (replace-match "\n" t t))
776               (setq handles (mm-dissect-buffer t))
777               (mm-destroy-parts handle)
778               (mm-set-handle-multipart-parameter
779                mm-security-handle 'gnus-info "OK")
780               (mm-set-handle-multipart-parameter
781                mm-security-handle 'gnus-details
782                (concat decrypt-status
783                        (when (stringp (car handles))
784                          "\n" (mm-handle-multipart-ctl-parameter
785                                handles 'gnus-details))))
786               (if (listp (car handles))
787                   handles
788                 (list handles)))
789           (mm-set-handle-multipart-parameter
790            mm-security-handle 'gnus-info "Failed")
791           (throw 'error handle))))))
792
793 (defun mml2015-pgg-clear-decrypt ()
794   (let ((pgg-errors-buffer mml2015-result-buffer))
795     (if (prog1
796             (pgg-decrypt-region (point-min) (point-max))
797           (mm-set-handle-multipart-parameter
798            mm-security-handle 'gnus-details
799            (with-current-buffer mml2015-result-buffer
800              (buffer-string))))
801         (progn
802           (erase-buffer)
803           ;; Treat data which pgg returns as a unibyte string.
804           (mm-disable-multibyte)
805           (insert-buffer-substring pgg-output-buffer)
806           (goto-char (point-min))
807           (while (search-forward "\r\n" nil t)
808             (replace-match "\n" t t))
809           (mm-set-handle-multipart-parameter
810            mm-security-handle 'gnus-info "OK"))
811       (mm-set-handle-multipart-parameter
812        mm-security-handle 'gnus-info "Failed"))))
813
814 (defun mml2015-pgg-verify (handle ctl)
815   (let ((pgg-errors-buffer mml2015-result-buffer)
816         signature-file part signature)
817     (if (or (null (setq part (mm-find-raw-part-by-type
818                               ctl (or (mm-handle-multipart-ctl-parameter
819                                        ctl 'protocol)
820                                       "application/pgp-signature")
821                               t)))
822             (null (setq signature (mm-find-part-by-type
823                                    (cdr handle) "application/pgp-signature" nil t))))
824         (progn
825           (mm-set-handle-multipart-parameter
826            mm-security-handle 'gnus-info "Corrupted")
827           handle)
828       (with-temp-buffer
829         (insert part)
830         ;; Convert <LF> to <CR><LF> in signed text.  If --textmode is
831         ;; specified when signing, the conversion is not necessary.
832         (goto-char (point-min))
833         (end-of-line)
834         (while (not (eobp))
835           (unless (eq (char-before) ?\r)
836             (insert "\r"))
837           (forward-line)
838           (end-of-line))
839         (with-temp-file (setq signature-file (mm-make-temp-file "pgg"))
840           (mm-insert-part signature))
841         (if (condition-case err
842                 (prog1
843                     (pgg-verify-region (point-min) (point-max)
844                                        signature-file t)
845                   (goto-char (point-min))
846                   (while (search-forward "\r\n" nil t)
847                     (replace-match "\n" t t))
848                   (mm-set-handle-multipart-parameter
849                    mm-security-handle 'gnus-details
850                    (concat (with-current-buffer pgg-output-buffer
851                              (buffer-string))
852                            (with-current-buffer pgg-errors-buffer
853                              (buffer-string)))))
854               (error
855                (mm-set-handle-multipart-parameter
856                 mm-security-handle 'gnus-details (mml2015-format-error err))
857                nil)
858               (quit
859                (mm-set-handle-multipart-parameter
860                 mm-security-handle 'gnus-details "Quit.")
861                nil))
862             (progn
863               (delete-file signature-file)
864               (mm-set-handle-multipart-parameter
865                mm-security-handle 'gnus-info
866                (with-current-buffer pgg-errors-buffer
867                  (mml2015-gpg-extract-signature-details))))
868           (delete-file signature-file)
869           (mm-set-handle-multipart-parameter
870            mm-security-handle 'gnus-info "Failed")))))
871   handle)
872
873 (defun mml2015-pgg-clear-verify ()
874   (let ((pgg-errors-buffer mml2015-result-buffer)
875         (text (buffer-string))
876         (coding-system buffer-file-coding-system))
877     (if (condition-case err
878             (prog1
879                 (mm-with-unibyte-buffer
880                   (insert (mm-encode-coding-string text coding-system))
881                   (pgg-verify-region (point-min) (point-max) nil t))
882               (goto-char (point-min))
883               (while (search-forward "\r\n" nil t)
884                 (replace-match "\n" t t))
885               (mm-set-handle-multipart-parameter
886                mm-security-handle 'gnus-details
887                (concat (with-current-buffer pgg-output-buffer
888                          (buffer-string))
889                        (with-current-buffer pgg-errors-buffer
890                          (buffer-string)))))
891           (error
892            (mm-set-handle-multipart-parameter
893             mm-security-handle 'gnus-details (mml2015-format-error err))
894            nil)
895           (quit
896            (mm-set-handle-multipart-parameter
897             mm-security-handle 'gnus-details "Quit.")
898            nil))
899         (mm-set-handle-multipart-parameter
900          mm-security-handle 'gnus-info
901          (with-current-buffer pgg-errors-buffer
902            (mml2015-gpg-extract-signature-details)))
903       (mm-set-handle-multipart-parameter
904        mm-security-handle 'gnus-info "Failed")))
905   (mml2015-extract-cleartext-signature))
906
907 (defun mml2015-pgg-sign (cont)
908   (let ((pgg-errors-buffer mml2015-result-buffer)
909         (boundary (mml-compute-boundary cont))
910         (pgg-default-user-id (or (message-options-get 'mml-sender)
911                                  pgg-default-user-id))
912         (pgg-text-mode t)
913         entry)
914     (unless (pgg-sign-region (point-min) (point-max))
915       (pop-to-buffer mml2015-result-buffer)
916       (error "Sign error"))
917     (goto-char (point-min))
918     (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
919                     boundary))
920     (if (setq entry (assq 2 (pgg-parse-armor
921                              (with-current-buffer pgg-output-buffer
922                                (buffer-string)))))
923         (setq entry (assq 'hash-algorithm (cdr entry))))
924     (insert (format "\tmicalg=%s; "
925                     (if (cdr entry)
926                         (downcase (format "pgp-%s" (cdr entry)))
927                       "pgp-sha1")))
928     (insert "protocol=\"application/pgp-signature\"\n")
929     (insert (format "\n--%s\n" boundary))
930     (goto-char (point-max))
931     (insert (format "\n--%s\n" boundary))
932     (insert "Content-Type: application/pgp-signature\n\n")
933     (insert-buffer-substring pgg-output-buffer)
934     (goto-char (point-max))
935     (insert (format "--%s--\n" boundary))
936     (goto-char (point-max))))
937
938 (defun mml2015-pgg-encrypt (cont &optional sign)
939   (let ((pgg-errors-buffer mml2015-result-buffer)
940         (pgg-text-mode t)
941         (boundary (mml-compute-boundary cont)))
942     (unless (pgg-encrypt-region (point-min) (point-max)
943                                 (split-string
944                                  (or
945                                   (message-options-get 'message-recipients)
946                                   (message-options-set 'message-recipients
947                                                        (read-string "Recipients: ")))
948                                  "[ \f\t\n\r\v,]+")
949                                 sign)
950       (pop-to-buffer mml2015-result-buffer)
951       (error "Encrypt error"))
952     (delete-region (point-min) (point-max))
953     (goto-char (point-min))
954     (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
955                     boundary))
956     (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
957     (insert (format "--%s\n" boundary))
958     (insert "Content-Type: application/pgp-encrypted\n\n")
959     (insert "Version: 1\n\n")
960     (insert (format "--%s\n" boundary))
961     (insert "Content-Type: application/octet-stream\n\n")
962     (insert-buffer-substring pgg-output-buffer)
963     (goto-char (point-max))
964     (insert (format "--%s--\n" boundary))
965     (goto-char (point-max))))
966
967 ;;; epg wrapper
968
969 (defvar epg-user-id-alist)
970 (defvar epg-digest-algorithm-alist)
971 (defvar inhibit-redisplay)
972
973 (autoload 'epg-make-context "epg")
974 (autoload 'epg-context-set-armor "epg")
975 (autoload 'epg-context-set-textmode "epg")
976 (autoload 'epg-context-set-signers "epg")
977 (autoload 'epg-context-result-for "epg")
978 (autoload 'epg-new-signature-digest-algorithm "epg")
979 (autoload 'epg-verify-result-to-string "epg")
980 (autoload 'epg-list-keys "epg")
981 (autoload 'epg-decrypt-string "epg")
982 (autoload 'epg-verify-string "epg")
983 (autoload 'epg-sign-string "epg")
984 (autoload 'epg-encrypt-string "epg")
985 (autoload 'epg-passphrase-callback-function "epg")
986 (autoload 'epg-context-set-passphrase-callback "epg")
987 (autoload 'epg-key-sub-key-list "epg")
988 (autoload 'epg-sub-key-capability "epg")
989 (autoload 'epg-sub-key-validity "epg")
990 (autoload 'epg-configuration "epg-config")
991 (autoload 'epg-expand-group "epg-config")
992 (autoload 'epa-select-keys "epa")
993
994 (defvar mml2015-epg-secret-key-id-list nil)
995
996 (defun mml2015-epg-passphrase-callback (context key-id ignore)
997   (if (eq key-id 'SYM)