* epg.el (epg--start): Make the process-buffer unibyte.
[elisp/epg.git] / epa.el
diff --git a/epa.el b/epa.el
index a88f59b..85421f2 100644 (file)
--- a/epa.el
+++ b/epa.el
@@ -27,6 +27,7 @@
 (require 'font-lock)
 (require 'widget)
 (eval-when-compile (require 'wid-edit))
 (require 'font-lock)
 (require 'widget)
 (eval-when-compile (require 'wid-edit))
+(require 'derived)
 
 (defgroup epa nil
   "The EasyPG Assistant"
 
 (defgroup epa nil
   "The EasyPG Assistant"
@@ -172,8 +173,9 @@ the separate window."
 (defvar epa-key nil)
 (defvar epa-list-keys-arguments nil)
 (defvar epa-info-buffer nil)
 (defvar epa-key nil)
 (defvar epa-list-keys-arguments nil)
 (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)
   (let ((keymap (make-sparse-keymap)))
     (define-key keymap "m" 'epa-mark)
     (define-key keymap "u" 'epa-unmark)
@@ -252,22 +254,22 @@ the separate window."
     (defalias 'epa--decode-coding-string 'decode-coding-string)
   (defalias 'epa--decode-coding-string 'identity))
 
     (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)
   "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)
        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)
   (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))
+  (run-hooks 'epa-key-list-mode-hook))
 
 (defun epa-key-mode ()
   "Major mode for a key description."
 
 (defun epa-key-mode ()
   "Major mode for a key description."
@@ -345,6 +347,7 @@ reads the public keyring."
               (buffer-live-p epa-keys-buffer))
     (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
   (set-buffer 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))
   (let ((inhibit-read-only t)
        buffer-read-only
        (point (point-min))
@@ -357,7 +360,6 @@ reads the public keyring."
                         (point-max)))
       (goto-char point))
     (epa--insert-keys context name mode)
                         (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)
     (widget-setup)
     (set-keymap-parent (current-local-map) widget-keymap))
   (make-local-variable 'epa-list-keys-arguments)
@@ -418,11 +420,15 @@ If SECRET is non-nil, list secret keys instead of public keys."
     (unless (and epa-keys-buffer
                 (buffer-live-p epa-keys-buffer))
       (setq epa-keys-buffer (generate-new-buffer "*Keys*")))
     (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)
     (let ((inhibit-read-only t)
          buffer-read-only)
-      (set-buffer epa-keys-buffer)
       (erase-buffer)
       (erase-buffer)
-      (insert prompt "\n")
+      (insert prompt "\n"
+             (substitute-command-keys "\
+- `\\[epa-mark]' to mark a key on the line
+- `\\[epa-unmark]' to unmark a key on the line\n"))
       (widget-create 'link
                     :notify (lambda (&rest ignore) (abort-recursive-edit))
                     :help-echo
       (widget-create 'link
                     :notify (lambda (&rest ignore) (abort-recursive-edit))
                     :help-echo
@@ -449,7 +455,6 @@ If SECRET is non-nil, list secret keys instead of public keys."
              (if (get-text-property (point) 'epa-list-keys)
                  (epa-mark)))
          (epa--insert-keys context nil nil)))
              (if (get-text-property (point) 'epa-list-keys)
                  (epa-mark)))
          (epa--insert-keys context nil nil)))
-      (epa-keys-mode)
       (widget-setup)
       (set-keymap-parent (current-local-map) widget-keymap)
       (setq epa-exit-buffer-function #'abort-recursive-edit)
       (widget-setup)
       (set-keymap-parent (current-local-map) widget-keymap)
       (setq epa-exit-buffer-function #'abort-recursive-edit)
@@ -463,6 +468,26 @@ 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))))
 
          (delete-window (get-buffer-window epa-keys-buffer)))
       (kill-buffer epa-keys-buffer))))
 
+(defun epa--format-fingerprint-1 (fingerprint unit-size block-size)
+  (let ((unit 0))
+    (with-temp-buffer
+      (insert fingerprint)
+      (goto-char (point-min))
+      (while (progn
+              (goto-char (+ (point) unit-size))
+              (not (eobp)))
+       (setq unit (1+ unit))
+       (insert (if (= (% unit block-size) 0) "  " " ")))
+      (buffer-string))))
+
+(defun epa--format-fingerprint (fingerprint)
+  (if fingerprint
+      (if (= (length fingerprint) 40)
+         ;; 1234 5678 9ABC DEF0 1234  5678 9ABC DEF0 1234 5678
+         (epa--format-fingerprint-1 fingerprint 4 5)
+       ;; 12 34 56 78 9A BC DE F0  12 34 56 78 9A BC DE F0
+       (epa--format-fingerprint-1 fingerprint 2 8))))
+
 (defun epa--show-key (key)
   (let* ((primary-sub-key (car (epg-key-sub-key-list key)))
         (entry (assoc (epg-sub-key-id primary-sub-key)
 (defun epa--show-key (key)
   (let* ((primary-sub-key (car (epg-key-sub-key-list key)))
         (entry (assoc (epg-sub-key-id primary-sub-key)
@@ -478,6 +503,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))
       (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)
     (make-local-variable 'epa-key)
     (setq epa-key key)
     (erase-buffer)
@@ -526,18 +552,19 @@ If SECRET is non-nil, list secret keys instead of public keys."
                         (epg-sub-key-capability (car pointer))
                         " ")
              "\n\tFingerprint: "
                         (epg-sub-key-capability (car pointer))
                         " ")
              "\n\tFingerprint: "
-             (epg-sub-key-fingerprint (car pointer))
+             (epa--format-fingerprint (epg-sub-key-fingerprint (car pointer)))
              "\n")
       (setq pointer (cdr pointer)))
     (goto-char (point-min))
              "\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
          (setq epa-info-buffer (generate-new-buffer "*Info*")))
 
 (defun epa-display-info (info)
   (if epa-popup-info-window
       (save-selected-window
        (unless 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)
        (save-excursion
          (set-buffer epa-info-buffer)
          (let ((inhibit-read-only t)
@@ -574,7 +601,8 @@ If SECRET is non-nil, list secret keys instead of public keys."
 
 (defun epa-progress-callback-function (context what char current total
                                               handback)
 
 (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))
 
           (if (> total 0) (floor (* (/ current (float total)) 100)) 0)
           current total))
 
@@ -595,7 +623,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
     (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)
     (message "Decrypting %s..." (file-name-nondirectory file))
     (epg-decrypt-file context file plain)
     (message "Decrypting %s...wrote %s" (file-name-nondirectory file)
@@ -613,7 +643,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
         (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))
     (message "Verifying %s..." (file-name-nondirectory file))
     (epg-verify-file context file plain)
     (message "Verifying %s...done" (file-name-nondirectory file))
@@ -621,35 +653,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))))))
 
        (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
 "))))
 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
   (let ((signature (concat file
                           (if (eq epa-protocol 'OpenPGP)
                               (if (or epa-armor
@@ -669,7 +708,9 @@ d - Create a detached signature
     (epg-context-set-passphrase-callback context
                                         #'epa-passphrase-callback-function)
     (epg-context-set-progress-callback context
     (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)
     (message "Signing %s..." (file-name-nondirectory file))
     (epg-sign-file context file signature mode)
     (message "Signing %s...wrote %s" (file-name-nondirectory file)
@@ -692,7 +733,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
     (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)
     (message "Encrypting %s..." (file-name-nondirectory file))
     (epg-encrypt-file context file recipients cipher)
     (message "Encrypting %s...wrote %s" (file-name-nondirectory file)
@@ -710,17 +753,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
       (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")
       (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 coding-system-for-read))
+      (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)))))))
 
       (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.
 ;;;###autoload
 (defun epa-decrypt-armor-in-region (start end)
   "Decrypt OpenPGP armors in the current region between START and END.
@@ -731,7 +797,7 @@ Don't use this command in Lisp programs!"
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
     (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-----$"
        (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
          (setq armor-start (match-beginning 0)
                armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
@@ -739,15 +805,13 @@ Don't use this command in Lisp programs!"
          (unless armor-end
            (error "No armor tail"))
          (goto-char armor-start)
          (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)))))))
+         (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)
 
 ;;;###autoload
 (defun epa-verify-region (start end)
@@ -757,11 +821,14 @@ Don't use this command in Lisp programs!"
   (interactive "r")
   (let ((context (epg-make-context epa-protocol)))
     (epg-context-set-progress-callback context
   (interactive "r")
   (let ((context (epg-make-context epa-protocol)))
     (epg-context-set-progress-callback context
-                                      #'epa-progress-callback-function)
+                                      #'epa-progress-callback-function
+                                      "Verifying...")
     (epg-verify-string context
                       (epa--encode-coding-string
                        (buffer-substring start end)
     (epg-verify-string context
                       (epa--encode-coding-string
                        (buffer-substring start end)
-                       coding-system-for-write))
+                       (or coding-system-for-write
+                           (get-text-property start
+                                              'epa-coding-system-used))))
     (if (epg-context-result-for context 'verify)
        (epa-display-info (epg-verify-result-to-string
                           (epg-context-result-for context 'verify))))))
     (if (epg-context-result-for context 'verify)
        (epa-display-info (epg-verify-result-to-string
                           (epg-context-result-for context 'verify))))))
@@ -791,37 +858,31 @@ Don't use this command in Lisp programs!"
            (error "No armor tail"))
          (epa-verify-region armor-start armor-end))))))
 
            (error "No armor tail"))
          (epa-verify-region armor-start armor-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))
+
 ;;;###autoload
 (defun epa-sign-region (start end signers mode)
   "Sign the current region between START and END by SIGNERS keys selected.
 
 Don't use this command in Lisp programs!"
   (interactive
 ;;;###autoload
 (defun epa-sign-region (start end signers mode)
   "Sign the current region between START and END by SIGNERS keys selected.
 
 Don't use this command in Lisp programs!"
   (interactive
-   (list (region-beginning) (region-end)
-        (epa-select-keys (epg-make-context epa-protocol)
-                         "Select keys for signing.
+   (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)
+          (if verbose
+              (epa-select-keys (epg-make-context epa-protocol)
+                               "Select keys for signing.
 If no one is selected, default secret key is used.  "
 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)
   (save-excursion
     (let ((context (epg-make-context epa-protocol))
          signature)
@@ -833,27 +894,62 @@ d - Create a detached signature
       (epg-context-set-passphrase-callback context
                                           #'epa-passphrase-callback-function)
       (epg-context-set-progress-callback context
       (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
                                        (buffer-substring start end)
       (message "Signing...")
       (setq signature (epg-sign-string context
                                       (epa--encode-coding-string
                                        (buffer-substring start end)
-                                       coding-system-for-write)
+                                       epa-last-coding-system-specified)
                                       mode))
       (message "Signing...done")
       (delete-region start end)
                                       mode))
       (message "Signing...done")
       (delete-region start end)
-      (insert (epa--decode-coding-string signature coding-system-for-read)))))
+      (goto-char start)
+      (add-text-properties (point)
+                          (progn
+                            (insert (epa--decode-coding-string
+                                     signature
+                                     (or coding-system-for-read
+                                         epa-last-coding-system-specified)))
+                            (point))
+                          (list 'epa-coding-system-used
+                                epa-last-coding-system-specified
+                                'front-sticky nil
+                                'rear-nonsticky t
+                                'start-open t
+                                'end-open t)))))
+
+(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
 
 ;;;###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
   "Encrypt the current region between START and END for RECIPIENTS.
 
 Don't use this command in Lisp programs!"
   (interactive
-   (list (region-beginning) (region-end)
-        (epa-select-keys (epg-make-context epa-protocol)
-                         "Select recipients for encryption.
-If no one is selected, symmetric encryption will be performed.  ")))
+   (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 context
+                           "Select recipients for encryption.
+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)
   (save-excursion
     (let ((context (epg-make-context epa-protocol))
          cipher)
@@ -861,19 +957,33 @@ 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)
       (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
       (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)
       (message "Encrypting...")
       (setq cipher (epg-encrypt-string context
                                       (epa--encode-coding-string
                                        (buffer-substring start end)
-                                       coding-system-for-write)
-                                      recipients))
+                                       epa-last-coding-system-specified)
+                                      recipients
+                                      sign))
       (message "Encrypting...done")
       (delete-region start end)
       (message "Encrypting...done")
       (delete-region start end)
-      (insert cipher))))
+      (goto-char start)
+      (add-text-properties (point)
+                          (progn
+                            (insert cipher)
+                            (point))
+                          (list 'epa-coding-system-used
+                                epa-last-coding-system-specified
+                                'front-sticky nil
+                                'rear-nonsticky t
+                                'start-open t
+                                'end-open t)))))
 
 ;;;###autoload
 (defun epa-delete-keys (keys &optional allow-secret)
 
 ;;;###autoload
 (defun epa-delete-keys (keys &optional allow-secret)
@@ -910,7 +1020,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 (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
        (apply #'epa-list-keys epa-list-keys-arguments))))
 
 ;;;###autoload
@@ -932,6 +1042,29 @@ Don't use this command in Lisp programs!"
                           (epg-context-result-for context 'import))))))
 
 ;;;###autoload
                           (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.
 
 (defun epa-export-keys (keys file)
   "Export selected KEYS to FILE.
 
@@ -988,7 +1121,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
     (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")))
     (message "Signing keys...")
     (epg-sign-keys context keys local)
     (message "Signing keys...done")))