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