Import Gnus v5.10.2.
[elisp/gnus.git-] / 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
281          (funcall mml-boundary-function (incf mml-multipart-number)))
282         hash point)
283     (goto-char (point-min))
284     (unless (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\r?$" nil t)
285       (error "Cannot find signed begin line"))
286     (goto-char (match-beginning 0))
287     (forward-line 1)
288     (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
289       (error "Cannot not find PGP hash"))
290     (setq hash (match-string 1))
291     (unless (re-search-forward "^$" nil t)
292       (error "Cannot not find PGP message"))
293     (forward-line 1)
294     (delete-region (point-min) (point))
295     (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
296                     boundary))
297     (insert (format "\tmicalg=pgp-%s; protocol=\"application/pgp-signature\"\n"
298                     (downcase hash)))
299     (insert (format "\n--%s\n" boundary))
300     (setq point (point))
301     (goto-char (point-max))
302     (unless (re-search-backward "^-----END PGP SIGNATURE-----\r?$" nil t)
303       (error "Cannot find signature part"))
304     (replace-match "-----END PGP MESSAGE-----" t t)
305     (goto-char (match-beginning 0))
306     (unless (re-search-backward "^-----BEGIN PGP SIGNATURE-----\r?$"
307                                 nil t)
308       (error "Cannot find signature part"))
309     (replace-match "-----BEGIN PGP MESSAGE-----" t t)
310     (goto-char (match-beginning 0))
311     (save-restriction
312       (narrow-to-region point (point))
313       (goto-char point)
314       (while (re-search-forward "^- -" nil t)
315         (replace-match "-" t t))
316       (goto-char (point-max)))
317     (insert (format "--%s\n" boundary))
318     (insert "Content-Type: application/pgp-signature\n\n")
319     (goto-char (point-max))
320     (insert (format "--%s--\n" boundary))
321     (goto-char (point-max))))
322
323 (defun mml2015-mailcrypt-encrypt (cont &optional sign)
324   (let ((mc-pgp-always-sign
325          (or mc-pgp-always-sign
326              sign
327              (eq t (or (message-options-get 'message-sign-encrypt)
328                        (message-options-set
329                         'message-sign-encrypt
330                         (or (y-or-n-p "Sign the message? ")
331                             'not))))
332              'never)))
333     (mm-with-unibyte-current-buffer
334       (mc-encrypt-generic
335        (or (message-options-get 'message-recipients)
336            (message-options-set 'message-recipients
337                               (mc-cleanup-recipient-headers
338                                (read-string "Recipients: "))))
339        nil nil nil
340        (message-options-get 'message-sender))))
341   (goto-char (point-min))
342   (unless (looking-at "-----BEGIN PGP MESSAGE-----")
343     (error "Fail to encrypt the message"))
344   (let ((boundary
345          (funcall mml-boundary-function (incf mml-multipart-number))))
346     (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
347                     boundary))
348     (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
349     (insert (format "--%s\n" boundary))
350     (insert "Content-Type: application/pgp-encrypted\n\n")
351     (insert "Version: 1\n\n")
352     (insert (format "--%s\n" boundary))
353     (insert "Content-Type: application/octet-stream\n\n")
354     (goto-char (point-max))
355     (insert (format "--%s--\n" boundary))
356     (goto-char (point-max))))
357
358 ;;; gpg wrapper
359
360 (eval-and-compile
361   (autoload 'gpg-decrypt "gpg")
362   (autoload 'gpg-verify "gpg")
363   (autoload 'gpg-verify-cleartext "gpg")
364   (autoload 'gpg-sign-detached "gpg")
365   (autoload 'gpg-sign-encrypt "gpg")
366   (autoload 'gpg-encrypt "gpg")
367   (autoload 'gpg-passphrase-read "gpg"))
368
369 (defun mml2015-gpg-passphrase ()
370   (or (message-options-get 'gpg-passphrase)
371       (message-options-set 'gpg-passphrase (gpg-passphrase-read))))
372
373 (defun mml2015-gpg-decrypt-1 ()
374   (let ((cipher (current-buffer)) plain result)
375     (if (with-temp-buffer
376           (prog1
377               (gpg-decrypt cipher (setq plain (current-buffer))
378                            mml2015-result-buffer nil)
379             (mm-set-handle-multipart-parameter
380              mm-security-handle 'gnus-details
381              (with-current-buffer mml2015-result-buffer
382                (buffer-string)))
383             (set-buffer cipher)
384             (erase-buffer)
385             (insert-buffer-substring plain)
386             (goto-char (point-min))
387             (while (search-forward "\r\n" nil t)
388               (replace-match "\n" t t))))
389         '(t)
390       ;; Some wrong with the return value, check plain text buffer.
391       (if (> (point-max) (point-min))
392           '(t)
393         nil))))
394
395 (defun mml2015-gpg-decrypt (handle ctl)
396   (let ((mml2015-decrypt-function 'mml2015-gpg-decrypt-1))
397     (mml2015-mailcrypt-decrypt handle ctl)))
398
399 (defun mml2015-gpg-clear-decrypt ()
400   (let (result)
401     (setq result (mml2015-gpg-decrypt-1))
402     (if (car result)
403         (mm-set-handle-multipart-parameter
404          mm-security-handle 'gnus-info "OK")
405       (mm-set-handle-multipart-parameter
406        mm-security-handle 'gnus-info "Failed"))))
407
408 (defun mml2015-gpg-pretty-print-fpr (fingerprint)
409   (let* ((result "")
410          (fpr-length (string-width fingerprint))
411          (n-slice 0)
412          slice)
413     (setq fingerprint (string-to-list fingerprint))
414     (while fingerprint
415       (setq fpr-length (- fpr-length 4))
416       (setq slice (butlast fingerprint fpr-length))
417       (setq fingerprint (nthcdr 4 fingerprint))
418       (setq n-slice (1+ n-slice))
419       (setq result
420             (concat
421              result
422              (case n-slice
423                (1  slice)
424                (otherwise (concat " " slice))))))
425     result))
426
427 (defun mml2015-gpg-extract-signature-details ()
428   (goto-char (point-min))
429   (let* ((expired (re-search-forward
430                    "^\\[GNUPG:\\] SIGEXPIRED$"
431                    nil t))
432          (signer (and (re-search-forward
433                        "^\\[GNUPG:\\] GOODSIG \\([0-9A-Za-z]*\\) \\(.*\\)$"
434                        nil t)
435                       (cons (match-string 1) (match-string 2))))
436          (fprint (and (re-search-forward
437                        "^\\[GNUPG:\\] VALIDSIG \\([0-9a-zA-Z]*\\) "
438                        nil t)
439                       (match-string 1)))
440          (trust  (and (re-search-forward
441                        "^\\[GNUPG:\\] \\(TRUST_.*\\)$"
442                        nil t)
443                       (match-string 1)))
444          (trust-good-enough-p
445           (cdr (assoc trust mml2015-unabbrev-trust-alist))))
446     (cond ((and signer fprint)
447            (concat (cdr signer)
448                    (unless trust-good-enough-p
449                      (concat "\nUntrusted, Fingerprint: "
450                              (mml2015-gpg-pretty-print-fpr fprint)))
451                    (when expired
452                      (format "\nWARNING: Signature from expired key (%s)"
453                              (car signer)))))
454           ((re-search-forward
455             "^\\(gpg: \\)?Good signature from \"\\(.*\\)\"$" nil t)
456            (match-string 2))
457           (t
458            "From unknown user"))))
459
460 (defun mml2015-gpg-verify (handle ctl)
461   (catch 'error
462     (let (part message signature info-is-set-p)
463       (unless (setq part (mm-find-raw-part-by-type
464                           ctl (or (mm-handle-multipart-ctl-parameter
465                                    ctl 'protocol)
466                                   "application/pgp-signature")
467                           t))
468         (mm-set-handle-multipart-parameter
469          mm-security-handle 'gnus-info "Corrupted")
470         (throw 'error handle))
471       (with-temp-buffer
472         (setq message (current-buffer))
473         (insert part)
474         ;; Convert <LF> to <CR><LF> in verify mode.  Sign and
475         ;; clearsign use --textmode. The conversion is not necessary.
476         ;; In clearverify, the conversion is not necessary either.
477         (goto-char (point-min))
478         (end-of-line)
479         (while (not (eobp))
480           (unless (eq (char-before) ?\r)
481             (insert "\r"))
482           (forward-line)
483           (end-of-line))
484         (with-temp-buffer
485           (setq signature (current-buffer))
486           (unless (setq part (mm-find-part-by-type
487                               (cdr handle) "application/pgp-signature" nil t))
488             (mm-set-handle-multipart-parameter
489              mm-security-handle 'gnus-info "Corrupted")
490             (throw 'error handle))
491           (mm-insert-part part)
492           (unless (condition-case err
493                       (prog1
494                           (gpg-verify message signature mml2015-result-buffer)
495                         (mm-set-handle-multipart-parameter
496                          mm-security-handle 'gnus-details
497                          (with-current-buffer mml2015-result-buffer
498                            (buffer-string))))
499                     (error
500                      (mm-set-handle-multipart-parameter
501                       mm-security-handle 'gnus-details (mml2015-format-error err))
502                      (mm-set-handle-multipart-parameter
503                       mm-security-handle 'gnus-info "Error.")
504                      (setq info-is-set-p t)
505                      nil)
506                     (quit
507                      (mm-set-handle-multipart-parameter
508                       mm-security-handle 'gnus-details "Quit.")
509                      (mm-set-handle-multipart-parameter
510                       mm-security-handle 'gnus-info "Quit.")
511                      (setq info-is-set-p t)
512                      nil))
513             (unless info-is-set-p
514               (mm-set-handle-multipart-parameter
515                mm-security-handle 'gnus-info "Failed"))
516             (throw 'error handle)))
517         (mm-set-handle-multipart-parameter
518          mm-security-handle 'gnus-info
519          (with-current-buffer mml2015-result-buffer
520            (mml2015-gpg-extract-signature-details))))
521       handle)))
522
523 (defun mml2015-gpg-clear-verify ()
524   (if (condition-case err
525           (prog1
526               (gpg-verify-cleartext (current-buffer) mml2015-result-buffer)
527             (mm-set-handle-multipart-parameter
528              mm-security-handle 'gnus-details
529              (with-current-buffer mml2015-result-buffer
530                (buffer-string))))
531         (error
532          (mm-set-handle-multipart-parameter
533           mm-security-handle 'gnus-details (mml2015-format-error err))
534          nil)
535         (quit
536          (mm-set-handle-multipart-parameter
537           mm-security-handle 'gnus-details "Quit.")
538          nil))
539       (mm-set-handle-multipart-parameter
540        mm-security-handle 'gnus-info
541        (with-current-buffer mml2015-result-buffer
542          (mml2015-gpg-extract-signature-details)))
543     (mm-set-handle-multipart-parameter
544      mm-security-handle 'gnus-info "Failed")))
545
546 (defun mml2015-gpg-sign (cont)
547   (let ((boundary
548          (funcall mml-boundary-function (incf mml-multipart-number)))
549         (text (current-buffer)) signature)
550     (goto-char (point-max))
551     (unless (bolp)
552       (insert "\n"))
553     (with-temp-buffer
554       (unless (gpg-sign-detached text (setq signature (current-buffer))
555                                  mml2015-result-buffer
556                                  nil
557                                  (message-options-get 'message-sender)
558                                  t t) ; armor & textmode
559         (unless (> (point-max) (point-min))
560           (pop-to-buffer mml2015-result-buffer)
561           (error "Sign error")))
562       (goto-char (point-min))
563       (while (re-search-forward "\r+$" nil t)
564         (replace-match "" t t))
565       (set-buffer text)
566       (goto-char (point-min))
567       (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
568                       boundary))
569       ;;; FIXME: what is the micalg?
570       (insert "\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"\n")
571       (insert (format "\n--%s\n" boundary))
572       (goto-char (point-max))
573       (insert (format "\n--%s\n" boundary))
574       (insert "Content-Type: application/pgp-signature\n\n")
575       (insert-buffer-substring signature)
576       (goto-char (point-max))
577       (insert (format "--%s--\n" boundary))
578       (goto-char (point-max)))))
579
580 (defun mml2015-gpg-encrypt (cont &optional sign)
581   (let ((boundary
582          (funcall mml-boundary-function (incf mml-multipart-number)))
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 (funcall mml-boundary-function (incf mml-multipart-number)))
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 (funcall mml-boundary-function (incf mml-multipart-number))))
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