1 ;;; epa.el --- the EasyPG Assistant
2 ;; Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Keywords: PGP, GnuPG
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs 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 3, or (at your option)
14 ;; GNU Emacs 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.
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.
29 (eval-when-compile (require 'wid-edit
))
33 "The EasyPG Assistant"
37 (defcustom epa-popup-info-window t
38 "If non-nil, status information from epa commands is displayed on
43 (defcustom epa-info-window-height
5
44 "Number of lines used to display status information."
48 (defcustom epa-global-minor-modes
'(epa-dired-mode
52 "Globally defined minor modes to hook into other modes."
53 :type
'(repeat symbol
)
56 (defgroup epa-faces nil
61 (defface epa-validity-high
62 `((((class color
) (background dark
))
63 (:foreground
"PaleTurquoise"
64 ,@(if (assq ':weight custom-face-attributes
)
68 (,@(if (assq ':weight custom-face-attributes
)
71 "Face used for displaying the high validity."
74 (defface epa-validity-medium
75 `((((class color
) (background dark
))
76 (:foreground
"PaleTurquoise"
77 ,@(if (assq ':slant custom-face-attributes
)
81 (,@(if (assq ':slant custom-face-attributes
)
84 "Face used for displaying the medium validity."
87 (defface epa-validity-low
89 (,@(if (assq ':slant custom-face-attributes
)
92 "Face used for displaying the low validity."
95 (defface epa-validity-disabled
97 (,@(if (assq ':slant custom-face-attributes
)
101 "Face used for displaying the disabled validity."
105 '((((class color
) (background dark
))
106 (:foreground
"lightyellow"))
107 (((class color
) (background light
))
108 (:foreground
"blue4")))
109 "Face used for displaying the string."
113 `((((class color
) (background dark
))
114 (:foreground
"orange"
115 ,@(if (assq ':weight custom-face-attributes
)
118 (((class color
) (background light
))
120 ,@(if (assq ':weight custom-face-attributes
)
124 (,@(if (assq ':weight custom-face-attributes
)
127 "Face used for displaying the high validity."
130 (defface epa-field-name
131 `((((class color
) (background dark
))
132 (:foreground
"PaleTurquoise"
133 ,@(if (assq ':weight custom-face-attributes
)
137 (,@(if (assq ':weight custom-face-attributes
)
140 "Face for the name of the attribute field."
143 (defface epa-field-body
144 `((((class color
) (background dark
))
145 (:foreground
"turquoise"
146 ,@(if (assq ':slant custom-face-attributes
)
150 (,@(if (assq ':slant custom-face-attributes
)
153 "Face for the body of the attribute field."
156 (defcustom epa-validity-face-alist
157 '((unknown . epa-validity-disabled
)
158 (invalid . epa-validity-disabled
)
159 (disabled . epa-validity-disabled
)
160 (revoked . epa-validity-disabled
)
161 (expired . epa-validity-disabled
)
162 (none . epa-validity-low
)
163 (undefined . epa-validity-low
)
164 (never . epa-validity-low
)
165 (marginal . epa-validity-medium
)
166 (full . epa-validity-high
)
167 (ultimate . epa-validity-high
))
168 "An alist mapping validity values to faces."
169 :type
'(repeat (cons symbol face
))
172 (defvar epa-font-lock-keywords
175 ("^\t\\([^\t:]+:\\)[ \t]*\\(.*\\)$"
177 (2 'epa-field-body
)))
178 "Default expressions to addon in epa-mode.")
180 (defconst epa-pubkey-algorithm-letter-alist
188 (defvar epa-protocol
'OpenPGP
189 "*The default protocol.
190 The value can be either OpenPGP or CMS.
192 You should bind this variable with `let', but do not set it globally.")
194 (defvar epa-armor nil
195 "*If non-nil, epa commands create ASCII armored output.
197 You should bind this variable with `let', but do not set it globally.")
199 (defvar epa-textmode nil
200 "*If non-nil, epa commands treat input files as text.
202 You should bind this variable with `let', but do not set it globally.")
204 (defvar epa-keys-buffer nil
)
205 (defvar epa-key-buffer-alist nil
)
207 (defvar epa-list-keys-arguments nil
)
208 (defvar epa-info-buffer nil
)
209 (defvar epa-last-coding-system-specified nil
)
211 (defvar epa-key-list-mode-map
212 (let ((keymap (make-sparse-keymap)))
213 (define-key keymap
"m" 'epa-mark-key
)
214 (define-key keymap
"u" 'epa-unmark-key
)
215 (define-key keymap
"d" 'epa-decrypt-file
)
216 (define-key keymap
"v" 'epa-verify-file
)
217 (define-key keymap
"s" 'epa-sign-file
)
218 (define-key keymap
"e" 'epa-encrypt-file
)
219 (define-key keymap
"r" 'epa-delete-keys
)
220 (define-key keymap
"i" 'epa-import-keys
)
221 (define-key keymap
"o" 'epa-export-keys
)
222 (define-key keymap
"g" 'revert-buffer
)
223 (define-key keymap
"n" 'next-line
)
224 (define-key keymap
"p" 'previous-line
)
225 (define-key keymap
" " 'scroll-up
)
226 (define-key keymap
[delete] 'scroll-down)
227 (define-key keymap "q" 'epa-exit-buffer)
230 (defvar epa-key-mode-map
231 (let ((keymap (make-sparse-keymap)))
232 (define-key keymap "q" 'epa-exit-buffer)
235 (defvar epa-info-mode-map
236 (let ((keymap (make-sparse-keymap)))
237 (define-key keymap "q" 'delete-window)
240 (defvar epa-menu nil)
242 (defconst epa-menu-items
245 ["File" epa-decrypt-file
246 :help "Decrypt a file"]
247 ["Region" epa-decrypt-region
248 :help "Decrypt the current region"])
250 ["File" epa-verify-file
251 :help "Verify digital signature of a file"]
252 ["Region" epa-verify-region
253 :help "Verify digital signature of the current region"])
255 ["File" epa-sign-file
256 :help "Create digital signature of a file"]
257 ["Region" epa-sign-region
258 :help "Create digital signature of the current region"])
260 ["File" epa-encrypt-file
261 :help "Encrypt a file"]
262 ["Region" epa-encrypt-region
263 :help "Encrypt the current region"])
265 ["Browse keyring" epa-list-keys
266 :help "Browse your public keyring"]
268 ["File" epa-import-keys
269 :help "Import public keys from a file"]
270 ["Region" epa-import-keys-region
271 :help "Import public keys from the current region"])
273 ["To a File" epa-export-keys
274 :help "Export public keys to a file"]
275 ["To a Buffer" epa-insert-keys
276 :help "Insert public keys after the current point"])))
278 (defvar epa-exit-buffer-function #'bury-buffer)
280 (define-widget 'epa-key 'push-button
281 "Button for representing a epg-key object."
283 :button-face-get 'epa--key-widget-button-face-get
284 :value-create 'epa--key-widget-value-create
285 :action 'epa--key-widget-action
286 :help-echo 'epa--key-widget-help-echo)
288 (defun epa--key-widget-action (widget &optional event)
289 (epa--show-key (widget-get widget :value)))
291 (defun epa--key-widget-value-create (widget)
292 (let* ((key (widget-get widget :value))
293 (primary-sub-key (car (epg-key-sub-key-list key)))
294 (primary-user-id (car (epg-key-user-id-list key))))
295 (insert (format "%c "
296 (if (epg-sub-key-validity primary-sub-key)
297 (car (rassq (epg-sub-key-validity primary-sub-key)
298 epg-key-validity-alist))
300 (epg-sub-key-id primary-sub-key)
303 (if (stringp (epg-user-id-string primary-user-id))
304 (epg-user-id-string primary-user-id)
305 (epg-decode-dn (epg-user-id-string primary-user-id)))
308 (defun epa--key-widget-button-face-get (widget)
309 (let ((validity (epg-sub-key-validity (car (epg-key-sub-key-list
310 (widget-get widget :value))))))
312 (cdr (assq validity epa-validity-face-alist))
315 (defun epa--key-widget-help-echo (widget)
317 (epg-sub-key-id (car (epg-key-sub-key-list
318 (widget-get widget :value))))))
321 (if (fboundp 'encode-coding-string)
322 (defalias 'epa--encode-coding-string 'encode-coding-string)
323 (defalias 'epa--encode-coding-string 'identity)))
326 (if (fboundp 'decode-coding-string)
327 (defalias 'epa--decode-coding-string 'decode-coding-string)
328 (defalias 'epa--decode-coding-string 'identity)))
330 (defun epa-key-list-mode ()
331 "Major mode for `epa-list-keys'."
332 (kill-all-local-variables)
333 (buffer-disable-undo)
334 (setq major-mode 'epa-key-list-mode
338 (use-local-map epa-key-list-mode-map)
339 (make-local-variable 'font-lock-defaults)
340 (setq font-lock-defaults '(epa-font-lock-keywords t))
341 ;; In XEmacs, auto-initialization of font-lock is not effective
342 ;; if buffer-file-name is not set.
343 (font-lock-set-defaults)
344 (make-local-variable 'epa-exit-buffer-function)
345 (make-local-variable 'revert-buffer-function)
346 (setq revert-buffer-function 'epa--key-list-revert-buffer)
347 (run-hooks 'epa-key-list-mode-hook))
349 (defun epa-key-mode ()
350 "Major mode for a key description."
351 (kill-all-local-variables)
352 (buffer-disable-undo)
353 (setq major-mode 'epa-key-mode
357 (use-local-map epa-key-mode-map)
358 (make-local-variable 'font-lock-defaults)
359 (setq font-lock-defaults '(epa-font-lock-keywords t))
360 ;; In XEmacs, auto-initialization of font-lock is not effective
361 ;; if buffer-file-name is not set.
362 (font-lock-set-defaults)
363 (make-local-variable 'epa-exit-buffer-function)
364 (run-hooks 'epa-key-mode-hook))
366 (defun epa-info-mode ()
367 "Major mode for `epa-info-buffer'."
368 (kill-all-local-variables)
369 (buffer-disable-undo)
370 (setq major-mode 'epa-info-mode
374 (use-local-map epa-info-mode-map)
375 (run-hooks 'epa-info-mode-hook))
377 (defun epa-mark-key (&optional arg)
378 "Mark a key on the current line.
379 If ARG is non-nil, unmark the key."
381 (let ((inhibit-read-only t)
385 (unless (get-text-property (point) 'epa-key)
386 (error "No key on this line"))
387 (setq properties (text-properties-at (point)))
389 (insert (if arg " " "*"))
390 (set-text-properties (1- (point)) (point) properties)
393 (defun epa-unmark-key (&optional arg)
394 "Unmark a key on the current line.
395 If ARG is non-nil, mark the key."
397 (epa-mark-key (not arg)))
399 (defun epa-exit-buffer ()
400 "Exit the current buffer.
401 `epa-exit-buffer-function' is called if it is set."
403 (funcall epa-exit-buffer-function))
405 (defun epa--insert-keys (keys)
408 (narrow-to-region (point) (point))
413 (add-text-properties point (point)
414 (list 'epa-key (car keys)
419 (widget-create 'epa-key :value (car keys))
421 (setq keys (cdr keys))))
422 (add-text-properties (point-min) (point-max)
423 (list 'epa-list-keys t
429 (defun epa--list-keys (name secret)
430 (unless (and epa-keys-buffer
431 (buffer-live-p epa-keys-buffer))
432 (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
433 (set-buffer epa-keys-buffer)
435 (let ((inhibit-read-only t)
438 (context (epg-make-context epa-protocol)))
439 (unless (get-text-property point 'epa-list-keys)
440 (setq point (next-single-property-change point 'epa-list-keys)))
443 (or (next-single-property-change point 'epa-list-keys)
446 (epa--insert-keys (epg-list-keys context name secret))
448 (set-keymap-parent (current-local-map) widget-keymap))
449 (make-local-variable 'epa-list-keys-arguments)
450 (setq epa-list-keys-arguments (list name secret))
451 (goto-char (point-min))
452 (pop-to-buffer (current-buffer)))
455 (defun epa-list-keys (&optional name)
456 "List all keys matched with NAME from the public keyring."
458 (if current-prefix-arg
459 (let ((name (read-string "Pattern: "
460 (if epa-list-keys-arguments
461 (car epa-list-keys-arguments)))))
462 (list (if (equal name "") nil name)))
464 (epa--list-keys name nil))
467 (defun epa-list-secret-keys (&optional name)
468 "List all keys matched with NAME from the private keyring."
470 (if current-prefix-arg
471 (let ((name (read-string "Pattern: "
472 (if epa-list-keys-arguments
473 (car epa-list-keys-arguments)))))
474 (list (if (equal name "") nil name)))
476 (epa--list-keys name t))
478 (defun epa--key-list-revert-buffer (&optional ignore-auto noconfirm)
479 (apply #'epa--list-keys epa-list-keys-arguments))
481 (defun epa--marked-keys ()
483 (set-buffer epa-keys-buffer)
484 (goto-char (point-min))
486 (while (re-search-forward "^\\*" nil t)
487 (if (setq key (get-text-property (match-beginning 0)
489 (setq keys (cons key keys))))
493 (let ((key (get-text-property (point) 'epa-key)))
497 (defun epa--select-keys (prompt keys)
499 (unless (and epa-keys-buffer
500 (buffer-live-p epa-keys-buffer))
501 (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
502 (set-buffer epa-keys-buffer)
504 (let ((inhibit-read-only t)
508 (substitute-command-keys "\
509 - `\\[epa-mark-key]' to mark a key on the line
510 - `\\[epa-unmark-key]' to unmark a key on the line\n"))
512 :notify (lambda (&rest ignore) (abort-recursive-edit))
514 (substitute-command-keys
515 "Click here or \\[abort-recursive-edit] to cancel")
518 :notify (lambda (&rest ignore) (exit-recursive-edit))
520 (substitute-command-keys
521 "Click here or \\[exit-recursive-edit] to finish")
524 (epa--insert-keys keys)
526 (set-keymap-parent (current-local-map) widget-keymap)
527 (setq epa-exit-buffer-function #'abort-recursive-edit)
528 (goto-char (point-min))
529 (pop-to-buffer (current-buffer)))
534 (if (get-buffer-window epa-keys-buffer)
535 (delete-window (get-buffer-window epa-keys-buffer)))
536 (kill-buffer epa-keys-buffer))))
539 (defun epa-select-keys (context prompt &optional names secret)
540 "Display a user's keyring and ask him to select keys.
541 CONTEXT is an epg-context.
542 PROMPT is a string to prompt with.
543 NAMES is a list of strings to be matched with keys. If it is nil, all
545 If SECRET is non-nil, list secret keys instead of public keys."
546 (let ((keys (epg-list-keys context names secret)))
547 (if (> (length keys) 1)
548 (epa--select-keys prompt keys)
551 (defun epa--show-key (key)
552 (let* ((primary-sub-key (car (epg-key-sub-key-list key)))
553 (entry (assoc (epg-sub-key-id primary-sub-key)
554 epa-key-buffer-alist))
555 (inhibit-read-only t)
559 (setq entry (cons (epg-sub-key-id primary-sub-key) nil)
560 epa-key-buffer-alist (cons entry epa-key-buffer-alist)))
561 (unless (and (cdr entry)
562 (buffer-live-p (cdr entry)))
563 (setcdr entry (generate-new-buffer
564 (format "*Key*%s" (epg-sub-key-id primary-sub-key)))))
565 (set-buffer (cdr entry))
567 (make-local-variable 'epa-key)
570 (setq pointer (epg-key-user-id-list key))
574 (if (epg-user-id-validity (car pointer))
576 (car (rassq (epg-user-id-validity (car pointer))
577 epg-key-validity-alist)))
580 (if (stringp (epg-user-id-string (car pointer)))
581 (epg-user-id-string (car pointer))
582 (epg-decode-dn (epg-user-id-string (car pointer))))
584 (setq pointer (cdr pointer)))
585 (setq pointer (epg-key-sub-key-list key))
588 (if (epg-sub-key-validity (car pointer))
590 (car (rassq (epg-sub-key-validity (car pointer))
591 epg-key-validity-alist)))
594 (epg-sub-key-id (car pointer))
597 (epg-sub-key-length (car pointer)))
599 (cdr (assq (epg-sub-key-algorithm (car pointer))
600 epg-pubkey-algorithm-alist))
603 (format-time-string "%Y-%m-%d"
604 (epg-sub-key-creation-time (car pointer)))
605 (error "????-??-??"))
606 (if (epg-sub-key-expiration-time (car pointer))
607 (format "\n\tExpires: %s"
609 (format-time-string "%Y-%m-%d"
610 (epg-sub-key-expiration-time
612 (error "????-??-??")))
615 (mapconcat #'symbol-name
616 (epg-sub-key-capability (car pointer))
619 (epg-sub-key-fingerprint (car pointer))
621 (setq pointer (cdr pointer)))
622 (goto-char (point-min))
623 (pop-to-buffer (current-buffer))))
625 (defun epa-display-info (info)
626 (if epa-popup-info-window
627 (save-selected-window
628 (unless (and epa-info-buffer (buffer-live-p epa-info-buffer))
629 (setq epa-info-buffer (generate-new-buffer "*Info*")))
630 (if (get-buffer-window epa-info-buffer)
631 (delete-window (get-buffer-window epa-info-buffer)))
633 (set-buffer epa-info-buffer)
634 (let ((inhibit-read-only t)
639 (goto-char (point-min)))
640 (if (> (window-height)
641 epa-info-window-height)
642 (set-window-buffer (split-window nil (- (window-height)
643 epa-info-window-height))
645 (pop-to-buffer epa-info-buffer)
646 (if (> (window-height) epa-info-window-height)
647 (shrink-window (- (window-height) epa-info-window-height)))))
648 (message "%s" info)))
650 (defun epa-display-verify-result (verify-result)
651 (epa-display-info (epg-verify-result-to-string verify-result)))
652 (make-obsolete 'epa-display-verify-result 'epa-display-info)
654 (defun epa-passphrase-callback-function (context key-id handback)
656 (read-passwd "Passphrase for symmetric encryption: "
657 (eq (epg-context-operation context) 'encrypt))
660 "Passphrase for PIN: "
661 (let ((entry (assoc key-id epg-user-id-alist)))
663 (format "Passphrase for %s %s: " key-id (cdr entry))
664 (format "Passphrase for %s: " key-id)))))))
666 (defun epa-progress-callback-function (context what char current total
668 (message "%s%d%% (%d/%d)" (or handback
670 (if (> total 0) (floor (* (/ current (float total)) 100)) 0)
674 (defun epa-decrypt-file (file)
676 (interactive "fFile: ")
677 (setq file (expand-file-name file))
678 (let* ((default-name (file-name-sans-extension file))
679 (plain (expand-file-name
681 (concat "To file (default "
682 (file-name-nondirectory default-name)
684 (file-name-directory default-name)
686 (context (epg-make-context epa-protocol)))
687 (epg-context-set-passphrase-callback context
688 #'epa-passphrase-callback-function)
689 (epg-context-set-progress-callback context
691 #'epa-progress-callback-function
692 (format "Decrypting %s..."
693 (file-name-nondirectory file))))
694 (message "Decrypting %s..." (file-name-nondirectory file))
695 (epg-decrypt-file context file plain)
696 (message "Decrypting %s...wrote %s" (file-name-nondirectory file)
697 (file-name-nondirectory plain))
698 (if (epg-context-result-for context 'verify)
699 (epa-display-info (epg-verify-result-to-string
700 (epg-context-result-for context 'verify))))))
703 (defun epa-verify-file (file)
705 (interactive "fFile: ")
706 (setq file (expand-file-name file))
707 (let* ((context (epg-make-context epa-protocol))
708 (plain (if (equal (file-name-extension file) "sig")
709 (file-name-sans-extension file))))
710 (epg-context-set-progress-callback context
712 #'epa-progress-callback-function
713 (format "Verifying %s..."
714 (file-name-nondirectory file))))
715 (message "Verifying %s..." (file-name-nondirectory file))
716 (epg-verify-file context file plain)
717 (message "Verifying %s...done" (file-name-nondirectory file))
718 (if (epg-context-result-for context 'verify)
719 (epa-display-info (epg-verify-result-to-string
720 (epg-context-result-for context 'verify))))))
722 (defun epa--read-signature-type ()
725 (message "Signature type (n,c,d,?) ")
730 (setq type 'detached))
732 (with-output-to-temp-buffer "*Help*"
734 (set-buffer standard-output)
736 n - Create a normal signature
737 c - Create a cleartext signature
738 d - Create a detached signature
742 (setq type 'normal))))))
745 (defun epa-sign-file (file signers mode)
746 "Sign FILE by SIGNERS keys selected."
748 (let ((verbose current-prefix-arg))
749 (list (expand-file-name (read-file-name "File: "))
751 (epa-select-keys (epg-make-context epa-protocol)
752 "Select keys for signing.
753 If no one is selected, default secret key is used. "
756 (epa--read-signature-type)
758 (let ((signature (concat file
759 (if (eq epa-protocol 'OpenPGP)
762 '(nil t normal detached))))
764 (if (memq mode '(t detached))
767 (if (memq mode '(t detached))
770 (context (epg-make-context epa-protocol)))
771 (epg-context-set-armor context epa-armor)
772 (epg-context-set-textmode context epa-textmode)
773 (epg-context-set-signers context signers)
774 (epg-context-set-passphrase-callback context
775 #'epa-passphrase-callback-function)
776 (epg-context-set-progress-callback context
778 #'epa-progress-callback-function
779 (format "Signing %s..."
780 (file-name-nondirectory file))))
781 (message "Signing %s..." (file-name-nondirectory file))
782 (epg-sign-file context file signature mode)
783 (message "Signing %s...wrote %s" (file-name-nondirectory file)
784 (file-name-nondirectory signature))))
787 (defun epa-encrypt-file (file recipients)
788 "Encrypt FILE for RECIPIENTS."
790 (list (expand-file-name (read-file-name "File: "))
791 (epa-select-keys (epg-make-context epa-protocol)
792 "Select recipients for encryption.
793 If no one is selected, symmetric encryption will be performed. ")))
794 (let ((cipher (concat file (if (eq epa-protocol 'OpenPGP)
795 (if epa-armor ".asc" ".gpg")
797 (context (epg-make-context epa-protocol)))
798 (epg-context-set-armor context epa-armor)
799 (epg-context-set-textmode context epa-textmode)
800 (epg-context-set-passphrase-callback context
801 #'epa-passphrase-callback-function)
802 (epg-context-set-progress-callback context
804 #'epa-progress-callback-function
805 (format "Encrypting %s..."
806 (file-name-nondirectory file))))
807 (message "Encrypting %s..." (file-name-nondirectory file))
808 (epg-encrypt-file context file recipients cipher)
809 (message "Encrypting %s...wrote %s" (file-name-nondirectory file)
810 (file-name-nondirectory cipher))))
813 (defun epa-decrypt-region (start end)
814 "Decrypt the current region between START and END.
816 Don't use this command in Lisp programs!"
819 (let ((context (epg-make-context epa-protocol))
821 (epg-context-set-passphrase-callback context
822 #'epa-passphrase-callback-function)
823 (epg-context-set-progress-callback context
825 #'epa-progress-callback-function
827 (message "Decrypting...")
828 (setq plain (epg-decrypt-string context (buffer-substring start end)))
829 (message "Decrypting...done")
830 (setq plain (epa--decode-coding-string
832 (or coding-system-for-read
833 (get-text-property start 'epa-coding-system-used))))
834 (if (y-or-n-p "Replace the original text? ")
835 (let ((inhibit-read-only t)
837 (delete-region start end)
840 (with-output-to-temp-buffer "*Temp*"
841 (set-buffer standard-output)
844 (if (epg-context-result-for context 'verify)
845 (epa-display-info (epg-verify-result-to-string
846 (epg-context-result-for context 'verify)))))))
848 (defun epa--find-coding-system-for-mime-charset (mime-charset)
849 (if (featurep 'xemacs)
850 (if (fboundp 'find-coding-system)
851 (find-coding-system mime-charset))
852 (let ((pointer (coding-system-list)))
854 (eq (coding-system-get (car pointer) 'mime-charset)
856 (setq pointer (cdr pointer)))
860 (defun epa-decrypt-armor-in-region (start end)
861 "Decrypt OpenPGP armors in the current region between START and END.
863 Don't use this command in Lisp programs!"
867 (narrow-to-region start end)
869 (let (armor-start armor-end)
870 (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
871 (setq armor-start (match-beginning 0)
872 armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
875 (error "No armor tail"))
876 (goto-char armor-start)
877 (let ((coding-system-for-read
878 (or coding-system-for-read
879 (if (re-search-forward "^Charset: \\(.*\\)" armor-end t)
880 (epa--find-coding-system-for-mime-charset
881 (intern (downcase (match-string 1))))))))
882 (goto-char armor-end)
883 (epa-decrypt-region armor-start armor-end)))))))
886 (defun epa-verify-region (start end)
887 "Verify the current region between START and END.
889 Don't use this command in Lisp programs!"
891 (let ((context (epg-make-context epa-protocol))
893 (epg-context-set-progress-callback context
895 #'epa-progress-callback-function
897 (message "Verifying...")
898 (setq plain (epg-verify-string
900 (epa--encode-coding-string
901 (buffer-substring start end)
902 (or coding-system-for-write
903 (get-text-property start 'epa-coding-system-used)))))
904 (message "Verifying...done")
905 (setq plain (epa--decode-coding-string
907 (or coding-system-for-read
908 (get-text-property start 'epa-coding-system-used))))
909 (if (y-or-n-p "Replace the original text? ")
910 (let ((inhibit-read-only t)
912 (delete-region start end)
915 (with-output-to-temp-buffer "*Temp*"
916 (set-buffer standard-output)
919 (if (epg-context-result-for context 'verify)
920 (epa-display-info (epg-verify-result-to-string
921 (epg-context-result-for context 'verify))))))
924 (defun epa-verify-cleartext-in-region (start end)
925 "Verify OpenPGP cleartext signed messages in the current region
926 between START and END.
928 Don't use this command in Lisp programs!"
932 (narrow-to-region start end)
934 (let (cleartext-start cleartext-end)
935 (while (re-search-forward "-----BEGIN PGP SIGNED MESSAGE-----$"
937 (setq cleartext-start (match-beginning 0))
938 (unless (re-search-forward "^-----BEGIN PGP SIGNATURE-----$"
940 (error "Invalid cleartext signed message"))
941 (setq cleartext-end (re-search-forward
942 "^-----END PGP SIGNATURE-----$"
944 (unless cleartext-end
945 (error "No cleartext tail"))
946 (epa-verify-region cleartext-start cleartext-end))))))
949 (if (fboundp 'select-safe-coding-system)
950 (defalias 'epa--select-safe-coding-system 'select-safe-coding-system)
951 (defun epa--select-safe-coding-system (from to)
952 buffer-file-coding-system)))
955 (defun epa-sign-region (start end signers mode)
956 "Sign the current region between START and END by SIGNERS keys selected.
958 Don't use this command in Lisp programs!"
960 (let ((verbose current-prefix-arg))
961 (setq epa-last-coding-system-specified
962 (or coding-system-for-write
963 (epa--select-safe-coding-system
964 (region-beginning) (region-end))))
965 (list (region-beginning) (region-end)
967 (epa-select-keys (epg-make-context epa-protocol)
968 "Select keys for signing.
969 If no one is selected, default secret key is used. "
972 (epa--read-signature-type)
975 (let ((context (epg-make-context epa-protocol))
977 ;;(epg-context-set-armor context epa-armor)
978 (epg-context-set-armor context t)
979 ;;(epg-context-set-textmode context epa-textmode)
980 (epg-context-set-textmode context t)
981 (epg-context-set-signers context signers)
982 (epg-context-set-passphrase-callback context
983 #'epa-passphrase-callback-function)
984 (epg-context-set-progress-callback context
986 #'epa-progress-callback-function
988 (message "Signing...")
989 (setq signature (epg-sign-string context
990 (epa--encode-coding-string
991 (buffer-substring start end)
992 epa-last-coding-system-specified)
994 (message "Signing...done")
995 (delete-region start end)
997 (add-text-properties (point)
999 (insert (epa--decode-coding-string
1001 (or coding-system-for-read
1002 epa-last-coding-system-specified)))
1004 (list 'epa-coding-system-used
1005 epa-last-coding-system-specified
1012 (if (fboundp 'derived-mode-p)
1013 (defalias 'epa--derived-mode-p 'derived-mode-p)
1014 (defun epa--derived-mode-p (&rest modes)
1015 "Non-nil if the current major mode is derived from one of MODES.
1016 Uses the `derived-mode-parent' property of the symbol to trace backwards."
1017 (let ((parent major-mode))
1018 (while (and (not (memq parent modes))
1019 (setq parent (get parent 'derived-mode-parent))))
1023 (defun epa-encrypt-region (start end recipients sign signers)
1024 "Encrypt the current region between START and END for RECIPIENTS.
1026 Don't use this command in Lisp programs!"
1028 (let ((verbose current-prefix-arg)
1029 (context (epg-make-context epa-protocol))
1031 (setq epa-last-coding-system-specified
1032 (or coding-system-for-write
1033 (epa--select-safe-coding-system
1034 (region-beginning) (region-end))))
1035 (list (region-beginning) (region-end)
1036 (epa-select-keys context
1037 "Select recipients for encryption.
1038 If no one is selected, symmetric encryption will be performed. ")
1039 (setq sign (if verbose (y-or-n-p "Sign? ")))
1041 (epa-select-keys context
1042 "Select keys for signing. ")))))
1044 (let ((context (epg-make-context epa-protocol))
1046 ;;(epg-context-set-armor context epa-armor)
1047 (epg-context-set-armor context t)
1048 ;;(epg-context-set-textmode context epa-textmode)
1049 (epg-context-set-textmode context t)
1051 (epg-context-set-signers context signers))
1052 (epg-context-set-passphrase-callback context
1053 #'epa-passphrase-callback-function)
1054 (epg-context-set-progress-callback context
1056 #'epa-progress-callback-function
1058 (message "Encrypting...")
1059 (setq cipher (epg-encrypt-string context
1060 (epa--encode-coding-string
1061 (buffer-substring start end)
1062 epa-last-coding-system-specified)
1065 (message "Encrypting...done")
1066 (delete-region start end)
1068 (add-text-properties (point)
1072 (list 'epa-coding-system-used
1073 epa-last-coding-system-specified
1080 (defun epa-delete-keys (keys &optional allow-secret)
1081 "Delete selected KEYS.
1083 Don't use this command in Lisp programs!"
1085 (let ((keys (epa--marked-keys)))
1087 (error "No keys selected"))
1089 (eq (nth 1 epa-list-keys-arguments) t))))
1090 (let ((context (epg-make-context epa-protocol)))
1091 (message "Deleting...")
1092 (epg-delete-keys context keys allow-secret)
1093 (message "Deleting...done")
1094 (apply #'epa-list-keys epa-list-keys-arguments)))
1097 (defun epa-import-keys (file)
1098 "Import keys from FILE.
1100 Don't use this command in Lisp programs!"
1101 (interactive "fFile: ")
1102 (setq file (expand-file-name file))
1103 (let ((context (epg-make-context epa-protocol)))
1104 (message "Importing %s..." (file-name-nondirectory file))
1107 (epg-import-keys-from-file context file)
1108 (message "Importing %s...done" (file-name-nondirectory file)))
1110 (message "Importing %s...failed" (file-name-nondirectory file))))
1111 (if (epg-context-result-for context 'import)
1112 (epa-display-info (epg-import-result-to-string
1113 (epg-context-result-for context 'import))))
1114 (if (eq major-mode 'epa-key-list-mode)
1115 (apply #'epa-list-keys epa-list-keys-arguments))))
1118 (defun epa-import-keys-region (start end)
1119 "Import keys from the region.
1121 Don't use this command in Lisp programs!"
1123 (let ((context (epg-make-context epa-protocol)))
1124 (message "Importing...")
1127 (epg-import-keys-from-string context (buffer-substring start end))
1128 (message "Importing...done"))
1130 (message "Importing...failed")))
1131 (if (epg-context-result-for context 'import)
1132 (epa-display-info (epg-import-result-to-string
1133 (epg-context-result-for context 'import))))))
1136 (defun epa-import-armor-in-region (start end)
1137 "Import keys in the OpenPGP armor format in the current region
1138 between START and END.
1140 Don't use this command in Lisp programs!"
1144 (narrow-to-region start end)
1146 (let (armor-start armor-end)
1147 (while (re-search-forward
1148 "-----BEGIN \\(PGP \\(PUBLIC\\|PRIVATE\\) KEY BLOCK\\)-----$"
1150 (setq armor-start (match-beginning 0)
1151 armor-end (re-search-forward
1152 (concat "^-----END " (match-string 1) "-----$")
1155 (error "No armor tail"))
1156 (epa-import-keys-region armor-start armor-end))))))
1159 (defun epa-export-keys (keys file)
1160 "Export selected KEYS to FILE.
1162 Don't use this command in Lisp programs!"
1164 (let ((keys (epa--marked-keys))
1167 (error "No keys selected"))
1170 (concat (epg-sub-key-id (car (epg-key-sub-key-list (car keys))))
1171 (if epa-armor ".asc" ".gpg"))
1176 (concat "To file (default "
1177 (file-name-nondirectory default-name)
1179 (file-name-directory 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))))
1188 (defun epa-insert-keys (keys)
1189 "Insert selected KEYS after the point.
1191 Don't use this command in Lisp programs!"
1193 (list (epa-select-keys (epg-make-context epa-protocol)
1194 "Select keys to export. ")))
1195 (let ((context (epg-make-context epa-protocol)))
1196 ;;(epg-context-set-armor context epa-armor)
1197 (epg-context-set-armor context t)
1198 (insert (epg-export-keys-to-string context keys))))
1200 ;; (defun epa-sign-keys (keys &optional local)
1201 ;; "Sign selected KEYS.
1202 ;; If a prefix-arg is specified, the signature is marked as non exportable.
1204 ;; Don't use this command in Lisp programs!"
1206 ;; (let ((keys (epa--marked-keys)))
1208 ;; (error "No keys selected"))
1209 ;; (list keys current-prefix-arg)))
1210 ;; (let ((context (epg-make-context epa-protocol)))
1211 ;; (epg-context-set-passphrase-callback context
1212 ;; #'epa-passphrase-callback-function)
1213 ;; (epg-context-set-progress-callback context
1215 ;; #'epa-progress-callback-function
1216 ;; "Signing keys..."))
1217 ;; (message "Signing keys...")
1218 ;; (epg-sign-keys context keys local)
1219 ;; (message "Signing keys...done")))
1220 ;; (make-obsolete 'epa-sign-keys "Do not use.")
1223 (define-minor-mode epa-menu-mode
1224 "Minor mode to hook EasyPG into the menu-bar."
1225 :global t :init-value nil :group 'epa :version "23.1"
1227 (easy-menu-define epa-menu nil "EasyPG Assistant global menu"
1229 (easy-menu-remove-item nil '("Tools") "EasyPG Assistant")
1231 (easy-menu-add-item nil '("Tools") epa-menu)))
1234 (define-minor-mode epa-mode
1235 "Minor mode to hook EasyPG into various modes.
1236 See `epa-global-minor-modes'."
1237 :global t :init-value nil :group 'epa :version "23.1"
1238 (let ((modes epa-global-minor-modes)
1241 (setq symbol (car modes))
1244 (funcall symbol (if epa-mode 1 0))
1245 (message "`%S' not found" (car modes)))
1246 (setq modes (cdr modes)))))
1250 ;; arch-tag: 38d20ced-20d5-4137-b17a-f206335423d7
1251 ;;; epa.el ends here