Remove non-free old and crusty clearcase pkg
[packages] / xemacs-packages / gnus / lisp / gpg-ring.el
1 ;;; gpg-ring.el --- Major mode for editing GnuPG key rings.
2
3 ;; Copyright (C) 2000 RUS-CERT, University Of Stuttgart
4
5 ;; Author: Florian Weimer <Florian.Weimer@RUS.Uni-Stuttgart.DE>
6 ;; Maintainer: Florian Weimer <Florian.Weimer@RUS.Uni-Stuttgart.DE>
7 ;; Keywords: crypto
8 ;; Created: 2000-04-28
9
10 ;; This file is NOT (yet?) part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 3, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
26
27
28 \f
29 ;;; Code:
30
31 (require 'gpg)
32 (eval-when-compile (require 'cl))
33
34 ;;;; Customization:
35
36 ;;; Customization: Groups:
37
38 (defgroup gpg-ring nil
39   "GNU Privacy Guard user interface."
40   :tag "GnuPG user interface"
41   :group 'gpg)
42
43 ;;; Customization: Variables:
44
45 (defface gpg-ring-key-invalid-face 
46   '((((class color))
47      (:foreground "yellow" :background "red"))
48     (t (:bold t :italic t :underline t)))
49   "Face for strings indicating key invalidity."
50   :group 'gpg-ring)
51
52 (defface gpg-ring-uncertain-validity-face
53   '((((class color)) (:foreground "red"))
54     (t (:bold t)))
55   "Face for strings indicating uncertain validity."
56   :group 'gpg-ring)
57
58 (defface gpg-ring-full-validity-face
59   '((((class color)) (:foreground "ForestGreen" :bold t))
60     (t (:bold t)))
61   "Face for strings indicating key invalidity."
62   :group 'gpg-ring)
63
64 (defvar gpg-ring-mode-hook nil
65   "Normal hook run when entering GnuPG ring mode.")
66
67 ;;; Constants
68
69 (defconst gpg-ring-algo-alist
70   '((rsa . "RSA")
71     (rsa-encrypt-only . "RSA-E")
72     (rsa-sign-only . "RSA-S")
73     (elgamal-encrypt-only . "ELG-E")
74     (dsa . "DSA")
75     (elgamal . "ELG-E"))
76   "Alist mapping algorithm IDs to algorithm abbreviations.")
77     
78 (defconst gpg-ring-trust-alist
79   '((not-known       "???" gpg-ring-uncertain-validity-face)
80     (disabled        "DIS" gpg-ring-key-invalid-face)
81     (revoked         "REV" gpg-ring-key-invalid-face)
82     (expired         "EXP" gpg-ring-key-invalid-face)
83     (trust-undefined "QES" gpg-ring-uncertain-validity-face)
84     (trust-none      "NON" gpg-ring-uncertain-validity-face)
85     (trust-marginal  "MAR")
86     (trust-full      "FUL" gpg-ring-full-validity-face)
87     (trust-ultimate  "ULT" gpg-ring-full-validity-face))
88   "Alist mapping trust IDs to trust abbrevs and faces.")
89
90 (defvar gpg-ring-mode-map
91   (let ((map (make-keymap)))
92     (suppress-keymap map t)
93     map)
94   "Keymap for `gpg-ring-mode'.")
95
96 (define-key gpg-ring-mode-map "0" 'delete-window)
97 (define-key gpg-ring-mode-map "1" 'delete-other-windows)
98 (define-key gpg-ring-mode-map "M" 'gpg-ring-mark-process-all)
99 (define-key gpg-ring-mode-map "U" 'gpg-ring-unmark-all)
100 (define-key gpg-ring-mode-map "a" 'gpg-ring-toggle-show-unusable)
101 (define-key gpg-ring-mode-map "d" 'gpg-ring-mark-delete)
102 (define-key gpg-ring-mode-map "f" 'gpg-ring-update-key)
103 (define-key gpg-ring-mode-map "g" 'gpg-ring-update)
104 (define-key gpg-ring-mode-map "i" 'gpg-ring-show-key)
105 (define-key gpg-ring-mode-map "l" 'gpg-ring-toggle-show-all-ids)
106 (define-key gpg-ring-mode-map "m" 'gpg-ring-mark-process)
107 (define-key gpg-ring-mode-map "n" 'gpg-ring-next-record)
108 (define-key gpg-ring-mode-map "p" 'gpg-ring-previous-record)
109 (define-key gpg-ring-mode-map "q" 'gpg-ring-quit)
110 (define-key gpg-ring-mode-map "u" 'gpg-ring-unmark)
111 (define-key gpg-ring-mode-map "x" 'gpg-ring-extract-keys)
112 (define-key gpg-ring-mode-map "X" 'gpg-ring-extract-keys-to-kill)
113
114 (define-key gpg-ring-mode-map "\C-c\C-c" 'gpg-ring-action)
115
116 ;;; Internal functions:
117
118 (defvar gpg-ring-key-list
119   nil
120   "List of keys in the key list buffer.")
121 (make-variable-buffer-local 'gpg-ring-key-list)
122
123 (defvar gpg-ring-update-funcs
124   nil
125   "List of functions called to obtain the key list.")
126 (make-variable-buffer-local 'gpg-ring-update-funcs)
127
128 (defvar gpg-ring-show-unusable
129   nil
130   "If t, show expired, revoked and disabled keys, too.")
131 (make-variable-buffer-local 'gpg-ring-show-unusable)
132
133 (defvar gpg-ring-show-all-ids
134   nil
135   "If t, show all user IDs.  If nil, show only the primary user ID.")
136 (make-variable-buffer-local 'gpg-ring-show-all-ids)
137
138 (defvar gpg-ring-marks-alist
139   nil
140   "Alist of (UNIQUE-ID MARK KEY).
141 UNIQUE-ID is a unique key ID from GnuPG.  MARK is either `?D'
142 (marked for deletion), or `?*' (marked for processing).")
143 (make-variable-buffer-local 'gpg-ring-marks-alist)
144
145 (defvar gpg-ring-action
146   nil
147   "Function to call when `gpg-ring-action' is invoked.
148 A list of the keys which are marked for processing is passed as argument.")
149 (make-variable-buffer-local 'gpg-ring-action)
150
151 (defun gpg-ring-mode ()
152   "Mode for editing GnuPG key rings.
153 \\{gpg-ring-mode-map}
154 Turning on gpg-ring-mode runs `gpg-ring-mode-hook'."
155   (interactive)
156   (kill-all-local-variables)
157   (buffer-disable-undo)
158   (setq truncate-lines t)
159   (setq buffer-read-only t)
160   (use-local-map gpg-ring-mode-map)
161   (setq mode-name "Key Ring")
162   (setq major-mode 'gpg-ring-mode)
163   (run-hooks 'gpg-ring-mode-hook))
164
165
166 (defmacro gpg-ring-record-start (&optional pos)
167   "Return buffer position of start of record containing POS."
168   `(get-text-property (or ,pos (point)) 'gpg-record-start))
169                                          
170 (defun gpg-ring-current-key (&optional pos)
171   "Return GnuPG key at POS, or at point if ommitted."
172   (or (get-text-property (or pos (point)) 'gpg-key)
173       (error "No record on current line")))
174
175 (defun gpg-ring-goto-record (pos)
176   "Go to record starting at POS.
177 Position point after the marks at the beginning of a record."
178   (goto-char pos)
179   (forward-char 2))
180
181 (defun gpg-ring-next-record ()
182   "Advances point to the start of the next record."
183   (interactive)
184   (let ((start (next-single-property-change 
185                 (point) 'gpg-record-start nil (point-max))))
186     ;; Don't advance to the last line of the buffer.
187     (when (/= start (point-max))
188         (gpg-ring-goto-record start))))
189
190 (defun gpg-ring-previous-record ()
191   "Advances point to the start of the previous record."
192   (interactive)
193   ;; The last line of the buffer doesn't contain a record.
194   (let ((start (gpg-ring-record-start)))
195     (if start
196         (gpg-ring-goto-record (previous-single-property-change 
197                                     start 'gpg-record-start nil (point-min)))
198       (gpg-ring-goto-record
199        (gpg-ring-record-start (1- (point-max)))))))
200       
201 (defun gpg-ring-set-mark (&optional pos mark)
202   "Set MARK on record at POS, or at point if POS is omitted.
203 If MARK is omitted, clear it."
204   (save-excursion
205     (let* ((start (gpg-ring-record-start pos))
206            (key (gpg-ring-current-key start))
207            (id (gpg-key-unique-id key))
208            (entry (assoc id gpg-ring-marks-alist))
209            buffer-read-only)
210       (goto-char start)
211       ;; Replace the mark character.
212       (subst-char-in-region (point) (1+ (point)) (char-after) 
213                             (or mark ? ))
214       ;; Store the mark in alist.
215       (if entry
216           (setcdr entry (if mark (list mark key)))
217         (when mark
218           (push (list id mark key) gpg-ring-marks-alist))))))
219
220 (defun gpg-ring-marked-keys (&optional only-marked mark)
221   "Return list of key specs which have MARK.
222 If no marks are present and ONLY-MARKED is not nil, return singleton
223 list with key of the current record.  If MARK is omitted, `?*' is
224 used."
225   (let ((the-marker (or mark ?*))
226         (marks gpg-ring-marks-alist)
227         key-list)
228     (while marks
229       (let ((mark (pop marks)))
230         ;; If this entry has got the right mark ...
231         (when (equal (nth 1 mark) the-marker)
232           ;; ... rember the key spec.
233           (push (nth 2 mark) key-list))))
234     (or key-list (if (not only-marked) (list (gpg-ring-current-key))))))
235
236 (defun gpg-ring-mark-process ()
237   "Mark record at point for processing."
238   (interactive)
239   (gpg-ring-set-mark nil ?*)
240   (gpg-ring-next-record))
241
242 (defun gpg-ring-mark-delete ()
243   "Mark record at point for processing."
244   (interactive)
245   (gpg-ring-set-mark nil ?D)
246   (gpg-ring-next-record))
247
248 (defun gpg-ring-unmark ()
249   "Mark record at point for processing."
250   (interactive)
251   (gpg-ring-set-mark)
252   (gpg-ring-next-record))
253
254 (defun gpg-ring-mark-process-all ()
255   "Put process mark on all records."
256   (interactive)
257   (setq gpg-ring-marks-alist 
258         (mapcar (lambda (key)
259                   (list (gpg-key-unique-id key) ?* key))
260                 gpg-ring-key-list))
261   (gpg-ring-regenerate))
262
263 (defun gpg-ring-unmark-all ()
264   "Remove all record marks."
265   (interactive)
266   (setq gpg-ring-marks-alist nil)
267   (gpg-ring-regenerate))
268
269 (defun gpg-ring-toggle-show-unusable ()
270   "Toggle value if `gpg-ring-show-unusable'."
271   (interactive)
272   (setq gpg-ring-show-unusable (not gpg-ring-show-unusable))
273   (gpg-ring-regenerate))
274   
275 (defun gpg-ring-toggle-show-all-ids ()
276   "Toggle value of `gpg-ring-show-all-ids'."
277   (interactive)
278   (setq gpg-ring-show-all-ids (not gpg-ring-show-all-ids))
279   (gpg-ring-regenerate))
280
281 (defvar gpg-ring-output-buffer-name "*GnuPG Output*"
282   "Name buffer to which output from GnuPG is sent.")
283
284 (defmacro gpg-ring-with-output-buffer (&rest body)
285   "Erase GnuPG output buffer, evaluate BODY in it, and display it."
286   `(with-current-buffer (get-buffer-create gpg-ring-output-buffer-name)
287      (erase-buffer)
288      (setq truncate-lines t)
289      ,@body
290      (goto-char (point-min))
291      (display-buffer gpg-ring-output-buffer-name)))
292
293 (defun gpg-ring-quit ()
294   "Bury key list buffer and kill GnuPG output buffer."
295   (interactive)
296   (let ((output (get-buffer gpg-ring-output-buffer-name)))
297     (when output
298       (kill-buffer output)))
299   (when (eq 'gpg-ring-mode major-mode)
300     (bury-buffer)))
301
302 (defun gpg-ring-show-key ()
303   "Show information for current key."
304   (interactive)
305   (let ((keys (gpg-ring-marked-keys)))
306     (gpg-ring-with-output-buffer
307      (gpg-key-insert-information (gpg-key-unique-id-list keys)))))
308
309 (defun gpg-ring-extract-keys ()
310   "Export currently selected public keys in ASCII armor."
311   (interactive)
312   (let ((keys (gpg-ring-marked-keys)))
313     (gpg-ring-with-output-buffer
314      (gpg-key-insert-public-key (gpg-key-unique-id-list keys)))))
315
316 (defun gpg-ring-extract-keys-to-kill ()
317   "Export currently selected public keys in ASCII armor to kill ring."
318   (interactive)
319   (let ((keys (gpg-ring-marked-keys)))
320     (with-temp-buffer
321       (gpg-key-insert-public-key (gpg-key-unique-id-list keys))
322       (copy-region-as-kill (point-min) (point-max)))))
323
324 (defun gpg-ring-update-key ()
325   "Fetch key information from key server."
326   (interactive)
327   (let ((keys (gpg-ring-marked-keys)))
328     (gpg-ring-with-output-buffer
329      (gpg-key-retrieve (gpg-key-unique-id-list keys)))))
330
331 (defun gpg-ring-insert-key-stat (key)
332   (let* ((validity (gpg-key-validity key))
333          (validity-entry (assq validity gpg-ring-trust-alist))
334          (trust (gpg-key-trust key))
335          (trust-entry (assq trust gpg-ring-trust-alist)))
336     ;; Insert abbrev for key status.
337     (let ((start (point)))
338       (insert (nth 1 validity-entry))
339       ;; Change face if necessary.
340       (when (nth 2 validity-entry)
341         (add-text-properties start (point) 
342                              (list 'face (nth 2 validity-entry)))))
343     ;; Trust, key ID, length, algorithm, creation date.
344     (insert (format "/%s %-8s/%4d/%-5s created %s"
345                     (nth 1 trust-entry)
346                     (gpg-short-key-id key)
347                     (gpg-key-length key) 
348                     (cdr (assq (gpg-key-algorithm key) gpg-ring-algo-alist))
349                     (gpg-key-creation-date key)))
350     ;; Expire date.
351     (when (gpg-key-expire-date key)
352       (insert ", ")
353       (let ((start (point))
354             (expired (eq 'expired validity))
355             (notice (concat )))
356         (insert (if expired "EXPIRED" "expires")
357                 " " (gpg-key-expire-date key))
358         (when expired
359           (add-text-properties start (point) 
360                                '(face gpg-ring-key-invalid-face)))))))
361
362 (defun gpg-ring-insert-key (key &optional mark)
363   "Inserts description for KEY into current buffer before point."
364   (let ((start (point)))
365     (insert (if mark mark " ")
366             " " (gpg-key-primary-user-id key) "\n"
367             "    ")
368     (gpg-ring-insert-key-stat key)
369     (insert "\n")
370     (when gpg-ring-show-all-ids
371       (let ((uids (gpg-key-user-ids key)))
372         (while uids
373           (insert "     ID " (pop uids) "\n"))))
374     (add-text-properties start (point)
375                          (list 'gpg-record-start start
376                                'gpg-key key))))
377
378 (defun gpg-ring-regenerate ()
379   "Regenerate the key list buffer from stored data."
380   (interactive)
381   (let* ((key-list gpg-ring-key-list)
382          ;; Record position of point.
383          (old-record (if (eobp)         ; No record on last line.
384                          nil 
385                        (gpg-key-unique-id (gpg-ring-current-key))))
386          (old-pos (if old-record (- (point) (gpg-ring-record-start))))
387          found new-pos new-pos-offset buffer-read-only new-marks)
388     ;; Replace buffer contents with new data.
389     (erase-buffer)
390     (while key-list
391       (let* ((key (pop key-list))
392              (id (gpg-key-unique-id key))
393              (mark (assoc id gpg-ring-marks-alist)))
394         (when (or gpg-ring-show-unusable
395                   (not (memq (gpg-key-validity key) 
396                              '(disabled revoked expired))))
397           ;; Check if point was in this record.
398           (when (and old-record 
399                      (string-equal old-record id))
400             (setq new-pos (point))
401             (setq new-pos-offset (+ new-pos old-pos)))
402           ;; Check if this record was marked.
403           (if (nth 1 mark)
404               (progn
405                 (push mark new-marks)
406                 (gpg-ring-insert-key key (nth 1 mark)))
407             (gpg-ring-insert-key key)))))
408     ;; Replace mark alist with the new one (which does not contain
409     ;; marks for records which vanished during this update).
410     (setq gpg-ring-marks-alist new-marks)
411     ;; Restore point.
412     (if (not old-record)
413         ;; We were at the end of the buffer before.
414         (goto-char (point-max))
415       (if new-pos
416           (if (and (< new-pos-offset (point-max))
417                    (equal old-record (gpg-key-unique-id 
418                                       (gpg-ring-current-key new-pos-offset))))
419               ;; Record is there, with offset.
420               (goto-char new-pos-offset)
421             ;; Record is there, but not offset.
422             (goto-char new-pos))
423         ;; Record is not there.
424         (goto-char (point-min))))))
425
426 (defun gpg-ring-update ()
427   "Update the key list buffer with new data."
428   (interactive)
429   (let ((funcs gpg-ring-update-funcs)
430         old)
431     ;; Merge the sorted lists obtained by calling elements of
432     ;; `gpg-ring-update-funcs'.
433     (while funcs 
434       (let ((additional (funcall (pop funcs)))
435             new)
436         (while (and additional old)
437           (if (gpg-key-lessp (car additional) (car old))
438               (push (pop additional) new)
439             (if (gpg-key-lessp (car old) (car additional))
440                 (push (pop old) new)
441               ;; Keys are perhaps equal.  Always Add old key.
442               (push (pop old) new)
443               ;; If new key is equal, drop it, otherwise add it as well.
444               (if (string-equal (gpg-key-unique-id (car old))
445                                 (gpg-key-unique-id (car additional)))
446                   (pop additional)
447                 (push (pop additional) new)))))
448         ;; Store new list as old one for next round.
449         (setq old (nconc (nreverse new) old additional))))
450     ;; Store the list in the buffer.
451     (setq gpg-ring-key-list old))
452   (gpg-ring-regenerate))
453
454 (defun gpg-ring-action ()
455   "Perform the action associated with this buffer."
456   (interactive)
457   (if gpg-ring-action
458       (funcall gpg-ring-action (gpg-ring-marked-keys))
459     (error "No action for this buffer specified")))
460      
461 ;;;###autoload
462 (defun gpg-ring-keys (&optional key-list-funcs action)
463   (interactive)
464   (let ((buffer (get-buffer-create "*GnuPG Key List*")))
465     (with-current-buffer buffer
466       (gpg-ring-mode)
467       (setq gpg-ring-action action)
468       (setq gpg-ring-update-funcs key-list-funcs key-list-funcs)
469       (gpg-ring-update)
470       (goto-char (point-min)))
471     (switch-to-buffer buffer)))
472
473 ;;;###autoload
474 (defun gpg-ring-public (key-spec)
475   "List public keys matching keys KEY-SPEC."
476   (interactive "sList public keys containing: ")
477   (gpg-ring-keys  `((lambda () (gpg-key-list-keys ,key-spec)))))
478
479 (provide 'gpg-ring)
480
481 ;;; arch-tag: a4c5b2d1-aff0-4ab6-96e9-267727226c2d
482 ;;; gpg-ring.el ends here