From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 62B17431FAF for ; Sun, 7 Jul 2013 04:13:40 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -5 X-Spam-Level: X-Spam-Status: No, score=-5 tagged_above=-999 required=5 tests=[RCVD_IN_DNSWL_HI=-5] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id C4ffbisR8eQa for ; Sun, 7 Jul 2013 04:13:35 -0700 (PDT) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by olra.theworths.org (Postfix) with ESMTP id 300AB431FB6 for ; Sun, 7 Jul 2013 04:13:30 -0700 (PDT) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga102.jf.intel.com with ESMTP; 07 Jul 2013 04:10:56 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.87,1013,1363158000"; d="scan'208";a="365957330" Received: from unknown (HELO neilpc.config) ([10.252.122.25]) by orsmga002.jf.intel.com with ESMTP; 07 Jul 2013 04:13:21 -0700 From: Neil Roberts To: notmuch@notmuchmail.org Subject: [PATCH 2/2] emacs: crypto: Handle prompting for passwords Date: Sun, 7 Jul 2013 12:14:32 +0100 Message-Id: <1373195672-9338-3-git-send-email-neil@linux.intel.com> X-Mailer: git-send-email 1.7.11.3.g3c3efa5 In-Reply-To: <1373195672-9338-1-git-send-email-neil@linux.intel.com> References: <1373195672-9338-1-git-send-email-neil@linux.intel.com> X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 07 Jul 2013 11:13:40 -0000 This makes the Emacs client handle prompting for passwords when decrypting PGP messages via the new --status-fd and --command-fd options to the show and reply commands. The prompting is handled directly in notmuch-call-notmuch-sexp. Instead of calling call-process to invoke notmuch synchronously it is now run asynchronously via notmuch-start-notmuch so that it can install a process filter to handle the status messages. The function then runs a loop calling accept-process-output so that it can block until the process actually completes. Emacs doesn't support having multiple file descriptors to talk to a child process apart from stdin/out so the --status-fd is set to 1 and the --command-fd is set to 0. This means that the status messages will actually be interleaved with the sexp output. I think this shouldn't be a problem because both the sexp output and the status messages are split into lines and it shouldn't be possible for a sexp message to begin with [NOTMUCH:] so it is easy to separate out the two types of message. The process filter collects the output into a temporary buffer. Whenever it receives a line beginning with [NOTMUCH:] it will process the command and remove the line so that the final buffer only contains the sexp. The only handler currently is for GET_HIDDEN which just calls read-passwd to prompt for the password and then writes it back out to the stdin of the notmuch process. This is based on similar functionality in epg.el which handles invoking GPG. --- emacs/notmuch-lib.el | 66 +++++++++++++++++++++++++++++++++++++++++++------- emacs/notmuch-mua.el | 2 +- emacs/notmuch-query.el | 3 ++- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 8deb7de..0cc23fe 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -467,6 +467,48 @@ You may need to restart Emacs or upgrade your notmuch package.")) ;; `notmuch-logged-error' does not return. )))) +(defun notmuch--status-GET_HIDDEN (process args) + ;; The args string should be readable by emacs if it is made into a list + (let* ((args-list (read (concat "(" args ")"))) + (prompt (nth 1 args-list)) + (pwd (read-passwd (if prompt + (concat prompt ": ") + "GPG password: ")))) + (process-send-string process (concat pwd "\n")) + (clear-string pwd))) + +(defun notmuch--process-filter (process string) + "Callback used by `notmuch-call-notmuch-sexp` used to handle the status fd." + + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + + (goto-char (point-max)) + + (let ((insertion-point (point))) + (insert string) + + ;; Check if the string contains any status lines + (goto-char insertion-point) + (beginning-of-line) + + (while (re-search-forward "^\\[NOTMUCH:\\].*\n" nil t) + (let ((line-beginning (match-beginning 0)) + (line-end (match-end 0))) + (goto-char (+ line-beginning 10)) + (when (looking-at " *\\([A-Z_]+\\)") + ;; If there is a function defined that looks like it is made + ;; to handle this particular status then call it passing the + ;; remainder of the line as an argument + (let ((symbol (intern-soft (concat "notmuch--status-" + (match-string 1))))) + (when (and symbol (fboundp symbol)) + (funcall symbol + process + (buffer-substring (match-end 0) line-end))))) + ;; Remove the status so that it won't form part of the sexp + (delete-region line-beginning line-end))))))) + (defun notmuch-call-notmuch-sexp (&rest args) "Invoke `notmuch-command' with ARGS and return the parsed S-exp output. @@ -474,15 +516,21 @@ If notmuch exits with a non-zero status, this will pop up a buffer containing notmuch's output and signal an error." (with-temp-buffer - (let ((err-file (make-temp-file "nmerr"))) - (unwind-protect - (let ((status (apply #'call-process - notmuch-command nil (list t err-file) nil args))) - (notmuch-check-exit-status status (cons notmuch-command args) - (buffer-string) err-file) - (goto-char (point-min)) - (read (current-buffer))) - (delete-file err-file))))) + (let ((proc (apply #'notmuch-start-notmuch + "notmuch" + (current-buffer) nil args))) + (set-process-filter proc #'notmuch--process-filter) + + ;; Synchronously wait until the process completes + (while (eq (process-status proc) 'run) + (accept-process-output proc 1)) + + ;; According to similar code in epg-wait-for-completion, this is + ;; needed to run the process filter right now. + (sleep-for 0.1) + + (goto-char (point-min)) + (read (current-buffer))))) (defun notmuch-start-notmuch (name buffer sentinel &rest args) "Start and return an asynchronous notmuch command. diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 329d342..af7dd03 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -150,7 +150,7 @@ list." reply original) (when notmuch-show-process-crypto - (setq args (append args '("--decrypt")))) + (setq args (append args '("--decrypt" "--status-fd=1" "--command-fd=0")))) (if reply-all (setq args (append args '("--reply-to=all"))) diff --git a/emacs/notmuch-query.el b/emacs/notmuch-query.el index 51d427f..0788e8c 100644 --- a/emacs/notmuch-query.el +++ b/emacs/notmuch-query.el @@ -31,7 +31,8 @@ is a possibly empty forest of replies. " (let ((args '("show" "--format=sexp" "--format-version=1"))) (if notmuch-show-process-crypto - (setq args (append args '("--decrypt")))) + (setq args (append args + '("--decrypt" "--status-fd=1" "--command-fd=0")))) (setq args (append args search-terms)) (apply #'notmuch-call-notmuch-sexp args))) -- 1.7.11.3.g3c3efa5