EasyPG: Improve NEWS entry.
[bpt/emacs.git] / lisp / epa.el
CommitLineData
c154c0be
MO
1;;; epa.el --- the EasyPG Assistant
2;; Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
3
4;; Author: Daiki Ueno <ueno@unixuser.org>
5;; Keywords: PGP, GnuPG
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 by
11;; the Free Software Foundation; either version 3, or (at your option)
12;; any later version.
13
14;; GNU Emacs 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.
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., 51 Franklin Street, Fifth Floor,
22;; Boston, MA 02110-1301, USA.
23
24;;; Code:
25
26(require 'epg)
27(require 'font-lock)
28(require 'widget)
29(eval-when-compile (require 'wid-edit))
30(require 'derived)
31
32(defgroup epa nil
33 "The EasyPG Assistant"
0bd4f317 34 :version "23.1"
c154c0be
MO
35 :group 'epg)
36
37(defcustom epa-popup-info-window t
38 "If non-nil, status information from epa commands is displayed on
39the separate window."
40 :type 'boolean
41 :group 'epa)
42
43(defcustom epa-info-window-height 5
44 "Number of lines used to display status information."
45 :type 'integer
46 :group 'epa)
47
48(defgroup epa-faces nil
49 "Faces for epa-mode."
0bd4f317 50 :version "23.1"
c154c0be
MO
51 :group 'epa)
52
53(defface epa-validity-high
54 `((((class color) (background dark))
55 (:foreground "PaleTurquoise"
56 ,@(if (assq ':weight custom-face-attributes)
57 '(:weight bold)
58 '(:bold t))))
59 (t
60 (,@(if (assq ':weight custom-face-attributes)
61 '(:weight bold)
62 '(:bold t)))))
63 "Face used for displaying the high validity."
64 :group 'epa-faces)
65
66(defface epa-validity-medium
67 `((((class color) (background dark))
68 (:foreground "PaleTurquoise"
69 ,@(if (assq ':slant custom-face-attributes)
70 '(:slant italic)
71 '(:italic t))))
72 (t
73 (,@(if (assq ':slant custom-face-attributes)
74 '(:slant italic)
75 '(:italic t)))))
76 "Face used for displaying the medium validity."
77 :group 'epa-faces)
78
79(defface epa-validity-low
80 `((t
81 (,@(if (assq ':slant custom-face-attributes)
82 '(:slant italic)
83 '(:italic t)))))
84 "Face used for displaying the low validity."
85 :group 'epa-faces)
86
87(defface epa-validity-disabled
88 `((t
89 (,@(if (assq ':slant custom-face-attributes)
90 '(:slant italic)
91 '(:italic t))
92 :inverse-video t)))
93 "Face used for displaying the disabled validity."
94 :group 'epa-faces)
95
96(defface epa-string
97 '((((class color) (background dark))
98 (:foreground "lightyellow"))
99 (((class color) (background light))
100 (:foreground "blue4")))
101 "Face used for displaying the string."
102 :group 'epa-faces)
103
104(defface epa-mark
105 `((((class color) (background dark))
106 (:foreground "orange"
107 ,@(if (assq ':weight custom-face-attributes)
108 '(:weight bold)
109 '(:bold t))))
110 (((class color) (background light))
111 (:foreground "red"
112 ,@(if (assq ':weight custom-face-attributes)
113 '(:weight bold)
114 '(:bold t))))
115 (t
116 (,@(if (assq ':weight custom-face-attributes)
117 '(:weight bold)
118 '(:bold t)))))
119 "Face used for displaying the high validity."
120 :group 'epa-faces)
121
122(defface epa-field-name
123 `((((class color) (background dark))
124 (:foreground "PaleTurquoise"
125 ,@(if (assq ':weight custom-face-attributes)
126 '(:weight bold)
127 '(:bold t))))
128 (t
129 (,@(if (assq ':weight custom-face-attributes)
130 '(:weight bold)
131 '(:bold t)))))
132 "Face for the name of the attribute field."
133 :group 'epa)
134
135(defface epa-field-body
136 `((((class color) (background dark))
137 (:foreground "turquoise"
138 ,@(if (assq ':slant custom-face-attributes)
139 '(:slant italic)
140 '(:italic t))))
141 (t
142 (,@(if (assq ':slant custom-face-attributes)
143 '(:slant italic)
144 '(:italic t)))))
145 "Face for the body of the attribute field."
146 :group 'epa)
147
148(defcustom epa-validity-face-alist
149 '((unknown . epa-validity-disabled)
150 (invalid . epa-validity-disabled)
151 (disabled . epa-validity-disabled)
152 (revoked . epa-validity-disabled)
153 (expired . epa-validity-disabled)
154 (none . epa-validity-low)
155 (undefined . epa-validity-low)
156 (never . epa-validity-low)
157 (marginal . epa-validity-medium)
158 (full . epa-validity-high)
159 (ultimate . epa-validity-high))
160 "An alist mapping validity values to faces."
161 :type '(repeat (cons symbol face))
162 :group 'epa)
163
164(defvar epa-font-lock-keywords
165 '(("^\\*"
166 (0 'epa-mark))
167 ("^\t\\([^\t:]+:\\)[ \t]*\\(.*\\)$"
168 (1 'epa-field-name)
169 (2 'epa-field-body)))
170 "Default expressions to addon in epa-mode.")
171
172(defconst epa-pubkey-algorithm-letter-alist
173 '((1 . ?R)
174 (2 . ?r)
175 (3 . ?s)
176 (16 . ?g)
177 (17 . ?D)
178 (20 . ?G)))
179
180(defvar epa-protocol 'OpenPGP
181 "*The default protocol.
182The value can be either OpenPGP or CMS.
183
184You should bind this variable with `let', but do not set it globally.")
185
186(defvar epa-armor nil
187 "*If non-nil, epa commands create ASCII armored output.
188
189You should bind this variable with `let', but do not set it globally.")
190
191(defvar epa-textmode nil
192 "*If non-nil, epa commands treat input files as text.
193
194You should bind this variable with `let', but do not set it globally.")
195
196(defvar epa-keys-buffer nil)
197(defvar epa-key-buffer-alist nil)
198(defvar epa-key nil)
199(defvar epa-list-keys-arguments nil)
200(defvar epa-info-buffer nil)
201(defvar epa-last-coding-system-specified nil)
202
203(defvar epa-key-list-mode-map
204 (let ((keymap (make-sparse-keymap)))
205 (define-key keymap "m" 'epa-mark-key)
206 (define-key keymap "u" 'epa-unmark-key)
207 (define-key keymap "d" 'epa-decrypt-file)
208 (define-key keymap "v" 'epa-verify-file)
209 (define-key keymap "s" 'epa-sign-file)
210 (define-key keymap "e" 'epa-encrypt-file)
211 (define-key keymap "r" 'epa-delete-keys)
212 (define-key keymap "i" 'epa-import-keys)
213 (define-key keymap "o" 'epa-export-keys)
214 (define-key keymap "g" 'revert-buffer)
215 (define-key keymap "n" 'next-line)
216 (define-key keymap "p" 'previous-line)
217 (define-key keymap " " 'scroll-up)
218 (define-key keymap [delete] 'scroll-down)
219 (define-key keymap "q" 'epa-exit-buffer)
220 keymap))
221
222(defvar epa-key-mode-map
223 (let ((keymap (make-sparse-keymap)))
224 (define-key keymap "q" 'epa-exit-buffer)
225 keymap))
226
227(defvar epa-info-mode-map
228 (let ((keymap (make-sparse-keymap)))
229 (define-key keymap "q" 'delete-window)
230 keymap))
231
232(defvar epa-exit-buffer-function #'bury-buffer)
233
234(define-widget 'epa-key 'push-button
235 "Button for representing a epg-key object."
236 :format "%[%v%]"
237 :button-face-get 'epa--key-widget-button-face-get
238 :value-create 'epa--key-widget-value-create
239 :action 'epa--key-widget-action
240 :help-echo 'epa--key-widget-help-echo)
241
242(defun epa--key-widget-action (widget &optional event)
243 (epa--show-key (widget-get widget :value)))
244
245(defun epa--key-widget-value-create (widget)
246 (let* ((key (widget-get widget :value))
247 (primary-sub-key (car (epg-key-sub-key-list key)))
248 (primary-user-id (car (epg-key-user-id-list key))))
249 (insert (format "%c "
250 (if (epg-sub-key-validity primary-sub-key)
251 (car (rassq (epg-sub-key-validity primary-sub-key)
252 epg-key-validity-alist))
253 ? ))
254 (epg-sub-key-id primary-sub-key)
255 " "
256 (if primary-user-id
257 (if (stringp (epg-user-id-string primary-user-id))
258 (epg-user-id-string primary-user-id)
259 (epg-decode-dn (epg-user-id-string primary-user-id)))
260 ""))))
261
262(defun epa--key-widget-button-face-get (widget)
263 (let ((validity (epg-sub-key-validity (car (epg-key-sub-key-list
264 (widget-get widget :value))))))
265 (if validity
266 (cdr (assq validity epa-validity-face-alist))
267 'default)))
268
269(defun epa--key-widget-help-echo (widget)
270 (format "Show %s"
271 (epg-sub-key-id (car (epg-key-sub-key-list
272 (widget-get widget :value))))))
273
274(eval-and-compile
275 (if (fboundp 'encode-coding-string)
276 (defalias 'epa--encode-coding-string 'encode-coding-string)
277 (defalias 'epa--encode-coding-string 'identity)))
278
279(eval-and-compile
280 (if (fboundp 'decode-coding-string)
281 (defalias 'epa--decode-coding-string 'decode-coding-string)
282 (defalias 'epa--decode-coding-string 'identity)))
283
284(defun epa-key-list-mode ()
285 "Major mode for `epa-list-keys'."
286 (kill-all-local-variables)
287 (buffer-disable-undo)
288 (setq major-mode 'epa-key-list-mode
289 mode-name "Keys"
290 truncate-lines t
291 buffer-read-only t)
292 (use-local-map epa-key-list-mode-map)
293 (make-local-variable 'font-lock-defaults)
294 (setq font-lock-defaults '(epa-font-lock-keywords t))
295 ;; In XEmacs, auto-initialization of font-lock is not effective
296 ;; if buffer-file-name is not set.
297 (font-lock-set-defaults)
298 (make-local-variable 'epa-exit-buffer-function)
299 (make-local-variable 'revert-buffer-function)
300 (setq revert-buffer-function 'epa--key-list-revert-buffer)
301 (run-hooks 'epa-key-list-mode-hook))
302
303(defun epa-key-mode ()
304 "Major mode for a key description."
305 (kill-all-local-variables)
306 (buffer-disable-undo)
307 (setq major-mode 'epa-key-mode
308 mode-name "Key"
309 truncate-lines t
310 buffer-read-only t)
311 (use-local-map epa-key-mode-map)
312 (make-local-variable 'font-lock-defaults)
313 (setq font-lock-defaults '(epa-font-lock-keywords t))
314 ;; In XEmacs, auto-initialization of font-lock is not effective
315 ;; if buffer-file-name is not set.
316 (font-lock-set-defaults)
317 (make-local-variable 'epa-exit-buffer-function)
318 (run-hooks 'epa-key-mode-hook))
319
320(defun epa-info-mode ()
321 "Major mode for `epa-info-buffer'."
322 (kill-all-local-variables)
323 (buffer-disable-undo)
324 (setq major-mode 'epa-info-mode
325 mode-name "Info"
326 truncate-lines t
327 buffer-read-only t)
328 (use-local-map epa-info-mode-map)
329 (run-hooks 'epa-info-mode-hook))
330
331(defun epa-mark-key (&optional arg)
332 "Mark a key on the current line.
333If ARG is non-nil, unmark the key."
334 (interactive "P")
335 (let ((inhibit-read-only t)
336 buffer-read-only
337 properties)
338 (beginning-of-line)
339 (unless (get-text-property (point) 'epa-key)
340 (error "No key on this line"))
341 (setq properties (text-properties-at (point)))
342 (delete-char 1)
343 (insert (if arg " " "*"))
344 (set-text-properties (1- (point)) (point) properties)
345 (forward-line)))
346
347(defun epa-unmark-key (&optional arg)
348 "Unmark a key on the current line.
349If ARG is non-nil, mark the key."
350 (interactive "P")
351 (epa-mark-key (not arg)))
352
353(defun epa-exit-buffer ()
354 "Exit the current buffer.
355`epa-exit-buffer-function' is called if it is set."
356 (interactive)
357 (funcall epa-exit-buffer-function))
358
359(defun epa--insert-keys (keys)
360 (save-excursion
361 (save-restriction
362 (narrow-to-region (point) (point))
363 (let (point)
364 (while keys
365 (setq point (point))
366 (insert " ")
367 (add-text-properties point (point)
368 (list 'epa-key (car keys)
369 'front-sticky nil
370 'rear-nonsticky t
371 'start-open t
372 'end-open t))
373 (widget-create 'epa-key :value (car keys))
374 (insert "\n")
375 (setq keys (cdr keys))))
376 (add-text-properties (point-min) (point-max)
377 (list 'epa-list-keys t
378 'front-sticky nil
379 'rear-nonsticky t
380 'start-open t
381 'end-open t)))))
382
383(defun epa--list-keys (name secret)
384 (unless (and epa-keys-buffer
385 (buffer-live-p epa-keys-buffer))
386 (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
387 (set-buffer epa-keys-buffer)
388 (epa-key-list-mode)
389 (let ((inhibit-read-only t)
390 buffer-read-only
391 (point (point-min))
392 (context (epg-make-context epa-protocol)))
393 (unless (get-text-property point 'epa-list-keys)
394 (setq point (next-single-property-change point 'epa-list-keys)))
395 (when point
396 (delete-region point
397 (or (next-single-property-change point 'epa-list-keys)
398 (point-max)))
399 (goto-char point))
400 (epa--insert-keys (epg-list-keys context name secret))
401 (widget-setup)
402 (set-keymap-parent (current-local-map) widget-keymap))
403 (make-local-variable 'epa-list-keys-arguments)
404 (setq epa-list-keys-arguments (list name secret))
405 (goto-char (point-min))
406 (pop-to-buffer (current-buffer)))
407
408;;;###autoload
409(defun epa-list-keys (&optional name)
410 "List all keys matched with NAME from the public keyring."
411 (interactive
412 (if current-prefix-arg
413 (let ((name (read-string "Pattern: "
414 (if epa-list-keys-arguments
415 (car epa-list-keys-arguments)))))
416 (list (if (equal name "") nil name)))
417 (list nil)))
418 (epa--list-keys name nil))
419
420;;;###autoload
421(defun epa-list-secret-keys (&optional name)
422 "List all keys matched with NAME from the private keyring."
423 (interactive
424 (if current-prefix-arg
425 (let ((name (read-string "Pattern: "
426 (if epa-list-keys-arguments
427 (car epa-list-keys-arguments)))))
428 (list (if (equal name "") nil name)))
429 (list nil)))
430 (epa--list-keys name t))
431
432(defun epa--key-list-revert-buffer (&optional ignore-auto noconfirm)
433 (apply #'epa--list-keys epa-list-keys-arguments))
434
435(defun epa--marked-keys ()
436 (or (save-excursion
437 (set-buffer epa-keys-buffer)
438 (goto-char (point-min))
439 (let (keys key)
440 (while (re-search-forward "^\\*" nil t)
441 (if (setq key (get-text-property (match-beginning 0)
442 'epa-key))
443 (setq keys (cons key keys))))
444 (nreverse keys)))
445 (save-excursion
446 (beginning-of-line)
447 (let ((key (get-text-property (point) 'epa-key)))
448 (if key
449 (list key))))))
450
451(defun epa--select-keys (prompt keys)
452 (save-excursion
453 (unless (and epa-keys-buffer
454 (buffer-live-p epa-keys-buffer))
455 (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
456 (set-buffer epa-keys-buffer)
457 (epa-key-list-mode)
458 (let ((inhibit-read-only t)
459 buffer-read-only)
460 (erase-buffer)
461 (insert prompt "\n"
462 (substitute-command-keys "\
463- `\\[epa-mark-key]' to mark a key on the line
464- `\\[epa-unmark-key]' to unmark a key on the line\n"))
465 (widget-create 'link
466 :notify (lambda (&rest ignore) (abort-recursive-edit))
467 :help-echo
468 (substitute-command-keys
469 "Click here or \\[abort-recursive-edit] to cancel")
470 "Cancel")
471 (widget-create 'link
472 :notify (lambda (&rest ignore) (exit-recursive-edit))
473 :help-echo
474 (substitute-command-keys
475 "Click here or \\[exit-recursive-edit] to finish")
476 "OK")
477 (insert "\n\n")
478 (epa--insert-keys keys)
479 (widget-setup)
480 (set-keymap-parent (current-local-map) widget-keymap)
481 (setq epa-exit-buffer-function #'abort-recursive-edit)
482 (goto-char (point-min))
483 (pop-to-buffer (current-buffer)))
484 (unwind-protect
485 (progn
486 (recursive-edit)
487 (epa--marked-keys))
488 (if (get-buffer-window epa-keys-buffer)
489 (delete-window (get-buffer-window epa-keys-buffer)))
490 (kill-buffer epa-keys-buffer))))
491
492;;;###autoload
493(defun epa-select-keys (context prompt &optional names secret)
494 "Display a user's keyring and ask him to select keys.
495CONTEXT is an epg-context.
496PROMPT is a string to prompt with.
497NAMES is a list of strings to be matched with keys. If it is nil, all
498the keys are listed.
499If SECRET is non-nil, list secret keys instead of public keys."
500 (let ((keys (epg-list-keys context names secret)))
501 (if (> (length keys) 1)
502 (epa--select-keys prompt keys)
503 keys)))
504
505(defun epa--show-key (key)
506 (let* ((primary-sub-key (car (epg-key-sub-key-list key)))
507 (entry (assoc (epg-sub-key-id primary-sub-key)
508 epa-key-buffer-alist))
509 (inhibit-read-only t)
510 buffer-read-only
511 pointer)
512 (unless entry
513 (setq entry (cons (epg-sub-key-id primary-sub-key) nil)
514 epa-key-buffer-alist (cons entry epa-key-buffer-alist)))
515 (unless (and (cdr entry)
516 (buffer-live-p (cdr entry)))
517 (setcdr entry (generate-new-buffer
518 (format "*Key*%s" (epg-sub-key-id primary-sub-key)))))
519 (set-buffer (cdr entry))
520 (epa-key-mode)
521 (make-local-variable 'epa-key)
522 (setq epa-key key)
523 (erase-buffer)
524 (setq pointer (epg-key-user-id-list key))
525 (while pointer
526 (if (car pointer)
527 (insert " "
528 (if (epg-user-id-validity (car pointer))
529 (char-to-string
530 (car (rassq (epg-user-id-validity (car pointer))
531 epg-key-validity-alist)))
532 " ")
533 " "
534 (if (stringp (epg-user-id-string (car pointer)))
535 (epg-user-id-string (car pointer))
536 (epg-decode-dn (epg-user-id-string (car pointer))))
537 "\n"))
538 (setq pointer (cdr pointer)))
539 (setq pointer (epg-key-sub-key-list key))
540 (while pointer
541 (insert " "
542 (if (epg-sub-key-validity (car pointer))
543 (char-to-string
544 (car (rassq (epg-sub-key-validity (car pointer))
545 epg-key-validity-alist)))
546 " ")
547 " "
548 (epg-sub-key-id (car pointer))
549 " "
550 (format "%dbits"
551 (epg-sub-key-length (car pointer)))
552 " "
553 (cdr (assq (epg-sub-key-algorithm (car pointer))
554 epg-pubkey-algorithm-alist))
555 "\n\tCreated: "
556 (condition-case nil
557 (format-time-string "%Y-%m-%d"
558 (epg-sub-key-creation-time (car pointer)))
559 (error "????-??-??"))
560 (if (epg-sub-key-expiration-time (car pointer))
561 (format "\n\tExpires: %s"
562 (condition-case nil
563 (format-time-string "%Y-%m-%d"
564 (epg-sub-key-expiration-time
565 (car pointer)))
566 (error "????-??-??")))
567 "")
568 "\n\tCapabilities: "
569 (mapconcat #'symbol-name
570 (epg-sub-key-capability (car pointer))
571 " ")
572 "\n\tFingerprint: "
573 (epg-sub-key-fingerprint (car pointer))
574 "\n")
575 (setq pointer (cdr pointer)))
576 (goto-char (point-min))
577 (pop-to-buffer (current-buffer))))
578
579(defun epa-display-info (info)
580 (if epa-popup-info-window
581 (save-selected-window
582 (unless (and epa-info-buffer (buffer-live-p epa-info-buffer))
583 (setq epa-info-buffer (generate-new-buffer "*Info*")))
584 (if (get-buffer-window epa-info-buffer)
585 (delete-window (get-buffer-window epa-info-buffer)))
586 (save-excursion
587 (set-buffer epa-info-buffer)
588 (let ((inhibit-read-only t)
589 buffer-read-only)
590 (erase-buffer)
591 (insert info))
592 (epa-info-mode)
593 (goto-char (point-min)))
594 (if (> (window-height)
595 epa-info-window-height)
596 (set-window-buffer (split-window nil (- (window-height)
597 epa-info-window-height))
598 epa-info-buffer)
599 (pop-to-buffer epa-info-buffer)
600 (if (> (window-height) epa-info-window-height)
601 (shrink-window (- (window-height) epa-info-window-height)))))
602 (message "%s" info)))
603
604(defun epa-display-verify-result (verify-result)
605 (epa-display-info (epg-verify-result-to-string verify-result)))
606(make-obsolete 'epa-display-verify-result 'epa-display-info)
607
608(defun epa-passphrase-callback-function (context key-id handback)
609 (if (eq key-id 'SYM)
610 (read-passwd "Passphrase for symmetric encryption: "
611 (eq (epg-context-operation context) 'encrypt))
612 (read-passwd
613 (if (eq key-id 'PIN)
614 "Passphrase for PIN: "
615 (let ((entry (assoc key-id epg-user-id-alist)))
616 (if entry
617 (format "Passphrase for %s %s: " key-id (cdr entry))
618 (format "Passphrase for %s: " key-id)))))))
619
620(defun epa-progress-callback-function (context what char current total
621 handback)
622 (message "%s%d%% (%d/%d)" (or handback
623 (concat what ": "))
624 (if (> total 0) (floor (* (/ current (float total)) 100)) 0)
625 current total))
626
627;;;###autoload
628(defun epa-decrypt-file (file)
629 "Decrypt FILE."
630 (interactive "fFile: ")
631 (setq file (expand-file-name file))
632 (let* ((default-name (file-name-sans-extension file))
633 (plain (expand-file-name
634 (read-file-name
635 (concat "To file (default "
636 (file-name-nondirectory default-name)
637 ") ")
638 (file-name-directory default-name)
639 default-name)))
640 (context (epg-make-context epa-protocol)))
641 (epg-context-set-passphrase-callback context
642 #'epa-passphrase-callback-function)
643 (epg-context-set-progress-callback context
644 (cons
645 #'epa-progress-callback-function
646 (format "Decrypting %s..."
647 (file-name-nondirectory file))))
648 (message "Decrypting %s..." (file-name-nondirectory file))
649 (epg-decrypt-file context file plain)
650 (message "Decrypting %s...wrote %s" (file-name-nondirectory file)
651 (file-name-nondirectory plain))
652 (if (epg-context-result-for context 'verify)
653 (epa-display-info (epg-verify-result-to-string
654 (epg-context-result-for context 'verify))))))
655
656;;;###autoload
657(defun epa-verify-file (file)
658 "Verify FILE."
659 (interactive "fFile: ")
660 (setq file (expand-file-name file))
661 (let* ((context (epg-make-context epa-protocol))
662 (plain (if (equal (file-name-extension file) "sig")
663 (file-name-sans-extension file))))
664 (epg-context-set-progress-callback context
665 (cons
666 #'epa-progress-callback-function
667 (format "Verifying %s..."
668 (file-name-nondirectory file))))
669 (message "Verifying %s..." (file-name-nondirectory file))
670 (epg-verify-file context file plain)
671 (message "Verifying %s...done" (file-name-nondirectory file))
672 (if (epg-context-result-for context 'verify)
673 (epa-display-info (epg-verify-result-to-string
674 (epg-context-result-for context 'verify))))))
675
676(defun epa--read-signature-type ()
677 (let (type c)
678 (while (null type)
679 (message "Signature type (n,c,d,?) ")
680 (setq c (read-char))
681 (cond ((eq c ?c)
682 (setq type 'clear))
683 ((eq c ?d)
684 (setq type 'detached))
685 ((eq c ??)
686 (with-output-to-temp-buffer "*Help*"
687 (save-excursion
688 (set-buffer standard-output)
689 (insert "\
690n - Create a normal signature
691c - Create a cleartext signature
692d - Create a detached signature
693? - Show this help
694"))))
695 (t
696 (setq type 'normal))))))
697
698;;;###autoload
699(defun epa-sign-file (file signers mode)
700 "Sign FILE by SIGNERS keys selected."
701 (interactive
702 (let ((verbose current-prefix-arg))
703 (list (expand-file-name (read-file-name "File: "))
704 (if verbose
705 (epa-select-keys (epg-make-context epa-protocol)
706 "Select keys for signing.
707If no one is selected, default secret key is used. "
708 nil t))
709 (if verbose
710 (epa--read-signature-type)
711 'clear))))
712 (let ((signature (concat file
713 (if (eq epa-protocol 'OpenPGP)
714 (if (or epa-armor
715 (not (memq mode
716 '(nil t normal detached))))
717 ".asc"
718 (if (memq mode '(t detached))
719 ".sig"
720 ".gpg"))
721 (if (memq mode '(t detached))
722 ".p7s"
723 ".p7m"))))
724 (context (epg-make-context epa-protocol)))
725 (epg-context-set-armor context epa-armor)
726 (epg-context-set-textmode context epa-textmode)
727 (epg-context-set-signers context signers)
728 (epg-context-set-passphrase-callback context
729 #'epa-passphrase-callback-function)
730 (epg-context-set-progress-callback context
731 (cons
732 #'epa-progress-callback-function
733 (format "Signing %s..."
734 (file-name-nondirectory file))))
735 (message "Signing %s..." (file-name-nondirectory file))
736 (epg-sign-file context file signature mode)
737 (message "Signing %s...wrote %s" (file-name-nondirectory file)
738 (file-name-nondirectory signature))))
739
740;;;###autoload
741(defun epa-encrypt-file (file recipients)
742 "Encrypt FILE for RECIPIENTS."
743 (interactive
744 (list (expand-file-name (read-file-name "File: "))
745 (epa-select-keys (epg-make-context epa-protocol)
746 "Select recipients for encryption.
747If no one is selected, symmetric encryption will be performed. ")))
748 (let ((cipher (concat file (if (eq epa-protocol 'OpenPGP)
749 (if epa-armor ".asc" ".gpg")
750 ".p7m")))
751 (context (epg-make-context epa-protocol)))
752 (epg-context-set-armor context epa-armor)
753 (epg-context-set-textmode context epa-textmode)
754 (epg-context-set-passphrase-callback context
755 #'epa-passphrase-callback-function)
756 (epg-context-set-progress-callback context
757 (cons
758 #'epa-progress-callback-function
759 (format "Encrypting %s..."
760 (file-name-nondirectory file))))
761 (message "Encrypting %s..." (file-name-nondirectory file))
762 (epg-encrypt-file context file recipients cipher)
763 (message "Encrypting %s...wrote %s" (file-name-nondirectory file)
764 (file-name-nondirectory cipher))))
765
766;;;###autoload
767(defun epa-decrypt-region (start end)
768 "Decrypt the current region between START and END.
769
770Don't use this command in Lisp programs!"
771 (interactive "r")
772 (save-excursion
773 (let ((context (epg-make-context epa-protocol))
774 plain)
775 (epg-context-set-passphrase-callback context
776 #'epa-passphrase-callback-function)
777 (epg-context-set-progress-callback context
778 (cons
779 #'epa-progress-callback-function
780 "Decrypting..."))
781 (message "Decrypting...")
782 (setq plain (epg-decrypt-string context (buffer-substring start end)))
783 (message "Decrypting...done")
784 (setq plain (epa--decode-coding-string
785 plain
786 (or coding-system-for-read
787 (get-text-property start 'epa-coding-system-used))))
788 (if (y-or-n-p "Replace the original text? ")
789 (let ((inhibit-read-only t)
790 buffer-read-only)
791 (delete-region start end)
792 (goto-char start)
793 (insert plain))
794 (with-output-to-temp-buffer "*Temp*"
795 (set-buffer standard-output)
796 (insert plain)
797 (epa-info-mode)))
798 (if (epg-context-result-for context 'verify)
799 (epa-display-info (epg-verify-result-to-string
800 (epg-context-result-for context 'verify)))))))
801
802(defun epa--find-coding-system-for-mime-charset (mime-charset)
803 (if (featurep 'xemacs)
804 (if (fboundp 'find-coding-system)
805 (find-coding-system mime-charset))
806 (let ((pointer (coding-system-list)))
807 (while (and pointer
808 (eq (coding-system-get (car pointer) 'mime-charset)
809 mime-charset))
810 (setq pointer (cdr pointer)))
811 pointer)))
812
813;;;###autoload
814(defun epa-decrypt-armor-in-region (start end)
815 "Decrypt OpenPGP armors in the current region between START and END.
816
817Don't use this command in Lisp programs!"
818 (interactive "r")
819 (save-excursion
820 (save-restriction
821 (narrow-to-region start end)
822 (goto-char start)
823 (let (armor-start armor-end)
824 (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
825 (setq armor-start (match-beginning 0)
826 armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
827 nil t))
828 (unless armor-end
829 (error "No armor tail"))
830 (goto-char armor-start)
831 (let ((coding-system-for-read
832 (or coding-system-for-read
833 (if (re-search-forward "^Charset: \\(.*\\)" armor-end t)
834 (epa--find-coding-system-for-mime-charset
835 (intern (downcase (match-string 1))))))))
836 (goto-char armor-end)
837 (epa-decrypt-region armor-start armor-end)))))))
838
839;;;###autoload
840(defun epa-verify-region (start end)
841 "Verify the current region between START and END.
842
843Don't use this command in Lisp programs!"
844 (interactive "r")
845 (let ((context (epg-make-context epa-protocol))
846 plain)
847 (epg-context-set-progress-callback context
848 (cons
849 #'epa-progress-callback-function
850 "Verifying..."))
851 (message "Verifying...")
852 (setq plain (epg-verify-string
853 context
854 (epa--encode-coding-string
855 (buffer-substring start end)
856 (or coding-system-for-write
857 (get-text-property start 'epa-coding-system-used)))))
858 (message "Verifying...done")
859 (setq plain (epa--decode-coding-string
860 plain
861 (or coding-system-for-read
862 (get-text-property start 'epa-coding-system-used))))
863 (if (y-or-n-p "Replace the original text? ")
864 (let ((inhibit-read-only t)
865 buffer-read-only)
866 (delete-region start end)
867 (goto-char start)
868 (insert plain))
869 (with-output-to-temp-buffer "*Temp*"
870 (set-buffer standard-output)
871 (insert plain)
872 (epa-info-mode)))
873 (if (epg-context-result-for context 'verify)
874 (epa-display-info (epg-verify-result-to-string
875 (epg-context-result-for context 'verify))))))
876
877;;;###autoload
878(defun epa-verify-cleartext-in-region (start end)
879 "Verify OpenPGP cleartext signed messages in the current region
880between START and END.
881
882Don't use this command in Lisp programs!"
883 (interactive "r")
884 (save-excursion
885 (save-restriction
886 (narrow-to-region start end)
887 (goto-char start)
888 (let (cleartext-start cleartext-end)
889 (while (re-search-forward "-----BEGIN PGP SIGNED MESSAGE-----$"
890 nil t)
891 (setq cleartext-start (match-beginning 0))
892 (unless (re-search-forward "^-----BEGIN PGP SIGNATURE-----$"
893 nil t)
894 (error "Invalid cleartext signed message"))
895 (setq cleartext-end (re-search-forward
896 "^-----END PGP SIGNATURE-----$"
897 nil t))
898 (unless cleartext-end
899 (error "No cleartext tail"))
900 (epa-verify-region cleartext-start cleartext-end))))))
901
902(eval-and-compile
903 (if (fboundp 'select-safe-coding-system)
904 (defalias 'epa--select-safe-coding-system 'select-safe-coding-system)
905 (defun epa--select-safe-coding-system (from to)
906 buffer-file-coding-system)))
907
908;;;###autoload
909(defun epa-sign-region (start end signers mode)
910 "Sign the current region between START and END by SIGNERS keys selected.
911
912Don't use this command in Lisp programs!"
913 (interactive
914 (let ((verbose current-prefix-arg))
915 (setq epa-last-coding-system-specified
916 (or coding-system-for-write
917 (epa--select-safe-coding-system
918 (region-beginning) (region-end))))
919 (list (region-beginning) (region-end)
920 (if verbose
921 (epa-select-keys (epg-make-context epa-protocol)
922 "Select keys for signing.
923If no one is selected, default secret key is used. "
924 nil t))
925 (if verbose
926 (epa--read-signature-type)
927 'clear))))
928 (save-excursion
929 (let ((context (epg-make-context epa-protocol))
930 signature)
931 ;;(epg-context-set-armor context epa-armor)
932 (epg-context-set-armor context t)
933 ;;(epg-context-set-textmode context epa-textmode)
934 (epg-context-set-textmode context t)
935 (epg-context-set-signers context signers)
936 (epg-context-set-passphrase-callback context
937 #'epa-passphrase-callback-function)
938 (epg-context-set-progress-callback context
939 (cons
940 #'epa-progress-callback-function
941 "Signing..."))
942 (message "Signing...")
943 (setq signature (epg-sign-string context
944 (epa--encode-coding-string
945 (buffer-substring start end)
946 epa-last-coding-system-specified)
947 mode))
948 (message "Signing...done")
949 (delete-region start end)
950 (goto-char start)
951 (add-text-properties (point)
952 (progn
953 (insert (epa--decode-coding-string
954 signature
955 (or coding-system-for-read
956 epa-last-coding-system-specified)))
957 (point))
958 (list 'epa-coding-system-used
959 epa-last-coding-system-specified
960 'front-sticky nil
961 'rear-nonsticky t
962 'start-open t
963 'end-open t)))))
964
965(eval-and-compile
966 (if (fboundp 'derived-mode-p)
967 (defalias 'epa--derived-mode-p 'derived-mode-p)
968 (defun epa--derived-mode-p (&rest modes)
969 "Non-nil if the current major mode is derived from one of MODES.
970Uses the `derived-mode-parent' property of the symbol to trace backwards."
971 (let ((parent major-mode))
972 (while (and (not (memq parent modes))
973 (setq parent (get parent 'derived-mode-parent))))
974 parent))))
975
976;;;###autoload
977(defun epa-encrypt-region (start end recipients sign signers)
978 "Encrypt the current region between START and END for RECIPIENTS.
979
980Don't use this command in Lisp programs!"
981 (interactive
982 (let ((verbose current-prefix-arg)
983 (context (epg-make-context epa-protocol))
984 sign)
985 (setq epa-last-coding-system-specified
986 (or coding-system-for-write
987 (epa--select-safe-coding-system
988 (region-beginning) (region-end))))
989 (list (region-beginning) (region-end)
990 (epa-select-keys context
991 "Select recipients for encryption.
992If no one is selected, symmetric encryption will be performed. ")
993 (setq sign (if verbose (y-or-n-p "Sign? ")))
994 (if sign
995 (epa-select-keys context
996 "Select keys for signing. ")))))
997 (save-excursion
998 (let ((context (epg-make-context epa-protocol))
999 cipher)
1000 ;;(epg-context-set-armor context epa-armor)
1001 (epg-context-set-armor context t)
1002 ;;(epg-context-set-textmode context epa-textmode)
1003 (epg-context-set-textmode context t)
1004 (if sign
1005 (epg-context-set-signers context signers))
1006 (epg-context-set-passphrase-callback context
1007 #'epa-passphrase-callback-function)
1008 (epg-context-set-progress-callback context
1009 (cons
1010 #'epa-progress-callback-function
1011 "Encrypting..."))
1012 (message "Encrypting...")
1013 (setq cipher (epg-encrypt-string context
1014 (epa--encode-coding-string
1015 (buffer-substring start end)
1016 epa-last-coding-system-specified)
1017 recipients
1018 sign))
1019 (message "Encrypting...done")
1020 (delete-region start end)
1021 (goto-char start)
1022 (add-text-properties (point)
1023 (progn
1024 (insert cipher)
1025 (point))
1026 (list 'epa-coding-system-used
1027 epa-last-coding-system-specified
1028 'front-sticky nil
1029 'rear-nonsticky t
1030 'start-open t
1031 'end-open t)))))
1032
1033;;;###autoload
1034(defun epa-delete-keys (keys &optional allow-secret)
1035 "Delete selected KEYS.
1036
1037Don't use this command in Lisp programs!"
1038 (interactive
1039 (let ((keys (epa--marked-keys)))
1040 (unless keys
1041 (error "No keys selected"))
1042 (list keys
1043 (eq (nth 1 epa-list-keys-arguments) t))))
1044 (let ((context (epg-make-context epa-protocol)))
1045 (message "Deleting...")
1046 (epg-delete-keys context keys allow-secret)
1047 (message "Deleting...done")
1048 (apply #'epa-list-keys epa-list-keys-arguments)))
1049
1050;;;###autoload
1051(defun epa-import-keys (file)
1052 "Import keys from FILE.
1053
1054Don't use this command in Lisp programs!"
1055 (interactive "fFile: ")
1056 (setq file (expand-file-name file))
1057 (let ((context (epg-make-context epa-protocol)))
1058 (message "Importing %s..." (file-name-nondirectory file))
1059 (condition-case nil
1060 (progn
1061 (epg-import-keys-from-file context file)
1062 (message "Importing %s...done" (file-name-nondirectory file)))
1063 (error
1064 (message "Importing %s...failed" (file-name-nondirectory file))))
1065 (if (epg-context-result-for context 'import)
1066 (epa-display-info (epg-import-result-to-string
1067 (epg-context-result-for context 'import))))
1068 (if (eq major-mode 'epa-key-list-mode)
1069 (apply #'epa-list-keys epa-list-keys-arguments))))
1070
1071;;;###autoload
1072(defun epa-import-keys-region (start end)
1073 "Import keys from the region.
1074
1075Don't use this command in Lisp programs!"
1076 (interactive "r")
1077 (let ((context (epg-make-context epa-protocol)))
1078 (message "Importing...")
1079 (condition-case nil
1080 (progn
1081 (epg-import-keys-from-string context (buffer-substring start end))
1082 (message "Importing...done"))
1083 (error
1084 (message "Importing...failed")))
1085 (if (epg-context-result-for context 'import)
1086 (epa-display-info (epg-import-result-to-string
1087 (epg-context-result-for context 'import))))))
1088
1089;;;###autoload
1090(defun epa-import-armor-in-region (start end)
1091 "Import keys in the OpenPGP armor format in the current region
1092between START and END.
1093
1094Don't use this command in Lisp programs!"
1095 (interactive "r")
1096 (save-excursion
1097 (save-restriction
1098 (narrow-to-region start end)
1099 (goto-char start)
1100 (let (armor-start armor-end)
1101 (while (re-search-forward
1102 "-----BEGIN \\(PGP \\(PUBLIC\\|PRIVATE\\) KEY BLOCK\\)-----$"
1103 nil t)
1104 (setq armor-start (match-beginning 0)
1105 armor-end (re-search-forward
1106 (concat "^-----END " (match-string 1) "-----$")
1107 nil t))
1108 (unless armor-end
1109 (error "No armor tail"))
1110 (epa-import-keys-region armor-start armor-end))))))
1111
1112;;;###autoload
1113(defun epa-export-keys (keys file)
1114 "Export selected KEYS to FILE.
1115
1116Don't use this command in Lisp programs!"
1117 (interactive
1118 (let ((keys (epa--marked-keys))
1119 default-name)
1120 (unless keys
1121 (error "No keys selected"))
1122 (setq default-name
1123 (expand-file-name
1124 (concat (epg-sub-key-id (car (epg-key-sub-key-list (car keys))))
1125 (if epa-armor ".asc" ".gpg"))
1126 default-directory))
1127 (list keys
1128 (expand-file-name
1129 (read-file-name
1130 (concat "To file (default "
1131 (file-name-nondirectory default-name)
1132 ") ")
1133 (file-name-directory default-name)
1134 default-name)))))
1135 (let ((context (epg-make-context epa-protocol)))
1136 (epg-context-set-armor context epa-armor)
1137 (message "Exporting to %s..." (file-name-nondirectory file))
1138 (epg-export-keys-to-file context keys file)
1139 (message "Exporting to %s...done" (file-name-nondirectory file))))
1140
1141;;;###autoload
1142(defun epa-insert-keys (keys)
1143 "Insert selected KEYS after the point.
1144
1145Don't use this command in Lisp programs!"
1146 (interactive
1147 (list (epa-select-keys (epg-make-context epa-protocol)
1148 "Select keys to export. ")))
1149 (let ((context (epg-make-context epa-protocol)))
1150 ;;(epg-context-set-armor context epa-armor)
1151 (epg-context-set-armor context t)
1152 (insert (epg-export-keys-to-string context keys))))
1153
1154;; (defun epa-sign-keys (keys &optional local)
1155;; "Sign selected KEYS.
1156;; If a prefix-arg is specified, the signature is marked as non exportable.
1157
1158;; Don't use this command in Lisp programs!"
1159;; (interactive
1160;; (let ((keys (epa--marked-keys)))
1161;; (unless keys
1162;; (error "No keys selected"))
1163;; (list keys current-prefix-arg)))
1164;; (let ((context (epg-make-context epa-protocol)))
1165;; (epg-context-set-passphrase-callback context
1166;; #'epa-passphrase-callback-function)
1167;; (epg-context-set-progress-callback context
1168;; (cons
1169;; #'epa-progress-callback-function
1170;; "Signing keys..."))
1171;; (message "Signing keys...")
1172;; (epg-sign-keys context keys local)
1173;; (message "Signing keys...done")))
1174;; (make-obsolete 'epa-sign-keys "Do not use.")
1175
1176(provide 'epa)
1177
37b77401 1178;; arch-tag: 38d20ced-20d5-4137-b17a-f206335423d7
c154c0be 1179;;; epa.el ends here