2000-11-06 13:51:37 ShengHuo ZHU <zsh@cs.rochester.edu>
[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 (progn (ignore-errors
32                                  (load "mc-toplev"))
33                                (and (fboundp 'mc-encrypt-generic)
34                                     (fboundp 'mc-sign-generic)
35                                     (fboundp 'mc-cleanup-recipient-headers)
36                                     'mailcrypt))
37                         (progn
38                           (ignore-errors
39                             (require 'gpg))
40                           (and (fboundp 'gpg-sign-detached)
41                                'gpg)))
42   "The package used for PGP/MIME.")
43
44 ;; Something is not RFC2015.
45 (defvar mml2015-function-alist
46   '((mailcrypt mml2015-mailcrypt-sign
47                mml2015-mailcrypt-encrypt
48                mml2015-mailcrypt-verify
49                mml2015-mailcrypt-decrypt
50                mml2015-mailcrypt-clear-verify
51                mml2015-mailcrypt-clear-decrypt
52                mml2015-mailcrypt-clear-snarf)
53     (gpg mml2015-gpg-sign
54          mml2015-gpg-encrypt
55          mml2015-gpg-verify
56          mml2015-gpg-decrypt
57          nil
58          mml2015-gpg-clear-decrypt
59          nil))
60   "Alist of PGP/MIME functions.")
61
62 (defvar mml2015-result-buffer nil)
63
64 ;;; mailcrypt wrapper
65
66 (eval-and-compile
67   (autoload 'mailcrypt-decrypt "mailcrypt")
68   (autoload 'mailcrypt-verify "mailcrypt")
69   (autoload 'mc-pgp-always-sign "mailcrypt")
70   (autoload 'mc-encrypt-generic "mc-toplev")
71   (autoload 'mc-cleanup-recipient-headers "mc-toplev")
72   (autoload 'mc-sign-generic "mc-toplev")
73   (autoload 'mc-snarf-keys "mc-toplev"))
74
75 (eval-when-compile
76   (defvar mc-default-scheme)
77   (defvar mc-schemes))
78
79 (defvar mml2015-decrypt-function 'mailcrypt-decrypt)
80 (defvar mml2015-verify-function 'mailcrypt-verify)
81 (defvar mml2015-snarf-function 'mc-snarf-keys)
82
83 (defun mml2015-mailcrypt-decrypt (handle ctl)
84   (let (child handles result)
85     (unless (setq child (mm-find-part-by-type 
86                          (cdr handle) 
87                          "application/octet-stream" nil t))
88       (error "Corrupted pgp-encrypted part."))
89     (with-temp-buffer
90       (mm-insert-part child)
91       (setq result (funcall mml2015-decrypt-function))
92       (unless (car result)
93         (error "Decrypting error."))
94       (setq handles (mm-dissect-buffer t)))
95     (mm-destroy-parts handle)
96     (if (listp (car handles))
97         handles
98       (list handles))))
99
100 (defun mml2015-mailcrypt-clear-decrypt ()
101   (let (result)
102     (setq result (funcall mml2015-decrypt-function))
103     (unless (car result)
104       (error "Decrypting error."))))
105
106 (defun mml2015-fix-micalg (alg)
107   (upcase
108    (if (and alg (string-match "^pgp-" alg))
109        (substring alg (match-end 0))
110      alg)))
111
112 (defun mml2015-mailcrypt-verify (handle ctl)
113   (let (part)
114     (unless (setq part (mm-find-raw-part-by-type 
115                          ctl (or (mail-content-type-get ctl 'protocol)
116                                  "application/pgp-signature")
117                          t))
118       (error "Corrupted pgp-signature part."))
119     (with-temp-buffer
120       (insert "-----BEGIN PGP SIGNED MESSAGE-----\n")
121       (insert (format "Hash: %s\n\n" 
122                       (or (mml2015-fix-micalg
123                            (mail-content-type-get ctl 'micalg))
124                           "SHA1")))
125       (insert part "\n")
126       (goto-char (point-max))
127       (unless (setq part (mm-find-part-by-type 
128                            (cdr handle) "application/pgp-signature" nil t))
129         (error "Corrupted pgp-signature part."))
130       (mm-insert-part part)
131       (unless (funcall mml2015-verify-function)
132         (error "Verify error.")))
133     handle))
134
135 (defun mml2015-mailcrypt-clear-verify ()
136   (unless (funcall mml2015-verify-function)
137     (error "Verify error.")))
138
139 (defun mml2015-mailcrypt-clear-snarf ()
140   (funcall mml2015-snarf-function))
141
142 (defun mml2015-mailcrypt-sign (cont)
143   (mc-sign-generic (message-options-get 'message-sender)
144                    nil nil nil nil)
145   (let ((boundary 
146          (funcall mml-boundary-function (incf mml-multipart-number)))
147         (scheme-alist (funcall (or mc-default-scheme 
148                                    (cdr (car mc-schemes)))))
149         hash)
150     (goto-char (point-min))
151     (unless (re-search-forward (cdr (assq 'signed-begin-line scheme-alist)))
152       (error "Cannot find signed begin line." ))
153     (goto-char (match-beginning 0))
154     (forward-line 1)
155     (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
156       (error "Cannot not find PGP hash." ))
157     (setq hash (match-string 1))
158     (unless (re-search-forward "^$" nil t)
159       (error "Cannot not find PGP message." ))
160     (forward-line 1)
161     (delete-region (point-min) (point))
162     (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
163                     boundary))
164     (insert (format "\tmicalg=pgp-%s; protocol=\"application/pgp-signature\"\n"
165                     (downcase hash)))
166     (insert (format "\n--%s\n" boundary))
167     (goto-char (point-max))
168     (unless (re-search-backward (cdr (assq 'signed-end-line scheme-alist)))
169       (error "Cannot find signature part." ))
170     (goto-char (match-beginning 0))
171     (unless (re-search-backward "^-+BEGIN" nil t)
172       (error "Cannot find signature part." ))
173     (goto-char (match-beginning 0))
174     (insert (format "--%s\n" boundary))
175     (insert "Content-Type: application/pgp-signature\n\n")
176     (goto-char (point-max))
177     (insert (format "--%s--\n" boundary))
178     (goto-char (point-max))))
179
180 (defun mml2015-mailcrypt-encrypt (cont)
181   (mc-encrypt-generic 
182    (or (message-options-get 'message-recipients)
183        (message-options-set 'message-recipients
184                             (mc-cleanup-recipient-headers 
185                              (read-string "Recipients: "))))
186    nil nil nil
187    (message-options-get 'message-sender)
188    (or mc-pgp-always-sign
189        (eq t
190            (or (message-options-get 'message-sign-encrypt)
191                (message-options-set 'message-sign-encrypt
192                                     (or (y-or-n-p "Sign the message? ")
193                                         'not))))))
194   (let ((boundary 
195          (funcall mml-boundary-function (incf mml-multipart-number))))
196     (goto-char (point-min))
197     (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
198                     boundary))
199     (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
200     (insert (format "--%s\n" boundary))
201     (insert "Content-Type: application/pgp-encrypted\n\n")
202     (insert "Version: 1\n\n")
203     (insert (format "--%s\n" boundary))
204     (insert "Content-Type: application/octet-stream\n\n")
205     (goto-char (point-max))
206     (insert (format "--%s--\n" boundary))
207     (goto-char (point-max))))
208
209 ;;; gpg wrapper
210
211 (eval-and-compile
212   (autoload 'gpg-decrypt "gpg")
213   (autoload 'gpg-verify "gpg")
214   (autoload 'gpg-sign-detached "gpg")
215   (autoload 'gpg-sign-encrypt "gpg")
216   (autoload 'gpg-passphrase-read "gpg"))
217
218 (defun mml2015-gpg-passphrase ()
219   (or (message-options-get 'gpg-passphrase)
220       (message-options-set 'gpg-passphrase (gpg-passphrase-read))))
221
222 (defun mml2015-gpg-decrypt-1 ()
223   (let ((cipher (current-buffer)) plain result)
224     (if (with-temp-buffer
225           (prog1
226               (gpg-decrypt cipher (setq plain (current-buffer))  
227                            mml2015-result-buffer nil)
228             (set-buffer cipher)
229             (erase-buffer)
230             (insert-buffer plain)))
231         '(t)
232       ;; Some wrong with the return value, check plain text buffer.
233       (if (> (point-max) (point-min))
234           '(t)
235         (pop-to-buffer mml2015-result-buffer)
236         nil))))
237
238 (defun mml2015-gpg-decrypt (handle ctl)
239   (let ((mml2015-decrypt-function 'mml2015-gpg-decrypt-1))
240     (mml2015-mailcrypt-decrypt handle ctl)))
241
242 (defun mml2015-gpg-clear-decrypt ()
243   (let (result)
244     (setq result (mml2015-gpg-decrypt-1))
245     (unless (car result)
246       (error "Decrypting error."))))
247
248 (defun mml2015-gpg-verify (handle ctl)
249   (let (part message signature)
250     (unless (setq part (mm-find-raw-part-by-type 
251                          ctl (or (mail-content-type-get ctl 'protocol)
252                                  "application/pgp-signature")
253                          t))
254       (error "Corrupted pgp-signature part."))
255     (with-temp-buffer
256       (setq message (current-buffer))
257       (insert part)
258       (with-temp-buffer
259         (setq signature (current-buffer))
260         (unless (setq part (mm-find-part-by-type 
261                             (cdr handle) "application/pgp-signature" nil t))
262           (error "Corrupted pgp-signature part."))
263         (mm-insert-part part)
264         (unless (gpg-verify message signature mml2015-result-buffer)
265           (pop-to-buffer mml2015-result-buffer)
266           (error "Verify error.")))))
267   handle)
268
269 (defun mml2015-gpg-sign (cont)
270   (let ((boundary 
271          (funcall mml-boundary-function (incf mml-multipart-number)))
272         (text (current-buffer)) signature)
273     (goto-char (point-max))
274     (unless (bolp)
275       (insert "\n"))
276     (with-temp-buffer
277       (unless (gpg-sign-detached text (setq signature (current-buffer))
278                                  mml2015-result-buffer 
279                                  nil
280                                  (message-options-get 'message-sender)
281                                  t t) ; armor & textmode
282         (unless (> (point-max) (point-min))
283           (pop-to-buffer mml2015-result-buffer)
284           (error "Sign error.")))
285       (set-buffer text)
286       (goto-char (point-min))
287       (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
288                       boundary))
289       ;;; FIXME: what is the micalg?
290       (insert "\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"\n")
291       (insert (format "\n--%s\n" boundary))
292       (goto-char (point-max))
293       (insert (format "\n--%s\n" boundary))
294       (insert "Content-Type: application/pgp-signature\n\n")
295       (insert-buffer signature)
296       (goto-char (point-max))
297       (insert (format "--%s--\n" boundary))
298       (goto-char (point-max)))))
299
300 (defun mml2015-gpg-encrypt (cont)
301   (let ((boundary 
302          (funcall mml-boundary-function (incf mml-multipart-number)))
303         (text (current-buffer))
304         cipher)
305     (with-temp-buffer
306       (unless (gpg-sign-encrypt 
307                text (setq cipher (current-buffer))
308                mml2015-result-buffer 
309                (split-string
310                 (or 
311                  (message-options-get 'message-recipients)
312                  (message-options-set 'message-recipients
313                                       (read-string "Recipients: ")))
314                 "[ \f\t\n\r\v,]+")
315                nil
316                (message-options-get 'message-sender)
317                t t) ; armor & textmode
318         (unless (> (point-max) (point-min))
319           (pop-to-buffer mml2015-result-buffer)
320           (error "Encrypt error.")))
321       (set-buffer text)
322       (delete-region (point-min) (point-max))
323       (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
324                       boundary))
325       (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
326       (insert (format "--%s\n" boundary))
327       (insert "Content-Type: application/pgp-encrypted\n\n")
328       (insert "Version: 1\n\n")
329       (insert (format "--%s\n" boundary))
330       (insert "Content-Type: application/octet-stream\n\n")
331       (insert-buffer cipher)
332       (goto-char (point-max))
333       (insert (format "--%s--\n" boundary))
334       (goto-char (point-max)))))
335
336 ;;; General wrapper
337
338 (defun mml2015-clean-buffer ()
339   (if (gnus-buffer-live-p mml2015-result-buffer)
340       (with-current-buffer mml2015-result-buffer
341         (erase-buffer)
342         t)
343     (setq mml2015-result-buffer
344           (gnus-get-buffer-create "*MML2015 Result*"))
345     nil))
346
347 (defsubst mml2015-clear-snarf-function ()
348   (nth 7 (assq mml2015-use mml2015-function-alist)))
349
350 (defsubst mml2015-clear-decrypt-function ()
351   (nth 6 (assq mml2015-use mml2015-function-alist)))
352
353 (defsubst mml2015-clear-verify-function ()
354   (nth 5 (assq mml2015-use mml2015-function-alist)))
355
356 ;;;###autoload
357 (defun mml2015-decrypt (handle ctl)
358   (mml2015-clean-buffer)
359   (let ((func (nth 4 (assq mml2015-use mml2015-function-alist))))
360     (if func
361         (funcall func handle ctl)
362       handle)))
363
364 ;;;###autoload
365 (defun mml2015-decrypt-test (handle ctl)
366   mml2015-use)
367
368 ;;;###autoload
369 (defun mml2015-verify (handle ctl)
370   (mml2015-clean-buffer)
371   (let ((func (nth 3 (assq mml2015-use mml2015-function-alist))))
372     (if func
373         (funcall func handle ctl)
374       handle)))
375
376 ;;;###autoload
377 (defun mml2015-verify-test (handle ctl)
378   mml2015-use)
379
380 ;;;###autoload
381 (defun mml2015-encrypt (cont)
382   (mml2015-clean-buffer)
383   (let ((func (nth 2 (assq mml2015-use mml2015-function-alist))))
384     (if func
385         (funcall func cont)
386       (error "Cannot find encrypt function."))))
387
388 ;;;###autoload
389 (defun mml2015-sign (cont)
390   (mml2015-clean-buffer)
391   (let ((func (nth 1 (assq mml2015-use mml2015-function-alist))))
392     (if func
393         (funcall func cont)
394       (error "Cannot find sign function."))))
395
396 (provide 'mml2015)
397
398 ;;; mml2015.el ends here