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