Cleaning up leading and trailing spaces.
[gnus] / lisp / mml2015.el
1 ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP)
2 ;; Copyright (C) 2000, 2001 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 ;;; Code:
27
28 (eval-when-compile (require 'cl))
29 (require 'mm-decode)
30
31 (defvar mml2015-use (or
32                      (progn
33                        (ignore-errors
34                          (require 'gpg))
35                        (and (fboundp 'gpg-sign-detached)
36                             'gpg))
37                      (progn (ignore-errors
38                               (load "mc-toplev"))
39                             (and (fboundp 'mc-encrypt-generic)
40                                  (fboundp 'mc-sign-generic)
41                                  (fboundp 'mc-cleanup-recipient-headers)
42                                  'mailcrypt)))
43   "The package used for PGP/MIME.")
44
45 ;; Something is not RFC2015.
46 (defvar mml2015-function-alist
47   '((mailcrypt mml2015-mailcrypt-sign
48                mml2015-mailcrypt-encrypt
49                mml2015-mailcrypt-verify
50                mml2015-mailcrypt-decrypt
51                mml2015-mailcrypt-clear-verify
52                mml2015-mailcrypt-clear-decrypt)
53     (gpg mml2015-gpg-sign
54          mml2015-gpg-encrypt
55          mml2015-gpg-verify
56          mml2015-gpg-decrypt
57          mml2015-gpg-clear-verify
58          mml2015-gpg-clear-decrypt))
59   "Alist of PGP/MIME functions.")
60
61 (defvar mml2015-result-buffer nil)
62
63 ;;; mailcrypt wrapper
64
65 (eval-and-compile
66   (autoload 'mailcrypt-decrypt "mailcrypt")
67   (autoload 'mailcrypt-verify "mailcrypt")
68   (autoload 'mc-pgp-always-sign "mailcrypt")
69   (autoload 'mc-encrypt-generic "mc-toplev")
70   (autoload 'mc-cleanup-recipient-headers "mc-toplev")
71   (autoload 'mc-sign-generic "mc-toplev"))
72
73 (eval-when-compile
74   (defvar mc-default-scheme)
75   (defvar mc-schemes))
76
77 (defvar mml2015-decrypt-function 'mailcrypt-decrypt)
78 (defvar mml2015-verify-function 'mailcrypt-verify)
79
80 (defun mml2015-format-error (err)
81   (if (stringp (cadr err))
82       (cadr err)
83     (format "%S" (cdr err))))
84
85 (defun mml2015-mailcrypt-decrypt (handle ctl)
86   (catch 'error
87     (let (child handles result)
88       (unless (setq child (mm-find-part-by-type
89                            (cdr handle)
90                            "application/octet-stream" nil t))
91         (mm-set-handle-multipart-parameter
92          mm-security-handle 'gnus-info "Corrupted")
93         (throw 'error handle))
94       (with-temp-buffer
95         (mm-insert-part child)
96         (setq result
97               (condition-case err
98                   (funcall mml2015-decrypt-function)
99                 (error
100                  (mm-set-handle-multipart-parameter
101                   mm-security-handle 'gnus-details (mml2015-format-error err))
102                  nil)
103                 (quit
104                  (mm-set-handle-multipart-parameter
105                   mm-security-handle 'gnus-details "Quit.")
106                  nil)))
107         (unless (car result)
108           (mm-set-handle-multipart-parameter
109            mm-security-handle 'gnus-info "Failed")
110           (throw 'error handle))
111         (setq handles (mm-dissect-buffer t)))
112       (mm-destroy-parts handle)
113       (mm-set-handle-multipart-parameter
114        mm-security-handle 'gnus-info "OK")
115       (if (listp (car handles))
116           handles
117         (list handles)))))
118
119 (defun mml2015-mailcrypt-clear-decrypt ()
120   (let (result)
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     (if (car result)
133         (mm-set-handle-multipart-parameter
134          mm-security-handle 'gnus-info "OK")
135       (mm-set-handle-multipart-parameter
136        mm-security-handle 'gnus-info "Failed"))))
137
138 (defun mml2015-fix-micalg (alg)
139   (and alg
140        (upcase (if (string-match "^pgp-" alg)
141                    (substring alg (match-end 0))
142                  alg))))
143
144 (defun mml2015-mailcrypt-verify (handle ctl)
145   (catch 'error
146     (let (part)
147       (unless (setq part (mm-find-raw-part-by-type
148                           ctl (or (mm-handle-multipart-ctl-parameter
149                                    ctl 'protocol)
150                                   "application/pgp-signature")
151                           t))
152         (mm-set-handle-multipart-parameter
153          mm-security-handle 'gnus-info "Corrupted")
154         (throw 'error handle))
155       (with-temp-buffer
156         (insert "-----BEGIN PGP SIGNED MESSAGE-----\n")
157         (insert (format "Hash: %s\n\n"
158                         (or (mml2015-fix-micalg
159                              (mm-handle-multipart-ctl-parameter
160                               ctl 'micalg))
161                             "SHA1")))
162         (save-restriction
163           (narrow-to-region (point) (point))
164           (insert part "\n")
165           (goto-char (point-min))
166           (while (not (eobp))
167             (if (looking-at "^-")
168                 (insert "- "))
169             (forward-line)))
170         (unless (setq part (mm-find-part-by-type
171                             (cdr handle) "application/pgp-signature" nil t))
172           (mm-set-handle-multipart-parameter
173            mm-security-handle 'gnus-info "Corrupted")
174           (throw 'error handle))
175         (save-restriction
176           (narrow-to-region (point) (point))
177           (mm-insert-part part)
178           (goto-char (point-min))
179           (if (re-search-forward "^-----BEGIN PGP [^-]+-----\r?$" nil t)
180               (replace-match "-----BEGIN PGP SIGNATURE-----" t t))
181           (if (re-search-forward "^-----END PGP [^-]+-----\r?$" nil t)
182               (replace-match "-----END PGP SIGNATURE-----" t t)))
183         (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
184           (unless (condition-case err
185                       (prog1
186                           (funcall mml2015-verify-function)
187                         (if (get-buffer " *mailcrypt stderr temp")
188                             (mm-set-handle-multipart-parameter
189                              mm-security-handle 'gnus-details
190                              (with-current-buffer " *mailcrypt stderr temp"
191                                (buffer-string))))
192                         (if (get-buffer " *mailcrypt stdout temp")
193                             (kill-buffer " *mailcrypt stdout temp"))
194                         (if (get-buffer " *mailcrypt stderr temp")
195                             (kill-buffer " *mailcrypt stderr temp"))
196                         (if (get-buffer " *mailcrypt status temp")
197                             (kill-buffer " *mailcrypt status temp"))
198                         (if (get-buffer mc-gpg-debug-buffer)
199                             (kill-buffer mc-gpg-debug-buffer)))
200                     (error
201                      (mm-set-handle-multipart-parameter
202                       mm-security-handle 'gnus-details (mml2015-format-error err))
203                      nil)
204                     (quit
205                      (mm-set-handle-multipart-parameter
206                       mm-security-handle 'gnus-details "Quit.")
207                      nil))
208             (mm-set-handle-multipart-parameter
209              mm-security-handle 'gnus-info "Failed")
210             (throw 'error handle))))
211       (mm-set-handle-multipart-parameter
212        mm-security-handle 'gnus-info "OK")
213       handle)))
214
215 (defun mml2015-mailcrypt-clear-verify ()
216   (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
217     (if (condition-case err
218             (prog1
219                 (funcall mml2015-verify-function)
220               (if (get-buffer " *mailcrypt stderr temp")
221                   (mm-set-handle-multipart-parameter
222                    mm-security-handle 'gnus-details
223                    (with-current-buffer " *mailcrypt stderr temp"
224                      (buffer-string))))
225               (if (get-buffer " *mailcrypt stdout temp")
226                   (kill-buffer " *mailcrypt stdout temp"))
227               (if (get-buffer " *mailcrypt stderr temp")
228                   (kill-buffer " *mailcrypt stderr temp"))
229               (if (get-buffer " *mailcrypt status temp")
230                   (kill-buffer " *mailcrypt status temp"))
231               (if (get-buffer mc-gpg-debug-buffer)
232                   (kill-buffer mc-gpg-debug-buffer)))
233           (error
234            (mm-set-handle-multipart-parameter
235             mm-security-handle 'gnus-details (mml2015-format-error err))
236            nil)
237           (quit
238            (mm-set-handle-multipart-parameter
239             mm-security-handle 'gnus-details "Quit.")
240            nil))
241         (mm-set-handle-multipart-parameter
242          mm-security-handle 'gnus-info "OK")
243       (mm-set-handle-multipart-parameter
244        mm-security-handle 'gnus-info "Failed"))))
245
246 (defun mml2015-mailcrypt-sign (cont)
247   (mc-sign-generic (message-options-get 'message-sender)
248                    nil nil nil nil)
249   (let ((boundary
250          (funcall mml-boundary-function (incf mml-multipart-number)))
251         hash point)
252     (goto-char (point-min))
253     (unless (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\r?$" nil t)
254       (error "Cannot find signed begin line"))
255     (goto-char (match-beginning 0))
256     (forward-line 1)
257     (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
258       (error "Cannot not find PGP hash"))
259     (setq hash (match-string 1))
260     (unless (re-search-forward "^$" nil t)
261       (error "Cannot not find PGP message"))
262     (forward-line 1)
263     (delete-region (point-min) (point))
264     (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
265                     boundary))
266     (insert (format "\tmicalg=pgp-%s; protocol=\"application/pgp-signature\"\n"
267                     (downcase hash)))
268     (insert (format "\n--%s\n" boundary))
269     (setq point (point))
270     (goto-char (point-max))
271     (unless (re-search-backward "^-----END PGP SIGNATURE-----\r?$" nil t)
272       (error "Cannot find signature part"))
273     (replace-match "-----END PGP MESSAGE-----" t t)
274     (goto-char (match-beginning 0))
275     (unless (re-search-backward "^-----BEGIN PGP SIGNATURE-----\r?$"
276                                 nil t)
277       (error "Cannot find signature part"))
278     (replace-match "-----BEGIN PGP MESSAGE-----" t t)
279     (goto-char (match-beginning 0))
280     (save-restriction
281       (narrow-to-region point (point))
282       (goto-char point)
283       (while (re-search-forward "^- -" nil t)
284         (replace-match "-" t t))
285       (goto-char (point-max)))
286     (insert (format "--%s\n" boundary))
287     (insert "Content-Type: application/pgp-signature\n\n")
288     (goto-char (point-max))
289     (insert (format "--%s--\n" boundary))
290     (goto-char (point-max))))
291
292 (defun mml2015-mailcrypt-encrypt (cont)
293   (let ((mc-pgp-always-sign
294          (or mc-pgp-always-sign
295              (eq t (or (message-options-get 'message-sign-encrypt)
296                        (message-options-set
297                         'message-sign-encrypt
298                         (or (y-or-n-p "Sign the message? ")
299                             'not))))
300              'never)))
301     (mm-with-unibyte-current-buffer-mule4
302       (mc-encrypt-generic
303        (or (message-options-get 'message-recipients)
304            (message-options-set 'message-recipients
305                               (mc-cleanup-recipient-headers
306                                (read-string "Recipients: "))))
307        nil nil nil
308        (message-options-get 'message-sender))))
309   (goto-char (point-min))
310   (unless (looking-at "-----BEGIN PGP MESSAGE-----")
311     (error "Fail to encrypt the message"))
312   (let ((boundary
313          (funcall mml-boundary-function (incf mml-multipart-number))))
314     (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
315                     boundary))
316     (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
317     (insert (format "--%s\n" boundary))
318     (insert "Content-Type: application/pgp-encrypted\n\n")
319     (insert "Version: 1\n\n")
320     (insert (format "--%s\n" boundary))
321     (insert "Content-Type: application/octet-stream\n\n")
322     (goto-char (point-max))
323     (insert (format "--%s--\n" boundary))
324     (goto-char (point-max))))
325
326 ;;; gpg wrapper
327
328 (eval-and-compile
329   (autoload 'gpg-decrypt "gpg")
330   (autoload 'gpg-verify "gpg")
331   (autoload 'gpg-verify-cleartext "gpg")
332   (autoload 'gpg-sign-detached "gpg")
333   (autoload 'gpg-sign-encrypt "gpg")
334   (autoload 'gpg-passphrase-read "gpg"))
335
336 (defun mml2015-gpg-passphrase ()
337   (or (message-options-get 'gpg-passphrase)
338       (message-options-set 'gpg-passphrase (gpg-passphrase-read))))
339
340 (defun mml2015-gpg-decrypt-1 ()
341   (let ((cipher (current-buffer)) plain result)
342     (if (with-temp-buffer
343           (prog1
344               (gpg-decrypt cipher (setq plain (current-buffer))
345                            mml2015-result-buffer nil)
346             (mm-set-handle-multipart-parameter
347              mm-security-handle 'gnus-details
348              (with-current-buffer mml2015-result-buffer
349                (buffer-string)))
350             (set-buffer cipher)
351             (erase-buffer)
352             (insert-buffer plain)))
353         '(t)
354       ;; Some wrong with the return value, check plain text buffer.
355       (if (> (point-max) (point-min))
356           '(t)
357         nil))))
358
359 (defun mml2015-gpg-decrypt (handle ctl)
360   (let ((mml2015-decrypt-function 'mml2015-gpg-decrypt-1))
361     (mml2015-mailcrypt-decrypt handle ctl)))
362
363 (defun mml2015-gpg-clear-decrypt ()
364   (let (result)
365     (setq result (mml2015-gpg-decrypt-1))
366     (if (car result)
367         (mm-set-handle-multipart-parameter
368          mm-security-handle 'gnus-info "OK")
369       (mm-set-handle-multipart-parameter
370        mm-security-handle 'gnus-info "Failed"))))
371
372 (defun mml2015-gpg-extract-from ()
373   (goto-char (point-min))
374   (if (re-search-forward "^gpg: Good signature from \"\\(.*\\)\"$" nil t)
375       (match-string 1)
376     "From unknown user"))
377
378 (defun mml2015-gpg-verify (handle ctl)
379   (catch 'error
380     (let (part message signature)
381       (unless (setq part (mm-find-raw-part-by-type
382                           ctl (or (mm-handle-multipart-ctl-parameter
383                                    ctl 'protocol)
384                                   "application/pgp-signature")
385                           t))
386         (mm-set-handle-multipart-parameter
387          mm-security-handle 'gnus-info "Corrupted")
388         (throw 'error handle))
389       (with-temp-buffer
390         (setq message (current-buffer))
391         (insert part)
392         (with-temp-buffer
393           (setq signature (current-buffer))
394           (unless (setq part (mm-find-part-by-type
395                               (cdr handle) "application/pgp-signature" nil t))
396             (mm-set-handle-multipart-parameter
397              mm-security-handle 'gnus-info "Corrupted")
398             (throw 'error handle))
399           (mm-insert-part part)
400           (unless (condition-case err
401                       (prog1
402                           (gpg-verify message signature mml2015-result-buffer)
403                         (mm-set-handle-multipart-parameter
404                          mm-security-handle 'gnus-details
405                          (with-current-buffer mml2015-result-buffer
406                            (buffer-string))))
407                     (error
408                      (mm-set-handle-multipart-parameter
409                       mm-security-handle 'gnus-details (mml2015-format-error err))
410                      nil)
411                     (quit
412                      (mm-set-handle-multipart-parameter
413                       mm-security-handle 'gnus-details "Quit.")
414                      nil))
415             (mm-set-handle-multipart-parameter
416              mm-security-handle 'gnus-info "Failed")
417             (throw 'error handle)))
418         (mm-set-handle-multipart-parameter
419          mm-security-handle 'gnus-info
420          (with-current-buffer mml2015-result-buffer
421            (mml2015-gpg-extract-from))))
422       handle)))
423
424 (defun mml2015-gpg-clear-verify ()
425   (if (condition-case err
426           (prog1
427               (gpg-verify-cleartext (current-buffer) mml2015-result-buffer)
428             (mm-set-handle-multipart-parameter
429              mm-security-handle 'gnus-details
430              (with-current-buffer mml2015-result-buffer
431                (buffer-string))))
432         (error
433          (mm-set-handle-multipart-parameter
434           mm-security-handle 'gnus-details (mml2015-format-error err))
435          nil)
436         (quit
437          (mm-set-handle-multipart-parameter
438           mm-security-handle 'gnus-details "Quit.")
439          nil))
440       (mm-set-handle-multipart-parameter
441        mm-security-handle 'gnus-info
442        (with-current-buffer mml2015-result-buffer
443          (mml2015-gpg-extract-from)))
444     (mm-set-handle-multipart-parameter
445      mm-security-handle 'gnus-info "Failed")))
446
447 (defun mml2015-gpg-sign (cont)
448   (let ((boundary
449          (funcall mml-boundary-function (incf mml-multipart-number)))
450         (text (current-buffer)) signature)
451     (goto-char (point-max))
452     (unless (bolp)
453       (insert "\n"))
454     (with-temp-buffer
455       (unless (gpg-sign-detached text (setq signature (current-buffer))
456                                  mml2015-result-buffer
457                                  nil
458                                  (message-options-get 'message-sender)
459                                  t t) ; armor & textmode
460         (unless (> (point-max) (point-min))
461           (pop-to-buffer mml2015-result-buffer)
462           (error "Sign error")))
463       (goto-char (point-min))
464       (while (re-search-forward "\r+$" nil t)
465         (replace-match "" t t))
466       (set-buffer text)
467       (goto-char (point-min))
468       (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
469                       boundary))
470       ;;; FIXME: what is the micalg?
471       (insert "\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"\n")
472       (insert (format "\n--%s\n" boundary))
473       (goto-char (point-max))
474       (insert (format "\n--%s\n" boundary))
475       (insert "Content-Type: application/pgp-signature\n\n")
476       (insert-buffer signature)
477       (goto-char (point-max))
478       (insert (format "--%s--\n" boundary))
479       (goto-char (point-max)))))
480
481 (defun mml2015-gpg-encrypt (cont)
482   (let ((boundary
483          (funcall mml-boundary-function (incf mml-multipart-number)))
484         (text (current-buffer))
485         cipher)
486     (mm-with-unibyte-current-buffer-mule4
487       (with-temp-buffer
488         (unless (gpg-sign-encrypt
489                  text (setq cipher (current-buffer))
490                  mml2015-result-buffer
491                  (split-string
492                   (or
493                    (message-options-get 'message-recipients)
494                    (message-options-set 'message-recipients
495                                         (read-string "Recipients: ")))
496                   "[ \f\t\n\r\v,]+")
497                  nil
498                  (message-options-get 'message-sender)
499                  t t) ; armor & textmode
500           (unless (> (point-max) (point-min))
501             (pop-to-buffer mml2015-result-buffer)
502             (error "Encrypt error")))
503         (goto-char (point-min))
504         (while (re-search-forward "\r+$" nil t)
505           (replace-match "" t t))
506         (set-buffer text)
507         (delete-region (point-min) (point-max))
508         (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
509                         boundary))
510         (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
511         (insert (format "--%s\n" boundary))
512         (insert "Content-Type: application/pgp-encrypted\n\n")
513         (insert "Version: 1\n\n")
514         (insert (format "--%s\n" boundary))
515         (insert "Content-Type: application/octet-stream\n\n")
516         (insert-buffer cipher)
517         (goto-char (point-max))
518         (insert (format "--%s--\n" boundary))
519         (goto-char (point-max))))))
520
521 ;;; General wrapper
522
523 (defun mml2015-clean-buffer ()
524   (if (gnus-buffer-live-p mml2015-result-buffer)
525       (with-current-buffer mml2015-result-buffer
526         (erase-buffer)
527         t)
528     (setq mml2015-result-buffer
529           (gnus-get-buffer-create "*MML2015 Result*"))
530     nil))
531
532 (defsubst mml2015-clear-decrypt-function ()
533   (nth 6 (assq mml2015-use mml2015-function-alist)))
534
535 (defsubst mml2015-clear-verify-function ()
536   (nth 5 (assq mml2015-use mml2015-function-alist)))
537
538 ;;;###autoload
539 (defun mml2015-decrypt (handle ctl)
540   (mml2015-clean-buffer)
541   (let ((func (nth 4 (assq mml2015-use mml2015-function-alist))))
542     (if func
543         (funcall func handle ctl)
544       handle)))
545
546 ;;;###autoload
547 (defun mml2015-decrypt-test (handle ctl)
548   mml2015-use)
549
550 ;;;###autoload
551 (defun mml2015-verify (handle ctl)
552   (mml2015-clean-buffer)
553   (let ((func (nth 3 (assq mml2015-use mml2015-function-alist))))
554     (if func
555         (funcall func handle ctl)
556       handle)))
557
558 ;;;###autoload
559 (defun mml2015-verify-test (handle ctl)
560   mml2015-use)
561
562 ;;;###autoload
563 (defun mml2015-encrypt (cont)
564   (mml2015-clean-buffer)
565   (let ((func (nth 2 (assq mml2015-use mml2015-function-alist))))
566     (if func
567         (funcall func cont)
568       (error "Cannot find encrypt function"))))
569
570 ;;;###autoload
571 (defun mml2015-sign (cont)
572   (mml2015-clean-buffer)
573   (let ((func (nth 1 (assq mml2015-use mml2015-function-alist))))
574     (if func
575         (funcall func cont)
576       (error "Cannot find sign function"))))
577
578 ;;;###autoload
579 (defun mml2015-self-encrypt ()
580   (mml2015-encrypt nil))
581
582 (provide 'mml2015)
583
584 ;;; mml2015.el ends here