[PATCH 10/11] emacs: Rewrite content ID handling
authorAustin Clements <amdragon@MIT.EDU>
Mon, 21 Apr 2014 18:37:47 +0000 (14:37 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 18:01:50 +0000 (10:01 -0800)
5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d [new file with mode: 0644]

diff --git a/5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d b/5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d
new file mode 100644 (file)
index 0000000..3f15599
--- /dev/null
@@ -0,0 +1,249 @@
+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 1F1EE431FBC\r
+       for <notmuch@notmuchmail.org>; Mon, 21 Apr 2014 11:38:18 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 26ae3WpmMoN7 for <notmuch@notmuchmail.org>;\r
+       Mon, 21 Apr 2014 11:38:14 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-3.mit.edu (dmz-mailsec-scanner-3.mit.edu\r
+       [18.9.25.14])\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 33118431FC7\r
+       for <notmuch@notmuchmail.org>; Mon, 21 Apr 2014 11:38:02 -0700 (PDT)\r
+X-AuditID: 1209190e-f79ee6d000000c40-9d-53556589d3f0\r
+Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
+       (using TLS with cipher AES256-SHA (256/256 bits))\r
+       (Client did not present a certificate)\r
+       by dmz-mailsec-scanner-3.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id 22.75.03136.98565535; Mon, 21 Apr 2014 14:38:01 -0400 (EDT)\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 s3LIc1vd018354; \r
+       Mon, 21 Apr 2014 14:38:01 -0400\r
+Received: from drake.dyndns.org\r
+       (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 s3LIbs8B029726\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Mon, 21 Apr 2014 14:38:00 -0400\r
+Received: from amthrax by drake.dyndns.org with local (Exim 4.77)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1WcJ6I-0003lI-HQ; Mon, 21 Apr 2014 14:37:54 -0400\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 10/11] emacs: Rewrite content ID handling\r
+Date: Mon, 21 Apr 2014 14:37:47 -0400\r
+Message-Id: <1398105468-14317-11-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 1.9.1\r
+In-Reply-To: <1398105468-14317-1-git-send-email-amdragon@mit.edu>\r
+References: <1398105468-14317-1-git-send-email-amdragon@mit.edu>\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFtrAIsWRmVeSWpSXmKPExsUixCmqrduZGhpssPmKucX1mzOZHRg9nq26\r
+       xRzAGMVlk5Kak1mWWqRvl8CVsXH6UfaCJ5YV09exNDA+0uti5OSQEDCRmLm8mQXCFpO4cG89\r
+       WxcjF4eQwGwmiYaj05ggnI2MEkcWzmGGcO4wSVy59ROsRUhgLqPEn4nOIDabgIbEtv3LGUFs\r
+       EQFpiZ13Z7N2MXJwMAuoSfzpUgEJCwtYSmxsmcUMEmYRUJVo7NAFCfMKOEos757GDnGEnMTJ\r
+       Y5NZQWxOoHjb6pfsEJscJDbsXcM0gZF/ASPDKkbZlNwq3dzEzJzi1GTd4uTEvLzUIl1jvdzM\r
+       Er3UlNJNjOCAkeTbwfj1oNIhRgEORiUe3gKj0GAh1sSy4srcQ4ySHExKoryvooBCfEn5KZUZ\r
+       icUZ8UWlOanFhxglOJiVRHjXawLleFMSK6tSi/JhUtIcLErivG+trYKFBNITS1KzU1MLUotg\r
+       sjIcHEoSvDdTgBoFi1LTUyvSMnNKENJMHJwgw3mAhl8EqeEtLkjMLc5Mh8ifYlSUEudtA0kI\r
+       gCQySvPgemER/YpRHOgVYd4WkCoeYDKA634FNJgJaPCTLSEgg0sSEVJSDYzuZqW/PxdWPLzy\r
+       arJeqsxH6WW1Szr/rufY6HvztHShkC939ym2LyIB54U0vRIqNu2KS3ZYqdCsJOkefvusYzNL\r
+       YrrjKrUDszzyancweXGu/Zuut3mT3dHPX1YZT6tUWaVhqr/jf6DpyRzOAmPbBx93ndVImXlY\r
+       dsL5cJtLrgKbJDVcxKJby5RYijMSDbWYi4oTASkhZonDAgAA\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: Mon, 21 Apr 2014 18:38:18 -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 9411c9a..f758091 100644\r
+--- a/emacs/notmuch-show.el\r
++++ b/emacs/notmuch-show.el\r
+@@ -503,6 +503,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
+@@ -527,56 +594,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
+@@ -888,6 +910,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
+1.9.1\r
+\r