emacs: Improve the acquisition of text parts.

`notmuch-get-bodypart-text' assumed that it is always possible to
acquire text/* parts via the sexp output format. This is not true if the
part in question has a content type of application/octet-stream but is
being interpreted as text/* based on the extension of the part filename.

Rework `notmuch-get-bodypart-text' to use the raw output format to
address this and make the implementation common with that of
`notmuch-get-bodypart-binary'.
This commit is contained in:
David Edmondson 2016-03-08 17:12:59 +00:00 committed by David Bremner
parent 742b566cac
commit c41d0db077

View file

@ -537,6 +537,34 @@ the given type."
(lambda (part) (notmuch-match-content-type (plist-get part :content-type) type)) (lambda (part) (notmuch-match-content-type (plist-get part :content-type) type))
parts)) parts))
(defun notmuch--get-bodypart-raw (msg part process-crypto binaryp cache)
(let* ((plist-elem (if binaryp :content-binary :content))
(data (or (plist-get part plist-elem)
(with-temp-buffer
;; Emacs internally uses a UTF-8-like multibyte string
;; representation by default (regardless of the coding
;; system, which only affects how it goes from outside data
;; to this internal representation). This *almost* never
;; matters. Annoyingly, it does matter if we use this data
;; in an image descriptor, since Emacs will use its internal
;; data buffer directly and this multibyte representation
;; corrupts binary image formats. Since the caller is
;; asking for binary data, a unibyte string is a more
;; appropriate representation anyway.
(when binaryp
(set-buffer-multibyte nil))
(let ((args `("show" "--format=raw"
,(format "--part=%s" (plist-get part :id))
,@(when process-crypto '("--decrypt"))
,(notmuch-id-to-query (plist-get msg :id))))
(coding-system-for-read
(if binaryp 'no-conversion 'utf-8)))
(apply #'call-process notmuch-command nil '(t nil) nil args)
(buffer-string))))))
(when (and cache data)
(plist-put part plist-elem data))
data))
(defun notmuch-get-bodypart-binary (msg part process-crypto &optional cache) (defun notmuch-get-bodypart-binary (msg part process-crypto &optional cache)
"Return the unprocessed content of PART in MSG as a unibyte string. "Return the unprocessed content of PART in MSG as a unibyte string.
@ -547,57 +575,18 @@ this does no charset conversion.
If CACHE is non-nil, the content of this part will be saved in If CACHE is non-nil, the content of this part will be saved in
MSG (if it isn't already)." MSG (if it isn't already)."
(let ((data (plist-get part :binary-content))) (notmuch--get-bodypart-raw msg part process-crypto t cache))
(when (not data)
(let ((args `("show" "--format=raw"
,(format "--part=%d" (plist-get part :id))
,@(when process-crypto '("--decrypt"))
,(notmuch-id-to-query (plist-get msg :id)))))
(with-temp-buffer
;; Emacs internally uses a UTF-8-like multibyte string
;; representation by default (regardless of the coding
;; system, which only affects how it goes from outside data
;; to this internal representation). This *almost* never
;; matters. Annoyingly, it does matter if we use this data
;; in an image descriptor, since Emacs will use its internal
;; data buffer directly and this multibyte representation
;; corrupts binary image formats. Since the caller is
;; asking for binary data, a unibyte string is a more
;; appropriate representation anyway.
(set-buffer-multibyte nil)
(let ((coding-system-for-read 'no-conversion))
(apply #'call-process notmuch-command nil '(t nil) nil args)
(setq data (buffer-string)))))
(when cache
;; Cheat. part is non-nil, and `plist-put' always modifies
;; the list in place if it's non-nil.
(plist-put part :binary-content data)))
data))
(defun notmuch-get-bodypart-text (msg part process-crypto &optional cache) (defun notmuch-get-bodypart-text (msg part process-crypto &optional cache)
"Return the text content of PART in MSG. "Return the text content of PART in MSG.
This returns the content of the given part as a multibyte Lisp This returns the content of the given part as a multibyte Lisp
string after performing content transfer decoding and any string after performing content transfer decoding and any
necessary charset decoding. It is an error to use this for necessary charset decoding.
non-text/* parts.
If CACHE is non-nil, the content of this part will be saved in If CACHE is non-nil, the content of this part will be saved in
MSG (if it isn't already)." MSG (if it isn't already)."
(let ((content (plist-get part :content))) (notmuch--get-bodypart-raw msg part process-crypto nil cache))
(when (not content)
;; Use show --format=sexp to fetch decoded content
(let* ((args `("show" "--format=sexp" "--include-html"
,(format "--part=%s" (plist-get part :id))
,@(when process-crypto '("--decrypt"))
,(notmuch-id-to-query (plist-get msg :id))))
(npart (apply #'notmuch-call-notmuch-sexp args)))
(setq content (plist-get npart :content))
(when (not content)
(error "Internal error: No :content from %S" args)))
(when cache
(plist-put part :content content)))
content))
;; Workaround: The call to `mm-display-part' below triggers a bug in ;; Workaround: The call to `mm-display-part' below triggers a bug in
;; Emacs 24 if it attempts to use the shr renderer to display an HTML ;; Emacs 24 if it attempts to use the shr renderer to display an HTML