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