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