update pinentry
This commit is contained in:
1 file changed
+148
-93
+148
-93
@@ -19,16 +19,15 @@
|
|||||||
;; GNU General Public License for more details.
|
;; GNU General Public License for more details.
|
||||||
|
|
||||||
;; You should have received a copy of the GNU General Public License
|
;; You should have received a copy of the GNU General Public License
|
||||||
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
;; This package allows GnuPG passphrase to be prompted through the
|
;; This package allows GnuPG passphrase to be prompted through the
|
||||||
;; minibuffer instead of graphical dialog. As of June 2015, this
|
;; minibuffer instead of graphical dialog.
|
||||||
;; feature requires newer versions of GnuPG (2.1.5 or later) and
|
|
||||||
;; Pinentry (not yet released).
|
|
||||||
;;
|
;;
|
||||||
;; To use, add allow-emacs-pinentry to ~/.gnupg/gpg-agent.conf, and
|
;; To use, add "allow-emacs-pinentry" to "~/.gnupg/gpg-agent.conf",
|
||||||
|
;; reload the configuration with "gpgconf --reload gpg-agent", and
|
||||||
;; start the server with M-x pinentry-start.
|
;; start the server with M-x pinentry-start.
|
||||||
;;
|
;;
|
||||||
;; The actual communication path between the relevant components is
|
;; The actual communication path between the relevant components is
|
||||||
@@ -41,12 +40,32 @@
|
|||||||
;;
|
;;
|
||||||
;; ${TMPDIR-/tmp}/emacs$(id -u)/pinentry
|
;; ${TMPDIR-/tmp}/emacs$(id -u)/pinentry
|
||||||
;;
|
;;
|
||||||
;; under the same directory as server.el uses. The protocol is a
|
;; under the same directory which server.el uses. The protocol is a
|
||||||
;; subset of the Pinentry Assuan protocol described in (info
|
;; subset of the Pinentry Assuan protocol described in (info
|
||||||
;; "(pinentry) Protocol").
|
;; "(pinentry) Protocol").
|
||||||
|
;;
|
||||||
|
;; NOTE: As of August 2015, this feature requires newer versions of
|
||||||
|
;; GnuPG (2.1.5+) and Pinentry (0.9.5+).
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
(defgroup pinentry nil
|
||||||
|
"The Pinentry server"
|
||||||
|
:version "25.1"
|
||||||
|
:group 'external)
|
||||||
|
|
||||||
|
(defcustom pinentry-popup-prompt-window t
|
||||||
|
"If non-nil, display multiline prompt in another window."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'pinentry)
|
||||||
|
|
||||||
|
(defcustom pinentry-prompt-window-height 5
|
||||||
|
"Number of lines used to display multiline prompt."
|
||||||
|
:type 'integer
|
||||||
|
:group 'pinentry)
|
||||||
|
|
||||||
|
(defvar pinentry-debug nil)
|
||||||
|
(defvar pinentry-debug-buffer nil)
|
||||||
(defvar pinentry--server-process nil)
|
(defvar pinentry--server-process nil)
|
||||||
(defvar pinentry--connection-process-list nil)
|
(defvar pinentry--connection-process-list nil)
|
||||||
|
|
||||||
@@ -55,6 +74,8 @@
|
|||||||
(defvar pinentry--read-point nil)
|
(defvar pinentry--read-point nil)
|
||||||
(put 'pinentry--read-point 'permanent-local t)
|
(put 'pinentry--read-point 'permanent-local t)
|
||||||
|
|
||||||
|
(defvar pinentry--prompt-buffer nil)
|
||||||
|
|
||||||
;; We use the same location as `server-socket-dir', when local sockets
|
;; We use the same location as `server-socket-dir', when local sockets
|
||||||
;; are supported.
|
;; are supported.
|
||||||
(defvar pinentry--socket-dir
|
(defvar pinentry--socket-dir
|
||||||
@@ -69,7 +90,7 @@ If local sockets are not supported, this is nil.")
|
|||||||
|
|
||||||
;; These error codes are defined in libgpg-error/src/err-codes.h.in.
|
;; These error codes are defined in libgpg-error/src/err-codes.h.in.
|
||||||
(defmacro pinentry--error-code (code)
|
(defmacro pinentry--error-code (code)
|
||||||
(logior (lsh 5 24) code))
|
(logior (ash 5 24) code))
|
||||||
(defconst pinentry--error-not-implemented
|
(defconst pinentry--error-not-implemented
|
||||||
(cons (pinentry--error-code 69) "not implemented"))
|
(cons (pinentry--error-code 69) "not implemented"))
|
||||||
(defconst pinentry--error-cancelled
|
(defconst pinentry--error-cancelled
|
||||||
@@ -79,34 +100,90 @@ If local sockets are not supported, this is nil.")
|
|||||||
|
|
||||||
(autoload 'server-ensure-safe-dir "server")
|
(autoload 'server-ensure-safe-dir "server")
|
||||||
|
|
||||||
|
(defvar pinentry-prompt-mode-map
|
||||||
|
(let ((keymap (make-sparse-keymap)))
|
||||||
|
(define-key keymap "q" 'quit-window)
|
||||||
|
keymap))
|
||||||
|
|
||||||
|
(define-derived-mode pinentry-prompt-mode special-mode "Pinentry"
|
||||||
|
"Major mode for `pinentry--prompt-buffer'."
|
||||||
|
(buffer-disable-undo)
|
||||||
|
(setq truncate-lines t
|
||||||
|
buffer-read-only t))
|
||||||
|
|
||||||
|
(defun pinentry--prompt (labels query-function &rest query-args)
|
||||||
|
(let ((desc (cdr (assq 'desc labels)))
|
||||||
|
(error (cdr (assq 'error labels)))
|
||||||
|
(prompt (cdr (assq 'prompt labels))))
|
||||||
|
(when (string-match "[ \n]*\\'" prompt)
|
||||||
|
(setq prompt (concat
|
||||||
|
(substring
|
||||||
|
prompt 0 (match-beginning 0)) " ")))
|
||||||
|
(when error
|
||||||
|
(setq desc (concat "Error: " (propertize error 'face 'error)
|
||||||
|
"\n" desc)))
|
||||||
|
(if (and desc pinentry-popup-prompt-window)
|
||||||
|
(save-window-excursion
|
||||||
|
(delete-other-windows)
|
||||||
|
(unless (and pinentry--prompt-buffer
|
||||||
|
(buffer-live-p pinentry--prompt-buffer))
|
||||||
|
(setq pinentry--prompt-buffer (generate-new-buffer "*Pinentry*")))
|
||||||
|
(if (get-buffer-window pinentry--prompt-buffer)
|
||||||
|
(delete-window (get-buffer-window pinentry--prompt-buffer)))
|
||||||
|
(with-current-buffer pinentry--prompt-buffer
|
||||||
|
(let ((inhibit-read-only t)
|
||||||
|
buffer-read-only)
|
||||||
|
(erase-buffer)
|
||||||
|
(insert desc))
|
||||||
|
(pinentry-prompt-mode)
|
||||||
|
(goto-char (point-min)))
|
||||||
|
(if (> (window-height)
|
||||||
|
pinentry-prompt-window-height)
|
||||||
|
(set-window-buffer (split-window nil
|
||||||
|
(- (window-height)
|
||||||
|
pinentry-prompt-window-height))
|
||||||
|
pinentry--prompt-buffer)
|
||||||
|
(pop-to-buffer pinentry--prompt-buffer)
|
||||||
|
(if (> (window-height) pinentry-prompt-window-height)
|
||||||
|
(shrink-window (- (window-height)
|
||||||
|
pinentry-prompt-window-height))))
|
||||||
|
(prog1 (apply query-function prompt query-args)
|
||||||
|
(quit-window)))
|
||||||
|
(apply query-function (concat desc "\n" prompt) query-args))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun pinentry-start ()
|
(defun pinentry-start (&optional quiet)
|
||||||
"Start a Pinentry service.
|
"Start a Pinentry service.
|
||||||
|
|
||||||
Once the environment is properly set, subsequent invocations of
|
Once the environment is properly set, subsequent invocations of
|
||||||
the gpg command will interact with Emacs for passphrase input."
|
the gpg command will interact with Emacs for passphrase input.
|
||||||
|
|
||||||
|
If the optional QUIET argument is non-nil, messages at startup
|
||||||
|
will not be shown."
|
||||||
(interactive)
|
(interactive)
|
||||||
(unless (featurep 'make-network-process '(:family local))
|
(unless (featurep 'make-network-process '(:family local))
|
||||||
(error "local sockets are not supported"))
|
(error "local sockets are not supported"))
|
||||||
(if (process-live-p pinentry--server-process)
|
(if (process-live-p pinentry--server-process)
|
||||||
(message "Pinentry service is already running")
|
(unless quiet
|
||||||
|
(message "Pinentry service is already running"))
|
||||||
(let* ((server-file (expand-file-name "pinentry" pinentry--socket-dir)))
|
(let* ((server-file (expand-file-name "pinentry" pinentry--socket-dir)))
|
||||||
(server-ensure-safe-dir pinentry--socket-dir)
|
(server-ensure-safe-dir pinentry--socket-dir)
|
||||||
;; Delete the socket files made by previous server invocations.
|
;; Delete the socket files made by previous server invocations.
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(let (delete-by-moving-to-trash)
|
(let (delete-by-moving-to-trash)
|
||||||
(delete-file server-file)))
|
(delete-file server-file)))
|
||||||
(setq pinentry--server-process
|
(with-file-modes ?\700
|
||||||
(make-network-process
|
(setq pinentry--server-process
|
||||||
:name "pinentry"
|
(make-network-process
|
||||||
:server t
|
:name "pinentry"
|
||||||
:noquery t
|
:server t
|
||||||
:sentinel #'pinentry--process-sentinel
|
:noquery t
|
||||||
:filter #'pinentry--process-filter
|
:sentinel #'pinentry--process-sentinel
|
||||||
:coding 'no-conversion
|
:filter #'pinentry--process-filter
|
||||||
:family 'local
|
:coding 'no-conversion
|
||||||
:service server-file))
|
:family 'local
|
||||||
(process-put pinentry--server-process :server-file server-file))))
|
:service server-file))
|
||||||
|
(process-put pinentry--server-process :server-file server-file)))))
|
||||||
|
|
||||||
(defun pinentry-stop ()
|
(defun pinentry-stop ()
|
||||||
"Stop a Pinentry service."
|
"Stop a Pinentry service."
|
||||||
@@ -224,6 +301,13 @@ Assuan protocol."
|
|||||||
(setq pinentry--read-point (point-min))
|
(setq pinentry--read-point (point-min))
|
||||||
(make-local-variable 'pinentry--labels))))
|
(make-local-variable 'pinentry--labels))))
|
||||||
(with-current-buffer (process-buffer process)
|
(with-current-buffer (process-buffer process)
|
||||||
|
(when pinentry-debug
|
||||||
|
(with-current-buffer
|
||||||
|
(or pinentry-debug-buffer
|
||||||
|
(setq pinentry-debug-buffer (generate-new-buffer
|
||||||
|
" *pinentry-debug*")))
|
||||||
|
(goto-char (point-max))
|
||||||
|
(insert input)))
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (point-max))
|
(goto-char (point-max))
|
||||||
(insert input)
|
(insert input)
|
||||||
@@ -248,32 +332,15 @@ Assuan protocol."
|
|||||||
(ignore-errors
|
(ignore-errors
|
||||||
(process-send-string process "OK\n")))
|
(process-send-string process "OK\n")))
|
||||||
("GETPIN"
|
("GETPIN"
|
||||||
(let ((prompt
|
(let ((confirm (not (null (assq 'repeat pinentry--labels))))
|
||||||
(or (cdr (assq 'desc pinentry--labels))
|
passphrase escaped-passphrase encoded-passphrase)
|
||||||
(cdr (assq 'prompt pinentry--labels))
|
(unwind-protect
|
||||||
""))
|
(condition-case err
|
||||||
(confirm (not (null (assq 'repeat pinentry--labels))))
|
(progn
|
||||||
entry)
|
(setq passphrase
|
||||||
(if (setq entry (assq 'error pinentry--labels))
|
(pinentry--prompt
|
||||||
(setq prompt (concat "Error: "
|
pinentry--labels
|
||||||
(propertize
|
#'read-passwd confirm))
|
||||||
(copy-sequence (cdr entry))
|
|
||||||
'face 'error)
|
|
||||||
"\n"
|
|
||||||
prompt)))
|
|
||||||
(if (setq entry (assq 'title pinentry--labels))
|
|
||||||
(setq prompt (format "[%s] %s"
|
|
||||||
(cdr entry) prompt)))
|
|
||||||
(if (string-match ":?[ \n]*\\'" prompt)
|
|
||||||
(setq prompt (concat
|
|
||||||
(substring
|
|
||||||
prompt 0 (match-beginning 0)) ": ")))
|
|
||||||
(let (passphrase escaped-passphrase encoded-passphrase)
|
|
||||||
(unwind-protect
|
|
||||||
(condition-case nil
|
|
||||||
(progn
|
|
||||||
(setq passphrase
|
|
||||||
(read-passwd prompt confirm))
|
|
||||||
(setq escaped-passphrase
|
(setq escaped-passphrase
|
||||||
(pinentry--escape-string
|
(pinentry--escape-string
|
||||||
passphrase))
|
passphrase))
|
||||||
@@ -284,7 +351,8 @@ Assuan protocol."
|
|||||||
(pinentry--send-data
|
(pinentry--send-data
|
||||||
process encoded-passphrase)
|
process encoded-passphrase)
|
||||||
(process-send-string process "OK\n")))
|
(process-send-string process "OK\n")))
|
||||||
(error
|
(error
|
||||||
|
(message "GETPIN error %S" err)
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(pinentry--send-error
|
(pinentry--send-error
|
||||||
process
|
process
|
||||||
@@ -295,59 +363,55 @@ Assuan protocol."
|
|||||||
(clear-string escaped-passphrase))
|
(clear-string escaped-passphrase))
|
||||||
(if encoded-passphrase
|
(if encoded-passphrase
|
||||||
(clear-string encoded-passphrase))))
|
(clear-string encoded-passphrase))))
|
||||||
(setq pinentry--labels nil)))
|
(setq pinentry--labels nil))
|
||||||
("CONFIRM"
|
("CONFIRM"
|
||||||
(let ((prompt
|
(let ((prompt
|
||||||
(or (cdr (assq 'desc pinentry--labels))
|
(or (cdr (assq 'prompt pinentry--labels))
|
||||||
""))
|
"Confirm? "))
|
||||||
(buttons
|
(buttons
|
||||||
(pinentry--labels-to-shortcuts
|
(delq nil
|
||||||
(list (cdr (assq 'ok pinentry--labels))
|
(pinentry--labels-to-shortcuts
|
||||||
(cdr (assq 'notok pinentry--labels))
|
(list (cdr (assq 'ok pinentry--labels))
|
||||||
(cdr (assq 'cancel pinentry--labels)))))
|
(cdr (assq 'notok pinentry--labels))
|
||||||
|
(cdr (assq 'cancel pinentry--labels))))))
|
||||||
entry)
|
entry)
|
||||||
(if (setq entry (assq 'error pinentry--labels))
|
(if buttons
|
||||||
(setq prompt (concat "Error: "
|
|
||||||
(propertize
|
|
||||||
(copy-sequence (cdr entry))
|
|
||||||
'face 'error)
|
|
||||||
"\n"
|
|
||||||
prompt)))
|
|
||||||
(if (setq entry (assq 'title pinentry--labels))
|
|
||||||
(setq prompt (format "[%s] %s"
|
|
||||||
(cdr entry) prompt)))
|
|
||||||
(if (remq nil buttons)
|
|
||||||
(progn
|
(progn
|
||||||
(setq prompt
|
(setq prompt
|
||||||
(concat prompt " ("
|
(concat prompt " ("
|
||||||
(mapconcat #'cdr (remq nil buttons)
|
(mapconcat #'cdr buttons
|
||||||
", ")
|
", ")
|
||||||
") "))
|
") "))
|
||||||
|
(if (setq entry (assq 'prompt pinentry--labels))
|
||||||
|
(setcdr entry prompt)
|
||||||
|
(setq pinentry--labels (cons (cons 'prompt prompt)
|
||||||
|
pinentry--labels)))
|
||||||
(condition-case nil
|
(condition-case nil
|
||||||
(let ((result (read-char prompt)))
|
(let ((result (pinentry--prompt pinentry--labels
|
||||||
|
#'read-char)))
|
||||||
(if (eq result (caar buttons))
|
(if (eq result (caar buttons))
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(process-send-string process "OK\n"))
|
(process-send-string process "OK\n"))
|
||||||
(if (eq result (car (nth 1 buttons)))
|
(if (eq result (car (nth 1 buttons)))
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(pinentry--send-error
|
(pinentry--send-error
|
||||||
process
|
process
|
||||||
pinentry--error-not-confirmed))
|
pinentry--error-not-confirmed))
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(pinentry--send-error
|
(pinentry--send-error
|
||||||
process
|
process
|
||||||
pinentry--error-cancelled)))))
|
pinentry--error-cancelled)))))
|
||||||
(error
|
(error
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(pinentry--send-error
|
(pinentry--send-error
|
||||||
process
|
process
|
||||||
pinentry--error-cancelled)))))
|
pinentry--error-cancelled)))))
|
||||||
(if (string-match "[ \n]*\\'" prompt)
|
(if (setq entry (assq 'prompt pinentry--labels))
|
||||||
(setq prompt (concat
|
(setcdr entry prompt)
|
||||||
(substring
|
(setq pinentry--labels (cons (cons 'prompt prompt)
|
||||||
prompt 0 (match-beginning 0)) " ")))
|
pinentry--labels)))
|
||||||
(if (condition-case nil
|
(if (condition-case nil
|
||||||
(y-or-n-p prompt)
|
(pinentry--prompt pinentry--labels #'y-or-n-p)
|
||||||
(quit))
|
(quit))
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(process-send-string process "OK\n"))
|
(process-send-string process "OK\n"))
|
||||||
@@ -389,15 +453,6 @@ Assuan protocol."
|
|||||||
(ignore-errors
|
(ignore-errors
|
||||||
(delete-file (process-get process :server-file)))))
|
(delete-file (process-get process :server-file)))))
|
||||||
|
|
||||||
;;;; ChangeLog:
|
|
||||||
|
|
||||||
;; 2015-06-12 Daiki Ueno <ueno@gnu.org>
|
|
||||||
;;
|
|
||||||
;; Merge commit '32b1944d5f0a65aa10c6768f4865f7ed1de8eb49' as
|
|
||||||
;; 'packages/pinentry'
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'pinentry)
|
(provide 'pinentry)
|
||||||
|
|
||||||
;;; pinentry.el ends here
|
;;; pinentry.el ends here
|
||||||
Reference in new issue
Block a user