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