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