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