[PATCH 2/2] emacs: crypto: Handle prompting for passwords
authorNeil Roberts <neil@linux.intel.com>
Sun, 7 Jul 2013 11:14:32 +0000 (12:14 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:55:55 +0000 (09:55 -0800)
06/4150b4b1e01922b0f8c83c341231da4f3bcb4e [new file with mode: 0644]

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