468fbc6cf15b04e36ab83c1fc359a96fff3f0d33
[gnus] / lisp / pgg.el
1 ;;; pgg.el --- glue for the various PGP implementations.
2
3 ;; Copyright (C) 1999, 2000, 2002, 2003, 2004,
4 ;;   2005, 2006 Free Software Foundation, Inc.
5
6 ;; Author: Daiki Ueno <ueno@unixuser.org>
7 ;; Symmetric encryption added by: Sascha Wilde <wilde@sha-bang.de>
8 ;; Created: 1999/10/28
9 ;; Keywords: PGP
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
27
28 ;;; Commentary:
29
30 ;;; Code:
31
32 (require 'pgg-def)
33 (require 'pgg-parse)
34 (require 'password)
35
36 ;; Don't merge these two `eval-when-compile's.
37 (eval-when-compile
38   (require 'cl))
39
40 ;;; @ utility functions
41 ;;;
42
43 (defun pgg-invoke (func scheme &rest args)
44   (progn
45     (require (intern (format "pgg-%s" scheme)))
46     (apply 'funcall (intern (format "pgg-%s-%s" scheme func)) args)))
47
48 (put 'pgg-save-coding-system 'lisp-indent-function 2)
49
50 (defmacro pgg-save-coding-system (start end &rest body)
51   `(if (interactive-p)
52        (let ((buffer (current-buffer)))
53          (with-temp-buffer
54            (let (buffer-undo-list)
55              (insert-buffer-substring buffer ,start ,end)
56              (encode-coding-region (point-min)(point-max)
57                                    buffer-file-coding-system)
58              (prog1 (save-excursion ,@body)
59                (push nil buffer-undo-list)
60                (ignore-errors (undo))))))
61      (save-restriction
62        (narrow-to-region ,start ,end)
63        ,@body)))
64
65 (defun pgg-temp-buffer-show-function (buffer)
66   (let ((window (or (get-buffer-window buffer 'visible)
67                     (split-window-vertically))))
68     (set-window-buffer window buffer)
69     (shrink-window-if-larger-than-buffer window)))
70
71 ;; XXX `pgg-display-output-buffer' is a horrible name for this function.
72 ;;     It should be something like `pgg-situate-output-or-display-error'.
73 (defun pgg-display-output-buffer (start end status)
74   "Situate en/decryption results or pop up an error buffer.
75
76 Text from START to END is replaced by contents of output buffer if STATUS
77 is true, or else the output buffer is displayed."
78   (if status
79       (pgg-situate-output start end)
80     (pgg-display-error-buffer)))
81
82 (defun pgg-situate-output (start end)
83   "Place en/decryption result in place of current text from START to END."
84   (delete-region start end)
85   (insert-buffer-substring pgg-output-buffer)
86   (decode-coding-region start (point) buffer-file-coding-system))
87
88 (defun pgg-display-error-buffer ()
89   "Pop up an error buffer indicating the reason for an en/decryption failure."
90   (let ((temp-buffer-show-function
91          (function pgg-temp-buffer-show-function)))
92     (with-output-to-temp-buffer pgg-echo-buffer
93       (set-buffer standard-output)
94       (insert-buffer-substring pgg-errors-buffer))))
95
96 (defun pgg-read-passphrase (prompt &optional key notruncate)
97   "Using PROMPT, obtain passphrase for KEY from cache or user.
98
99 Truncate the key to 8 trailing characters unless NOTRUNCATE is true
100 \(default false).
101
102 Custom variables `pgg-cache-passphrase' and `pgg-passphrase-cache-expiry'
103 regulate cache behavior."
104   (password-read prompt (if notruncate
105                             key
106                           (pgg-truncate-key-identifier key))))
107
108 (defun pgg-add-passphrase-to-cache (key passphrase &optional notruncate)
109   "Associate KEY with PASSPHRASE in time-limited passphrase cache.
110
111 Truncate the key to 8 trailing characters unless NOTRUNCATE is true
112 \(default false).
113
114 Custom variables `pgg-cache-passphrase' and `pgg-passphrase-cache-expiry'
115 regulate cache behavior."
116   (let ((password-cache-expiry pgg-passphrase-cache-expiry))
117     (password-cache-add (if notruncate
118                             key
119                           (pgg-truncate-key-identifier key))
120                         passphrase)))
121
122 (defun pgg-remove-passphrase-from-cache (key &optional notruncate)
123   "Omit passphrase associated with KEY in time-limited passphrase cache.
124
125 Truncate the key to 8 trailing characters unless NOTRUNCATE is true
126 \(default false).
127
128 This is a no-op if there is not entry for KEY (eg, it's already expired.
129
130 The memory for the passphrase is filled with underscores to clear any
131 references to it.
132
133 Custom variables `pgg-cache-passphrase' and `pgg-passphrase-cache-expiry'
134 regulate cache behavior."
135   (password-cache-remove (if notruncate
136                              key
137                            (pgg-truncate-key-identifier key))))
138
139 (defmacro pgg-convert-lbt-region (start end lbt)
140   `(let ((pgg-conversion-end (set-marker (make-marker) ,end)))
141      (goto-char ,start)
142      (case ,lbt
143        (CRLF
144         (while (progn
145                  (end-of-line)
146                  (> (marker-position pgg-conversion-end) (point)))
147           (insert "\r")
148           (forward-line 1)))
149        (LF
150         (while (re-search-forward "\r$" pgg-conversion-end t)
151           (replace-match ""))))))
152
153 (put 'pgg-as-lbt 'lisp-indent-function 3)
154
155 (defmacro pgg-as-lbt (start end lbt &rest body)
156   `(let ((inhibit-read-only t)
157          buffer-read-only
158          buffer-undo-list)
159      (pgg-convert-lbt-region ,start ,end ,lbt)
160      (let ((,end (point)))
161        ,@body)
162      (push nil buffer-undo-list)
163      (ignore-errors (undo))))
164
165 (put 'pgg-process-when-success 'lisp-indent-function 0)
166
167 (defmacro pgg-process-when-success (&rest body)
168   `(with-current-buffer pgg-output-buffer
169      (if (zerop (buffer-size)) nil ,@body t)))
170
171 (defalias 'pgg-make-temp-file
172   (if (fboundp 'make-temp-file)
173       'make-temp-file
174     (lambda (prefix &optional dir-flag)
175       (let ((file (expand-file-name
176                    (make-temp-name prefix)
177                    (if (fboundp 'temp-directory)
178                        (temp-directory)
179                      temporary-file-directory))))
180         (if dir-flag
181             (make-directory file))
182         file))))
183
184 ;;; @ interface functions
185 ;;;
186
187 ;;;###autoload
188 (defun pgg-encrypt-region (start end rcpts &optional sign passphrase)
189   "Encrypt the current region between START and END for RCPTS.
190
191 If optional argument SIGN is non-nil, do a combined sign and encrypt.
192
193 If optional PASSPHRASE is not specified, it will be obtained from the
194 passphrase cache or user."
195   (interactive
196    (list (region-beginning)(region-end)
197          (split-string (read-string "Recipients: ") "[ \t,]+")))
198   (let ((status
199          (pgg-save-coding-system start end
200            (pgg-invoke "encrypt-region" (or pgg-scheme pgg-default-scheme)
201                        (point-min) (point-max) rcpts sign passphrase))))
202     (when (interactive-p)
203       (pgg-display-output-buffer start end status))
204     status))
205
206 ;;;###autoload
207 (defun pgg-encrypt-symmetric-region (start end &optional passphrase)
208   "Encrypt the current region between START and END symmetric with passphrase.
209
210 If optional PASSPHRASE is not specified, it will be obtained from the
211 cache or user."
212   (interactive "r")
213   (let ((status
214          (pgg-save-coding-system start end
215            (pgg-invoke "encrypt-symmetric-region"
216                        (or pgg-scheme pgg-default-scheme)
217                        (point-min) (point-max) passphrase))))
218     (when (interactive-p)
219       (pgg-display-output-buffer start end status))
220     status))
221
222 ;;;###autoload
223 (defun pgg-encrypt-symmetric (&optional start end passphrase)
224   "Encrypt the current buffer using a symmetric, rather than key-pair, cipher.
225
226 If optional arguments START and END are specified, only encrypt within
227 the region.
228
229 If optional PASSPHRASE is not specified, it will be obtained from the
230 passphrase cache or user."
231   (interactive)
232   (let* ((start (or start (point-min)))
233          (end (or end (point-max)))
234          (status (pgg-encrypt-symmetric-region start end passphrase)))
235     (when (interactive-p)
236       (pgg-display-output-buffer start end status))
237     status))
238
239 ;;;###autoload
240 (defun pgg-encrypt (rcpts &optional sign start end passphrase)
241   "Encrypt the current buffer for RCPTS.
242
243 If optional argument SIGN is non-nil, do a combined sign and encrypt.
244
245 If optional arguments START and END are specified, only encrypt within
246 the region.
247
248 If optional PASSPHRASE is not specified, it will be obtained from the
249 passphrase cache or user."
250   (interactive (list (split-string (read-string "Recipients: ") "[ \t,]+")))
251   (let* ((start (or start (point-min)))
252          (end (or end (point-max)))
253          (status (pgg-encrypt-region start end rcpts sign passphrase)))
254     (when (interactive-p)
255       (pgg-display-output-buffer start end status))
256     status))
257
258 ;;;###autoload
259 (defun pgg-decrypt-region (start end &optional passphrase)
260   "Decrypt the current region between START and END.
261
262 If optional PASSPHRASE is not specified, it will be obtained from the
263 passphrase cache or user."
264   (interactive "r")
265   (let* ((buf (current-buffer))
266          (status
267           (pgg-save-coding-system start end
268             (pgg-invoke "decrypt-region" (or pgg-scheme pgg-default-scheme)
269                         (point-min) (point-max) passphrase))))
270     (when (interactive-p)
271       (pgg-display-output-buffer start end status))
272     status))
273
274 ;;;###autoload
275 (defun pgg-decrypt (&optional start end passphrase)
276   "Decrypt the current buffer.
277
278 If optional arguments START and END are specified, only decrypt within
279 the region.
280
281 If optional PASSPHRASE is not specified, it will be obtained from the
282 passphrase cache or user."
283   (interactive "")
284   (let* ((start (or start (point-min)))
285          (end (or end (point-max)))
286          (status (pgg-decrypt-region start end passphrase)))
287     (when (interactive-p)
288       (pgg-display-output-buffer start end status))
289     status))
290
291 ;;;###autoload
292 (defun pgg-sign-region (start end &optional cleartext passphrase)
293   "Make the signature from text between START and END.
294
295 If the optional 3rd argument CLEARTEXT is non-nil, it does not create
296 a detached signature.
297
298 If this function is called interactively, CLEARTEXT is enabled
299 and the the output is displayed.
300
301 If optional PASSPHRASE is not specified, it will be obtained from the
302 passphrase cache or user."
303   (interactive "r")
304   (let ((status (pgg-save-coding-system start end
305                   (pgg-invoke "sign-region" (or pgg-scheme pgg-default-scheme)
306                               (point-min) (point-max)
307                               (or (interactive-p) cleartext)
308                               passphrase))))
309     (when (interactive-p)
310       (pgg-display-output-buffer start end status))
311     status))
312
313 ;;;###autoload
314 (defun pgg-sign (&optional cleartext start end passphrase)
315   "Sign the current buffer.
316
317 If the optional argument CLEARTEXT is non-nil, it does not create a
318 detached signature.
319
320 If optional arguments START and END are specified, only sign data
321 within the region.
322
323 If this function is called interactively, CLEARTEXT is enabled
324 and the the output is displayed.
325
326 If optional PASSPHRASE is not specified, it will be obtained from the
327 passphrase cache or user."
328   (interactive "")
329   (let* ((start (or start (point-min)))
330          (end (or end (point-max)))
331          (status (pgg-sign-region start end
332                                   (or (interactive-p) cleartext)
333                                   passphrase)))
334     (when (interactive-p)
335       (pgg-display-output-buffer start end status))
336     status))
337
338 ;;;###autoload
339 (defun pgg-verify-region (start end &optional signature fetch)
340   "Verify the current region between START and END.
341 If the optional 3rd argument SIGNATURE is non-nil, it is treated as
342 the detached signature of the current region.
343
344 If the optional 4th argument FETCH is non-nil, we attempt to fetch the
345 signer's public key from `pgg-default-keyserver-address'."
346   (interactive "r")
347   (let* ((packet
348           (if (null signature) nil
349             (with-temp-buffer
350               (buffer-disable-undo)
351               (if (fboundp 'set-buffer-multibyte)
352                   (set-buffer-multibyte nil))
353               (insert-file-contents signature)
354               (cdr (assq 2 (pgg-decode-armor-region
355                             (point-min)(point-max)))))))
356          (key (cdr (assq 'key-identifier packet)))
357          status keyserver)
358     (and (stringp key)
359          pgg-query-keyserver
360          (setq key (concat "0x" (pgg-truncate-key-identifier key)))
361          (null (pgg-lookup-key key))
362          (or fetch (interactive-p))
363          (y-or-n-p (format "Key %s not found; attempt to fetch? " key))
364          (setq keyserver
365                (or (cdr (assq 'preferred-key-server packet))
366                    pgg-default-keyserver-address))
367          (pgg-fetch-key keyserver key))
368     (setq status
369           (pgg-save-coding-system start end
370             (pgg-invoke "verify-region" (or pgg-scheme pgg-default-scheme)
371                         (point-min) (point-max) signature)))
372     (when (interactive-p)
373       (let ((temp-buffer-show-function
374              (function pgg-temp-buffer-show-function)))
375         (with-output-to-temp-buffer pgg-echo-buffer
376           (set-buffer standard-output)
377           (insert-buffer-substring (if status pgg-output-buffer
378                                      pgg-errors-buffer)))))
379     status))
380
381 ;;;###autoload
382 (defun pgg-verify (&optional signature fetch start end)
383   "Verify the current buffer.
384 If the optional argument SIGNATURE is non-nil, it is treated as
385 the detached signature of the current region.
386 If the optional argument FETCH is non-nil, we attempt to fetch the
387 signer's public key from `pgg-default-keyserver-address'.
388 If optional arguments START and END are specified, only verify data
389 within the region."
390   (interactive "")
391   (let* ((start (or start (point-min)))
392          (end (or end (point-max)))
393          (status (pgg-verify-region start end signature fetch)))
394     (when (interactive-p)
395       (let ((temp-buffer-show-function
396              (function pgg-temp-buffer-show-function)))
397         (with-output-to-temp-buffer pgg-echo-buffer
398           (set-buffer standard-output)
399           (insert-buffer-substring (if status pgg-output-buffer
400                                      pgg-errors-buffer)))))
401     status))
402
403 ;;;###autoload
404 (defun pgg-insert-key ()
405   "Insert the ASCII armored public key."
406   (interactive)
407   (pgg-invoke "insert-key" (or pgg-scheme pgg-default-scheme)))
408
409 ;;;###autoload
410 (defun pgg-snarf-keys-region (start end)
411   "Import public keys in the current region between START and END."
412   (interactive "r")
413   (pgg-save-coding-system start end
414     (pgg-invoke "snarf-keys-region" (or pgg-scheme pgg-default-scheme)
415                 start end)))
416
417 ;;;###autoload
418 (defun pgg-snarf-keys ()
419   "Import public keys in the current buffer."
420   (interactive "")
421   (pgg-snarf-keys-region (point-min) (point-max)))
422
423 (defun pgg-lookup-key (string &optional type)
424   (pgg-invoke "lookup-key" (or pgg-scheme pgg-default-scheme) string type))
425
426 (defvar pgg-insert-url-function  (function pgg-insert-url-with-w3))
427
428 (defun pgg-insert-url-with-w3 (url)
429   (ignore-errors
430     (require 'url)
431     (let (buffer-file-name)
432       (url-insert-file-contents url))))
433
434 (defvar pgg-insert-url-extra-arguments nil)
435 (defvar pgg-insert-url-program nil)
436
437 (defun pgg-insert-url-with-program (url)
438   (let ((args (copy-sequence pgg-insert-url-extra-arguments))
439         process)
440     (insert
441      (with-temp-buffer
442        (setq process
443              (apply #'start-process " *PGG url*" (current-buffer)
444                     pgg-insert-url-program (nconc args (list url))))
445        (set-process-sentinel process #'ignore)
446        (while (eq 'run (process-status process))
447          (accept-process-output process 5))
448        (delete-process process)
449        (if (and process (eq 'run (process-status process)))
450            (interrupt-process process))
451        (buffer-string)))))
452
453 (defun pgg-fetch-key (keyserver key)
454   "Attempt to fetch a KEY from KEYSERVER for addition to PGP or GnuPG keyring."
455   (with-current-buffer (get-buffer-create pgg-output-buffer)
456     (buffer-disable-undo)
457     (erase-buffer)
458     (let ((proto (if (string-match "^[a-zA-Z\\+\\.\\\\-]+:" keyserver)
459                      (substring keyserver 0 (1- (match-end 0))))))
460       (save-excursion
461         (funcall pgg-insert-url-function
462                  (if proto keyserver
463                    (format "http://%s:11371/pks/lookup?op=get&search=%s"
464                            keyserver key))))
465       (when (re-search-forward "^-+BEGIN" nil 'last)
466         (delete-region (point-min) (match-beginning 0))
467         (when (re-search-forward "^-+END" nil t)
468           (delete-region (progn (end-of-line) (point))
469                          (point-max)))
470         (insert "\n")
471         (with-temp-buffer
472           (insert-buffer-substring pgg-output-buffer)
473           (pgg-snarf-keys-region (point-min)(point-max)))))))
474
475
476 (provide 'pgg)
477
478 ;;; arch-tag: 9cc705dd-1e6a-4c90-8dce-c3561f9a2cf4
479 ;;; pgg.el ends here