shr.el (shr-insert): Don't insert double spaces.
[bpt/emacs.git] / lisp / gnus / mm-decode.el
CommitLineData
23f87bed 1;;; mm-decode.el --- Functions for decoding MIME things
e84b4b86
TTN
2
3;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
114f9c96 4;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
c113de23
GM
5
6;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
8;; This file is part of GNU Emacs.
9
5e809f55 10;; GNU Emacs is free software: you can redistribute it and/or modify
c113de23 11;; it under the terms of the GNU General Public License as published by
5e809f55
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
c113de23
GM
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
5e809f55 17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
c113de23
GM
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
5e809f55 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
c113de23
GM
22
23;;; Commentary:
24
25;;; Code:
26
60ef4154
GM
27;; For Emacs < 22.2.
28(eval-and-compile
29 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
30
c113de23 31(require 'mail-parse)
c113de23 32(require 'mm-bodies)
23f87bed
MB
33(eval-when-compile (require 'cl)
34 (require 'term))
c113de23 35
aa8f8277
GM
36(autoload 'gnus-map-function "gnus-util")
37(autoload 'gnus-replace-in-string "gnus-util")
38(autoload 'gnus-read-shell-command "gnus-util")
39
12c9fab6
GM
40(autoload 'mm-inline-partial "mm-partial")
41(autoload 'mm-inline-external-body "mm-extern")
42(autoload 'mm-extern-cache-contents "mm-extern")
43(autoload 'mm-insert-inline "mm-view")
c113de23 44
b7e9d9c3
JB
45(defvar gnus-current-window-configuration)
46
23f87bed
MB
47(add-hook 'gnus-exit-gnus-hook 'mm-destroy-postponed-undisplay-list)
48
c113de23
GM
49(defgroup mime-display ()
50 "Display of MIME in mail and news articles."
23f87bed 51 :link '(custom-manual "(emacs-mime)Display Customization")
c113de23
GM
52 :version "21.1"
53 :group 'mail
54 :group 'news
55 :group 'multimedia)
56
23f87bed
MB
57(defgroup mime-security ()
58 "MIME security in mail and news articles."
59 :link '(custom-manual "(emacs-mime)Display Customization")
60 :group 'mail
61 :group 'news
62 :group 'multimedia)
63
c113de23
GM
64;;; Convenience macros.
65
66(defmacro mm-handle-buffer (handle)
67 `(nth 0 ,handle))
68(defmacro mm-handle-type (handle)
69 `(nth 1 ,handle))
70(defsubst mm-handle-media-type (handle)
71 (if (stringp (car handle))
72 (car handle)
73 (car (mm-handle-type handle))))
74(defsubst mm-handle-media-supertype (handle)
75 (car (split-string (mm-handle-media-type handle) "/")))
76(defsubst mm-handle-media-subtype (handle)
77 (cadr (split-string (mm-handle-media-type handle) "/")))
78(defmacro mm-handle-encoding (handle)
79 `(nth 2 ,handle))
80(defmacro mm-handle-undisplayer (handle)
81 `(nth 3 ,handle))
82(defmacro mm-handle-set-undisplayer (handle function)
83 `(setcar (nthcdr 3 ,handle) ,function))
84(defmacro mm-handle-disposition (handle)
85 `(nth 4 ,handle))
86(defmacro mm-handle-description (handle)
87 `(nth 5 ,handle))
88(defmacro mm-handle-cache (handle)
89 `(nth 6 ,handle))
90(defmacro mm-handle-set-cache (handle contents)
91 `(setcar (nthcdr 6 ,handle) ,contents))
92(defmacro mm-handle-id (handle)
93 `(nth 7 ,handle))
23f87bed
MB
94(defmacro mm-handle-multipart-original-buffer (handle)
95 `(get-text-property 0 'buffer (car ,handle)))
96(defmacro mm-handle-multipart-from (handle)
97 `(get-text-property 0 'from (car ,handle)))
98(defmacro mm-handle-multipart-ctl-parameter (handle parameter)
99 `(get-text-property 0 ,parameter (car ,handle)))
100
c113de23
GM
101(defmacro mm-make-handle (&optional buffer type encoding undisplayer
102 disposition description cache
103 id)
104 `(list ,buffer ,type ,encoding ,undisplayer
105 ,disposition ,description ,cache ,id))
106
23f87bed 107(defcustom mm-text-html-renderer
870409d4
G
108 (cond ((fboundp 'libxml-parse-html-region) 'mm-shr)
109 ((executable-find "w3m") 'gnus-article-html)
23f87bed
MB
110 ((executable-find "links") 'links)
111 ((executable-find "lynx") 'lynx)
8f7abae3
MB
112 ((locate-library "w3") 'w3)
113 ((locate-library "html2text") 'html2text)
114 (t nil))
23f87bed
MB
115 "Render of HTML contents.
116It is one of defined renderer types, or a rendering function.
117The defined renderer types are:
36d3245f 118`mm-shr': use Gnus simple HTML renderer;
20a673b2 119`gnus-article-html' : use Gnus renderer based on w3m;
23f87bed
MB
120`w3m' : use emacs-w3m;
121`w3m-standalone': use w3m;
122`links': use links;
123`lynx' : use lynx;
8f7abae3 124`w3' : use Emacs/W3;
23f87bed 125`html2text' : use html2text;
8f7abae3 126nil : use external viewer (default web browser)."
1a10d421 127 :version "24.1"
36d3245f
G
128 :type '(choice (const mm-shr)
129 (const gnus-article-html)
20a673b2
KY
130 (const w3)
131 (const w3m :tag "emacs-w3m")
8f7abae3 132 (const w3m-standalone :tag "standalone w3m" )
23f87bed
MB
133 (const links)
134 (const lynx)
135 (const html2text)
8f7abae3 136 (const nil :tag "External viewer")
23f87bed 137 (function))
23f87bed
MB
138 :group 'mime-display)
139
140(defvar mm-inline-text-html-renderer nil
141 "Function used for rendering inline HTML contents.
142It is suggested to customize `mm-text-html-renderer' instead.")
143
144(defcustom mm-inline-text-html-with-images nil
145 "If non-nil, Gnus will allow retrieving images in HTML contents with
146the <img> tags. It has no effect on Emacs/w3. See also the
147documentation for the `mm-w3m-safe-url-regexp' variable."
bf247b6e 148 :version "22.1"
23f87bed
MB
149 :type 'boolean
150 :group 'mime-display)
151
152(defcustom mm-w3m-safe-url-regexp "\\`cid:"
153 "Regexp matching URLs which are considered to be safe.
154Some HTML mails might contain a nasty trick used by spammers, using
155the <img> tag which is far more evil than the [Click Here!] button.
156It is most likely intended to check whether the ominous spam mail has
157reached your eyes or not, in which case the spammer knows for sure
158that your email address is valid. It is done by embedding an
159identifier string into a URL that you might automatically retrieve
160when displaying the image. The default value is \"\\\\`cid:\" which only
161matches parts embedded to the Multipart/Related type MIME contents and
162Gnus will never connect to the spammer's site arbitrarily. You may
163set this variable to nil if you consider all urls to be safe."
bf247b6e 164 :version "22.1"
23f87bed
MB
165 :type '(choice (regexp :tag "Regexp")
166 (const :tag "All URLs are safe" nil))
167 :group 'mime-display)
168
169(defcustom mm-inline-text-html-with-w3m-keymap t
170 "If non-nil, use emacs-w3m command keys in the article buffer."
bf247b6e 171 :version "22.1"
23f87bed
MB
172 :type 'boolean
173 :group 'mime-display)
174
175(defcustom mm-enable-external t
176 "Indicate whether external MIME handlers should be used.
177
178If t, all defined external MIME handlers are used. If nil, files are saved by
179`mailcap-save-binary-file'. If it is the symbol `ask', you are prompted
180before the external MIME handler is invoked."
bf247b6e 181 :version "22.1"
23f87bed
MB
182 :type '(choice (const :tag "Always" t)
183 (const :tag "Never" nil)
184 (const :tag "Ask" ask))
185 :group 'mime-display)
186
c113de23 187(defcustom mm-inline-media-tests
23f87bed 188 '(("image/p?jpeg"
c113de23
GM
189 mm-inline-image
190 (lambda (handle)
191 (mm-valid-and-fit-image-p 'jpeg handle)))
192 ("image/png"
193 mm-inline-image
194 (lambda (handle)
195 (mm-valid-and-fit-image-p 'png handle)))
196 ("image/gif"
197 mm-inline-image
198 (lambda (handle)
199 (mm-valid-and-fit-image-p 'gif handle)))
200 ("image/tiff"
201 mm-inline-image
202 (lambda (handle)
203 (mm-valid-and-fit-image-p 'tiff handle)) )
204 ("image/xbm"
205 mm-inline-image
206 (lambda (handle)
207 (mm-valid-and-fit-image-p 'xbm handle)))
208 ("image/x-xbitmap"
209 mm-inline-image
210 (lambda (handle)
211 (mm-valid-and-fit-image-p 'xbm handle)))
212 ("image/xpm"
213 mm-inline-image
214 (lambda (handle)
215 (mm-valid-and-fit-image-p 'xpm handle)))
23f87bed 216 ("image/x-xpixmap"
c113de23
GM
217 mm-inline-image
218 (lambda (handle)
219 (mm-valid-and-fit-image-p 'xpm handle)))
220 ("image/bmp"
221 mm-inline-image
222 (lambda (handle)
223 (mm-valid-and-fit-image-p 'bmp handle)))
5bb548d2
DL
224 ("image/x-portable-bitmap"
225 mm-inline-image
226 (lambda (handle)
227 (mm-valid-and-fit-image-p 'pbm handle)))
c113de23
GM
228 ("text/plain" mm-inline-text identity)
229 ("text/enriched" mm-inline-text identity)
230 ("text/richtext" mm-inline-text identity)
231 ("text/x-patch" mm-display-patch-inline
232 (lambda (handle)
c615a00c
SM
233 ;; If the diff-mode.el package is installed, the function is
234 ;; autoloaded. Checking (locate-library "diff-mode") would be trying
235 ;; to cater to broken installations. OTOH checking the function
236 ;; makes it possible to install another package which provides an
237 ;; alternative implementation of diff-mode. --Stef
238 (fboundp 'diff-mode)))
7ab0253d
SM
239 ;; In case mime.types uses x-diff (as does Debian's mime-support-3.40).
240 ("text/x-diff" mm-display-patch-inline
241 (lambda (handle) (fboundp 'diff-mode)))
ce9401f3 242 ("application/emacs-lisp" mm-display-elisp-inline identity)
23f87bed 243 ("application/x-emacs-lisp" mm-display-elisp-inline identity)
01c52d31 244 ("text/dns" mm-display-dns-inline identity)
c113de23 245 ("text/html"
23f87bed 246 mm-inline-text-html
c113de23 247 (lambda (handle)
23f87bed
MB
248 (or mm-inline-text-html-renderer
249 mm-text-html-renderer)))
c113de23 250 ("text/x-vcard"
23f87bed 251 mm-inline-text-vcard
c113de23
GM
252 (lambda (handle)
253 (or (featurep 'vcard)
254 (locate-library "vcard"))))
255 ("message/delivery-status" mm-inline-text identity)
256 ("message/rfc822" mm-inline-message identity)
257 ("message/partial" mm-inline-partial identity)
23f87bed 258 ("message/external-body" mm-inline-external-body identity)
c113de23
GM
259 ("text/.*" mm-inline-text identity)
260 ("audio/wav" mm-inline-audio
261 (lambda (handle)
262 (and (or (featurep 'nas-sound) (featurep 'native-sound))
263 (device-sound-enabled-p))))
264 ("audio/au"
265 mm-inline-audio
266 (lambda (handle)
267 (and (or (featurep 'nas-sound) (featurep 'native-sound))
268 (device-sound-enabled-p))))
269 ("application/pgp-signature" ignore identity)
23f87bed
MB
270 ("application/x-pkcs7-signature" ignore identity)
271 ("application/pkcs7-signature" ignore identity)
272 ("application/x-pkcs7-mime" ignore identity)
273 ("application/pkcs7-mime" ignore identity)
c113de23
GM
274 ("multipart/alternative" ignore identity)
275 ("multipart/mixed" ignore identity)
23f87bed
MB
276 ("multipart/related" ignore identity)
277 ;; Disable audio and image
278 ("audio/.*" ignore ignore)
279 ("image/.*" ignore ignore)
280 ;; Default to displaying as text
281 (".*" mm-inline-text mm-readable-p))
c113de23 282 "Alist of media types/tests saying whether types can be displayed inline."
23f87bed 283 :type '(repeat (list (regexp :tag "MIME type")
c113de23
GM
284 (function :tag "Display function")
285 (function :tag "Display test")))
286 :group 'mime-display)
287
288(defcustom mm-inlined-types
289 '("image/.*" "text/.*" "message/delivery-status" "message/rfc822"
23f87bed
MB
290 "message/partial" "message/external-body" "application/emacs-lisp"
291 "application/x-emacs-lisp"
292 "application/pgp-signature" "application/x-pkcs7-signature"
293 "application/pkcs7-signature" "application/x-pkcs7-mime"
e499bc94
MB
294 "application/pkcs7-mime"
295 ;; Mutt still uses this even though it has already been withdrawn.
296 "application/pgp")
23f87bed
MB
297 "List of media types that are to be displayed inline.
298See also `mm-inline-media-tests', which says how to display a media
299type inline."
3031d8b0 300 :type '(repeat regexp)
23f87bed
MB
301 :group 'mime-display)
302
303(defcustom mm-keep-viewer-alive-types
304 '("application/postscript" "application/msword" "application/vnd.ms-excel"
305 "application/pdf" "application/x-dvi")
306 "List of media types for which the external viewer will not be killed
307when selecting a different article."
bf247b6e 308 :version "22.1"
3031d8b0 309 :type '(repeat regexp)
c113de23 310 :group 'mime-display)
a1506d29 311
c113de23 312(defcustom mm-automatic-display
01c52d31 313 '("text/plain" "text/enriched" "text/richtext" "text/html" "text/x-verbatim"
c113de23 314 "text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
01c52d31 315 "message/rfc822" "text/x-patch" "text/dns" "application/pgp-signature"
23f87bed
MB
316 "application/emacs-lisp" "application/x-emacs-lisp"
317 "application/x-pkcs7-signature"
318 "application/pkcs7-signature" "application/x-pkcs7-mime"
e499bc94
MB
319 "application/pkcs7-mime"
320 ;; Mutt still uses this even though it has already been withdrawn.
0565caeb 321 "application/pgp\\'")
c113de23 322 "A list of MIME types to be displayed automatically."
3031d8b0 323 :type '(repeat regexp)
c113de23
GM
324 :group 'mime-display)
325
23f87bed
MB
326(defcustom mm-attachment-override-types '("text/x-vcard"
327 "application/pkcs7-mime"
328 "application/x-pkcs7-mime"
329 "application/pkcs7-signature"
330 "application/x-pkcs7-signature")
c113de23 331 "Types to have \"attachment\" ignored if they can be displayed inline."
3031d8b0 332 :type '(repeat regexp)
c113de23
GM
333 :group 'mime-display)
334
335(defcustom mm-inline-override-types nil
336 "Types to be treated as attachments even if they can be displayed inline."
3031d8b0 337 :type '(repeat regexp)
c113de23
GM
338 :group 'mime-display)
339
340(defcustom mm-automatic-external-display nil
341 "List of MIME type regexps that will be displayed externally automatically."
3031d8b0 342 :type '(repeat regexp)
c113de23
GM
343 :group 'mime-display)
344
345(defcustom mm-discouraged-alternatives nil
346 "List of MIME types that are discouraged when viewing multipart/alternative.
347Viewing agents are supposed to view the last possible part of a message,
348as that is supposed to be the richest. However, users may prefer other
349types instead, and this list says what types are most unwanted. If,
350for instance, text/html parts are very unwanted, and text/richtext are
351somewhat unwanted, then the value of this variable should be set
352to:
353
58090a8d
MB
354 (\"text/html\" \"text/richtext\")
355
356Adding \"image/.*\" might also be useful. Spammers use it as the
3031d8b0
MB
357prefered part of multipart/alternative messages. See also
358`gnus-buttonized-mime-types', to which adding \"multipart/alternative\"
359enables you to choose manually one of two types those mails include."
58090a8d 360 :type '(repeat regexp) ;; See `mm-preferred-alternative-precedence'.
c113de23
GM
361 :group 'mime-display)
362
23f87bed
MB
363(defcustom mm-tmp-directory
364 (if (fboundp 'temp-directory)
365 (temp-directory)
366 (if (boundp 'temporary-file-directory)
367 temporary-file-directory
368 "/tmp/"))
369 "Where mm will store its temporary files."
370 :type 'directory
371 :group 'mime-display)
c113de23
GM
372
373(defcustom mm-inline-large-images nil
a41c2e6d
G
374 "If t, then all images fit in the buffer.
375If 'resize, try to resize the images so they fit."
376 :type '(radio
377 (const :tag "Inline large images as they are." t)
378 (const :tag "Resize large images." resize)
379 (const :tag "Do not inline large images." nil))
c113de23
GM
380 :group 'mime-display)
381
01c52d31 382(defcustom mm-file-name-rewrite-functions
23f87bed 383 '(mm-file-name-delete-control mm-file-name-delete-gotchas)
01c52d31 384 "List of functions used for rewriting file names of MIME parts.
23f87bed
MB
385Each function takes a file name as input and returns a file name.
386
01c52d31
MB
387Ready-made functions include `mm-file-name-delete-control',
388`mm-file-name-delete-gotchas' (you should not remove these two
389functions), `mm-file-name-delete-whitespace',
390`mm-file-name-trim-whitespace', `mm-file-name-collapse-whitespace',
391`mm-file-name-replace-whitespace', `capitalize', `downcase',
392`upcase', and `upcase-initials'."
393 :type '(list (set :inline t
394 (const mm-file-name-delete-control)
395 (const mm-file-name-delete-gotchas)
396 (const mm-file-name-delete-whitespace)
397 (const mm-file-name-trim-whitespace)
398 (const mm-file-name-collapse-whitespace)
399 (const mm-file-name-replace-whitespace)
400 (const capitalize)
401 (const downcase)
402 (const upcase)
403 (const upcase-initials)
404 (repeat :inline t
405 :tag "Function"
406 function)))
330f707b 407 :version "23.1" ;; No Gnus
01c52d31
MB
408 :group 'mime-display)
409
23f87bed
MB
410
411(defvar mm-path-name-rewrite-functions nil
412 "*List of functions for rewriting the full file names of MIME parts.
413This is used when viewing parts externally, and is meant for
414transforming the absolute name so that non-compliant programs can find
415the file where it's saved.
416
417Each function takes a file name as input and returns a file name.")
418
419(defvar mm-file-name-replace-whitespace nil
420 "String used for replacing whitespace characters; default is `\"_\"'.")
421
422(defcustom mm-default-directory nil
423 "The default directory where mm will save files.
424If not set, `default-directory' will be used."
425 :type '(choice directory (const :tag "Default" nil))
426 :group 'mime-display)
427
428(defcustom mm-attachment-file-modes 384
429 "Set the mode bits of saved attachments to this integer."
bf247b6e 430 :version "22.1"
23f87bed
MB
431 :type 'integer
432 :group 'mime-display)
433
434(defcustom mm-external-terminal-program "xterm"
435 "The program to start an external terminal."
bf247b6e 436 :version "22.1"
23f87bed
MB
437 :type 'string
438 :group 'mime-display)
439
c113de23
GM
440;;; Internal variables.
441
c113de23
GM
442(defvar mm-last-shell-command "")
443(defvar mm-content-id-alist nil)
23f87bed 444(defvar mm-postponed-undisplay-list nil)
c113de23
GM
445
446;; According to RFC2046, in particular, in a digest, the default
447;; Content-Type value for a body part is changed from "text/plain" to
448;; "message/rfc822".
449(defvar mm-dissect-default-type "text/plain")
450
23f87bed
MB
451(autoload 'mml2015-verify "mml2015")
452(autoload 'mml2015-verify-test "mml2015")
453(autoload 'mml-smime-verify "mml-smime")
454(autoload 'mml-smime-verify-test "mml-smime")
455
456(defvar mm-verify-function-alist
457 '(("application/pgp-signature" mml2015-verify "PGP" mml2015-verify-test)
458 ("application/x-gnus-pgp-signature" mm-uu-pgp-signed-extract-1 "PGP"
459 mm-uu-pgp-signed-test)
460 ("application/pkcs7-signature" mml-smime-verify "S/MIME"
461 mml-smime-verify-test)
462 ("application/x-pkcs7-signature" mml-smime-verify "S/MIME"
463 mml-smime-verify-test)))
464
465(defcustom mm-verify-option 'never
466 "Option of verifying signed parts.
467`never', not verify; `always', always verify;
01c52d31
MB
468`known', only verify known protocols. Otherwise, ask user.
469
470When set to `always' or `known', you should add
471\"multipart/signed\" to `gnus-buttonized-mime-types' to see
472result of the verification."
bf247b6e 473 :version "22.1"
23f87bed
MB
474 :type '(choice (item always)
475 (item never)
476 (item :tag "only known protocols" known)
477 (item :tag "ask" nil))
478 :group 'mime-security)
479
480(autoload 'mml2015-decrypt "mml2015")
481(autoload 'mml2015-decrypt-test "mml2015")
482
483(defvar mm-decrypt-function-alist
484 '(("application/pgp-encrypted" mml2015-decrypt "PGP" mml2015-decrypt-test)
485 ("application/x-gnus-pgp-encrypted" mm-uu-pgp-encrypted-extract-1 "PGP"
486 mm-uu-pgp-encrypted-test)))
487
488(defcustom mm-decrypt-option nil
489 "Option of decrypting encrypted parts.
490`never', not decrypt; `always', always decrypt;
491`known', only decrypt known protocols. Otherwise, ask user."
bf247b6e 492 :version "22.1"
23f87bed
MB
493 :type '(choice (item always)
494 (item never)
495 (item :tag "only known protocols" known)
496 (item :tag "ask" nil))
497 :group 'mime-security)
498
499(defvar mm-viewer-completion-map
500 (let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
501 (set-keymap-parent map minibuffer-local-completion-map)
c615a00c
SM
502 ;; Should we bind other key to minibuffer-complete-word?
503 (define-key map " " 'self-insert-command)
23f87bed
MB
504 map)
505 "Keymap for input viewer with completion.")
506
62a27ccf
DL
507(defvar mm-viewer-completion-map
508 (let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
509 (set-keymap-parent map minibuffer-local-completion-map)
c615a00c
SM
510 ;; Should we bind other key to minibuffer-complete-word?
511 (define-key map " " 'self-insert-command)
62a27ccf
DL
512 map)
513 "Keymap for input viewer with completion.")
514
c113de23
GM
515;;; The functions.
516
23f87bed
MB
517(defun mm-alist-to-plist (alist)
518 "Convert association list ALIST into the equivalent property-list form.
519The plist is returned. This converts from
520
521\((a . 1) (b . 2) (c . 3))
522
523into
524
525\(a 1 b 2 c 3)
526
527The original alist is not modified. See also `destructive-alist-to-plist'."
528 (let (plist)
529 (while alist
530 (let ((el (car alist)))
531 (setq plist (cons (cdr el) (cons (car el) plist))))
532 (setq alist (cdr alist)))
533 (nreverse plist)))
534
535(defun mm-keep-viewer-alive-p (handle)
536 "Say whether external viewer for HANDLE should stay alive."
537 (let ((types mm-keep-viewer-alive-types)
538 (type (mm-handle-media-type handle))
539 ty)
540 (catch 'found
541 (while (setq ty (pop types))
542 (when (string-match ty type)
543 (throw 'found t))))))
544
545(defun mm-handle-set-external-undisplayer (handle function)
546 "Set the undisplayer for HANDLE to FUNCTION.
547Postpone undisplaying of viewers for types in
548`mm-keep-viewer-alive-types'."
549 (if (mm-keep-viewer-alive-p handle)
550 (let ((new-handle (copy-sequence handle)))
551 (mm-handle-set-undisplayer new-handle function)
552 (mm-handle-set-undisplayer handle nil)
553 (push new-handle mm-postponed-undisplay-list))
554 (mm-handle-set-undisplayer handle function)))
555
556(defun mm-destroy-postponed-undisplay-list ()
557 (when mm-postponed-undisplay-list
558 (message "Destroying external MIME viewers")
559 (mm-destroy-parts mm-postponed-undisplay-list)))
560
aa8f8277
GM
561(autoload 'message-fetch-field "message")
562
ee7d3cc0 563(defun mm-dissect-buffer (&optional no-strict-mime loose-mime from)
c113de23
GM
564 "Dissect the current buffer and return a list of MIME handles."
565 (save-excursion
ee7d3cc0 566 (let (ct ctl type subtype cte cd description id result)
c113de23
GM
567 (save-restriction
568 (mail-narrow-to-head)
569 (when (or no-strict-mime
23f87bed 570 loose-mime
c113de23
GM
571 (mail-fetch-field "mime-version"))
572 (setq ct (mail-fetch-field "content-type")
c96ec15a 573 ctl (and ct (mail-header-parse-content-type ct))
c113de23
GM
574 cte (mail-fetch-field "content-transfer-encoding")
575 cd (mail-fetch-field "content-disposition")
aa227ba5
KY
576 ;; Newlines in description should be stripped so as
577 ;; not to break the MIME tag into two or more lines.
578 description (message-fetch-field "content-description")
23f87bed 579 id (mail-fetch-field "content-id"))
ee7d3cc0 580 (unless from
c96ec15a 581 (setq from (mail-fetch-field "from")))
23f87bed
MB
582 ;; FIXME: In some circumstances, this code is running within
583 ;; an unibyte macro. mail-extract-address-components
584 ;; creates unibyte buffers. This `if', though not a perfect
585 ;; solution, avoids most of them.
586 (if from
4599d0ec
MB
587 (setq from (cadr (mail-extract-address-components from))))
588 (if description
589 (setq description (mail-decode-encoded-word-string
590 description)))))
c113de23
GM
591 (if (or (not ctl)
592 (not (string-match "/" (car ctl))))
593 (mm-dissect-singlepart
594 (list mm-dissect-default-type)
01c52d31 595 (and cte (intern (downcase (mail-header-strip cte))))
c113de23 596 no-strict-mime
c96ec15a 597 (and cd (mail-header-parse-content-disposition cd))
c113de23
GM
598 description)
599 (setq type (split-string (car ctl) "/"))
600 (setq subtype (cadr type)
54e573e6 601 type (car type))
c113de23
GM
602 (setq
603 result
604 (cond
605 ((equal type "multipart")
606 (let ((mm-dissect-default-type (if (equal subtype "digest")
607 "message/rfc822"
23f87bed
MB
608 "text/plain"))
609 (start (cdr (assq 'start (cdr ctl)))))
610 (add-text-properties 0 (length (car ctl))
611 (mm-alist-to-plist (cdr ctl)) (car ctl))
612
613 ;; what really needs to be done here is a way to link a
614 ;; MIME handle back to it's parent MIME handle (in a multilevel
615 ;; MIME article). That would probably require changing
c615a00c 616 ;; the mm-handle API so we simply store the multipart buffer
23f87bed
MB
617 ;; name as a text property of the "multipart/whatever" string.
618 (add-text-properties 0 (length (car ctl))
619 (list 'buffer (mm-copy-to-buffer)
620 'from from
621 'start start)
622 (car ctl))
ee7d3cc0 623 (cons (car ctl) (mm-dissect-multipart ctl from))))
c113de23 624 (t
23f87bed
MB
625 (mm-possibly-verify-or-decrypt
626 (mm-dissect-singlepart
627 ctl
01c52d31 628 (and cte (intern (downcase (mail-header-strip cte))))
23f87bed 629 no-strict-mime
c96ec15a 630 (and cd (mail-header-parse-content-disposition cd))
23f87bed
MB
631 description id)
632 ctl))))
c113de23
GM
633 (when id
634 (when (string-match " *<\\(.*\\)> *" id)
635 (setq id (match-string 1 id)))
636 (push (cons id result) mm-content-id-alist))
637 result))))
638
639(defun mm-dissect-singlepart (ctl cte &optional force cdl description id)
640 (when (or force
641 (if (equal "text/plain" (car ctl))
642 (assoc 'format ctl)
643 t))
23f87bed
MB
644 (mm-make-handle
645 (mm-copy-to-buffer) ctl cte nil cdl description nil id)))
c113de23 646
ee7d3cc0 647(defun mm-dissect-multipart (ctl from)
c113de23
GM
648 (goto-char (point-min))
649 (let* ((boundary (concat "\n--" (mail-content-type-get ctl 'boundary)))
650 (close-delimiter (concat (regexp-quote boundary) "--[ \t]*$"))
651 start parts
652 (end (save-excursion
653 (goto-char (point-max))
654 (if (re-search-backward close-delimiter nil t)
655 (match-beginning 0)
656 (point-max)))))
657 (setq boundary (concat (regexp-quote boundary) "[ \t]*$"))
4481aa98 658 (while (and (< (point) end) (re-search-forward boundary end t))
c113de23
GM
659 (goto-char (match-beginning 0))
660 (when start
661 (save-excursion
662 (save-restriction
663 (narrow-to-region start (point))
ee7d3cc0 664 (setq parts (nconc (list (mm-dissect-buffer t nil from)) parts)))))
23f87bed
MB
665 (end-of-line 2)
666 (or (looking-at boundary)
667 (forward-line 1))
c113de23 668 (setq start (point)))
4481aa98 669 (when (and start (< start end))
c113de23
GM
670 (save-excursion
671 (save-restriction
672 (narrow-to-region start end)
ee7d3cc0 673 (setq parts (nconc (list (mm-dissect-buffer t nil from)) parts)))))
23f87bed 674 (mm-possibly-verify-or-decrypt (nreverse parts) ctl)))
c113de23
GM
675
676(defun mm-copy-to-buffer ()
677 "Copy the contents of the current buffer to a fresh buffer."
d4eb2b7e 678 (let ((obuf (current-buffer))
61dc89b0 679 (mb (mm-multibyte-p))
d4eb2b7e
SM
680 beg)
681 (goto-char (point-min))
682 (search-forward-regexp "^\n" nil t)
683 (setq beg (point))
54e573e6 684 (with-current-buffer
61dc89b0
SM
685 (generate-new-buffer " *mm*")
686 ;; Preserve the data's unibyteness (for url-insert-file-contents).
687 (mm-set-buffer-multibyte mb)
c113de23
GM
688 (insert-buffer-substring obuf beg)
689 (current-buffer))))
690
23f87bed
MB
691(defun mm-display-parts (handle &optional no-default)
692 (if (stringp (car handle))
693 (mapcar 'mm-display-parts (cdr handle))
694 (if (bufferp (car handle))
695 (save-restriction
696 (narrow-to-region (point) (point))
697 (mm-display-part handle)
698 (goto-char (point-max)))
699 (mapcar 'mm-display-parts handle))))
700
aa8f8277
GM
701(autoload 'mailcap-parse-mailcaps "mailcap")
702(autoload 'mailcap-mime-info "mailcap")
703
c113de23
GM
704(defun mm-display-part (handle &optional no-default)
705 "Display the MIME part represented by HANDLE.
706Returns nil if the part is removed; inline if displayed inline;
707external if displayed external."
708 (save-excursion
709 (mailcap-parse-mailcaps)
710 (if (mm-handle-displayed-p handle)
711 (mm-remove-part handle)
163cb72d
MB
712 (let* ((ehandle (if (equal (mm-handle-media-type handle)
713 "message/external-body")
714 (progn
715 (unless (mm-handle-cache handle)
716 (mm-extern-cache-contents handle))
717 (mm-handle-cache handle))
718 handle))
719 (type (mm-handle-media-type ehandle))
23f87bed
MB
720 (method (mailcap-mime-info type))
721 (filename (or (mail-content-type-get
722 (mm-handle-disposition handle) 'filename)
723 (mail-content-type-get
724 (mm-handle-type handle) 'name)
725 "<file>"))
726 (external mm-enable-external))
163cb72d
MB
727 (if (and (mm-inlinable-p ehandle)
728 (mm-inlined-p ehandle))
c113de23
GM
729 (progn
730 (forward-line 1)
731 (mm-display-inline handle)
732 'inline)
733 (when (or method
734 (not no-default))
735 (if (and (not method)
163cb72d 736 (equal "text" (car (split-string type "/"))))
c113de23
GM
737 (progn
738 (forward-line 1)
739 (mm-insert-inline handle (mm-get-part handle))
740 'inline)
54e573e6
MB
741 (setq external
742 (and method ;; If nil, we always use "save".
23f87bed
MB
743 (stringp method) ;; 'mailcap-save-binary-file
744 (or (eq mm-enable-external t)
745 (and (eq mm-enable-external 'ask)
746 (y-or-n-p
747 (concat
748 "Display part (" type
749 ") using external program"
750 ;; Can non-string method ever happen?
751 (if (stringp method)
752 (concat
753 " \"" (format method filename) "\"")
754 "")
54e573e6 755 "? "))))))
23f87bed
MB
756 (if external
757 (mm-display-external
758 handle (or method 'mailcap-save-binary-file))
759 (mm-display-external
760 handle 'mailcap-save-binary-file)))))))))
c113de23 761
60ef4154 762(declare-function gnus-configure-windows "gnus-win" (setting &optional force))
aa8f8277 763(defvar mailcap-mime-extensions) ; mailcap-mime-info autoloads
60ef4154 764
c113de23
GM
765(defun mm-display-external (handle method)
766 "Display HANDLE using METHOD."
767 (let ((outbuf (current-buffer)))
768 (mm-with-unibyte-buffer
769 (if (functionp method)
770 (let ((cur (current-buffer)))
771 (if (eq method 'mailcap-save-binary-file)
772 (progn
6eb681a3 773 (set-buffer (generate-new-buffer " *mm*"))
c113de23
GM
774 (setq method nil))
775 (mm-insert-part handle)
87035689 776 (mm-add-meta-html-tag handle)
c113de23
GM
777 (let ((win (get-buffer-window cur t)))
778 (when win
779 (select-window win)))
6eb681a3 780 (switch-to-buffer (generate-new-buffer " *mm*")))
23f87bed 781 (buffer-disable-undo)
c113de23
GM
782 (mm-set-buffer-file-coding-system mm-binary-coding-system)
783 (insert-buffer-substring cur)
784 (goto-char (point-min))
23f87bed
MB
785 (when method
786 (message "Viewing with %s" method))
c113de23
GM
787 (let ((mm (current-buffer))
788 (non-viewer (assq 'non-viewer
789 (mailcap-mime-info
790 (mm-handle-media-type handle) t))))
791 (unwind-protect
792 (if method
793 (funcall method)
794 (mm-save-part handle))
795 (when (and (not non-viewer)
796 method)
797 (mm-handle-set-undisplayer handle mm)))))
798 ;; The function is a string to be executed.
799 (mm-insert-part handle)
87035689 800 (mm-add-meta-html-tag handle)
23f87bed 801 (let* ((dir (mm-make-temp-file
3efe5554 802 (expand-file-name "emm." mm-tmp-directory) 'dir))
23f87bed
MB
803 (filename (or
804 (mail-content-type-get
805 (mm-handle-disposition handle) 'filename)
806 (mail-content-type-get
807 (mm-handle-type handle) 'name)))
c113de23
GM
808 (mime-info (mailcap-mime-info
809 (mm-handle-media-type handle) t))
810 (needsterm (or (assoc "needsterm" mime-info)
811 (assoc "needsterminal" mime-info)))
812 (copiousoutput (assoc "copiousoutput" mime-info))
813 file buffer)
814 ;; We create a private sub-directory where we store our files.
d55fe5bb 815 (set-file-modes dir #o700)
c113de23 816 (if filename
23f87bed
MB
817 (setq file (expand-file-name
818 (gnus-map-function mm-file-name-rewrite-functions
819 (file-name-nondirectory filename))
820 dir))
cf5a5c38
MB
821 ;; Use nametemplate (defined in RFC1524) if it is specified
822 ;; in mailcap.
823 (let ((suffix (cdr (assoc "nametemplate" mime-info))))
824 (if (and suffix
825 (string-match "\\`%s\\(\\..+\\)\\'" suffix))
826 (setq suffix (match-string 1 suffix))
827 ;; Otherwise, use a suffix according to
828 ;; `mailcap-mime-extensions'.
829 (setq suffix (car (rassoc (mm-handle-media-type handle)
830 mailcap-mime-extensions))))
831 (setq file (mm-make-temp-file (expand-file-name "mm." dir)
832 nil suffix))))
c113de23
GM
833 (let ((coding-system-for-write mm-binary-coding-system))
834 (write-region (point-min) (point-max) file nil 'nomesg))
d55fe5bb
MB
835 ;; The file is deleted after the viewer exists. If the users edits
836 ;; the file, changes will be lost. Set file to read-only to make it
837 ;; clear.
838 (set-file-modes file #o400)
c113de23 839 (message "Viewing with %s" method)
23f87bed
MB
840 (cond
841 (needsterm
842 (let ((command (mm-mailcap-command
843 method file (mm-handle-type handle))))
844 (unwind-protect
845 (if window-system
846 (start-process "*display*" nil
847 mm-external-terminal-program
848 "-e" shell-file-name
849 shell-command-switch command)
850 (require 'term)
851 (require 'gnus-win)
852 (set-buffer
853 (setq buffer
854 (make-term "display"
855 shell-file-name
856 nil
857 shell-command-switch command)))
858 (term-mode)
859 (term-char-mode)
860 (set-process-sentinel
861 (get-buffer-process buffer)
862 `(lambda (process state)
863 (if (eq 'exit (process-status process))
864 (gnus-configure-windows
865 ',gnus-current-window-configuration))))
866 (gnus-configure-windows 'display-term))
867 (mm-handle-set-external-undisplayer handle (cons file buffer)))
868 (message "Displaying %s..." command))
869 'external)
870 (copiousoutput
871 (with-current-buffer outbuf
872 (forward-line 1)
873 (mm-insert-inline
874 handle
875 (unwind-protect
876 (progn
877 (call-process shell-file-name nil
878 (setq buffer
879 (generate-new-buffer " *mm*"))
880 nil
881 shell-command-switch
882 (mm-mailcap-command
883 method file (mm-handle-type handle)))
884 (if (buffer-live-p buffer)
c615a00c 885 (with-current-buffer buffer
23f87bed
MB
886 (buffer-string))))
887 (progn
888 (ignore-errors (delete-file file))
889 (ignore-errors (delete-directory
890 (file-name-directory file)))
891 (ignore-errors (kill-buffer buffer))))))
892 'inline)
893 (t
58090a8d
MB
894 ;; Deleting the temp file should be postponed for some wrappers,
895 ;; shell scripts, and so on, which might exit right after having
896 ;; started a viewer command as a background job.
23f87bed
MB
897 (let ((command (mm-mailcap-command
898 method file (mm-handle-type handle))))
899 (unwind-protect
4b91459a
MB
900 (progn
901 (start-process "*display*"
902 (setq buffer
903 (generate-new-buffer " *mm*"))
904 shell-file-name
905 shell-command-switch command)
906 (set-process-sentinel
907 (get-buffer-process buffer)
58090a8d
MB
908 (lexical-let ;; Don't use `let'.
909 ;; Function used to remove temp file and directory.
910 ((fn `(lambda nil
911 ;; Don't use `ignore-errors'.
912 (condition-case nil
913 (delete-file ,file)
914 (error))
915 (condition-case nil
916 (delete-directory
917 ,(file-name-directory file))
918 (error))))
919 ;; Form uses to kill the process buffer and
920 ;; remove the undisplayer.
921 (fm `(progn
922 (kill-buffer ,buffer)
923 ,(macroexpand
924 (list 'mm-handle-set-undisplayer
925 (list 'quote handle)
926 nil))))
927 ;; Message to be issued when the process exits.
928 (done (format "Displaying %s...done" command))
929 ;; In particular, the timer object (which is
930 ;; a vector in Emacs but is a list in XEmacs)
931 ;; requires that it is lexically scoped.
932 (timer (run-at-time 2.0 nil 'ignore)))
9efa445f 933 (if (featurep 'xemacs)
ba361211
MB
934 (lambda (process state)
935 (when (eq 'exit (process-status process))
936 (if (memq timer itimer-list)
937 (set-itimer-function timer fn)
938 (funcall fn))
939 (ignore-errors (eval fm))
940 (message "%s" done)))
941 (lambda (process state)
942 (when (eq 'exit (process-status process))
943 (if (memq timer timer-list)
944 (timer-set-function timer fn)
945 (funcall fn))
946 (ignore-errors (eval fm))
947 (message "%s" done)))))))
23f87bed
MB
948 (mm-handle-set-external-undisplayer
949 handle (cons file buffer)))
950 (message "Displaying %s..." command))
951 'external)))))))
a1506d29 952
c113de23
GM
953(defun mm-mailcap-command (method file type-list)
954 (let ((ctl (cdr type-list))
955 (beg 0)
956 (uses-stdin t)
957 out sub total)
23f87bed
MB
958 (while (string-match "%{\\([^}]+\\)}\\|'%s'\\|\"%s\"\\|%s\\|%t\\|%%"
959 method beg)
c113de23
GM
960 (push (substring method beg (match-beginning 0)) out)
961 (setq beg (match-end 0)
962 total (match-string 0 method)
963 sub (match-string 1 method))
964 (cond
965 ((string= total "%%")
966 (push "%" out))
23f87bed
MB
967 ((or (string= total "%s")
968 ;; We do our own quoting.
969 (string= total "'%s'")
970 (string= total "\"%s\""))
c113de23 971 (setq uses-stdin nil)
01c52d31 972 (push (shell-quote-argument
23f87bed 973 (gnus-map-function mm-path-name-rewrite-functions file)) out))
c113de23 974 ((string= total "%t")
01c52d31 975 (push (shell-quote-argument (car type-list)) out))
c113de23 976 (t
01c52d31 977 (push (shell-quote-argument (or (cdr (assq (intern sub) ctl)) "")) out))))
c113de23 978 (push (substring method beg (length method)) out)
23f87bed
MB
979 (when uses-stdin
980 (push "<" out)
01c52d31 981 (push (shell-quote-argument
23f87bed
MB
982 (gnus-map-function mm-path-name-rewrite-functions file))
983 out))
c113de23 984 (mapconcat 'identity (nreverse out) "")))
a1506d29 985
c113de23
GM
986(defun mm-remove-parts (handles)
987 "Remove the displayed MIME parts represented by HANDLES."
988 (if (and (listp handles)
989 (bufferp (car handles)))
990 (mm-remove-part handles)
991 (let (handle)
992 (while (setq handle (pop handles))
993 (cond
994 ((stringp handle)
23f87bed
MB
995 (when (buffer-live-p (get-text-property 0 'buffer handle))
996 (kill-buffer (get-text-property 0 'buffer handle))))
c113de23
GM
997 ((and (listp handle)
998 (stringp (car handle)))
999 (mm-remove-parts (cdr handle)))
1000 (t
1001 (mm-remove-part handle)))))))
1002
1003(defun mm-destroy-parts (handles)
1004 "Remove the displayed MIME parts represented by HANDLES."
1005 (if (and (listp handles)
1006 (bufferp (car handles)))
1007 (mm-destroy-part handles)
1008 (let (handle)
1009 (while (setq handle (pop handles))
1010 (cond
1011 ((stringp handle)
23f87bed
MB
1012 (when (buffer-live-p (get-text-property 0 'buffer handle))
1013 (kill-buffer (get-text-property 0 'buffer handle))))
c113de23
GM
1014 ((and (listp handle)
1015 (stringp (car handle)))
23f87bed 1016 (mm-destroy-parts handle))
c113de23
GM
1017 (t
1018 (mm-destroy-part handle)))))))
1019
1020(defun mm-remove-part (handle)
1021 "Remove the displayed MIME part represented by HANDLE."
1022 (when (listp handle)
1023 (let ((object (mm-handle-undisplayer handle)))
1024 (ignore-errors
1025 (cond
1026 ;; Internally displayed part.
1027 ((mm-annotationp object)
60ef4154
GM
1028 (if (featurep 'xemacs)
1029 (delete-annotation object)))
c113de23
GM
1030 ((or (functionp object)
1031 (and (listp object)
1032 (eq (car object) 'lambda)))
1033 (funcall object))
1034 ;; Externally displayed part.
1035 ((consp object)
23f87bed
MB
1036 (condition-case ()
1037 (while (get-buffer-process (cdr object))
1038 (interrupt-process (get-buffer-process (cdr object)))
1039 (message "Waiting for external displayer to die...")
1040 (sit-for 1))
1041 (quit)
1042 (error))
1043 (ignore-errors (and (cdr object) (kill-buffer (cdr object))))
1044 (message "Waiting for external displayer to die...done")
c113de23 1045 (ignore-errors (delete-file (car object)))
23f87bed
MB
1046 (ignore-errors (delete-directory (file-name-directory
1047 (car object)))))
c113de23
GM
1048 ((bufferp object)
1049 (when (buffer-live-p object)
1050 (kill-buffer object)))))
1051 (mm-handle-set-undisplayer handle nil))))
1052
1053(defun mm-display-inline (handle)
1054 (let* ((type (mm-handle-media-type handle))
1055 (function (cadr (mm-assoc-string-match mm-inline-media-tests type))))
1056 (funcall function handle)
1057 (goto-char (point-min))))
1058
1059(defun mm-assoc-string-match (alist type)
1060 (dolist (elem alist)
1061 (when (string-match (car elem) type)
1062 (return elem))))
1063
23f87bed
MB
1064(defun mm-automatic-display-p (handle)
1065 "Say whether the user wants HANDLE to be displayed automatically."
1066 (let ((methods mm-automatic-display)
1067 (type (mm-handle-media-type handle))
1068 method result)
1069 (while (setq method (pop methods))
1070 (when (and (not (mm-inline-override-p handle))
1071 (string-match method type))
1072 (setq result t
1073 methods nil)))
1074 result))
1075
54e573e6
MB
1076(defun mm-inlinable-p (handle &optional type)
1077 "Say whether HANDLE can be displayed inline.
1078TYPE is the mime-type of the object; it defaults to the one given
1079in HANDLE."
1080 (unless type (setq type (mm-handle-media-type handle)))
c113de23 1081 (let ((alist mm-inline-media-tests)
c113de23
GM
1082 test)
1083 (while alist
1084 (when (string-match (caar alist) type)
1085 (setq test (caddar alist)
1086 alist nil)
1087 (setq test (funcall test handle)))
1088 (pop alist))
1089 test))
1090
c113de23 1091(defun mm-inlined-p (handle)
23f87bed 1092 "Say whether the user wants HANDLE to be displayed inline."
c113de23
GM
1093 (let ((methods mm-inlined-types)
1094 (type (mm-handle-media-type handle))
1095 method result)
1096 (while (setq method (pop methods))
1097 (when (and (not (mm-inline-override-p handle))
23f87bed 1098 (string-match method type))
c113de23
GM
1099 (setq result t
1100 methods nil)))
1101 result))
1102
1103(defun mm-attachment-override-p (handle)
1104 "Say whether HANDLE should have attachment behavior overridden."
1105 (let ((types mm-attachment-override-types)
1106 (type (mm-handle-media-type handle))
1107 ty)
1108 (catch 'found
1109 (while (setq ty (pop types))
1110 (when (and (string-match ty type)
1111 (mm-inlinable-p handle))
1112 (throw 'found t))))))
1113
1114(defun mm-inline-override-p (handle)
1115 "Say whether HANDLE should have inline behavior overridden."
1116 (let ((types mm-inline-override-types)
1117 (type (mm-handle-media-type handle))
1118 ty)
1119 (catch 'found
1120 (while (setq ty (pop types))
1121 (when (string-match ty type)
1122 (throw 'found t))))))
1123
1124(defun mm-automatic-external-display-p (type)
1125 "Return the user-defined method for TYPE."
1126 (let ((methods mm-automatic-external-display)
1127 method result)
1128 (while (setq method (pop methods))
1129 (when (string-match method type)
1130 (setq result t
1131 methods nil)))
1132 result))
1133
1134(defun mm-destroy-part (handle)
1135 "Destroy the data structures connected to HANDLE."
1136 (when (listp handle)
1137 (mm-remove-part handle)
1138 (when (buffer-live-p (mm-handle-buffer handle))
1139 (kill-buffer (mm-handle-buffer handle)))))
1140
1141(defun mm-handle-displayed-p (handle)
1142 "Say whether HANDLE is displayed or not."
1143 (mm-handle-undisplayer handle))
1144
1145;;;
1146;;; Functions for outputting parts
1147;;;
1148
531bedc3
MB
1149(defmacro mm-with-part (handle &rest forms)
1150 "Run FORMS in the temp buffer containing the contents of HANDLE."
d4eb2b7e
SM
1151 ;; The handle-buffer's content is a sequence of bytes, not a sequence of
1152 ;; chars, so the buffer should be unibyte. It may happen that the
1153 ;; handle-buffer is multibyte for some reason, in which case now is a good
1154 ;; time to adjust it, since we know at this point that it should
1155 ;; be unibyte.
1156 `(let* ((handle ,handle))
8ccbef23
G
1157 (when (and (mm-handle-buffer handle)
1158 (buffer-name (mm-handle-buffer handle)))
1159 (with-temp-buffer
1160 (mm-disable-multibyte)
1161 (insert-buffer-substring (mm-handle-buffer handle))
1162 (mm-decode-content-transfer-encoding
1163 (mm-handle-encoding handle)
1164 (mm-handle-media-type handle))
1165 ,@forms))))
531bedc3
MB
1166(put 'mm-with-part 'lisp-indent-function 1)
1167(put 'mm-with-part 'edebug-form-spec '(body))
1168
77218834
MB
1169(defun mm-get-part (handle &optional no-cache)
1170 "Return the contents of HANDLE as a string.
1171If NO-CACHE is non-nil, cached contents of a message/external-body part
1172are ignored."
1173 (if (and (not no-cache)
1174 (equal (mm-handle-media-type handle) "message/external-body"))
531bedc3
MB
1175 (progn
1176 (unless (mm-handle-cache handle)
1177 (mm-extern-cache-contents handle))
1178 (with-current-buffer (mm-handle-buffer (mm-handle-cache handle))
1179 (buffer-string)))
1180 (mm-with-part handle
719120ef 1181 (buffer-string))))
c113de23 1182
77218834
MB
1183(defun mm-insert-part (handle &optional no-cache)
1184 "Insert the contents of HANDLE in the current buffer.
1185If NO-CACHE is non-nil, cached contents of a message/external-body part
1186are ignored."
01c52d31
MB
1187 (let ((text (cond ((eq (mail-content-type-get (mm-handle-type handle)
1188 'charset)
1189 'gnus-decoded)
1190 (with-current-buffer (mm-handle-buffer handle)
1191 (buffer-string)))
1192 ((mm-multibyte-p)
1193 (mm-string-to-multibyte (mm-get-part handle no-cache)))
1194 (t
1195 (mm-get-part handle no-cache)))))
1196 (save-restriction
1197 (widen)
1198 (goto-char
1199 (prog1
1200 (point)
1201 (if (and (eq (get-char-property (max (point-min) (1- (point))) 'face)
1202 'mm-uu-extract)
1203 (eq (get-char-property 0 'face text) 'mm-uu-extract))
1204 ;; Separate the extracted parts that have the same faces.
1205 (insert "\n" text)
1206 (insert text)))))))
23f87bed
MB
1207
1208(defun mm-file-name-delete-whitespace (file-name)
1209 "Remove all whitespace characters from FILE-NAME."
1210 (while (string-match "\\s-+" file-name)
1211 (setq file-name (replace-match "" t t file-name)))
1212 file-name)
1213
1214(defun mm-file-name-trim-whitespace (file-name)
1215 "Remove leading and trailing whitespace characters from FILE-NAME."
1216 (when (string-match "\\`\\s-+" file-name)
1217 (setq file-name (substring file-name (match-end 0))))
1218 (when (string-match "\\s-+\\'" file-name)
1219 (setq file-name (substring file-name 0 (match-beginning 0))))
1220 file-name)
1221
1222(defun mm-file-name-collapse-whitespace (file-name)
1223 "Collapse multiple whitespace characters in FILE-NAME."
1224 (while (string-match "\\s-\\s-+" file-name)
1225 (setq file-name (replace-match " " t t file-name)))
1226 file-name)
1227
1228(defun mm-file-name-replace-whitespace (file-name)
1229 "Replace whitespace characters in FILE-NAME with underscores.
1230Set the option `mm-file-name-replace-whitespace' to any other
1231string if you do not like underscores."
1232 (let ((s (or mm-file-name-replace-whitespace "_")))
1233 (while (string-match "\\s-" file-name)
1234 (setq file-name (replace-match s t t file-name))))
1235 file-name)
1236
1237(defun mm-file-name-delete-control (filename)
1238 "Delete control characters from FILENAME."
1239 (gnus-replace-in-string filename "[\x00-\x1f\x7f]" ""))
1240
1241(defun mm-file-name-delete-gotchas (filename)
1242 "Delete shell gotchas from FILENAME."
1243 (setq filename (gnus-replace-in-string filename "[<>|]" ""))
1244 (gnus-replace-in-string filename "^[.-]+" ""))
c113de23 1245
01c52d31
MB
1246(defun mm-save-part (handle &optional prompt)
1247 "Write HANDLE to a file.
1248PROMPT overrides the default one used to ask user for a file name."
531bedc3
MB
1249 (let ((filename (or (mail-content-type-get
1250 (mm-handle-disposition handle) 'filename)
1251 (mail-content-type-get
1252 (mm-handle-type handle) 'name)))
1253 file)
c113de23 1254 (when filename
23f87bed
MB
1255 (setq filename (gnus-map-function mm-file-name-rewrite-functions
1256 (file-name-nondirectory filename))))
c113de23 1257 (setq file
8ccbef23
G
1258 (read-file-name (or prompt
1259 (format "Save MIME part to (default %s): "
1260 (or filename "")))
7ab0253d 1261 (or mm-default-directory default-directory)
8ccbef23 1262 (or filename "")))
3d319c8f
LMI
1263 (if (file-directory-p file)
1264 (setq file (expand-file-name filename file))
1265 (setq file (expand-file-name
1266 file (or mm-default-directory default-directory))))
c113de23 1267 (setq mm-default-directory (file-name-directory file))
23f87bed
MB
1268 (and (or (not (file-exists-p file))
1269 (yes-or-no-p (format "File %s already exists; overwrite? "
1270 file)))
1271 (progn
1272 (mm-save-part-to-file handle file)
1273 file))))
c113de23 1274
82fc7980 1275(defun mm-add-meta-html-tag (handle &optional charset force-charset)
bbbe940b
MB
1276 "Add meta html tag to specify CHARSET of HANDLE in the current buffer.
1277CHARSET defaults to the one HANDLE specifies. Existing meta tag that
82fc7980
KY
1278specifies charset will not be modified unless FORCE-CHARSET is non-nil.
1279Return t if meta tag is added or replaced."
bbbe940b
MB
1280 (when (equal (mm-handle-media-type handle) "text/html")
1281 (when (or charset
1282 (setq charset (mail-content-type-get (mm-handle-type handle)
1283 'charset)))
1284 (setq charset (format "\
1285<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">" charset))
1286 (let ((case-fold-search t))
1287 (goto-char (point-min))
1288 (if (re-search-forward "\
1289<meta\\s-+http-equiv=[\"']?content-type[\"']?\\s-+content=[\"']\
9d9ef8e8 1290text/\\(\\sw+\\)\\(?:\;\\s-*charset=\\(.+\\)\\)?[\"'][^>]*>" nil t)
82fc7980
KY
1291 (if (and (not force-charset)
1292 (match-beginning 2)
bbbe940b
MB
1293 (string-match "\\`html\\'" (match-string 1)))
1294 ;; Don't modify existing meta tag.
1295 nil
1296 ;; Replace it with the one specifying charset.
1297 (replace-match charset)
1298 t)
1299 (if (re-search-forward "<head>\\s-*" nil t)
1300 (insert charset "\n")
1301 (re-search-forward "<html\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t)
1302 (insert "<head>\n" charset "\n</head>\n"))
1303 t)))))
1304
c113de23
GM
1305(defun mm-save-part-to-file (handle file)
1306 (mm-with-unibyte-buffer
1307 (mm-insert-part handle)
bbbe940b 1308 (mm-add-meta-html-tag handle)
01c52d31
MB
1309 (let ((current-file-modes (default-file-modes)))
1310 (set-default-file-modes mm-attachment-file-modes)
1311 (unwind-protect
c113de23
GM
1312 ;; Don't re-compress .gz & al. Arguably we should make
1313 ;; `file-name-handler-alist' nil, but that would chop
1314 ;; ange-ftp, which is reasonable to use here.
01c52d31 1315 (mm-write-region (point-min) (point-max) file nil nil nil 'binary t)
23f87bed 1316 (set-default-file-modes current-file-modes)))))
c113de23 1317
9581ba4d
KY
1318(defun mm-pipe-part (handle &optional cmd)
1319 "Pipe HANDLE to a process.
1320Use CMD as the process."
1321 (let ((name (mail-content-type-get (mm-handle-type handle) 'name))
1322 (command (or cmd
1323 (gnus-read-shell-command
1324 "Shell command on MIME part: " mm-last-shell-command))))
c113de23
GM
1325 (mm-with-unibyte-buffer
1326 (mm-insert-part handle)
bbbe940b 1327 (mm-add-meta-html-tag handle)
23f87bed
MB
1328 (let ((coding-system-for-write 'binary))
1329 (shell-command-on-region (point-min) (point-max) command nil)))))
c113de23
GM
1330
1331(defun mm-interactively-view-part (handle)
1332 "Display HANDLE using METHOD."
1333 (let* ((type (mm-handle-media-type handle))
1334 (methods
229b59da 1335 (mapcar (lambda (i) (cdr (assoc 'viewer i)))
c113de23 1336 (mailcap-mime-info type 'all)))
62a27ccf
DL
1337 (method (let ((minibuffer-local-completion-map
1338 mm-viewer-completion-map))
229b59da 1339 (gnus-completing-read "Viewer" methods))))
c113de23
GM
1340 (when (string= method "")
1341 (error "No method given"))
5bb548d2 1342 (if (string-match "^[^% \t]+$" method)
c113de23 1343 (setq method (concat method " %s")))
619ac84f 1344 (mm-display-external handle method)))
c113de23
GM
1345
1346(defun mm-preferred-alternative (handles &optional preferred)
1347 "Say which of HANDLES are preferred."
1348 (let ((prec (if preferred (list preferred)
1349 (mm-preferred-alternative-precedence handles)))
1350 p h result type handle)
1351 (while (setq p (pop prec))
1352 (setq h handles)
1353 (while h
1354 (setq handle (car h))
1355 (setq type (mm-handle-media-type handle))
1356 (when (and (equal p type)
1357 (mm-automatic-display-p handle)
1358 (or (stringp (car handle))
1359 (not (mm-handle-disposition handle))
1360 (equal (car (mm-handle-disposition handle))
1361 "inline")))
1362 (setq result handle
1363 h nil
1364 prec nil))
1365 (pop h)))
1366 result))
1367
1368(defun mm-preferred-alternative-precedence (handles)
1369 "Return the precedence based on HANDLES and `mm-discouraged-alternatives'."
1370 (let ((seq (nreverse (mapcar #'mm-handle-media-type
1371 handles))))
1372 (dolist (disc (reverse mm-discouraged-alternatives))
1373 (dolist (elem (copy-sequence seq))
1374 (when (string-match disc elem)
1375 (setq seq (nconc (delete elem seq) (list elem))))))
1376 seq))
1377
1378(defun mm-get-content-id (id)
1379 "Return the handle(s) referred to by ID."
1380 (cdr (assoc id mm-content-id-alist)))
1381
23f87bed
MB
1382(defconst mm-image-type-regexps
1383 '(("/\\*.*XPM.\\*/" . xpm)
1384 ("P[1-6]" . pbm)
1385 ("GIF8" . gif)
1386 ("\377\330" . jpeg)
1387 ("\211PNG\r\n" . png)
1388 ("#define" . xbm)
1389 ("\\(MM\0\\*\\)\\|\\(II\\*\0\\)" . tiff)
1390 ("%!PS" . postscript))
1391 "Alist of (REGEXP . IMAGE-TYPE) pairs used to auto-detect image types.
1392When the first bytes of an image file match REGEXP, it is assumed to
1393be of image type IMAGE-TYPE.")
1394
1395;; Steal from image.el. image-type-from-data suffers multi-line matching bug.
1396(defun mm-image-type-from-buffer ()
1397 "Determine the image type from data in the current buffer.
1398Value is a symbol specifying the image type or nil if type cannot
1399be determined."
1400 (let ((types mm-image-type-regexps)
1401 type)
1402 (goto-char (point-min))
1403 (while (and types (null type))
1404 (let ((regexp (car (car types)))
1405 (image-type (cdr (car types))))
1406 (when (looking-at regexp)
1407 (setq type image-type))
1408 (setq types (cdr types))))
1409 type))
1410
c113de23
GM
1411(defun mm-get-image (handle)
1412 "Return an image instance based on HANDLE."
1413 (let ((type (mm-handle-media-subtype handle))
1414 spec)
1415 ;; Allow some common translations.
1416 (setq type
1417 (cond
1418 ((equal type "x-pixmap")
1419 "xpm")
1420 ((equal type "x-xbitmap")
1421 "xbm")
5bb548d2
DL
1422 ((equal type "x-portable-bitmap")
1423 "pbm")
c113de23
GM
1424 (t type)))
1425 (or (mm-handle-cache handle)
1426 (mm-with-unibyte-buffer
1427 (mm-insert-part handle)
1428 (prog1
1429 (setq spec
1430 (ignore-errors
23f87bed
MB
1431 ;; Avoid testing `make-glyph' since W3 may define
1432 ;; a bogus version of it.
c113de23 1433 (if (fboundp 'create-image)
23f87bed
MB
1434 (create-image (buffer-string)
1435 (or (mm-image-type-from-buffer)
1436 (intern type))
1437 'data-p)
1438 (mm-create-image-xemacs type))))
c113de23
GM
1439 (mm-handle-set-cache handle spec))))))
1440
23f87bed 1441(defun mm-create-image-xemacs (type)
9efa445f
DN
1442 (when (featurep 'xemacs)
1443 (cond
1444 ((equal type "xbm")
1445 ;; xbm images require special handling, since
1446 ;; the only way to create glyphs from these
1447 ;; (without a ton of work) is to write them
1448 ;; out to a file, and then create a file
1449 ;; specifier.
1450 (let ((file (mm-make-temp-file
1451 (expand-file-name "emm" mm-tmp-directory)
1452 nil ".xbm")))
1453 (unwind-protect
1454 (progn
1455 (write-region (point-min) (point-max) file)
1456 (make-glyph (list (cons 'x file))))
1457 (ignore-errors
1458 (delete-file file)))))
1459 (t
1460 (make-glyph
1461 (vector
1462 (or (mm-image-type-from-buffer)
1463 (intern type))
1464 :data (buffer-string)))))))
23f87bed 1465
12c9fab6
GM
1466(declare-function image-size "image.c" (spec &optional pixels frame))
1467
c113de23
GM
1468(defun mm-image-fit-p (handle)
1469 "Say whether the image in HANDLE will fit the current window."
1470 (let ((image (mm-get-image handle)))
524705ae 1471 (or (not image)
9efa445f 1472 (if (featurep 'xemacs)
524705ae
MB
1473 ;; XEmacs' glyphs can actually tell us about their width, so
1474 ;; lets be nice and smart about them.
1475 (or mm-inline-large-images
1476 (and (<= (glyph-width image) (window-pixel-width))
1477 (<= (glyph-height image) (window-pixel-height))))
1478 (let* ((size (image-size image))
1479 (w (car size))
1480 (h (cdr size)))
1481 (or mm-inline-large-images
1482 (and (<= h (1- (window-height))) ; Don't include mode line.
1483 (<= w (window-width)))))))))
c113de23
GM
1484
1485(defun mm-valid-image-format-p (format)
1486 "Say whether FORMAT can be displayed natively by Emacs."
1487 (cond
1488 ;; Handle XEmacs
1489 ((fboundp 'valid-image-instantiator-format-p)
1490 (valid-image-instantiator-format-p format))
5b5dafd2 1491 ;; Handle Emacs
c113de23
GM
1492 ((fboundp 'image-type-available-p)
1493 (and (display-graphic-p)
1494 (image-type-available-p format)))
1495 ;; Nobody else can do images yet.
1496 (t
1497 nil)))
1498
1499(defun mm-valid-and-fit-image-p (format handle)
1500 "Say whether FORMAT can be displayed natively and HANDLE fits the window."
62a27ccf 1501 (and (mm-valid-image-format-p format)
c113de23
GM
1502 (mm-image-fit-p handle)))
1503
23f87bed
MB
1504(defun mm-find-part-by-type (handles type &optional notp recursive)
1505 "Search in HANDLES for part with TYPE.
1506If NOTP, returns first non-matching part.
1507If RECURSIVE, search recursively."
1508 (let (handle)
1509 (while handles
1510 (if (and recursive (stringp (caar handles)))
1511 (if (setq handle (mm-find-part-by-type (cdar handles) type
1512 notp recursive))
1513 (setq handles nil))
1514 (if (if notp
1515 (not (equal (mm-handle-media-type (car handles)) type))
1516 (equal (mm-handle-media-type (car handles)) type))
1517 (setq handle (car handles)
1518 handles nil)))
1519 (setq handles (cdr handles)))
1520 handle))
1521
1522(defun mm-find-raw-part-by-type (ctl type &optional notp)
1523 (goto-char (point-min))
1524 (let* ((boundary (concat "--" (mm-handle-multipart-ctl-parameter ctl
1525 'boundary)))
1526 (close-delimiter (concat "^" (regexp-quote boundary) "--[ \t]*$"))
1527 start
1528 (end (save-excursion
1529 (goto-char (point-max))
1530 (if (re-search-backward close-delimiter nil t)
1531 (match-beginning 0)
1532 (point-max))))
1533 result)
1534 (setq boundary (concat "^" (regexp-quote boundary) "[ \t]*$"))
1535 (while (and (not result)
1536 (re-search-forward boundary end t))
1537 (goto-char (match-beginning 0))
1538 (when start
1539 (save-excursion
1540 (save-restriction
1541 (narrow-to-region start (1- (point)))
c96ec15a
MB
1542 (when (let* ((ct (mail-fetch-field "content-type"))
1543 (ctl (and ct (mail-header-parse-content-type ct))))
23f87bed
MB
1544 (if notp
1545 (not (equal (car ctl) type))
1546 (equal (car ctl) type)))
1547 (setq result (buffer-string))))))
1548 (forward-line 1)
1549 (setq start (point)))
1550 (when (and (not result) start)
1551 (save-excursion
1552 (save-restriction
1553 (narrow-to-region start end)
c96ec15a
MB
1554 (when (let* ((ct (mail-fetch-field "content-type"))
1555 (ctl (and ct (mail-header-parse-content-type ct))))
23f87bed
MB
1556 (if notp
1557 (not (equal (car ctl) type))
1558 (equal (car ctl) type)))
1559 (setq result (buffer-string))))))
1560 result))
1561
1562(defvar mm-security-handle nil)
1563
1564(defsubst mm-set-handle-multipart-parameter (handle parameter value)
1565 ;; HANDLE could be a CTL.
1566 (when handle
1567 (put-text-property 0 (length (car handle)) parameter value
1568 (car handle))))
1569
60ef4154
GM
1570(autoload 'mm-view-pkcs7 "mm-view")
1571
23f87bed
MB
1572(defun mm-possibly-verify-or-decrypt (parts ctl)
1573 (let ((type (car ctl))
1574 (subtype (cadr (split-string (car ctl) "/")))
1575 (mm-security-handle ctl) ;; (car CTL) is the type.
1576 protocol func functest)
1577 (cond
1578 ((or (equal type "application/x-pkcs7-mime")
1579 (equal type "application/pkcs7-mime"))
1580 (with-temp-buffer
1581 (when (and (cond
1582 ((eq mm-decrypt-option 'never) nil)
1583 ((eq mm-decrypt-option 'always) t)
1584 ((eq mm-decrypt-option 'known) t)
1585 (t (y-or-n-p
1586 (format "Decrypt (S/MIME) part? "))))
1587 (mm-view-pkcs7 parts))
1588 (setq parts (mm-dissect-buffer t)))))
1589 ((equal subtype "signed")
1590 (unless (and (setq protocol
1591 (mm-handle-multipart-ctl-parameter ctl 'protocol))
1592 (not (equal protocol "multipart/mixed")))
1593 ;; The message is broken or draft-ietf-openpgp-multsig-01.
1594 (let ((protocols mm-verify-function-alist))
1595 (while protocols
1596 (if (and (or (not (setq functest (nth 3 (car protocols))))
1597 (funcall functest parts ctl))
1598 (mm-find-part-by-type parts (caar protocols) nil t))
1599 (setq protocol (caar protocols)
1600 protocols nil)
1601 (setq protocols (cdr protocols))))))
1602 (setq func (nth 1 (assoc protocol mm-verify-function-alist)))
1603 (when (cond
1604 ((eq mm-verify-option 'never) nil)
1605 ((eq mm-verify-option 'always) t)
1606 ((eq mm-verify-option 'known)
1607 (and func
1608 (or (not (setq functest
1609 (nth 3 (assoc protocol
1610 mm-verify-function-alist))))
1611 (funcall functest parts ctl))))
1612 (t
1613 (y-or-n-p
1614 (format "Verify signed (%s) part? "
1615 (or (nth 2 (assoc protocol mm-verify-function-alist))
1616 (format "protocol=%s" protocol))))))
1617 (save-excursion
1618 (if func
01c52d31 1619 (setq parts (funcall func parts ctl))
23f87bed
MB
1620 (mm-set-handle-multipart-parameter
1621 mm-security-handle 'gnus-details
1622 (format "Unknown sign protocol (%s)" protocol))))))
1623 ((equal subtype "encrypted")
1624 (unless (setq protocol
1625 (mm-handle-multipart-ctl-parameter ctl 'protocol))
1626 ;; The message is broken.
1627 (let ((parts parts))
1628 (while parts
1629 (if (assoc (mm-handle-media-type (car parts))
1630 mm-decrypt-function-alist)
1631 (setq protocol (mm-handle-media-type (car parts))
1632 parts nil)
1633 (setq parts (cdr parts))))))
1634 (setq func (nth 1 (assoc protocol mm-decrypt-function-alist)))
1635 (when (cond
1636 ((eq mm-decrypt-option 'never) nil)
1637 ((eq mm-decrypt-option 'always) t)
1638 ((eq mm-decrypt-option 'known)
1639 (and func
1640 (or (not (setq functest
1641 (nth 3 (assoc protocol
1642 mm-decrypt-function-alist))))
1643 (funcall functest parts ctl))))
1644 (t
1645 (y-or-n-p
1646 (format "Decrypt (%s) part? "
1647 (or (nth 2 (assoc protocol mm-decrypt-function-alist))
1648 (format "protocol=%s" protocol))))))
1649 (save-excursion
1650 (if func
1651 (setq parts (funcall func parts ctl))
1652 (mm-set-handle-multipart-parameter
1653 mm-security-handle 'gnus-details
1654 (format "Unknown encrypt protocol (%s)" protocol))))))
1655 (t nil))
1656 parts))
1657
1658(defun mm-multiple-handles (handles)
1659 (and (listp handles)
1660 (> (length handles) 1)
1661 (or (listp (car handles))
1662 (stringp (car handles)))))
1663
1664(defun mm-complicated-handles (handles)
1665 (and (listp (car handles))
1666 (> (length handles) 1)))
1667
1668(defun mm-merge-handles (handles1 handles2)
1669 (append
1670 (if (listp (car handles1))
1671 handles1
1672 (list handles1))
1673 (if (listp (car handles2))
1674 handles2
1675 (list handles2))))
1676
1677(defun mm-readable-p (handle)
1678 "Say whether the content of HANDLE is readable."
1679 (and (< (with-current-buffer (mm-handle-buffer handle)
1680 (buffer-size)) 10000)
1681 (mm-with-unibyte-buffer
1682 (mm-insert-part handle)
1683 (and (eq (mm-body-7-or-8) '7bit)
1684 (not (mm-long-lines-p 76))))))
1685
cfadea45
KY
1686(declare-function libxml-parse-html-region "xml.c"
1687 (start end &optional base-url))
1688(declare-function shr-insert-document "shr" (dom))
1689
870409d4 1690(defun mm-shr (handle)
130e977f
LMI
1691 ;; Require since we bind its variables.
1692 (require 'shr)
cfadea45 1693 (let ((article-buffer (current-buffer))
130e977f
LMI
1694 (shr-blocked-images (with-current-buffer gnus-summary-buffer
1695 gnus-blocked-images))
1696 (shr-content-function (lambda (id)
1697 (let ((handle (mm-get-content-id id)))
1698 (when handle
1699 (mm-with-part handle
1700 (buffer-string))))))
cfadea45 1701 charset)
870409d4
G
1702 (unless handle
1703 (setq handle (mm-dissect-buffer t)))
cfadea45 1704 (setq charset (mail-content-type-get (mm-handle-type handle) 'charset))
a41c2e6d
G
1705 (save-restriction
1706 (narrow-to-region (point) (point))
1707 (shr-insert-document
1708 (mm-with-part handle
cfadea45
KY
1709 (when (and charset
1710 (setq charset (mm-charset-to-coding-system charset))
1711 (not (eq charset 'ascii)))
1712 (insert (prog1
1713 (mm-decode-coding-string (buffer-string) charset)
1714 (erase-buffer)
1715 (mm-enable-multibyte))))
a41c2e6d 1716 (libxml-parse-html-region (point-min) (point-max)))))))
870409d4 1717
c113de23
GM
1718(provide 'mm-decode)
1719
1720;;; mm-decode.el ends here