"If non-nil, epa commands treat input files as text."
:type 'boolean
:group 'epa)
-
+
+(defcustom epa-popup-info-window nil
+ "If non-nil, status information from epa commands is displayed on
+the separate window."
+ :type 'boolean
+ :group 'epa)
+
+(defcustom epa-info-window-height 5
+ "Number of lines used to display status information."
+ :type 'integer
+ :group 'epa)
+
(defgroup epa-faces nil
"Faces for epa-mode."
:group 'epa)
(defvar epa-key-buffer-alist nil)
(defvar epa-key nil)
(defvar epa-list-keys-arguments nil)
+(defvar epa-info-buffer nil)
(defvar epa-keys-mode-map
(let ((keymap (make-sparse-keymap)))
(define-key keymap "q" 'epa-exit-buffer)
keymap))
+(defvar epa-key-mode-map
+ (let ((keymap (make-sparse-keymap)))
+ (define-key keymap "q" 'bury-buffer)
+ keymap))
+
+(defvar epa-info-mode-map
+ (let ((keymap (make-sparse-keymap)))
+ (define-key keymap "q" 'delete-window)
+ keymap))
+
(defvar epa-exit-buffer-function #'bury-buffer)
(define-widget 'epa-key 'push-button
(make-local-variable 'epa-exit-buffer-function)
(run-hooks 'epa-keys-mode-hook))
-(defvar epa-key-mode-map
- (let ((keymap (make-sparse-keymap)))
- (define-key keymap "q" 'bury-buffer)
- keymap))
-
(defun epa-key-mode ()
"Major mode for `epa-show-key'."
(kill-all-local-variables)
(or (next-single-property-change point 'epa-list-keys)
(point-max)))
(goto-char point))
- (epa-list-keys-1 context name mode)
+ (epa-insert-keys context name mode)
(epa-keys-mode))
(make-local-variable 'epa-list-keys-arguments)
(setq epa-list-keys-arguments (list name mode protocol))
(goto-char (point-min))
(pop-to-buffer (current-buffer)))
-(defun epa-list-keys-1 (context name mode)
- (save-restriction
- (narrow-to-region (point) (point))
- (let ((inhibit-read-only t)
- buffer-read-only
- (keys (epg-list-keys context name mode))
- point)
- (while keys
- (setq point (point))
- (insert " ")
- (put-text-property point (point) 'epa-key (car keys))
- (widget-create 'epa-key :value (car keys))
- (insert "\n")
- (setq keys (cdr keys))))
- (put-text-property (point-min) (point-max) 'epa-list-keys t)))
+(defun epa-insert-keys (context name mode)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region (point) (point))
+ (let ((keys (epg-list-keys context name mode))
+ point)
+ (while keys
+ (setq point (point))
+ (insert " ")
+ (add-text-properties point (point)
+ (list 'epa-key (car keys)
+ 'front-sticky nil
+ 'rear-nonsticky t
+ 'start-open t
+ 'end-open t))
+ (widget-create 'epa-key :value (car keys))
+ (insert "\n")
+ (setq keys (cdr keys))))
+ (add-text-properties (point-min) (point-max)
+ (list 'epa-list-keys t
+ 'front-sticky nil
+ 'rear-nonsticky t
+ 'start-open t
+ 'end-open t)))))
(defun epa-marked-keys ()
(or (save-excursion
(if names
(while names
(setq point (point))
- (epa-list-keys-1 context (car names) secret)
+ (epa-insert-keys context (car names) secret)
(goto-char point)
(epa-mark)
(goto-char (point-max))
(setq names (cdr names)))
- (epa-list-keys-1 context nil secret))
+ (epa-insert-keys context nil secret))
(epa-keys-mode)
(setq epa-exit-buffer-function #'abort-recursive-edit)
(goto-char (point-min))
(delete-window (get-buffer-window epa-keys-buffer)))
(kill-buffer epa-keys-buffer))))
+(defun epa--format-seconds (seconds)
+ (let ((number-seconds (string-to-number (concat seconds ".0"))))
+ (format-time-string "%Y-%m-%d"
+ (cons (floor (/ number-seconds 65536))
+ (floor (mod number-seconds 65536))))))
+
(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)
(cdr (assq (epg-sub-key-algorithm (car pointer))
epg-pubkey-algorithm-alist))
"\n\tCreated: "
- (epg-sub-key-creation-time (car pointer))
+ (epa--format-seconds (epg-sub-key-creation-time (car pointer)))
(if (epg-sub-key-expiration-time (car pointer))
- (format "\n\tExpires: %s" (epg-sub-key-expiration-time
- (car pointer)))
+ (format "\n\tExpires: %s"
+ (epa--format-seconds (epg-sub-key-expiration-time
+ (car pointer))))
"")
"\n\tCapabilities: "
(mapconcat #'symbol-name
(interactive)
(funcall epa-exit-buffer-function))
+(defun epa-display-verify-result (verify-result)
+ (if epa-popup-info-window
+ (progn
+ (unless epa-info-buffer
+ (setq epa-info-buffer (generate-new-buffer "*Info*")))
+ (save-excursion
+ (set-buffer epa-info-buffer)
+ (let ((inhibit-read-only t)
+ buffer-read-only)
+ (erase-buffer)
+ (insert (epg-verify-result-to-string verify-result)))
+ (epa-info-mode))
+ (pop-to-buffer epa-info-buffer)
+ (if (> (window-height) epa-info-window-height)
+ (shrink-window (- (window-height) epa-info-window-height)))
+ (goto-char (point-min)))
+ (message "%s" (epg-verify-result-to-string verify-result))))
+
+(defun epa-info-mode ()
+ "Major mode for `epa-info-buffer'."
+ (kill-all-local-variables)
+ (buffer-disable-undo)
+ (setq major-mode 'epa-info-mode
+ mode-name "Info"
+ truncate-lines t
+ buffer-read-only t)
+ (use-local-map epa-info-mode-map)
+ (run-hooks 'epa-info-mode-hook))
+
;;;###autoload
(defun epa-decrypt-file (file)
"Decrypt FILE."
(epg-decrypt-file context file plain)
(message "Decrypting %s...done" (file-name-nondirectory file))
(if (epg-context-result-for context 'verify)
- (message "%s"
- (epg-verify-result-to-string
- (epg-context-result-for context 'verify))))))
+ (epa-display-verify-result (epg-context-result-for context 'verify)))))
;;;###autoload
(defun epa-verify-file (file)
(message "Verifying %s..." (file-name-nondirectory file))
(epg-verify-file context file plain)
(message "Verifying %s...done" (file-name-nondirectory file))
- (message "%s"
- (epg-verify-result-to-string
- (epg-context-result-for context 'verify)))))
+ (if (epg-context-result-for context 'verify)
+ (epa-display-verify-result (epg-context-result-for context 'verify)))))
;;;###autoload
-(defun epa-sign-file (file signers detached)
- "Sign FILE by selected SIGNERS keys.
-If DETACHED is non-nil, it creates a detached signature."
+(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) "Select keys for signing.
If no one is selected, default secret key is used. "
nil t)
- (y-or-n-p "Make a detached signature? ")))
- (let ((signature (concat file (if epa-armor
- ".asc"
- (if detached
- ".sig"
- ".gpg"))))
+ (if (y-or-n-p "Make a detached signature? ")
+ 'detached
+ (if (y-or-n-p "Make a cleartext signature? ")
+ 'clear))))
+ (let ((signature (concat file
+ (if (or epa-armor
+ (not (memq mode '(nil t normal detached))))
+ ".asc"
+ (if (memq mode '(t detached))
+ ".sig"
+ ".gpg"))))
(context (epg-make-context)))
(epg-context-set-armor context epa-armor)
(epg-context-set-textmode context epa-textmode)
- (message "Signing %s..." (file-name-nondirectory file))
(epg-context-set-signers context signers)
- (epg-sign-file context file signature (not (null detached)))
+ (message "Signing %s..." (file-name-nondirectory file))
+ (epg-sign-file context file signature mode)
(message "Signing %s...done" (file-name-nondirectory file))))
;;;###autoload
(message "Encrypting %s...done" (file-name-nondirectory file))))
;;;###autoload
+(defun epa-decrypt-region (start end)
+ "Decrypt the current region between START and END.
+
+Don't use this command in Lisp programs!"
+ (interactive "r")
+ (save-excursion
+ (let ((context (epg-make-context))
+ plain)
+ (message "Decrypting...")
+ (setq plain (epg-decrypt-string context (buffer-substring start end)))
+ (message "Decrypting...done")
+ (delete-region start end)
+ (goto-char start)
+ (insert (decode-coding-string plain coding-system-for-read))
+ (if (epg-context-result-for context 'verify)
+ (epa-display-verify-result (epg-context-result-for context 'verify))))))
+
+;;;###autoload
+(defun epa-decrypt-armor-in-region (start end)
+ "Decrypt OpenPGP armors 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 charset coding-system)
+ (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
+ (setq armor-start (match-beginning 0)
+ armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
+ nil t))
+ (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)))))))
+
+;;;###autoload
+(defun epa-verify-region (start end)
+ "Verify the current region between START and END.
+
+Don't use this command in Lisp programs!"
+ (interactive "r")
+ (let ((context (epg-make-context)))
+ (epg-verify-string context
+ (encode-coding-string
+ (buffer-substring start end)
+ coding-system-for-write))
+ (if (epg-context-result-for context 'verify)
+ (epa-display-verify-result (epg-context-result-for context 'verify)))))
+
+;;;###autoload
+(defun epa-verify-armor-in-region (start end)
+ "Verify OpenPGP armors 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\\( SIGNED\\)? MESSAGE-----$"
+ nil t)
+ (setq armor-start (match-beginning 0))
+ (if (match-beginning 1) ;cleartext signed message
+ (progn
+ (unless (re-search-forward "^-----BEGIN PGP SIGNATURE-----$"
+ nil t)
+ (error "Invalid cleartext signed message"))
+ (setq armor-end (re-search-forward
+ "^-----END PGP SIGNATURE-----$"
+ nil t)))
+ (setq armor-end (re-search-forward
+ "^-----END PGP MESSAGE-----$"
+ nil t)))
+ (unless armor-end
+ (error "No armor tail"))
+ (epa-verify-region armor-start armor-end))))))
+
+;;;###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) "Select keys for signing.
+If no one is selected, default secret key is used. "
+ nil t)
+ (if (y-or-n-p "Make a detached signature? ")
+ 'detached
+ (if (y-or-n-p "Make a cleartext signature? ")
+ 'clear))))
+ (save-excursion
+ (let ((context (epg-make-context))
+ signature)
+ (epg-context-set-armor context epa-armor)
+ (epg-context-set-textmode context epa-textmode)
+ (epg-context-set-signers context signers)
+ (message "Signing...")
+ (setq signature (epg-sign-string context
+ (encode-coding-string
+ (buffer-substring start end)
+ coding-system-for-write)
+ mode))
+ (message "Signing...done")
+ (delete-region start end)
+ (insert (decode-coding-string signature coding-system-for-read)))))
+
+;;;###autoload
+(defun epa-encrypt-region (start end recipients)
+ "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) "Select recipents for encryption.
+If no one is selected, symmetric encryption will be performed. ")))
+ (save-excursion
+ (let ((context (epg-make-context))
+ cipher)
+ (epg-context-set-armor context epa-armor)
+ (epg-context-set-textmode context epa-textmode)
+ (message "Encrypting...")
+ (setq cipher (epg-encrypt-string context
+ (encode-coding-string
+ (buffer-substring start end)
+ coding-system-for-write)
+ recipients))
+ (message "Encrypting...done")
+ (delete-region start end)
+ (insert cipher))))
+
+;;;###autoload
(defun epa-delete-keys (keys &optional allow-secret)
"Delete selected KEYS."
(interactive
(let ((context (epg-make-context)))
(message "Deleting...")
(epg-delete-keys context keys allow-secret)
- (apply #'epa-list-keys epa-list-keys-arguments)
- (message "Deleting...done")))
+ (message "Deleting...done")
+ (apply #'epa-list-keys epa-list-keys-arguments)))
;;;###autoload
(defun epa-import-keys (file)
(let ((context (epg-make-context)))
(message "Importing %s..." (file-name-nondirectory file))
(epg-import-keys-from-file context (expand-file-name file))
- (apply #'epa-list-keys epa-list-keys-arguments)
- (message "Importing %s...done" (file-name-nondirectory file))))
+ (message "Importing %s...done" (file-name-nondirectory file))
+ (apply #'epa-list-keys epa-list-keys-arguments)))
;;;###autoload
(defun epa-export-keys (keys file)
(epg-sign-keys context keys local)
(message "Signing keys...done")))
-;;;###autoload
-(defun epa-decrypt-region (start end)
- "Decrypt OpenPGP armors in the current region between START and END."
- (interactive "r")
- (save-excursion
- (save-restriction
- (narrow-to-region start end)
- (goto-char start)
- (let (armor-start armor-end charset context plain coding-system)
- (while (re-search-forward "-----BEGIN PGP MESSAGE-----$" nil t)
- (setq armor-start (match-beginning 0)
- armor-end (re-search-forward "^-----END PGP MESSAGE-----$"
- nil t))
- (unless armor-end
- (error "No armor tail"))
- (goto-char armor-start)
- (if (re-search-forward "^Charset: \\(.*\\)" nil t)
- (setq charset (match-string 1)))
- (setq context (epg-make-context)
- plain (epg-decrypt-string
- context
- (buffer-substring armor-start armor-end)))
- (delete-region armor-start armor-end)
- (goto-char armor-start)
- (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)))
- (insert (decode-coding-string plain coding-system))
- (if (epg-context-result-for context 'verify)
- (message "%s"
- (epg-verify-result-to-string
- (epg-context-result-for context 'verify)))))))))
-
(provide 'epa)
;;; epa.el ends here