* epa.el (epa-decrypt-region): New command.
[elisp/epg.git] / epa.el
1 ;;; epa.el --- the EasyPG Assistant
2 ;; Copyright (C) 2006 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Keywords: PGP, GnuPG
6
7 ;; This file is part of EasyPG.
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
23
24 ;;; Code:
25
26 (require 'epg)
27 (require 'font-lock)
28 (require 'widget)
29 (eval-when-compile (require 'wid-edit))
30
31 (defgroup epa nil
32   "The EasyPG Assistant"
33   :group 'epg)
34
35 (defcustom epa-armor nil
36   "If non-nil, epa commands create ASCII armored output."
37   :type 'boolean
38   :group 'epa)
39
40 (defcustom epa-textmode nil
41   "If non-nil, epa commands treat input files as text."
42   :type 'boolean
43   :group 'epa)
44   
45 (defgroup epa-faces nil
46   "Faces for epa-mode."
47   :group 'epa)
48
49 (defface epa-validity-high-face
50   '((((class color) (background dark))
51      (:foreground "PaleTurquoise" :bold t))
52     (t
53      (:bold t)))
54   "Face used for displaying the high validity."
55   :group 'epa-faces)
56 (defvar epa-validity-high-face 'epa-validity-high-face)
57
58 (defface epa-validity-medium-face
59   '((((class color) (background dark))
60      (:foreground "PaleTurquoise" :italic t))
61     (t
62      ()))
63   "Face used for displaying the medium validity."
64   :group 'epa-faces)
65 (defvar epa-validity-medium-face 'epa-validity-medium-face)
66
67 (defface epa-validity-low-face
68   '((t
69      (:italic t)))
70   "Face used for displaying the low validity."
71   :group 'epa-faces)
72 (defvar epa-validity-low-face 'epa-validity-low-face)
73
74 (defface epa-validity-disabled-face
75   '((t
76      (:italic t :inverse-video t)))
77   "Face used for displaying the disabled validity."
78   :group 'epa-faces)
79 (defvar epa-validity-disabled-face 'epa-validity-disabled-face)
80
81 (defface epa-string-face
82   '((((class color)
83       (background dark))
84      (:foreground "lightyellow"))
85     (((class color)
86       (background light))
87      (:foreground "blue4"))
88     (t
89      ()))
90   "Face used for displaying the string."
91   :group 'epa-faces)
92 (defvar epa-string-face 'epa-string-face)
93
94 (defface epa-mark-face
95   '((((class color) (background dark))
96      (:foreground "orange" :bold t))
97     (t
98      (:foreground "red" :bold t)))
99   "Face used for displaying the high validity."
100   :group 'epa-faces)
101 (defvar epa-mark-face 'epa-mark-face)
102
103 (defface epa-field-name-face
104   '((((class color) (background dark))
105      (:foreground "PaleTurquoise" :bold t))
106     (t (:bold t)))
107   "Face for the name of the attribute field."
108   :group 'epa)
109 (defvar epa-field-name-face 'epa-field-name-face)
110
111 (defface epa-field-body-face
112   '((((class color) (background dark))
113      (:foreground "turquoise" :italic t))
114     (t (:italic t)))
115   "Face for the body of the attribute field."
116   :group 'epa)
117 (defvar epa-field-body-face 'epa-field-body-face)
118
119 (defcustom epa-validity-face-alist
120   '((unknown . epa-validity-disabled-face)
121     (invalid . epa-validity-disabled-face)
122     (disabled . epa-validity-disabled-face)
123     (revoked . epa-validity-disabled-face)
124     (expired . epa-validity-disabled-face)
125     (none . epa-validity-low-face)
126     (undefined . epa-validity-low-face)
127     (never . epa-validity-low-face)
128     (marginal . epa-validity-medium-face)
129     (full . epa-validity-high-face)
130     (ultimate . epa-validity-high-face))
131   "An alist mapping validity values to faces."
132   :type 'list
133   :group 'epa)
134
135 (defcustom epa-font-lock-keywords
136   '(("^\\*"
137      (0 epa-mark-face))
138     ("^\t\\([^\t:]+:\\)[ \t]*\\(.*\\)$"
139      (1 epa-field-name-face)
140      (2 epa-field-body-face)))
141   "Default expressions to addon in epa-mode."
142   :type '(repeat (list string))
143   :group 'epa)
144
145 (defconst epa-pubkey-algorithm-letter-alist
146   '((1 . ?R)
147     (2 . ?r)
148     (3 . ?s)
149     (16 . ?g)
150     (17 . ?D)
151     (20 . ?G)))
152
153 (defvar epa-keys-buffer nil)
154 (defvar epa-key-buffer-alist nil)
155 (defvar epa-key nil)
156 (defvar epa-list-keys-arguments nil)
157
158 (defvar epa-keys-mode-map
159   (let ((keymap (make-sparse-keymap)))
160     (define-key keymap "m" 'epa-mark)
161     (define-key keymap "u" 'epa-unmark)
162     (define-key keymap "d" 'epa-decrypt-file)
163     (define-key keymap "v" 'epa-verify-file)
164     (define-key keymap "s" 'epa-sign-file)
165     (define-key keymap "S" 'epa-sign-keys)
166     (define-key keymap "e" 'epa-encrypt-file)
167     (define-key keymap "r" 'epa-delete-keys)
168     (define-key keymap "i" 'epa-import-keys)
169     (define-key keymap "o" 'epa-export-keys)
170     (define-key keymap "g" 'epa-list-keys)
171     (define-key keymap "n" 'next-line)
172     (define-key keymap "p" 'previous-line)
173     (define-key keymap " " 'scroll-up)
174     (define-key keymap [delete] 'scroll-down)
175     (define-key keymap "q" 'epa-exit-buffer)
176     keymap))
177
178 (defvar epa-exit-buffer-function #'bury-buffer)
179
180 (define-widget 'epa-key 'push-button
181   "Button for representing a epg-key object."
182   :format "%[%v%]"
183   :button-face-get 'epa-key-widget-button-face-get
184   :value-create 'epa-key-widget-value-create
185   :action 'epa-key-widget-action
186   :help-echo 'epa-key-widget-help-echo)
187
188 (defun epa-key-widget-action (widget &optional event)
189   (epa-show-key (widget-get widget :value)))
190
191 (defun epa-key-widget-value-create (widget)
192   (let* ((key (widget-get widget :value))
193          (primary-sub-key (car (epg-key-sub-key-list key)))
194          (primary-user-id (car (epg-key-user-id-list key))))
195     (insert (format "%c "
196                     (if (epg-sub-key-validity primary-sub-key)
197                         (car (rassq (epg-sub-key-validity primary-sub-key)
198                                     epg-key-validity-alist))
199                       ? ))
200             (epg-sub-key-id primary-sub-key)
201             " "
202             (if (stringp (epg-user-id-string primary-user-id))
203                 (epg-user-id-string primary-user-id)
204               (epg-decode-dn (epg-user-id-string primary-user-id))))))
205
206 (defun epa-key-widget-button-face-get (widget)
207   (let ((validity (epg-sub-key-validity (car (epg-key-sub-key-list
208                                               (widget-get widget :value))))))
209     (if validity
210         (cdr (assq validity epa-validity-face-alist))
211       'default)))
212
213 (defun epa-key-widget-help-echo (widget)
214   (format "Show %s"
215           (epg-sub-key-id (car (epg-key-sub-key-list
216                                 (widget-get widget :value))))))
217
218 (defun epa-keys-mode ()
219   "Major mode for `epa-list-keys'."
220   (kill-all-local-variables)
221   (buffer-disable-undo)
222   (setq major-mode 'epa-keys-mode
223         mode-name "Keys"
224         truncate-lines t
225         buffer-read-only t)
226   (use-local-map epa-keys-mode-map)
227   (set-keymap-parent (current-local-map) widget-keymap)
228   (make-local-variable 'font-lock-defaults)
229   (setq font-lock-defaults '(epa-font-lock-keywords t))
230   ;; In XEmacs, auto-initialization of font-lock is not effective
231   ;; if buffer-file-name is not set.
232   (font-lock-set-defaults)
233   (widget-setup)
234   (make-local-variable 'epa-exit-buffer-function)
235   (run-hooks 'epa-keys-mode-hook))
236
237 (defvar epa-key-mode-map
238   (let ((keymap (make-sparse-keymap)))
239     (define-key keymap "q" 'bury-buffer)
240     keymap))
241
242 (defun epa-key-mode ()
243   "Major mode for `epa-show-key'."
244   (kill-all-local-variables)
245   (buffer-disable-undo)
246   (setq major-mode 'epa-key-mode
247         mode-name "Key"
248         truncate-lines t
249         buffer-read-only t)
250   (use-local-map epa-key-mode-map)
251   (make-local-variable 'font-lock-defaults)
252   (setq font-lock-defaults '(epa-font-lock-keywords t))
253   ;; In XEmacs, auto-initialization of font-lock is not effective
254   ;; if buffer-file-name is not set.
255   (font-lock-set-defaults)
256   (make-local-variable 'epa-exit-buffer-function)
257   (run-hooks 'epa-key-mode-hook))
258
259 ;;;###autoload
260 (defun epa-list-keys (&optional name mode protocol)
261   (interactive
262    (if current-prefix-arg
263        (let ((name (read-string "Pattern: "
264                                 (if epa-list-keys-arguments
265                                     (car epa-list-keys-arguments)))))
266          (list (if (equal name "") nil name)
267                (y-or-n-p "Secret keys? ")
268                (intern (completing-read "Protocol? "
269                                         '(("OpenPGP") ("CMS"))
270                                         nil t))))
271      (or epa-list-keys-arguments (list nil nil nil))))
272   (unless (and epa-keys-buffer
273                (buffer-live-p epa-keys-buffer))
274     (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
275   (set-buffer epa-keys-buffer)
276   (let ((inhibit-read-only t)
277         buffer-read-only
278         (point (point-min))
279         (context (epg-make-context protocol)))
280     (unless (get-text-property point 'epa-list-keys)
281       (setq point (next-single-property-change point 'epa-list-keys)))
282     (when point
283       (delete-region point
284                      (or (next-single-property-change point 'epa-list-keys)
285                          (point-max)))
286       (goto-char point))
287     (epa-list-keys-1 context name mode)
288     (epa-keys-mode))
289   (make-local-variable 'epa-list-keys-arguments)
290   (setq epa-list-keys-arguments (list name mode protocol))
291   (goto-char (point-min))
292   (pop-to-buffer (current-buffer)))
293
294 (defun epa-list-keys-1 (context name mode)
295   (save-restriction
296     (narrow-to-region (point) (point))
297     (let ((inhibit-read-only t)
298           buffer-read-only
299           (keys (epg-list-keys context name mode))
300           point)
301       (while keys
302         (setq point (point))
303         (insert "  ")
304         (put-text-property point (point) 'epa-key (car keys))
305         (widget-create 'epa-key :value (car keys))
306         (insert "\n")
307         (setq keys (cdr keys))))      
308     (put-text-property (point-min) (point-max) 'epa-list-keys t)))
309
310 (defun epa-marked-keys ()
311   (or (save-excursion
312         (set-buffer epa-keys-buffer)
313         (goto-char (point-min))
314         (let (keys key)
315           (while (re-search-forward "^\\*" nil t)
316             (if (setq key (get-text-property (match-beginning 0)
317                                              'epa-key))
318                 (setq keys (cons key keys))))
319           (nreverse keys)))
320       (save-excursion
321         (beginning-of-line)
322         (let ((key (get-text-property (point) 'epa-key)))
323           (if key
324               (list key))))))
325
326 ;;;###autoload
327 (defun epa-select-keys (context prompt &optional names secret)
328   "Display a user's keyring and ask him to select keys.
329 CONTEXT is an epg-context.
330 PROMPT is a string to prompt with.
331 NAMES is a list of strings to be matched with keys.  If it is nil, all
332 the keys are listed.
333 If SECRET is non-nil, list secret keys instead of public keys."
334   (save-excursion
335     (unless (and epa-keys-buffer
336                  (buffer-live-p epa-keys-buffer))
337       (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
338     (let ((inhibit-read-only t)
339           buffer-read-only
340           point)
341       (set-buffer epa-keys-buffer)
342       (erase-buffer)
343       (insert prompt "\n")
344       (widget-create 'link
345                      :notify (lambda (&rest ignore) (abort-recursive-edit))
346                      :help-echo
347                      (substitute-command-keys
348                       "Click here or \\[abort-recursive-edit] to cancel")
349                      "Cancel")
350       (widget-create 'link
351                      :notify (lambda (&rest ignore) (exit-recursive-edit))
352                      :help-echo
353                      (substitute-command-keys
354                       "Click here or \\[exit-recursive-edit] to finish")
355                      "OK")
356       (insert "\n\n")
357       (if names
358           (while names
359             (setq point (point))
360             (epa-list-keys-1 context (car names) secret)
361             (goto-char point)
362             (epa-mark)
363             (goto-char (point-max))
364             (setq names (cdr names)))
365         (epa-list-keys-1 context nil secret))
366       (epa-keys-mode)
367       (setq epa-exit-buffer-function #'abort-recursive-edit)
368       (goto-char (point-min))
369       (pop-to-buffer (current-buffer)))
370     (unwind-protect
371           (progn
372             (recursive-edit)
373             (epa-marked-keys))
374         (if (get-buffer-window epa-keys-buffer)
375             (delete-window (get-buffer-window epa-keys-buffer)))
376         (kill-buffer epa-keys-buffer))))
377
378 (defun epa-show-key (key)
379   (let* ((primary-sub-key (car (epg-key-sub-key-list key)))
380          (entry (assoc (epg-sub-key-id primary-sub-key)
381                        epa-key-buffer-alist))
382          (inhibit-read-only t)
383          buffer-read-only
384          pointer)
385     (unless entry
386       (setq entry (cons (epg-sub-key-id primary-sub-key) nil)
387             epa-key-buffer-alist (cons entry epa-key-buffer-alist)))
388     (unless (and (cdr entry)
389                  (buffer-live-p (cdr entry)))
390       (setcdr entry (generate-new-buffer
391                      (format "*Key*%s" (epg-sub-key-id primary-sub-key)))))
392     (set-buffer (cdr entry))
393     (make-local-variable 'epa-key)
394     (setq epa-key key)
395     (erase-buffer)
396     (setq pointer (epg-key-user-id-list key))
397     (while pointer
398       (insert " "
399               (if (epg-user-id-validity (car pointer))
400                   (char-to-string
401                    (car (rassq (epg-user-id-validity (car pointer))
402                                epg-key-validity-alist)))
403                 " ")
404               " "
405               (if (stringp (epg-user-id-string (car pointer)))
406                   (epg-user-id-string (car pointer))
407                 (epg-decode-dn (epg-user-id-string (car pointer))))
408               "\n")
409       (setq pointer (cdr pointer)))
410     (setq pointer (epg-key-sub-key-list key))
411     (while pointer
412       (insert " "
413               (if (epg-sub-key-validity (car pointer))
414                   (char-to-string
415                    (car (rassq (epg-sub-key-validity (car pointer))
416                                epg-key-validity-alist)))
417                 " ")
418               " "
419               (epg-sub-key-id (car pointer))
420               " "
421               (format "%dbits"
422                       (epg-sub-key-length (car pointer)))
423               " "
424               (cdr (assq (epg-sub-key-algorithm (car pointer))
425                          epg-pubkey-algorithm-alist))
426               "\n\tCreated: "
427               (epg-sub-key-creation-time (car pointer))
428               (if (epg-sub-key-expiration-time (car pointer))
429                   (format "\n\tExpires: %s" (epg-sub-key-expiration-time
430                                              (car pointer)))
431                 "")
432               "\n\tCapabilities: "
433               (mapconcat #'symbol-name
434                          (epg-sub-key-capability (car pointer))
435                          " ")
436               "\n\tFingerprint: "
437               (epg-sub-key-fingerprint (car pointer))
438               "\n")
439       (setq pointer (cdr pointer)))
440     (goto-char (point-min))
441     (pop-to-buffer (current-buffer))
442     (epa-key-mode)))
443
444 (defun epa-show-key-notify (widget &rest ignore)
445   (epa-show-key (widget-get widget :value)))
446
447 (defun epa-mark (&optional arg)
448   "Mark the current line.
449 If ARG is non-nil, unmark the current line."
450   (interactive "P")
451   (let ((inhibit-read-only t)
452         buffer-read-only
453         properties)
454     (beginning-of-line)
455     (setq properties (text-properties-at (point)))
456     (delete-char 1)
457     (insert (if arg " " "*"))
458     (set-text-properties (1- (point)) (point) properties)
459     (forward-line)))
460
461 (defun epa-unmark (&optional arg)
462   "Unmark the current line.
463 If ARG is non-nil, mark the current line."
464   (interactive "P")
465   (epa-mark (not arg)))
466
467 (defun epa-exit-buffer ()
468   "Exit the current buffer.
469 `epa-exit-buffer-function' is called if it is set."
470   (interactive)
471   (funcall epa-exit-buffer-function))
472
473 ;;;###autoload
474 (defun epa-decrypt-file (file)
475   "Decrypt FILE."
476   (interactive "fFile: ")
477   (let* ((default-name (file-name-sans-extension file))
478          (plain (expand-file-name
479                  (read-file-name
480                   (concat "To file (default "
481                           (file-name-nondirectory default-name)
482                           ") ")
483                   (file-name-directory default-name)
484                   default-name)))
485          (context (epg-make-context)))
486     (message "Decrypting %s..." (file-name-nondirectory file))
487     (epg-decrypt-file context file plain)
488     (message "Decrypting %s...done" (file-name-nondirectory file))
489     (if (epg-context-result-for context 'verify)
490         (message "%s"
491                  (epg-verify-result-to-string
492                   (epg-context-result-for context 'verify))))))
493
494 ;;;###autoload
495 (defun epa-verify-file (file)
496   "Verify FILE."
497   (interactive "fFile: ")
498   (let* ((context (epg-make-context))
499          (plain (if (equal (file-name-extension file) "sig")
500                     (file-name-sans-extension file))))
501     (message "Verifying %s..." (file-name-nondirectory file))
502     (epg-verify-file context file plain)
503     (message "Verifying %s...done" (file-name-nondirectory file))
504     (message "%s"
505              (epg-verify-result-to-string
506               (epg-context-result-for context 'verify)))))
507
508 ;;;###autoload
509 (defun epa-sign-file (file signers mode)
510   "Sign FILE by SIGNERS keys selected."
511   (interactive
512    (list (expand-file-name (read-file-name "File: "))
513          (epa-select-keys (epg-make-context) "Select keys for signing.
514 If no one is selected, default secret key is used.  "
515                           nil t)
516          (if (y-or-n-p "Make a detached signature? ")
517              'detached
518            (if (y-or-n-p "Make a cleartext signature? ")
519                'clear))))
520   (let ((signature (concat file
521                            (if (or epa-armor
522                                    (not (memq mode '(nil t normal detached))))
523                                ".asc"
524                              (if (memq mode '(t detached))
525                                  ".sig"
526                                ".gpg"))))
527         (context (epg-make-context)))
528     (epg-context-set-armor context epa-armor)
529     (epg-context-set-textmode context epa-textmode)
530     (message "Signing %s..." (file-name-nondirectory file))
531     (epg-context-set-signers context signers)
532     (epg-sign-file context file signature mode)
533     (message "Signing %s...done" (file-name-nondirectory file))))
534
535 ;;;###autoload
536 (defun epa-encrypt-file (file recipients)
537   "Encrypt FILE for RECIPIENTS."
538   (interactive
539    (list (expand-file-name (read-file-name "File: "))
540          (epa-select-keys (epg-make-context) "Select recipents for encryption.
541 If no one is selected, symmetric encryption will be performed.  ")))
542   (let ((cipher (concat file (if epa-armor ".asc" ".gpg")))
543         (context (epg-make-context)))
544     (epg-context-set-armor context epa-armor)
545     (epg-context-set-textmode context epa-textmode)
546     (message "Encrypting %s..." (file-name-nondirectory file))
547     (epg-encrypt-file context file recipients cipher)
548     (message "Encrypting %s...done" (file-name-nondirectory file))))
549
550 ;;;###autoload
551 (defun epa-decrypt-region (start end)
552   "Decrypt the current region between START and END."
553   (interactive "r")
554   (save-excursion
555     (let ((context (epg-make-context))
556           charset plain coding-system)
557       (message "Decrypting...")
558       (setq plain (epg-decrypt-string context
559                                       (buffer-substring start end)))
560       (message "Decrypting...done")
561       (delete-region start end)
562       (goto-char start)
563       (insert (decode-coding-string plain coding-system-for-read))
564       (if (epg-context-result-for context 'verify)
565           (message "%s"
566                    (epg-verify-result-to-string
567                     (epg-context-result-for context 'verify)))))))
568
569 ;;;###autoload
570 (defun epa-decrypt-armor-in-region (start end)
571   "Decrypt OpenPGP armors in the current region between START and END."
572   (interactive "r")
573   (save-excursion
574     (save-restriction
575       (narrow-to-region start end)
576       (goto-char start)
577       (let (armor-start armor-end charset plain coding-system)
578         (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
579           (setq armor-start (match-beginning 0)
580                 armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
581                                              nil t))
582           (unless armor-end
583             (error "No armor tail"))
584           (goto-char armor-start)
585           (if (re-search-forward "^Charset: \\(.*\\)" armor-end t)
586               (setq charset (match-string 1)))
587           (if coding-system-for-read
588               (setq coding-system coding-system-for-read)
589             (if charset
590                 (setq coding-system (intern (downcase charset)))
591               (setq coding-system 'utf-8)))
592           (let ((coding-system-for-read coding-system))
593             (epa-decrypt-region start end)))))))
594
595 ;;;###autoload
596 (defun epa-verify-region (start end)
597   "Verify the current region between START and END."
598   (interactive "r")
599   (let ((context (epg-make-context)))
600     (epg-verify-string context
601                        (encode-coding-string
602                         (buffer-substring start end)
603                         coding-system-for-write))
604     (message "%s"
605              (epg-verify-result-to-string
606               (epg-context-result-for context 'verify)))))
607
608 ;;;###autoload
609 (defun epa-verify-armor-in-region (start end)
610   "Verify OpenPGP armors in the current region between START and END."
611   (interactive "r")
612   (save-excursion
613     (save-restriction
614       (narrow-to-region start end)
615       (goto-char start)
616       (let (armor-start armor-end)
617         (while (re-search-forward "-----BEGIN PGP\\( SIGNED\\)? MESSAGE-----$"
618                                   nil t)
619           (setq armor-start (match-beginning 0))
620           (if (match-beginning 1)       ;cleartext signed message
621               (progn
622                 (unless (re-search-forward "^-----BEGIN PGP SIGNATURE-----$"
623                                            nil t)
624                   (error "Invalid cleartext signed message"))
625                 (setq armor-end (re-search-forward
626                                  "^-----END PGP SIGNATURE-----$"
627                                  nil t)))
628             (setq armor-end (re-search-forward
629                              "^-----END PGP MESSAGE-----$"
630                              nil t)))
631           (unless armor-end
632             (error "No armor tail"))
633           (epa-verify-region armor-start armor-end))))))
634
635 ;;;###autoload
636 (defun epa-sign-region (start end signers mode)
637   "Sign the current region between START and END by SIGNERS keys selected."
638   (interactive
639    (list (region-beginning) (region-end)
640          (epa-select-keys (epg-make-context) "Select keys for signing.
641 If no one is selected, default secret key is used.  "
642                           nil t)
643          (if (y-or-n-p "Make a detached signature? ")
644              'detached
645            (if (y-or-n-p "Make a cleartext signature? ")
646                'clear))))
647   (save-excursion
648     (let ((context (epg-make-context))
649           signature)
650       (epg-context-set-armor context epa-armor)
651       (epg-context-set-textmode context epa-textmode)
652       (message "Signing...")
653       (epg-context-set-signers context signers)
654       (setq signature (epg-sign-string context
655                                        (encode-coding-string
656                                         (buffer-substring start end)
657                                         coding-system-for-write)
658                                        mode))
659       (message "Signing...done")
660       (delete-region start end)
661       (insert (decode-coding-string signature coding-system-for-read)))))
662
663 ;;;###autoload
664 (defun epa-encrypt-region (start end recipients)
665   "Encrypt the current region between START and END for RECIPIENTS."
666   (interactive
667    (list (region-beginning) (region-end)
668          (epa-select-keys (epg-make-context) "Select recipents for encryption.
669 If no one is selected, symmetric encryption will be performed.  ")))
670   (save-excursion
671     (let ((context (epg-make-context))
672           cipher)
673       (epg-context-set-armor context epa-armor)
674       (epg-context-set-textmode context epa-textmode)
675       (message "Encrypting...")
676       (setq cipher (epg-encrypt-string context
677                                        (encode-coding-string
678                                         (buffer-substring start end)
679                                         coding-system-for-write)
680                                        recipients))
681       (message "Encrypting...done")
682       (delete-region start end)
683       (insert cipher))))
684
685 ;;;###autoload
686 (defun epa-delete-keys (keys &optional allow-secret)
687   "Delete selected KEYS."
688   (interactive
689    (let ((keys (epa-marked-keys)))
690      (unless keys
691        (error "No keys selected"))
692      (list keys
693            (eq (nth 1 epa-list-keys-arguments) t))))
694   (let ((context (epg-make-context)))
695     (message "Deleting...")
696     (epg-delete-keys context keys allow-secret)
697     (apply #'epa-list-keys epa-list-keys-arguments)
698     (message "Deleting...done")))
699
700 ;;;###autoload
701 (defun epa-import-keys (file)
702   "Import keys from FILE."
703   (interactive "fFile: ")
704   (let ((context (epg-make-context)))
705     (message "Importing %s..." (file-name-nondirectory file))
706     (epg-import-keys-from-file context (expand-file-name file))
707     (apply #'epa-list-keys epa-list-keys-arguments)
708     (message "Importing %s...done" (file-name-nondirectory file))))
709
710 ;;;###autoload
711 (defun epa-export-keys (keys file)
712   "Export selected KEYS to FILE."
713   (interactive
714    (let ((keys (epa-marked-keys))
715          default-name)
716      (unless keys
717        (error "No keys selected"))
718      (setq default-name
719            (expand-file-name
720             (concat (epg-sub-key-id (car (epg-key-sub-key-list (car keys))))
721                     (if epa-armor ".asc" ".gpg"))
722             default-directory))
723      (list keys
724            (expand-file-name
725             (read-file-name
726              (concat "To file (default "
727                      (file-name-nondirectory default-name)
728                      ") ")
729              (file-name-directory default-name)
730              default-name)))))
731   (let ((context (epg-make-context)))
732     (epg-context-set-armor context epa-armor)
733     (message "Exporting to %s..." (file-name-nondirectory file))
734     (epg-export-keys-to-file context keys file)
735     (message "Exporting to %s...done" (file-name-nondirectory file))))
736
737 ;;;###autoload
738 (defun epa-sign-keys (keys &optional local)
739   "Sign selected KEYS.
740 If LOCAL is non-nil, the signature is marked as non exportable."
741   (interactive
742    (let ((keys (epa-marked-keys)))
743      (unless keys
744        (error "No keys selected"))
745      (list keys current-prefix-arg)))
746   (let ((context (epg-make-context)))
747     (message "Signing keys...")
748     (epg-sign-keys context keys local)
749     (message "Signing keys...done")))
750
751 (provide 'epa)
752
753 ;;; epa.el ends here