1 ;;; epa.el --- EasyPG Assistant, GUI of EasyPG
2 ;; Copyright (C) 2006 Daiki Ueno
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Keywords: PGP, GnuPG
7 ;; This file is part of EasyPG.
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
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., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
30 "EasyPG Assistant, GUI of EasyPG."
33 (defgroup epa-faces nil
37 (defvar epa-buffer nil)
39 (defface epa-validity-full-face
40 '((((class color) (background dark))
41 (:foreground "PaleTurquoise" :bold t))
44 "Face used for displaying the validity-full addon."
46 (defvar epa-validity-full-face 'epa-validity-full-face)
48 (defface epa-validity-disabled-face
49 '((((class color) (background dark))
50 (:foreground "PaleTurquoise" :italic t))
53 "Face used for displaying the disabled validity."
55 (defvar epa-validity-disabled-face 'epa-validity-disabled-face)
57 (defface epa-validity-unknown-face
60 "Face used for displaying the validity-unknown addon."
62 (defvar epa-validity-unknown-face 'epa-validity-unknown-face)
64 (defface epa-validity-marginal-face
66 (:italic t :inverse-video t)))
67 "Face used for displaying the validity-marginal addon."
69 (defvar epa-validity-marginal-face 'epa-validity-marginal-face)
71 (defface epa-user-id-face
74 (:foreground "lightyellow"))
77 (:foreground "blue4"))
80 "Face used for displaying the user-id addon."
82 (defvar epa-user-id-face 'epa-user-id-face)
84 (defcustom epa-validity-face-alist
85 '((?o . epa-validity-unknown-face)
86 (?i . epa-validity-disabled-face)
87 (?d . epa-validity-disabled-face)
88 (?r . epa-validity-disabled-face)
89 (?e . epa-validity-disabled-face)
90 (?- . epa-validity-unknown-face)
91 (?q . epa-validity-unknown-face)
92 (?n . epa-validity-disabled-face)
93 (?m . epa-validity-marginal-face)
94 (?f . epa-validity-full-face)
95 (?u . epa-validity-full-face)
96 (? . epa-validity-full-face))
97 "An alist mapping marks on epa-buffer to faces."
101 (defcustom epa-font-lock-keywords
102 '(("^[* ]\\([-oidreqnmfu ]\\)\\s-+\\(\\S-+\\)\\s-+\\(.*\\)"
103 (2 (cdr (assq (aref (match-string 1) 0)
104 epa-validity-face-alist)))
105 (3 epa-user-id-face)))
106 "Default expressions to addon in epa-mode."
107 :type '(repeat (list string))
111 (let ((keymap (make-sparse-keymap)))
112 (define-key keymap "m" 'epa-command-mark-key)
113 (define-key keymap "u" 'epa-command-unmark-key)
114 (define-key keymap "n" 'epa-command-next-line)
115 (define-key keymap "p" 'previous-line)
116 (define-key keymap "e" 'epa-command-encrypt-file)
117 (define-key keymap "s" 'epa-command-sign-file)
118 (define-key keymap " " 'scroll-up)
119 (define-key keymap [delete] 'scroll-down)
120 (define-key keymap "q" 'bury-buffer)
124 "Major mode for EasyPG Assistant.
125 All normal editing commands are turned off."
126 (kill-all-local-variables)
127 (buffer-disable-undo)
128 (setq major-mode 'epa-mode
132 (use-local-map epa-mode-map)
133 (make-local-variable 'font-lock-defaults)
134 (setq font-lock-defaults '(epa-font-lock-keywords t))
135 ;; In XEmacs, auto-initialization of font-lock is not effective
136 ;; if buffer-file-name is not set.
137 (font-lock-set-defaults)
138 (make-local-variable 'epa-marked-keys)
139 (run-hooks 'epa-mode-hook))
145 (setq epa-buffer (generate-new-buffer "*EPA*")))
146 (set-buffer epa-buffer)
148 (let ((inhibit-read-only t)
150 configuration pointer entry point)
152 (insert "EasyPG Assistant\n\n")
153 (setq configuration (epg-configuration))
154 (if (setq entry (assq 'version configuration))
155 (insert (format "GnuPG %s\n" (cdr entry))))
156 (if (setq entry (assq 'pubkey configuration))
157 (insert (format "Pubkey: %s\n"
162 epg-pubkey-algorithm-alist))
164 (format "(unknown: %d)" algorithm)))
166 (if (setq entry (assq 'cipher configuration))
167 (insert (format "Cipher: %s\n"
172 epg-cipher-algorithm-alist))
174 (format "(unknown: %d)" algorithm)))
176 (if (setq entry (assq 'digest configuration))
177 (insert (format "Hash: %s\n"
182 epg-digest-algorithm-alist))
184 (format "(unknown: %d)" algorithm)))
186 (if (setq entry (assq 'compress configuration))
187 (insert (format "Compression: %s\n"
192 epg-compress-algorithm-alist))
194 (format "(unknown: %d)" algorithm)))
196 (insert "\nSecret keys:\n\n")
197 (setq pointer (epg-list-keys nil t))
200 (setq entry (cdr (assq 'sec (car pointer))))
201 (setq key-id (cdr (assq 'key-id entry)))
202 (insert (format " %s %s\n"
204 (cdr (assq 'user-id (assq 'uid (car pointer))))))
205 (put-text-property point (point) 'epa-key-id key-id)
206 (put-text-property point (point) 'epa-key-secret t)
207 (setq pointer (cdr pointer)))
208 (insert "\nPublic keys:\n\n")
209 (setq pointer (epg-list-keys nil))
212 (setq entry (cdr (assq 'pub (car pointer))))
213 (setq key-id (cdr (assq 'key-id entry)))
214 (insert (format " %s %s %s\n"
215 (or (cdr (assq 'validity entry)) ? )
217 (cdr (assq 'user-id (assq 'uid (car pointer))))))
218 (put-text-property point (point) 'epa-key-id key-id)
219 (setq pointer (cdr pointer)))
220 (goto-char (point-min))
221 (pop-to-buffer (current-buffer))
222 (delete-other-windows)))
224 (defun epa-key-id (point)
225 (let ((key-id (get-text-property point 'epa-key-id)))
227 (setq point (next-single-property-change point 'epa-key-id))
230 (setq key-id (get-text-property point 'epa-key-id))))
233 (defun epa-command-mark-key (key-id)
234 "Mark a key on the current line."
237 (unless (eq major-mode 'epa-mode)
238 (error "Not in `epa-mode'"))
239 (list (epa-key-id (point)))))
240 (let ((point (point))
241 (inhibit-read-only t)
244 (not (equal (get-text-property point 'epa-key-id) key-id)))
245 (setq point (next-single-property-change point)))
247 (error "Key %s not found" key-id))
253 (put-text-property point (point) 'epa-key-id key-id)
256 (defun epa-command-unmark-key (key-id)
257 "Unmark a key on the current line."
260 (unless (eq major-mode 'epa-mode)
261 (error "Not in `epa-mode'"))
262 (list (epa-key-id (point)))))
263 (let ((point (point))
264 (inhibit-read-only t)
267 (not (equal (get-text-property point 'epa-key-id) key-id)))
268 (setq point (next-single-property-change point)))
270 (error "Key %s not found" key-id))
276 (put-text-property point (point) 'epa-key-id key-id)
279 (defun epa-command-next-line (count)
280 "Same as next-line except that if you are at the beginning of buffer,
281 you will be jumped to the first secret key."
283 (if (get-text-property (point) 'epa-key-id)
285 (let ((point (next-single-property-change (point) 'epa-key-id)))
287 (get-text-property point 'epa-key-id))
288 (goto-char point)))))
290 (defun epa-command-encrypt-file (plain cipher recipients sign)
291 "Encrypt a file PLAIN for RECIPIENTS."
294 (set-buffer epa-buffer)
295 (goto-char (point-min))
296 (let (plain recipients)
297 (while (re-search-forward "^\\*" nil t)
298 (unless (get-text-property (point) 'epa-key-secret)
299 (setq recipients (cons (get-text-property (point) 'epa-key-id)
301 (list (setq plain (expand-file-name (read-file-name "Plain file: ")))
303 (read-file-name (format "Cipher file (default %s.gpg) "
304 (file-name-nondirectory plain))
305 (file-name-directory plain)
306 (concat plain ".gpg")))
308 current-prefix-arg))))
309 (message "Encrypting %s..." (file-name-nondirectory plain))
310 (epg-encrypt-file (epg-make-context)
313 (expand-file-name cipher)
315 (message "Encrypting %s...done" (file-name-nondirectory plain)))
317 (defun epa-command-sign-file (plain signature detached signers)
321 (set-buffer epa-buffer)
322 (goto-char (point-min))
323 (let ((extension (if current-prefix-arg ".sig" ".gpg"))
325 (while (re-search-forward "^\\*" nil t)
326 (if (get-text-property (point) 'epa-key-secret)
327 (setq signers (cons (get-text-property (point) 'epa-key-id)
330 (list (setq plain (expand-file-name (read-file-name "Plain file: ")))
332 (read-file-name (format "Signature file (default %s%s) "
333 (file-name-nondirectory plain)
335 (file-name-directory plain)
336 (concat plain extension)))
339 (let ((context (epg-make-context)))
340 (epg-context-set-signers context signers)
341 (message "Signing %s..." (file-name-nondirectory plain))
342 (epg-sign-file context
345 (if detached 'detached))
346 (message "Signing %s...done" (file-name-nondirectory plain))))