OSDN Git Service

* epa-file.el (epa-file-insert-file-contents): If a file is not
[epg/epg.git] / epa.el
diff --git a/epa.el b/epa.el
index 2dab9a1..a53db29 100644 (file)
--- a/epa.el
+++ b/epa.el
 (require 'font-lock)
 (require 'widget)
 (eval-when-compile (require 'wid-edit))
+(require 'derived)
 
 (defgroup epa nil
   "The EasyPG Assistant"
   :group 'epg)
 
-(defcustom epa-protocol 'OpenPGP
-  "The default protocol."
-  :type '(choice (const :tag "OpenPGP" OpenPGP)
-                (const :tag "CMS" CMS))
-  :group 'epa)
-
-(defcustom epa-armor nil
-  "If non-nil, epa commands create ASCII armored output."
-  :type 'boolean
-  :group 'epa)
-
-(defcustom epa-textmode nil
-  "If non-nil, epa commands treat input files as text."
-  :type 'boolean
-  :group 'epa)
-
 (defcustom epa-popup-info-window t
   "If non-nil, status information from epa commands is displayed on
 the separate window."
@@ -63,39 +48,35 @@ the separate window."
   "Faces for epa-mode."
   :group 'epa)
 
-(defface epa-validity-high-face
+(defface epa-validity-high
   '((((class color) (background dark))
      (:foreground "PaleTurquoise" :bold t))
     (t
      (:bold t)))
   "Face used for displaying the high validity."
   :group 'epa-faces)
-(defvar epa-validity-high-face 'epa-validity-high-face)
 
-(defface epa-validity-medium-face
+(defface epa-validity-medium
   '((((class color) (background dark))
      (:foreground "PaleTurquoise" :italic t))
     (t
      ()))
   "Face used for displaying the medium validity."
   :group 'epa-faces)
-(defvar epa-validity-medium-face 'epa-validity-medium-face)
 
-(defface epa-validity-low-face
+(defface epa-validity-low
   '((t
      (:italic t)))
   "Face used for displaying the low validity."
   :group 'epa-faces)
-(defvar epa-validity-low-face 'epa-validity-low-face)
 
-(defface epa-validity-disabled-face
+(defface epa-validity-disabled
   '((t
      (:italic t :inverse-video t)))
   "Face used for displaying the disabled validity."
   :group 'epa-faces)
-(defvar epa-validity-disabled-face 'epa-validity-disabled-face)
 
-(defface epa-string-face
+(defface epa-string
   '((((class color)
       (background dark))
      (:foreground "lightyellow"))
@@ -106,55 +87,51 @@ the separate window."
      ()))
   "Face used for displaying the string."
   :group 'epa-faces)
-(defvar epa-string-face 'epa-string-face)
 
-(defface epa-mark-face
+(defface epa-mark
   '((((class color) (background dark))
      (:foreground "orange" :bold t))
     (t
      (:foreground "red" :bold t)))
   "Face used for displaying the high validity."
   :group 'epa-faces)
-(defvar epa-mark-face 'epa-mark-face)
 
-(defface epa-field-name-face
+(defface epa-field-name
   '((((class color) (background dark))
      (:foreground "PaleTurquoise" :bold t))
     (t (:bold t)))
   "Face for the name of the attribute field."
   :group 'epa)
-(defvar epa-field-name-face 'epa-field-name-face)
 
-(defface epa-field-body-face
+(defface epa-field-body
   '((((class color) (background dark))
      (:foreground "turquoise" :italic t))
     (t (:italic t)))
   "Face for the body of the attribute field."
   :group 'epa)
-(defvar epa-field-body-face 'epa-field-body-face)
 
 (defcustom epa-validity-face-alist
-  '((unknown . epa-validity-disabled-face)
-    (invalid . epa-validity-disabled-face)
-    (disabled . epa-validity-disabled-face)
-    (revoked . epa-validity-disabled-face)
-    (expired . epa-validity-disabled-face)
-    (none . epa-validity-low-face)
-    (undefined . epa-validity-low-face)
-    (never . epa-validity-low-face)
-    (marginal . epa-validity-medium-face)
-    (full . epa-validity-high-face)
-    (ultimate . epa-validity-high-face))
+  '((unknown . epa-validity-disabled)
+    (invalid . epa-validity-disabled)
+    (disabled . epa-validity-disabled)
+    (revoked . epa-validity-disabled)
+    (expired . epa-validity-disabled)
+    (none . epa-validity-low)
+    (undefined . epa-validity-low)
+    (never . epa-validity-low)
+    (marginal . epa-validity-medium)
+    (full . epa-validity-high)
+    (ultimate . epa-validity-high))
   "An alist mapping validity values to faces."
   :type 'list
   :group 'epa)
 
 (defcustom epa-font-lock-keywords
   '(("^\\*"
-     (0 epa-mark-face))
+     (0 'epa-mark))
     ("^\t\\([^\t:]+:\\)[ \t]*\\(.*\\)$"
-     (1 epa-field-name-face)
-     (2 epa-field-body-face)))
+     (1 'epa-field-name)
+     (2 'epa-field-body)))
   "Default expressions to addon in epa-mode."
   :type '(repeat (list string))
   :group 'epa)
@@ -167,6 +144,22 @@ the separate window."
     (17 . ?D)
     (20 . ?G)))
 
+(defvar epa-protocol 'OpenPGP
+  "*The default protocol.
+The value can be either OpenPGP or CMS.
+
+You should bind this variable with `let', but do not set it globally.")
+
+(defvar epa-armor nil
+  "*If non-nil, epa commands create ASCII armored output.
+
+You should bind this variable with `let', but do not set it globally.")
+
+(defvar epa-textmode nil
+  "*If non-nil, epa commands treat input files as text.
+
+You should bind this variable with `let', but do not set it globally.")
+
 (defvar epa-keys-buffer nil)
 (defvar epa-key-buffer-alist nil)
 (defvar epa-key nil)
@@ -174,10 +167,10 @@ the separate window."
 (defvar epa-info-buffer nil)
 (defvar epa-last-coding-system-specified nil)
 
-(defvar epa-keys-mode-map
+(defvar epa-key-list-mode-map
   (let ((keymap (make-sparse-keymap)))
-    (define-key keymap "m" 'epa-mark)
-    (define-key keymap "u" 'epa-unmark)
+    (define-key keymap "m" 'epa-mark-key)
+    (define-key keymap "u" 'epa-unmark-key)
     (define-key keymap "d" 'epa-decrypt-file)
     (define-key keymap "v" 'epa-verify-file)
     (define-key keymap "s" 'epa-sign-file)
@@ -185,7 +178,7 @@ the separate window."
     (define-key keymap "r" 'epa-delete-keys)
     (define-key keymap "i" 'epa-import-keys)
     (define-key keymap "o" 'epa-export-keys)
-    (define-key keymap "g" 'epa-list-keys)
+    (define-key keymap "g" 'revert-buffer)
     (define-key keymap "n" 'next-line)
     (define-key keymap "p" 'previous-line)
     (define-key keymap " " 'scroll-up)
@@ -195,7 +188,7 @@ the separate window."
 
 (defvar epa-key-mode-map
   (let ((keymap (make-sparse-keymap)))
-    (define-key keymap "q" 'bury-buffer)
+    (define-key keymap "q" 'epa-exit-buffer)
     keymap))
 
 (defvar epa-info-mode-map
@@ -245,30 +238,34 @@ the separate window."
          (epg-sub-key-id (car (epg-key-sub-key-list
                                (widget-get widget :value))))))
 
-(if (fboundp 'encode-coding-string)
-    (defalias 'epa--encode-coding-string 'encode-coding-string)
-  (defalias 'epa--encode-coding-string 'identity))
+(eval-and-compile
+  (if (fboundp 'encode-coding-string)
+      (defalias 'epa--encode-coding-string 'encode-coding-string)
+    (defalias 'epa--encode-coding-string 'identity)))
 
-(if (fboundp 'decode-coding-string)
-    (defalias 'epa--decode-coding-string 'decode-coding-string)
-  (defalias 'epa--decode-coding-string 'identity))
+(eval-and-compile
+  (if (fboundp 'decode-coding-string)
+      (defalias 'epa--decode-coding-string 'decode-coding-string)
+    (defalias 'epa--decode-coding-string 'identity)))
 
-(defun epa-keys-mode ()
+(defun epa-key-list-mode ()
   "Major mode for `epa-list-keys'."
   (kill-all-local-variables)
   (buffer-disable-undo)
-  (setq major-mode 'epa-keys-mode
+  (setq major-mode 'epa-key-list-mode
        mode-name "Keys"
        truncate-lines t
        buffer-read-only t)
-  (use-local-map epa-keys-mode-map)
+  (use-local-map epa-key-list-mode-map)
   (make-local-variable 'font-lock-defaults)
   (setq font-lock-defaults '(epa-font-lock-keywords t))
   ;; In XEmacs, auto-initialization of font-lock is not effective
   ;; if buffer-file-name is not set.
   (font-lock-set-defaults)
   (make-local-variable 'epa-exit-buffer-function)
-  (run-hooks 'epa-keys-mode-hook))
+  (make-local-variable 'revert-buffer-function)
+  (setq revert-buffer-function 'epa--key-list-revert-buffer)
+  (run-hooks 'epa-key-list-mode-hook))
 
 (defun epa-key-mode ()
   "Major mode for a key description."
@@ -298,30 +295,27 @@ the separate window."
   (use-local-map epa-info-mode-map)
   (run-hooks 'epa-info-mode-hook))
 
-(defun epa-mark (&optional arg)
-  "Mark the current line.
-If ARG is non-nil, unmark the current line."
+(defun epa-mark-key (&optional arg)
+  "Mark a key on the current line.
+If ARG is non-nil, unmark the key."
   (interactive "P")
   (let ((inhibit-read-only t)
        buffer-read-only
        properties)
     (beginning-of-line)
+    (unless (get-text-property (point) 'epa-key)
+      (error "No key on this line"))
     (setq properties (text-properties-at (point)))
     (delete-char 1)
     (insert (if arg " " "*"))
     (set-text-properties (1- (point)) (point) properties)
     (forward-line)))
 
-(defun epa-unmark (&optional arg)
-  "Unmark the current line.
-If ARG is non-nil, mark the current line."
+(defun epa-unmark-key (&optional arg)
+  "Unmark a key on the current line.
+If ARG is non-nil, mark the key."
   (interactive "P")
-  (epa-mark (not arg)))
-
-(defun epa-toggle-mark ()
-  "Toggle the mark the current line."
-  (interactive)
-  (epa-mark (eq (char-after (save-excursion (beginning-of-line) (point))) ?*)))
+  (epa-mark-key (not arg)))
 
 (defun epa-exit-buffer ()
   "Exit the current buffer.
@@ -329,49 +323,11 @@ If ARG is non-nil, mark the current line."
   (interactive)
   (funcall epa-exit-buffer-function))
 
-;;;###autoload
-(defun epa-list-keys (&optional name mode)
-  "List all keys matched with NAME from the keyring.
-If MODE is non-nil, it reads the private keyring.  Otherwise, it
-reads the public keyring."
-  (interactive
-   (if current-prefix-arg
-       (let ((name (read-string "Pattern: "
-                               (if epa-list-keys-arguments
-                                   (car epa-list-keys-arguments)))))
-        (list (if (equal name "") nil name)
-              (y-or-n-p "Secret keys? ")))
-     (or epa-list-keys-arguments (list nil nil))))
-  (unless (and epa-keys-buffer
-              (buffer-live-p epa-keys-buffer))
-    (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
-  (set-buffer epa-keys-buffer)
-  (let ((inhibit-read-only t)
-       buffer-read-only
-       (point (point-min))
-       (context (epg-make-context epa-protocol)))
-    (unless (get-text-property point 'epa-list-keys)
-      (setq point (next-single-property-change point 'epa-list-keys)))
-    (when point
-      (delete-region point
-                    (or (next-single-property-change point 'epa-list-keys)
-                        (point-max)))
-      (goto-char point))
-    (epa--insert-keys context name mode)
-    (epa-keys-mode)
-    (widget-setup)
-    (set-keymap-parent (current-local-map) widget-keymap))
-  (make-local-variable 'epa-list-keys-arguments)
-  (setq epa-list-keys-arguments (list name mode))
-  (goto-char (point-min))
-  (pop-to-buffer (current-buffer)))
-
-(defun epa--insert-keys (context name mode)
+(defun epa--insert-keys (keys)
   (save-excursion
     (save-restriction
       (narrow-to-region (point) (point))
-      (let ((keys (epg-list-keys context name mode))
-           point)
+      (let (point)
        (while keys
          (setq point (point))
          (insert "  ")
@@ -391,6 +347,58 @@ reads the public keyring."
                                 'start-open t
                                 'end-open t)))))
 
+(defun epa--list-keys (name secret)
+  (unless (and epa-keys-buffer
+              (buffer-live-p epa-keys-buffer))
+    (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
+  (set-buffer epa-keys-buffer)
+  (epa-key-list-mode)
+  (let ((inhibit-read-only t)
+       buffer-read-only
+       (point (point-min))
+       (context (epg-make-context epa-protocol)))
+    (unless (get-text-property point 'epa-list-keys)
+      (setq point (next-single-property-change point 'epa-list-keys)))
+    (when point
+      (delete-region point
+                    (or (next-single-property-change point 'epa-list-keys)
+                        (point-max)))
+      (goto-char point))
+    (epa--insert-keys (epg-list-keys context name secret))
+    (widget-setup)
+    (set-keymap-parent (current-local-map) widget-keymap))
+  (make-local-variable 'epa-list-keys-arguments)
+  (setq epa-list-keys-arguments (list name secret))
+  (goto-char (point-min))
+  (pop-to-buffer (current-buffer)))
+
+;;;###autoload
+(defun epa-list-keys (&optional name)
+  "List all keys matched with NAME from the public keyring."
+  (interactive
+   (if current-prefix-arg
+       (let ((name (read-string "Pattern: "
+                               (if epa-list-keys-arguments
+                                   (car epa-list-keys-arguments)))))
+        (list (if (equal name "") nil name)))
+     (list nil)))
+  (epa--list-keys name nil))
+
+;;;###autoload
+(defun epa-list-secret-keys (&optional name)
+  "List all keys matched with NAME from the private keyring."
+  (interactive
+   (if current-prefix-arg
+       (let ((name (read-string "Pattern: "
+                               (if epa-list-keys-arguments
+                                   (car epa-list-keys-arguments)))))
+        (list (if (equal name "") nil name)))
+     (list nil)))
+  (epa--list-keys name t))
+
+(defun epa--key-list-revert-buffer (&optional ignore-auto noconfirm)
+  (apply #'epa--list-keys epa-list-keys-arguments))
+
 (defun epa--marked-keys ()
   (or (save-excursion
        (set-buffer epa-keys-buffer)
@@ -407,23 +415,20 @@ reads the public keyring."
          (if key
              (list key))))))
 
-;;;###autoload
-(defun epa-select-keys (context prompt &optional names secret)
-  "Display a user's keyring and ask him to select keys.
-CONTEXT is an epg-context.
-PROMPT is a string to prompt with.
-NAMES is a list of strings to be matched with keys.  If it is nil, all
-the keys are listed.
-If SECRET is non-nil, list secret keys instead of public keys."
+(defun epa--select-keys (prompt keys)
   (save-excursion
     (unless (and epa-keys-buffer
                 (buffer-live-p epa-keys-buffer))
       (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
+    (set-buffer epa-keys-buffer)
+    (epa-key-list-mode)
     (let ((inhibit-read-only t)
          buffer-read-only)
-      (set-buffer epa-keys-buffer)
       (erase-buffer)
-      (insert prompt "\n")
+      (insert prompt "\n"
+             (substitute-command-keys "\
+- `\\[epa-mark-key]' to mark a key on the line
+- `\\[epa-unmark-key]' to unmark a key on the line\n"))
       (widget-create 'link
                     :notify (lambda (&rest ignore) (abort-recursive-edit))
                     :help-echo
@@ -437,20 +442,7 @@ If SECRET is non-nil, list secret keys instead of public keys."
                      "Click here or \\[exit-recursive-edit] to finish")
                     "OK")
       (insert "\n\n")
-      (if names
-         (while names
-           (epa--insert-keys context (car names) secret)
-           (if (get-text-property (point) 'epa-list-keys)
-               (epa-mark))
-           (goto-char (point-max))
-           (setq names (cdr names)))
-       (if secret
-           (progn
-             (epa--insert-keys context nil secret)
-             (if (get-text-property (point) 'epa-list-keys)
-                 (epa-mark)))
-         (epa--insert-keys context nil nil)))
-      (epa-keys-mode)
+      (epa--insert-keys keys)
       (widget-setup)
       (set-keymap-parent (current-local-map) widget-keymap)
       (setq epa-exit-buffer-function #'abort-recursive-edit)
@@ -464,6 +456,19 @@ If SECRET is non-nil, list secret keys instead of public keys."
          (delete-window (get-buffer-window epa-keys-buffer)))
       (kill-buffer epa-keys-buffer))))
 
+;;;###autoload
+(defun epa-select-keys (context prompt &optional names secret)
+  "Display a user's keyring and ask him to select keys.
+CONTEXT is an epg-context.
+PROMPT is a string to prompt with.
+NAMES is a list of strings to be matched with keys.  If it is nil, all
+the keys are listed.
+If SECRET is non-nil, list secret keys instead of public keys."
+  (let ((keys (epg-list-keys context names secret)))
+    (if (> (length keys) 1)
+       (epa--select-keys prompt keys)
+      keys)))
+
 (defun epa--format-fingerprint-1 (fingerprint unit-size block-size)
   (let ((unit 0))
     (with-temp-buffer
@@ -499,6 +504,7 @@ If SECRET is non-nil, list secret keys instead of public keys."
       (setcdr entry (generate-new-buffer
                     (format "*Key*%s" (epg-sub-key-id primary-sub-key)))))
     (set-buffer (cdr entry))
+    (epa-key-mode)
     (make-local-variable 'epa-key)
     (setq epa-key key)
     (erase-buffer)
@@ -551,14 +557,15 @@ If SECRET is non-nil, list secret keys instead of public keys."
              "\n")
       (setq pointer (cdr pointer)))
     (goto-char (point-min))
-    (pop-to-buffer (current-buffer))
-    (epa-key-mode)))
+    (pop-to-buffer (current-buffer))))
 
 (defun epa-display-info (info)
   (if epa-popup-info-window
       (save-selected-window
-       (unless epa-info-buffer
+       (unless (and epa-info-buffer (buffer-live-p epa-info-buffer))
          (setq epa-info-buffer (generate-new-buffer "*Info*")))
+       (if (get-buffer-window epa-info-buffer)
+           (delete-window (get-buffer-window epa-info-buffer)))
        (save-excursion
          (set-buffer epa-info-buffer)
          (let ((inhibit-read-only t)
@@ -595,7 +602,8 @@ If SECRET is non-nil, list secret keys instead of public keys."
 
 (defun epa-progress-callback-function (context what char current total
                                               handback)
-  (message "%s: %d%% (%d/%d)" what
+  (message "%s%d%% (%d/%d)" (or handback
+                               (concat what ": "))
           (if (> total 0) (floor (* (/ current (float total)) 100)) 0)
           current total))
 
@@ -616,7 +624,9 @@ If SECRET is non-nil, list secret keys instead of public keys."
     (epg-context-set-passphrase-callback context
                                         #'epa-passphrase-callback-function)
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      (format "Decrypting %s..."
+                                              (file-name-nondirectory file)))
     (message "Decrypting %s..." (file-name-nondirectory file))
     (epg-decrypt-file context file plain)
     (message "Decrypting %s...wrote %s" (file-name-nondirectory file)
@@ -634,7 +644,9 @@ If SECRET is non-nil, list secret keys instead of public keys."
         (plain (if (equal (file-name-extension file) "sig")
                    (file-name-sans-extension file))))
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      (format "Verifying %s..."
+                                              (file-name-nondirectory file)))
     (message "Verifying %s..." (file-name-nondirectory file))
     (epg-verify-file context file plain)
     (message "Verifying %s...done" (file-name-nondirectory file))
@@ -642,35 +654,42 @@ If SECRET is non-nil, list secret keys instead of public keys."
        (epa-display-info (epg-verify-result-to-string
                           (epg-context-result-for context 'verify))))))
 
-;;;###autoload
-(defun epa-sign-file (file signers mode)
-  "Sign FILE by SIGNERS keys selected."
-  (interactive
-   (list (expand-file-name (read-file-name "File: "))
-        (epa-select-keys (epg-make-context epa-protocol)
-                         "Select keys for signing.
-If no one is selected, default secret key is used.  "
-                         nil t)
-        (catch 'done
-          (while t
-            (message "Signature type (n,c,d,?) ")
-            (let ((c (read-char)))
-              (cond ((eq c ?c)
-                     (throw 'done 'clear))
-                    ((eq c ?d)
-                     (throw 'done 'detached))
-                    ((eq c ??)
-                     (with-output-to-temp-buffer "*Help*"
-                       (save-excursion
-                         (set-buffer standard-output)
-                         (insert "\
+(defun epa--read-signature-type ()
+  (let (type c)
+    (while (null type)
+      (message "Signature type (n,c,d,?) ")
+      (setq c (read-char))
+      (cond ((eq c ?c)
+            (setq type 'clear))
+           ((eq c ?d)
+            (setq type 'detached))
+           ((eq c ??)
+            (with-output-to-temp-buffer "*Help*"
+              (save-excursion
+                (set-buffer standard-output)
+                (insert "\
 n - Create a normal signature
 c - Create a cleartext signature
 d - Create a detached signature
 ? - Show this help
 "))))
-                    (t
-                     (throw 'done nil))))))))
+           (t
+            (setq type 'normal))))))
+
+;;;###autoload
+(defun epa-sign-file (file signers mode)
+  "Sign FILE by SIGNERS keys selected."
+  (interactive
+   (let ((verbose current-prefix-arg))
+     (list (expand-file-name (read-file-name "File: "))
+          (if verbose
+              (epa-select-keys (epg-make-context epa-protocol)
+                               "Select keys for signing.
+If no one is selected, default secret key is used.  "
+                               nil t))
+          (if verbose
+              (epa--read-signature-type)
+            'clear))))
   (let ((signature (concat file
                           (if (eq epa-protocol 'OpenPGP)
                               (if (or epa-armor
@@ -690,7 +709,9 @@ d - Create a detached signature
     (epg-context-set-passphrase-callback context
                                         #'epa-passphrase-callback-function)
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      (format "Signing %s..."
+                                              (file-name-nondirectory file)))
     (message "Signing %s..." (file-name-nondirectory file))
     (epg-sign-file context file signature mode)
     (message "Signing %s...wrote %s" (file-name-nondirectory file)
@@ -713,7 +734,9 @@ If no one is selected, symmetric encryption will be performed.  ")))
     (epg-context-set-passphrase-callback context
                                         #'epa-passphrase-callback-function)
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      (format "Encrypting %s..."
+                                              (file-name-nondirectory file)))
     (message "Encrypting %s..." (file-name-nondirectory file))
     (epg-encrypt-file context file recipients cipher)
     (message "Encrypting %s...wrote %s" (file-name-nondirectory file)
@@ -731,20 +754,40 @@ Don't use this command in Lisp programs!"
       (epg-context-set-passphrase-callback context
                                           #'epa-passphrase-callback-function)
       (epg-context-set-progress-callback context
-                                        #'epa-progress-callback-function)
+                                        #'epa-progress-callback-function
+                                        "Decrypting...")
       (message "Decrypting...")
       (setq plain (epg-decrypt-string context (buffer-substring start end)))
       (message "Decrypting...done")
-      (delete-region start end)
-      (goto-char start)
-      (insert (epa--decode-coding-string plain
-                                        (or coding-system-for-read
-                                            (get-text-property
-                                             start 'epa-coding-system-used))))
+      (setq plain (epa--decode-coding-string
+                  plain
+                  (or coding-system-for-read
+                      (get-text-property start 'epa-coding-system-used))))
+      (if (y-or-n-p "Replace the original text? ")
+         (let ((inhibit-read-only t)
+               buffer-read-only)
+           (delete-region start end)
+           (goto-char start)
+           (insert plain))
+       (with-output-to-temp-buffer "*Temp*"
+         (set-buffer standard-output)
+         (insert plain)
+         (epa-info-mode)))
       (if (epg-context-result-for context 'verify)
          (epa-display-info (epg-verify-result-to-string
                             (epg-context-result-for context 'verify)))))))
 
+(defun epa--find-coding-system-for-mime-charset (mime-charset)
+  (if (featurep 'xemacs)
+      (if (fboundp 'find-coding-system)
+         (find-coding-system mime-charset))
+    (let ((pointer (coding-system-list)))
+      (while (and pointer
+                 (eq (coding-system-get (car pointer) 'mime-charset)
+                     mime-charset))
+       (setq pointer (cdr pointer)))
+      pointer)))
+
 ;;;###autoload
 (defun epa-decrypt-armor-in-region (start end)
   "Decrypt OpenPGP armors in the current region between START and END.
@@ -755,7 +798,7 @@ Don't use this command in Lisp programs!"
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
-      (let (armor-start armor-end charset coding-system)
+      (let (armor-start armor-end)
        (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
          (setq armor-start (match-beginning 0)
                armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
@@ -763,20 +806,13 @@ Don't use this command in Lisp programs!"
          (unless armor-end
            (error "No armor tail"))
          (goto-char armor-start)
-         (if (re-search-forward "^Charset: \\(.*\\)" armor-end t)
-             (setq charset (match-string 1)))
-         (if coding-system-for-read
-             (setq coding-system coding-system-for-read)
-           (if charset
-               (setq coding-system (intern (downcase charset)))
-             (setq coding-system 'utf-8)))
-         (let ((coding-system-for-read coding-system))
-           (epa-decrypt-region start end)))))))
-
-(if (fboundp 'select-safe-coding-system)
-    (defalias 'epa--select-safe-coding-system 'select-safe-coding-system)
-  (defun epa--select-safe-coding-system (from to)
-    buffer-file-coding-system))
+         (let ((coding-system-for-read
+                (or coding-system-for-read
+                    (if (re-search-forward "^Charset: \\(.*\\)" armor-end t)
+                        (epa--find-coding-system-for-mime-charset
+                         (intern (downcase (match-string 1))))))))
+           (goto-char armor-end)
+           (epa-decrypt-region armor-start armor-end)))))))
 
 ;;;###autoload
 (defun epa-verify-region (start end)
@@ -784,15 +820,28 @@ Don't use this command in Lisp programs!"
 
 Don't use this command in Lisp programs!"
   (interactive "r")
-  (let ((context (epg-make-context epa-protocol)))
+  (let ((context (epg-make-context epa-protocol))
+       plain)
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
-    (epg-verify-string context
-                      (epa--encode-coding-string
-                       (buffer-substring start end)
-                       (or coding-system-for-write
-                           (get-text-property start
-                                              'epa-coding-system-used))))
+                                      #'epa-progress-callback-function
+                                      "Verifying...")
+    (setq plain (epg-verify-string
+                context
+                (epa--encode-coding-string
+                 (buffer-substring start end)
+                 (or coding-system-for-write
+                     (get-text-property start
+                                        'epa-coding-system-used)))))
+    (if (y-or-n-p "Replace the original text? ")
+       (let ((inhibit-read-only t)
+             buffer-read-only)
+         (delete-region start end)
+         (goto-char start)
+         (insert plain))
+       (with-output-to-temp-buffer "*Temp*"
+         (set-buffer standard-output)
+         (insert plain)
+         (epa-info-mode)))
     (if (epg-context-result-for context 'verify)
        (epa-display-info (epg-verify-result-to-string
                           (epg-context-result-for context 'verify))))))
@@ -808,19 +857,25 @@ Don't use this command in Lisp programs!"
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
-      (let (armor-start armor-end)
+      (let (cleartext-start cleartext-end)
        (while (re-search-forward "-----BEGIN PGP SIGNED MESSAGE-----$"
                                  nil t)
-         (setq armor-start (match-beginning 0))
+         (setq cleartext-start (match-beginning 0))
          (unless (re-search-forward "^-----BEGIN PGP SIGNATURE-----$"
                                           nil t)
            (error "Invalid cleartext signed message"))
-         (setq armor-end (re-search-forward
+         (setq cleartext-end (re-search-forward
                           "^-----END PGP SIGNATURE-----$"
                           nil t))
-         (unless armor-end
-           (error "No armor tail"))
-         (epa-verify-region armor-start armor-end))))))
+         (unless cleartext-end
+           (error "No cleartext tail"))
+         (epa-verify-region cleartext-start cleartext-end))))))
+
+(eval-and-compile
+  (if (fboundp 'select-safe-coding-system)
+      (defalias 'epa--select-safe-coding-system 'select-safe-coding-system)
+    (defun epa--select-safe-coding-system (from to)
+      buffer-file-coding-system)))
 
 ;;;###autoload
 (defun epa-sign-region (start end signers mode)
@@ -828,36 +883,20 @@ Don't use this command in Lisp programs!"
 
 Don't use this command in Lisp programs!"
   (interactive
-   (progn
+   (let ((verbose current-prefix-arg))
      (setq epa-last-coding-system-specified
           (or coding-system-for-write
               (epa--select-safe-coding-system
                (region-beginning) (region-end))))
      (list (region-beginning) (region-end)
-          (epa-select-keys (epg-make-context epa-protocol)
-                           "Select keys for signing.
+          (if verbose
+              (epa-select-keys (epg-make-context epa-protocol)
+                               "Select keys for signing.
 If no one is selected, default secret key is used.  "
-                           nil t)
-          (catch 'done
-            (while t
-              (message "Signature type (n,c,d,?) ")
-              (let ((c (read-char)))
-                (cond ((eq c ?c)
-                       (throw 'done 'clear))
-                      ((eq c ?d)
-                       (throw 'done 'detached))
-                      ((eq c ??)
-                       (with-output-to-temp-buffer "*Help*"
-                         (save-excursion
-                           (set-buffer standard-output)
-                           (insert "\
-n - Create a normal signature
-c - Create a cleartext signature
-d - Create a detached signature
-? - Show this help
-"))))
-                      (t
-                       (throw 'done nil)))))))))
+                               nil t))
+          (if verbose
+              (epa--read-signature-type)
+            'clear))))
   (save-excursion
     (let ((context (epg-make-context epa-protocol))
          signature)
@@ -869,7 +908,8 @@ d - Create a detached signature
       (epg-context-set-passphrase-callback context
                                           #'epa-passphrase-callback-function)
       (epg-context-set-progress-callback context
-                                        #'epa-progress-callback-function)
+                                        #'epa-progress-callback-function
+                                        "Signing...")
       (message "Signing...")
       (setq signature (epg-sign-string context
                                       (epa--encode-coding-string
@@ -878,6 +918,7 @@ d - Create a detached signature
                                       mode))
       (message "Signing...done")
       (delete-region start end)
+      (goto-char start)
       (add-text-properties (point)
                           (progn
                             (insert (epa--decode-coding-string
@@ -892,21 +933,38 @@ d - Create a detached signature
                                 'start-open t
                                 'end-open t)))))
 
+(eval-and-compile
+  (if (fboundp 'derived-mode-p)
+      (defalias 'epa--derived-mode-p 'derived-mode-p)
+    (defun epa--derived-mode-p (&rest modes)
+      "Non-nil if the current major mode is derived from one of MODES.
+Uses the `derived-mode-parent' property of the symbol to trace backwards."
+      (let ((parent major-mode))
+       (while (and (not (memq parent modes))
+                   (setq parent (get parent 'derived-mode-parent))))
+       parent))))
+
 ;;;###autoload
-(defun epa-encrypt-region (start end recipients)
+(defun epa-encrypt-region (start end recipients sign signers)
   "Encrypt the current region between START and END for RECIPIENTS.
 
 Don't use this command in Lisp programs!"
   (interactive
-   (progn
+   (let ((verbose current-prefix-arg)
+        (context (epg-make-context epa-protocol))
+        sign)
      (setq epa-last-coding-system-specified
           (or coding-system-for-write
               (epa--select-safe-coding-system
                (region-beginning) (region-end))))
      (list (region-beginning) (region-end)
-          (epa-select-keys (epg-make-context epa-protocol)
+          (epa-select-keys context
                            "Select recipients for encryption.
-If no one is selected, symmetric encryption will be performed.  "))))
+If no one is selected, symmetric encryption will be performed.  ")
+          (setq sign (if verbose (y-or-n-p "Sign? ")))
+          (if sign
+              (epa-select-keys context
+                               "Select keys for signing.  ")))))
   (save-excursion
     (let ((context (epg-make-context epa-protocol))
          cipher)
@@ -914,18 +972,23 @@ If no one is selected, symmetric encryption will be performed.  "))))
       (epg-context-set-armor context t)
       ;;(epg-context-set-textmode context epa-textmode)
       (epg-context-set-textmode context t)
+      (if sign
+         (epg-context-set-signers context signers))
       (epg-context-set-passphrase-callback context
                                           #'epa-passphrase-callback-function)
       (epg-context-set-progress-callback context
-                                        #'epa-progress-callback-function)
+                                        #'epa-progress-callback-function
+                                        "Encrypting...")
       (message "Encrypting...")
       (setq cipher (epg-encrypt-string context
                                       (epa--encode-coding-string
                                        (buffer-substring start end)
                                        epa-last-coding-system-specified)
-                                      recipients))
+                                      recipients
+                                      sign))
       (message "Encrypting...done")
       (delete-region start end)
+      (goto-char start)
       (add-text-properties (point)
                           (progn
                             (insert cipher)
@@ -972,7 +1035,7 @@ Don't use this command in Lisp programs!"
     (if (epg-context-result-for context 'import)
        (epa-display-info (epg-import-result-to-string
                           (epg-context-result-for context 'import))))
-    (if (eq major-mode 'epa-keys-mode)
+    (if (eq major-mode 'epa-key-list-mode)
        (apply #'epa-list-keys epa-list-keys-arguments))))
 
 ;;;###autoload
@@ -994,6 +1057,29 @@ Don't use this command in Lisp programs!"
                           (epg-context-result-for context 'import))))))
 
 ;;;###autoload
+(defun epa-import-armor-in-region (start end)
+  "Import keys in the OpenPGP armor format in the current region
+between START and END.
+
+Don't use this command in Lisp programs!"
+  (interactive "r")
+  (save-excursion
+    (save-restriction
+      (narrow-to-region start end)
+      (goto-char start)
+      (let (armor-start armor-end)
+       (while (re-search-forward
+               "-----BEGIN \\(PGP \\(PUBLIC\\|PRIVATE\\) KEY BLOCK\\)-----$"
+               nil t)
+         (setq armor-start (match-beginning 0)
+               armor-end (re-search-forward
+                          (concat "^-----END " (match-string 1) "-----$")
+                          nil t))
+         (unless armor-end
+           (error "No armor tail"))
+         (epa-import-keys-region armor-start armor-end))))))
+
+;;;###autoload
 (defun epa-export-keys (keys file)
   "Export selected KEYS to FILE.
 
@@ -1050,7 +1136,8 @@ Don't use this command in Lisp programs!"
     (epg-context-set-passphrase-callback context
                                         #'epa-passphrase-callback-function)
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      "Signing keys...")
     (message "Signing keys...")
     (epg-sign-keys context keys local)
     (message "Signing keys...done")))