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