From c66ebc1e9a38026a232b1d43078e8bde66ad5585 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 25 Jan 2015 16:17:02 +1900 Subject: [PATCH] [PATCH v2 7/8] emacs: Rewrite content ID handling --- f9/0a101bd598eafc5e53f48460ff390df7918fa8 | 251 ++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 f9/0a101bd598eafc5e53f48460ff390df7918fa8 diff --git a/f9/0a101bd598eafc5e53f48460ff390df7918fa8 b/f9/0a101bd598eafc5e53f48460ff390df7918fa8 new file mode 100644 index 000000000..63d35fa3e --- /dev/null +++ b/f9/0a101bd598eafc5e53f48460ff390df7918fa8 @@ -0,0 +1,251 @@ +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 C9DC0431FAE + for ; Sat, 24 Jan 2015 13:17:25 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: 0.138 +X-Spam-Level: +X-Spam-Status: No, score=0.138 tagged_above=-999 required=5 + tests=[DNS_FROM_AHBL_RHSBL=2.438, RCVD_IN_DNSWL_MED=-2.3] + 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 77GkLXxtUMUA for ; + Sat, 24 Jan 2015 13:17:22 -0800 (PST) +Received: from dmz-mailsec-scanner-2.mit.edu (dmz-mailsec-scanner-2.mit.edu + [18.9.25.13]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id D13F0431FD6 + for ; Sat, 24 Jan 2015 13:17:13 -0800 (PST) +X-AuditID: 1209190d-f79006d000000cfe-3e-54c40bd77bcd +Received: from mailhub-auth-3.mit.edu ( [18.9.21.43]) + (using TLS with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (Client did not present a certificate) + by dmz-mailsec-scanner-2.mit.edu (Symantec Messaging Gateway) with SMTP + id BD.F2.03326.7DB04C45; Sat, 24 Jan 2015 16:17:11 -0500 (EST) +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 t0OLHA06027949; + Sat, 24 Jan 2015 16:17:10 -0500 +Received: from drake (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 t0OLH6ma007462 + (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=NOT); + Sat, 24 Jan 2015 16:17:07 -0500 +Received: from amthrax by drake with local (Exim 4.84) + (envelope-from ) + id 1YF84n-0005Ro-Mw; Sat, 24 Jan 2015 16:17:05 -0500 +From: Austin Clements +To: notmuch@notmuchmail.org +Subject: [PATCH v2 7/8] emacs: Rewrite content ID handling +Date: Sat, 24 Jan 2015 16:17:02 -0500 +Message-Id: <1422134223-20739-8-git-send-email-amdragon@mit.edu> +X-Mailer: git-send-email 2.1.3 +In-Reply-To: <1422134223-20739-1-git-send-email-amdragon@mit.edu> +References: <1422134223-20739-1-git-send-email-amdragon@mit.edu> +X-Brightmail-Tracker: + H4sIAAAAAAAAA+NgFrrJIsWRmVeSWpSXmKPExsUixCmqrXud+0iIwc3X0hY3WrsZLfbd2cJk + sXouj8X1mzOZLd6snMfqwOqx6/lfJo+ds+6yexz+upDF49mqW8weWw69Zw5gjeKySUnNySxL + LdK3S+DKOHfoKXPBE8uKVVPOMDYwPtLrYuTkkBAwkZh2s50RwhaTuHBvPVsXIxeHkMBiJokT + bdPYIZyNjBLNTQ+ZIJyLTBJbn02HKpvEKPFszy2wfjYBDYnftxYzgdgiAtISO+/OZgWxmQXq + JP7OOQ9WIyxgJXHmTT9QDQcHi4CqxK2jISBhXgEHifv9exlBwhICchJb13mDhDkFHCVubNjF + AmILAZV0f25km8DIv4CRYRWjbEpulW5uYmZOcWqybnFyYl5eapGukV5uZoleakrpJkZwCEry + 7mB8d1DpEKMAB6MSD++Pf4dChFgTy4orcw8xSnIwKYnyrvp1OESILyk/pTIjsTgjvqg0J7X4 + EKMEB7OSCO+FDUA53pTEyqrUonyYlDQHi5I476YffCFCAumJJanZqakFqUUwWRkODiUJ3sVc + R0KEBItS01Mr0jJzShDSTBycIMN5gIZ3g9TwFhck5hZnpkPkTzEqSonzrgdJCIAkMkrz4Hph + KeIVozjQK8K89SBVPMD0Atf9CmgwE9Dggu0HQAaXJCKkpBoYV534esOlOupyud5Pk+Xhmp/+ + qzmFbLwsJi4/+3mt9coNer9u2nbe7uYWeysV9jPxeeqTTWuOm/ZYs//YfTpF4Wax3YId37TM + bpxKvvxUrzrpbanmTqWv6tbm26RYStRfT25eff9gfkh32XUl972VTam2zBGvdk3Z6qt3RrDt + z//ty+QUTm2JUGIpzkg01GIuKk4EAAEqXh3sAgAA +Cc: tomi.ollila@iki.fi +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: Sat, 24 Jan 2015 21:17:26 -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 11eac5f..34dcedd 100644 +--- a/emacs/notmuch-show.el ++++ b/emacs/notmuch-show.el +@@ -525,6 +525,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) +@@ -549,56 +616,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) +@@ -910,6 +932,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) +-- +2.1.3 + -- 2.26.2