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