[PATCH v2 7/8] emacs: Rewrite content ID handling
authorAustin Clements <amdragon@mit.edu>
Sat, 24 Jan 2015 21:17:02 +0000 (16:17 +1900)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Aug 2016 21:47:49 +0000 (14:47 -0700)
f9/0a101bd598eafc5e53f48460ff390df7918fa8 [new file with mode: 0644]

diff --git a/f9/0a101bd598eafc5e53f48460ff390df7918fa8 b/f9/0a101bd598eafc5e53f48460ff390df7918fa8
new file mode 100644 (file)
index 0000000..63d35fa
--- /dev/null
@@ -0,0 +1,251 @@
+Return-Path: <amdragon@mit.edu>\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 C9DC0431FAE\r
+       for <notmuch@notmuchmail.org>; Sat, 24 Jan 2015 13:17:25 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.138\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.138 tagged_above=-999 required=5\r
+       tests=[DNS_FROM_AHBL_RHSBL=2.438, RCVD_IN_DNSWL_MED=-2.3]\r
+       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 77GkLXxtUMUA for <notmuch@notmuchmail.org>;\r
+       Sat, 24 Jan 2015 13:17:22 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-2.mit.edu (dmz-mailsec-scanner-2.mit.edu\r
+       [18.9.25.13])\r
+       (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id D13F0431FD6\r
+       for <notmuch@notmuchmail.org>; Sat, 24 Jan 2015 13:17:13 -0800 (PST)\r
+X-AuditID: 1209190d-f79006d000000cfe-3e-54c40bd77bcd\r
+Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
+       (using TLS with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (Client did not present a certificate)\r
+       by dmz-mailsec-scanner-2.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id BD.F2.03326.7DB04C45; Sat, 24 Jan 2015 16:17:11 -0500 (EST)\r
+Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11])\r
+       by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id t0OLHA06027949; \r
+       Sat, 24 Jan 2015 16:17:10 -0500\r
+Received: from drake (216-15-114-40.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com\r
+       [216.15.114.40]) (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id t0OLH6ma007462\r
+       (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=NOT);\r
+       Sat, 24 Jan 2015 16:17:07 -0500\r
+Received: from amthrax by drake with local (Exim 4.84)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1YF84n-0005Ro-Mw; Sat, 24 Jan 2015 16:17:05 -0500\r
+From: Austin Clements <amdragon@mit.edu>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v2 7/8] emacs: Rewrite content ID handling\r
+Date: Sat, 24 Jan 2015 16:17:02 -0500\r
+Message-Id: <1422134223-20739-8-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 2.1.3\r
+In-Reply-To: <1422134223-20739-1-git-send-email-amdragon@mit.edu>\r
+References: <1422134223-20739-1-git-send-email-amdragon@mit.edu>\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFrrJIsWRmVeSWpSXmKPExsUixCmqrXud+0iIwc3X0hY3WrsZLfbd2cJk\r
+       sXouj8X1mzOZLd6snMfqwOqx6/lfJo+ds+6yexz+upDF49mqW8weWw69Zw5gjeKySUnNySxL\r
+       LdK3S+DKOHfoKXPBE8uKVVPOMDYwPtLrYuTkkBAwkZh2s50RwhaTuHBvPVsXIxeHkMBiJokT\r
+       bdPYIZyNjBLNTQ+ZIJyLTBJbn02HKpvEKPFszy2wfjYBDYnftxYzgdgiAtISO+/OZgWxmQXq\r
+       JP7OOQ9WIyxgJXHmTT9QDQcHi4CqxK2jISBhXgEHifv9exlBwhICchJb13mDhDkFHCVubNjF\r
+       AmILAZV0f25km8DIv4CRYRWjbEpulW5uYmZOcWqybnFyYl5eapGukV5uZoleakrpJkZwCEry\r
+       7mB8d1DpEKMAB6MSD++Pf4dChFgTy4orcw8xSnIwKYnyrvp1OESILyk/pTIjsTgjvqg0J7X4\r
+       EKMEB7OSCO+FDUA53pTEyqrUonyYlDQHi5I476YffCFCAumJJanZqakFqUUwWRkODiUJ3sVc\r
+       R0KEBItS01Mr0jJzShDSTBycIMN5gIZ3g9TwFhck5hZnpkPkTzEqSonzrgdJCIAkMkrz4Hph\r
+       KeIVozjQK8K89SBVPMD0Atf9CmgwE9Dggu0HQAaXJCKkpBoYV534esOlOupyud5Pk+Xhmp/+\r
+       qzmFbLwsJi4/+3mt9coNer9u2nbe7uYWeysV9jPxeeqTTWuOm/ZYs//YfTpF4Wax3YId37TM\r
+       bpxKvvxUrzrpbanmTqWv6tbm26RYStRfT25eff9gfkh32XUl972VTam2zBGvdk3Z6qt3RrDt\r
+       z//ty+QUTm2JUGIpzkg01GIuKk4EAAEqXh3sAgAA\r
+Cc: tomi.ollila@iki.fi\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: Sat, 24 Jan 2015 21:17:26 -0000\r
+\r
+Besides generally cleaning up the code and separating the general\r
+content ID handling from the w3m-specific code, this fixes several\r
+problems.\r
+\r
+Foremost is that, previously, the code roughly assumed that referenced\r
+parts would be in the same multipart/related as the reference.\r
+According to RFC 2392, nothing could be further from the truth:\r
+content IDs are supposed to be globally unique and globally\r
+addressable.  This is nonsense, but this patch at least fixes things\r
+so content IDs can be anywhere in the same message.\r
+\r
+As a side-effect of the above, this handles multipart/alternate\r
+content-IDs more in line with RFC 2046 section 5.1.2 (not that I've\r
+ever seen this in the wild).  This also properly URL-decodes cid:\r
+URLs, as per RFC 2392 (the previous code did not), and applies crypto\r
+settings from the show buffer (the previous code used the global\r
+crypto settings).\r
+---\r
+ emacs/notmuch-show.el | 120 +++++++++++++++++++++++++++++++-------------------\r
+ 1 file changed, 74 insertions(+), 46 deletions(-)\r
+\r
+diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
+index 11eac5f..34dcedd 100644\r
+--- a/emacs/notmuch-show.el\r
++++ b/emacs/notmuch-show.el\r
+@@ -525,6 +525,73 @@ (defun notmuch-show-toggle-part-invisibility (&optional button)\r
+         (overlay-put overlay 'invisible (not show))\r
+         t)))))\r
\r
++;; Part content ID handling\r
++\r
++(defvar notmuch-show--cids nil\r
++  "Alist from raw content ID to (MSG PART).")\r
++(make-variable-buffer-local 'notmuch-show--cids)\r
++\r
++(defun notmuch-show--register-cids (msg part)\r
++  "Register content-IDs in PART and all of PART's sub-parts."\r
++  (let ((content-id (plist-get part :content-id)))\r
++    (when content-id\r
++      ;; Note that content-IDs are globally unique, except when they\r
++      ;; aren't: RFC 2046 section 5.1.4 permits children of a\r
++      ;; multipart/alternative to have the same content-ID, in which\r
++      ;; case the MUA is supposed to pick the best one it can render.\r
++      ;; We simply add the content-ID to the beginning of our alist;\r
++      ;; so if this happens, we'll take the last (and "best")\r
++      ;; alternative (even if we can't render it).\r
++      (push (list content-id msg part) notmuch-show--cids)))\r
++  ;; Recurse on sub-parts\r
++  (let ((ctype (notmuch-split-content-type\r
++              (downcase (plist-get part :content-type)))))\r
++    (cond ((equal (first ctype) "multipart")\r
++         (mapc (apply-partially #'notmuch-show--register-cids msg)\r
++               (plist-get part :content)))\r
++        ((equal ctype '("message" "rfc822"))\r
++         (notmuch-show--register-cids\r
++          msg\r
++          (first (plist-get (first (plist-get part :content)) :body)))))))\r
++\r
++(defun notmuch-show--get-cid-content (cid)\r
++  "Return a list (CID-content content-type) or nil.\r
++\r
++This will only find parts from messages that have been inserted\r
++into the current buffer.  CID must be a raw content ID, without\r
++enclosing angle brackets, a cid: prefix, or URL encoding.  This\r
++will return nil if the CID is unknown or cannot be retrieved."\r
++  (let ((descriptor (cdr (assoc cid notmuch-show--cids))))\r
++    (when descriptor\r
++      (let* ((msg (first descriptor))\r
++           (part (second descriptor))\r
++           ;; Request caching for this content, as some messages\r
++           ;; reference the same cid: part many times (hundreds!).\r
++           (content (notmuch-get-bodypart-binary\r
++                     msg part notmuch-show-process-crypto 'cache))\r
++           (content-type (plist-get part :content-type)))\r
++      (list content content-type)))))\r
++\r
++(defun notmuch-show-setup-w3m ()\r
++  "Instruct w3m how to retrieve content from a \"related\" part of a message."\r
++  (interactive)\r
++  (if (boundp 'w3m-cid-retrieve-function-alist)\r
++    (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist)\r
++      (push (cons 'notmuch-show-mode #'notmuch-show--cid-w3m-retrieve)\r
++          w3m-cid-retrieve-function-alist)))\r
++  (setq mm-inline-text-html-with-images t))\r
++\r
++(defvar w3m-current-buffer) ;; From `w3m.el'.\r
++(defun notmuch-show--cid-w3m-retrieve (url &rest args)\r
++  ;; url includes the cid: prefix and is URL encoded (see RFC 2392).\r
++  (let* ((cid (url-unhex-string (substring url 4)))\r
++       (content-and-type\r
++        (with-current-buffer w3m-current-buffer\r
++          (notmuch-show--get-cid-content cid))))\r
++    (when content-and-type\r
++      (insert (first content-and-type))\r
++      (second content-and-type))))\r
++\r
+ ;; MIME part renderers\r
\r
+ (defun notmuch-show-multipart/*-to-list (part)\r
+@@ -549,56 +616,11 @@ (defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth\r
+       (indent-rigidly start (point) 1)))\r
+   t)\r
\r
+-(defun notmuch-show-setup-w3m ()\r
+-  "Instruct w3m how to retrieve content from a \"related\" part of a message."\r
+-  (interactive)\r
+-  (if (boundp 'w3m-cid-retrieve-function-alist)\r
+-    (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist)\r
+-      (push (cons 'notmuch-show-mode 'notmuch-show-w3m-cid-retrieve)\r
+-          w3m-cid-retrieve-function-alist)))\r
+-  (setq mm-inline-text-html-with-images t))\r
+-\r
+-(defvar w3m-current-buffer) ;; From `w3m.el'.\r
+-(defvar notmuch-show-w3m-cid-store nil)\r
+-(make-variable-buffer-local 'notmuch-show-w3m-cid-store)\r
+-\r
+-(defun notmuch-show-w3m-cid-store-internal (content-id msg part)\r
+-  (push (list content-id msg part) notmuch-show-w3m-cid-store))\r
+-\r
+-(defun notmuch-show-w3m-cid-store (msg part)\r
+-  (let ((content-id (plist-get part :content-id)))\r
+-    (when content-id\r
+-      (notmuch-show-w3m-cid-store-internal (concat "cid:" content-id)\r
+-                                         msg part))))\r
+-\r
+-(defun notmuch-show-w3m-cid-retrieve (url &rest args)\r
+-  (let ((matching-part (with-current-buffer w3m-current-buffer\r
+-                       (assoc url notmuch-show-w3m-cid-store))))\r
+-    (if matching-part\r
+-      (let* ((msg (nth 1 matching-part))\r
+-             (part (nth 2 matching-part))\r
+-             (content-type (plist-get part :content-type)))\r
+-        ;; Request content caching, as some messages reference the\r
+-        ;; same cid: part many times (hundreds!), which results in\r
+-        ;; many calls to `notmuch show'.\r
+-        (insert (notmuch-get-bodypart-binary\r
+-                 msg part notmuch-show-process-crypto 'cache))\r
+-        content-type)\r
+-      nil)))\r
+-\r
+ (defun notmuch-show-insert-part-multipart/related (msg part content-type nth depth button)\r
+   (let ((inner-parts (plist-get part :content))\r
+       (start (point)))\r
\r
+-    ;; We assume that the first part is text/html and the remainder\r
+-    ;; things that it references.\r
+-\r
+-    ;; Stash the non-primary parts.\r
+-    (mapc (lambda (part)\r
+-          (notmuch-show-w3m-cid-store msg part))\r
+-        (cdr inner-parts))\r
+-\r
+-    ;; Render the primary part.\r
++    ;; Render the primary part.  FIXME: Support RFC 2387 Start header.\r
+     (notmuch-show-insert-bodypart msg (car inner-parts) depth)\r
+     ;; Add hidden buttons for the rest\r
+     (mapc (lambda (inner-part)\r
+@@ -910,6 +932,12 @@ (defun notmuch-show-insert-bodypart (msg part depth &optional hide)\r
\r
+ (defun notmuch-show-insert-body (msg body depth)\r
+   "Insert the body BODY at depth DEPTH in the current thread."\r
++\r
++  ;; Register all content IDs for this message.  According to RFC\r
++  ;; 2392, content IDs are *global*, but it's okay if an MUA treats\r
++  ;; them as only global within a message.\r
++  (notmuch-show--register-cids msg (first body))\r
++\r
+   (mapc (lambda (part) (notmuch-show-insert-bodypart msg part depth)) body))\r
\r
+ (defun notmuch-show-make-symbol (type)\r
+-- \r
+2.1.3\r
+\r