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