From d81aa445343d2ba1403c305fbb1f667839f238b2 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 22 Apr 2014 14:37:47 +2000 Subject: [PATCH] [PATCH 10/11] emacs: Rewrite content ID handling --- 5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d | 249 ++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d diff --git a/5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d b/5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d new file mode 100644 index 000000000..3f1559916 --- /dev/null +++ b/5a/85fc7ccbbc83dfed3f44029d7bbebc79167c2d @@ -0,0 +1,249 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 1F1EE431FBC + for ; Mon, 21 Apr 2014 11:38:18 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -0.7 +X-Spam-Level: +X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 26ae3WpmMoN7 for ; + Mon, 21 Apr 2014 11:38:14 -0700 (PDT) +Received: from dmz-mailsec-scanner-3.mit.edu (dmz-mailsec-scanner-3.mit.edu + [18.9.25.14]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id 33118431FC7 + for ; Mon, 21 Apr 2014 11:38:02 -0700 (PDT) +X-AuditID: 1209190e-f79ee6d000000c40-9d-53556589d3f0 +Received: from mailhub-auth-3.mit.edu ( [18.9.21.43]) + (using TLS with cipher AES256-SHA (256/256 bits)) + (Client did not present a certificate) + by dmz-mailsec-scanner-3.mit.edu (Symantec Messaging Gateway) with SMTP + id 22.75.03136.98565535; Mon, 21 Apr 2014 14:38:01 -0400 (EDT) +Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11]) + by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id s3LIc1vd018354; + Mon, 21 Apr 2014 14:38:01 -0400 +Received: from drake.dyndns.org + (216-15-114-40.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com + [216.15.114.40]) (authenticated bits=0) + (User authenticated as amdragon@ATHENA.MIT.EDU) + by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id s3LIbs8B029726 + (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); + Mon, 21 Apr 2014 14:38:00 -0400 +Received: from amthrax by drake.dyndns.org with local (Exim 4.77) + (envelope-from ) + id 1WcJ6I-0003lI-HQ; Mon, 21 Apr 2014 14:37:54 -0400 +From: Austin Clements +To: notmuch@notmuchmail.org +Subject: [PATCH 10/11] emacs: Rewrite content ID handling +Date: Mon, 21 Apr 2014 14:37:47 -0400 +Message-Id: <1398105468-14317-11-git-send-email-amdragon@mit.edu> +X-Mailer: git-send-email 1.9.1 +In-Reply-To: <1398105468-14317-1-git-send-email-amdragon@mit.edu> +References: <1398105468-14317-1-git-send-email-amdragon@mit.edu> +X-Brightmail-Tracker: + H4sIAAAAAAAAA+NgFtrAIsWRmVeSWpSXmKPExsUixCmqrduZGhpssPmKucX1mzOZHRg9nq26 + xRzAGMVlk5Kak1mWWqRvl8CVsXH6UfaCJ5YV09exNDA+0uti5OSQEDCRmLm8mQXCFpO4cG89 + WxcjF4eQwGwmiYaj05ggnI2MEkcWzmGGcO4wSVy59ROsRUhgLqPEn4nOIDabgIbEtv3LGUFs + EQFpiZ13Z7N2MXJwMAuoSfzpUgEJCwtYSmxsmcUMEmYRUJVo7NAFCfMKOEos757GDnGEnMTJ + Y5NZQWxOoHjb6pfsEJscJDbsXcM0gZF/ASPDKkbZlNwq3dzEzJzi1GTd4uTEvLzUIl1jvdzM + Er3UlNJNjOCAkeTbwfj1oNIhRgEORiUe3gKj0GAh1sSy4srcQ4ySHExKoryvooBCfEn5KZUZ + icUZ8UWlOanFhxglOJiVRHjXawLleFMSK6tSi/JhUtIcLErivG+trYKFBNITS1KzU1MLUotg + sjIcHEoSvDdTgBoFi1LTUyvSMnNKENJMHJwgw3mAhl8EqeEtLkjMLc5Mh8ifYlSUEudtA0kI + gCQySvPgemER/YpRHOgVYd4WkCoeYDKA634FNJgJaPCTLSEgg0sSEVJSDYzuZqW/PxdWPLzy + arJeqsxH6WW1Szr/rufY6HvztHShkC939ym2LyIB54U0vRIqNu2KS3ZYqdCsJOkefvusYzNL + YrrjKrUDszzyancweXGu/Zuut3mT3dHPX1YZT6tUWaVhqr/jf6DpyRzOAmPbBx93ndVImXlY + dsL5cJtLrgKbJDVcxKJby5RYijMSDbWYi4oTASkhZonDAgAA +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: Mon, 21 Apr 2014 18:38:18 -0000 + +Besides generally cleaning up the code and separating the general +content ID handling from the w3m-specific code, this fixes several +problems. + +Foremost is that, previously, the code roughly assumed that referenced +parts would be in the same multipart/related as the reference. +According to RFC 2392, nothing could be further from the truth: +content IDs are supposed to be globally unique and globally +addressable. This is nonsense, but this patch at least fixes things +so content IDs can be anywhere in the same message. + +As a side-effect of the above, this handles multipart/alternate +content-IDs more in line with RFC 2046 section 5.1.2 (not that I've +ever seen this in the wild). This also properly URL-decodes cid: +URLs, as per RFC 2392 (the previous code did not), and applies crypto +settings from the show buffer (the previous code used the global +crypto settings). +--- + emacs/notmuch-show.el | 120 +++++++++++++++++++++++++++++++------------------- + 1 file changed, 74 insertions(+), 46 deletions(-) + +diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el +index 9411c9a..f758091 100644 +--- a/emacs/notmuch-show.el ++++ b/emacs/notmuch-show.el +@@ -503,6 +503,73 @@ (defun notmuch-show-toggle-part-invisibility (&optional button) + (overlay-put overlay 'invisible (not show)) + t))))) + ++;; Part content ID handling ++ ++(defvar notmuch-show--cids nil ++ "Alist from raw content ID to (MSG PART).") ++(make-variable-buffer-local 'notmuch-show--cids) ++ ++(defun notmuch-show--register-cids (msg part) ++ "Register content-IDs in PART and all of PART's sub-parts." ++ (let ((content-id (plist-get part :content-id))) ++ (when content-id ++ ;; Note that content-IDs are globally unique, except when they ++ ;; aren't: RFC 2046 section 5.1.4 permits children of a ++ ;; multipart/alternative to have the same content-ID, in which ++ ;; case the MUA is supposed to pick the best one it can render. ++ ;; We simply add the content-ID to the beginning of our alist; ++ ;; so if this happens, we'll take the last (and "best") ++ ;; alternative (even if we can't render it). ++ (push (list content-id msg part) notmuch-show--cids))) ++ ;; Recurse on sub-parts ++ (let ((ctype (notmuch-split-content-type ++ (downcase (plist-get part :content-type))))) ++ (cond ((equal (first ctype) "multipart") ++ (mapc (apply-partially #'notmuch-show--register-cids msg) ++ (plist-get part :content))) ++ ((equal ctype '("message" "rfc822")) ++ (notmuch-show--register-cids ++ msg ++ (first (plist-get (first (plist-get part :content)) :body))))))) ++ ++(defun notmuch-show--get-cid-content (cid) ++ "Return a list (CID-content content-type) or nil. ++ ++This will only find parts from messages that have been inserted ++into the current buffer. CID must be a raw content ID, without ++enclosing angle brackets, a cid: prefix, or URL encoding. This ++will return nil if the CID is unknown or cannot be retrieved." ++ (let ((descriptor (cdr (assoc cid notmuch-show--cids)))) ++ (when descriptor ++ (let* ((msg (first descriptor)) ++ (part (second descriptor)) ++ ;; Request caching for this content, as some messages ++ ;; reference the same cid: part many times (hundreds!). ++ (content (notmuch-get-bodypart-binary ++ msg part notmuch-show-process-crypto 'cache)) ++ (content-type (plist-get part :content-type))) ++ (list content content-type))))) ++ ++(defun notmuch-show-setup-w3m () ++ "Instruct w3m how to retrieve content from a \"related\" part of a message." ++ (interactive) ++ (if (boundp 'w3m-cid-retrieve-function-alist) ++ (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist) ++ (push (cons 'notmuch-show-mode #'notmuch-show--cid-w3m-retrieve) ++ w3m-cid-retrieve-function-alist))) ++ (setq mm-inline-text-html-with-images t)) ++ ++(defvar w3m-current-buffer) ;; From `w3m.el'. ++(defun notmuch-show--cid-w3m-retrieve (url &rest args) ++ ;; url includes the cid: prefix and is URL encoded (see RFC 2392). ++ (let* ((cid (url-unhex-string (substring url 4))) ++ (content-and-type ++ (with-current-buffer w3m-current-buffer ++ (notmuch-show--get-cid-content cid)))) ++ (when content-and-type ++ (insert (first content-and-type)) ++ (second content-and-type)))) ++ + ;; MIME part renderers + + (defun notmuch-show-multipart/*-to-list (part) +@@ -527,56 +594,11 @@ (defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth + (indent-rigidly start (point) 1))) + t) + +-(defun notmuch-show-setup-w3m () +- "Instruct w3m how to retrieve content from a \"related\" part of a message." +- (interactive) +- (if (boundp 'w3m-cid-retrieve-function-alist) +- (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist) +- (push (cons 'notmuch-show-mode 'notmuch-show-w3m-cid-retrieve) +- w3m-cid-retrieve-function-alist))) +- (setq mm-inline-text-html-with-images t)) +- +-(defvar w3m-current-buffer) ;; From `w3m.el'. +-(defvar notmuch-show-w3m-cid-store nil) +-(make-variable-buffer-local 'notmuch-show-w3m-cid-store) +- +-(defun notmuch-show-w3m-cid-store-internal (content-id msg part) +- (push (list content-id msg part) notmuch-show-w3m-cid-store)) +- +-(defun notmuch-show-w3m-cid-store (msg part) +- (let ((content-id (plist-get part :content-id))) +- (when content-id +- (notmuch-show-w3m-cid-store-internal (concat "cid:" content-id) +- msg part)))) +- +-(defun notmuch-show-w3m-cid-retrieve (url &rest args) +- (let ((matching-part (with-current-buffer w3m-current-buffer +- (assoc url notmuch-show-w3m-cid-store)))) +- (if matching-part +- (let* ((msg (nth 1 matching-part)) +- (part (nth 2 matching-part)) +- (content-type (plist-get part :content-type))) +- ;; Request content caching, as some messages reference the +- ;; same cid: part many times (hundreds!), which results in +- ;; many calls to `notmuch show'. +- (insert (notmuch-get-bodypart-binary +- msg part notmuch-show-process-crypto 'cache)) +- content-type) +- nil))) +- + (defun notmuch-show-insert-part-multipart/related (msg part content-type nth depth button) + (let ((inner-parts (plist-get part :content)) + (start (point))) + +- ;; We assume that the first part is text/html and the remainder +- ;; things that it references. +- +- ;; Stash the non-primary parts. +- (mapc (lambda (part) +- (notmuch-show-w3m-cid-store msg part)) +- (cdr inner-parts)) +- +- ;; Render the primary part. ++ ;; Render the primary part. FIXME: Support RFC 2387 Start header. + (notmuch-show-insert-bodypart msg (car inner-parts) depth) + ;; Add hidden buttons for the rest + (mapc (lambda (inner-part) +@@ -888,6 +910,12 @@ (defun notmuch-show-insert-bodypart (msg part depth &optional hide) + + (defun notmuch-show-insert-body (msg body depth) + "Insert the body BODY at depth DEPTH in the current thread." ++ ++ ;; Register all content IDs for this message. According to RFC ++ ;; 2392, content IDs are *global*, but it's okay if an MUA treats ++ ;; them as only global within a message. ++ (notmuch-show--register-cids msg (first body)) ++ + (mapc (lambda (part) (notmuch-show-insert-bodypart msg part depth)) body)) + + (defun notmuch-show-make-symbol (type) +-- +1.9.1 + -- 2.26.2