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