Keep track of the natural width of TD elements, so we know which ones to expand.
[gnus] / lisp / mm-view.el
1 ;;; mm-view.el --- functions for viewing MIME objects
2
3 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4 ;;   2007, 2008, 2009, 2010  Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
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 3 of the License, or
12 ;; (at your option) 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.  If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;;; Code:
25 (eval-and-compile
26   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
27 (eval-when-compile (require 'cl))
28 (require 'mail-parse)
29 (require 'mailcap)
30 (require 'mm-bodies)
31 (require 'mm-decode)
32 (require 'smime)
33
34 (autoload 'gnus-completing-read "gnus-util")
35 (autoload 'gnus-window-inside-pixel-edges "gnus-ems")
36 (autoload 'gnus-article-prepare-display "gnus-art")
37 (autoload 'vcard-parse-string "vcard")
38 (autoload 'vcard-format-string "vcard")
39 (autoload 'fill-flowed "flow-fill")
40 (autoload 'html2text "html2text" nil t)
41
42 (defvar gnus-article-mime-handles)
43 (defvar gnus-newsgroup-charset)
44 (defvar smime-keys)
45 (defvar w3m-cid-retrieve-function-alist)
46 (defvar w3m-current-buffer)
47 (defvar w3m-display-inline-images)
48 (defvar w3m-minor-mode-map)
49
50 (defvar mm-text-html-renderer-alist
51   '((w3  . mm-inline-text-html-render-with-w3)
52     (w3m . mm-inline-text-html-render-with-w3m)
53     (w3m-standalone . mm-inline-text-html-render-with-w3m-standalone)
54     (links mm-inline-render-with-file
55            mm-links-remove-leading-blank
56            "links" "-dump" file)
57     (lynx  mm-inline-render-with-stdin nil
58            "lynx" "-dump" "-force_html" "-stdin" "-nolist")
59     (html2text  mm-inline-render-with-function html2text))
60   "The attributes of renderer types for text/html.")
61
62 (defvar mm-text-html-washer-alist
63   '((w3  . gnus-article-wash-html-with-w3)
64     (w3m . gnus-article-wash-html-with-w3m)
65     (w3m-standalone . gnus-article-wash-html-with-w3m-standalone)
66     (links mm-inline-wash-with-file
67            mm-links-remove-leading-blank
68            "links" "-dump" file)
69     (lynx  mm-inline-wash-with-stdin nil
70            "lynx" "-dump" "-force_html" "-stdin" "-nolist")
71     (html2text  html2text))
72   "The attributes of washer types for text/html.")
73
74 (defcustom mm-fill-flowed t
75   "If non-nil a format=flowed article will be displayed flowed."
76   :type 'boolean
77   :version "22.1"
78   :group 'mime-display)
79
80 (defcustom mm-inline-large-images-proportion 0.9
81   "Maximum proportion of large image resized when
82 `mm-inline-large-images' is set to resize."
83   :type 'float
84   :version "24.1"
85   :group 'mime-display)
86
87 ;;; Internal variables.
88
89 ;;;
90 ;;; Functions for displaying various formats inline
91 ;;;
92
93 (defun mm-inline-image-emacs (handle)
94   (let ((b (point-marker))
95         (inhibit-read-only t))
96     (put-image
97      (let ((image (mm-get-image handle)))
98        (if (eq mm-inline-large-images 'resize)
99            (gnus-rescale-image image
100                                (let ((edges (gnus-window-inside-pixel-edges
101                                              (get-buffer-window (current-buffer)))))
102                                  (cons (truncate (* mm-inline-large-images-proportion
103                                                     (- (nth 2 edges) (nth 0 edges))))
104                                        (truncate (* mm-inline-large-images-proportion
105                                                     (- (nth 3 edges) (nth 1 edges)))))))
106          image))
107      b)
108     (insert "\n\n")
109     (mm-handle-set-undisplayer
110      handle
111      `(lambda ()
112         (let ((b ,b)
113               (inhibit-read-only t))
114           (remove-images b b)
115           (delete-region b (+ b 2)))))))
116
117 (defun mm-inline-image-xemacs (handle)
118   (when (featurep 'xemacs)
119     (insert "\n\n")
120     (forward-char -2)
121     (let ((annot (make-annotation (mm-get-image handle) nil 'text))
122         (inhibit-read-only t))
123       (mm-handle-set-undisplayer
124        handle
125        `(lambda ()
126           (let ((b ,(point-marker))
127               (inhibit-read-only t))
128             (delete-annotation ,annot)
129             (delete-region (- b 2) b))))
130       (set-extent-property annot 'mm t)
131       (set-extent-property annot 'duplicable t))))
132
133 (eval-and-compile
134   (if (featurep 'xemacs)
135       (defalias 'mm-inline-image 'mm-inline-image-xemacs)
136     (defalias 'mm-inline-image 'mm-inline-image-emacs)))
137
138 ;; External.
139 (declare-function w3-do-setup       "ext:w3"         ())
140 (declare-function w3-region         "ext:w3-display" (st nd))
141 (declare-function w3-prepare-buffer "ext:w3-display" (&rest args))
142
143 (defvar mm-w3-setup nil)
144 (defun mm-setup-w3 ()
145   (unless mm-w3-setup
146     (require 'w3)
147     (w3-do-setup)
148     (require 'url)
149     (require 'w3-vars)
150     (require 'url-vars)
151     (setq mm-w3-setup t)))
152
153 (defun mm-inline-text-html-render-with-w3 (handle)
154   (mm-setup-w3)
155   (let ((text (mm-get-part handle))
156         (b (point))
157         (url-standalone-mode t)
158         (url-gateway-unplugged t)
159         (w3-honor-stylesheets nil)
160         (url-current-object
161          (url-generic-parse-url (format "cid:%s" (mm-handle-id handle))))
162         (width (window-width))
163         (charset (mail-content-type-get
164                   (mm-handle-type handle) 'charset)))
165     (save-excursion
166       (insert (if charset (mm-decode-string text charset) text))
167       (save-restriction
168         (narrow-to-region b (point))
169         (unless charset
170           (goto-char (point-min))
171           (when (or (and (boundp 'w3-meta-content-type-charset-regexp)
172                          (re-search-forward
173                           w3-meta-content-type-charset-regexp nil t))
174                     (and (boundp 'w3-meta-charset-content-type-regexp)
175                          (re-search-forward
176                           w3-meta-charset-content-type-regexp nil t)))
177             (setq charset
178                   (let ((bsubstr (buffer-substring-no-properties
179                                   (match-beginning 2)
180                                   (match-end 2))))
181                     (if (fboundp 'w3-coding-system-for-mime-charset)
182                         (w3-coding-system-for-mime-charset bsubstr)
183                       (mm-charset-to-coding-system bsubstr))))
184             (delete-region (point-min) (point-max))
185             (insert (mm-decode-string text charset))))
186         (save-window-excursion
187           (save-restriction
188             (let ((w3-strict-width width)
189                   ;; Don't let w3 set the global version of
190                   ;; this variable.
191                   (fill-column fill-column))
192               (if (or debug-on-error debug-on-quit)
193                   (w3-region (point-min) (point-max))
194                 (condition-case ()
195                     (w3-region (point-min) (point-max))
196                   (error
197                    (delete-region (point-min) (point-max))
198                    (let ((b (point))
199                          (charset (mail-content-type-get
200                                    (mm-handle-type handle) 'charset)))
201                      (if (or (eq charset 'gnus-decoded)
202                              (eq mail-parse-charset 'gnus-decoded))
203                        (save-restriction
204                          (narrow-to-region (point) (point))
205                          (mm-insert-part handle)
206                          (goto-char (point-max)))
207                        (insert (mm-decode-string (mm-get-part handle)
208                                                  charset))))
209                    (message
210                     "Error while rendering html; showing as text/plain")))))))
211         (mm-handle-set-undisplayer
212          handle
213          `(lambda ()
214             (let ((inhibit-read-only t))
215               ,@(if (functionp 'remove-specifier)
216                     '((dolist (prop '(background background-pixmap foreground))
217                         (remove-specifier
218                          (face-property 'default prop)
219                          (current-buffer)))))
220               (delete-region ,(point-min-marker)
221                              ,(point-max-marker)))))))))
222
223 (defvar mm-w3m-setup nil
224   "Whether gnus-article-mode has been setup to use emacs-w3m.")
225
226 ;; External.
227 (declare-function w3m-detect-meta-charset "ext:w3m" ())
228 (declare-function w3m-region "ext:w3m" (start end &optional url charset))
229
230 (defun mm-setup-w3m ()
231   "Setup gnus-article-mode to use emacs-w3m."
232   (unless mm-w3m-setup
233     (require 'w3m)
234     (unless (assq 'gnus-article-mode w3m-cid-retrieve-function-alist)
235       (push (cons 'gnus-article-mode 'mm-w3m-cid-retrieve)
236             w3m-cid-retrieve-function-alist))
237     (setq mm-w3m-setup t))
238   (setq w3m-display-inline-images mm-inline-text-html-with-images))
239
240 (defun mm-w3m-cid-retrieve-1 (url handle)
241   (dolist (elem handle)
242     (when (consp elem)
243       (when (equal url (mm-handle-id elem))
244         (mm-insert-part elem)
245         (throw 'found-handle (mm-handle-media-type elem)))
246       (when (and (stringp (car elem))
247                  (equal "multipart" (mm-handle-media-supertype elem)))
248         (mm-w3m-cid-retrieve-1 url elem)))))
249
250 (defun mm-w3m-cid-retrieve (url &rest args)
251   "Insert a content pointed by URL if it has the cid: scheme."
252   (when (string-match "\\`cid:" url)
253     (or (catch 'found-handle
254           (mm-w3m-cid-retrieve-1
255            (setq url (concat "<" (substring url (match-end 0)) ">"))
256            (with-current-buffer w3m-current-buffer
257              gnus-article-mime-handles)))
258         (prog1
259             nil
260           (message "Failed to find \"Content-ID: %s\"" url)))))
261
262 (defun mm-inline-text-html-render-with-w3m (handle)
263   "Render a text/html part using emacs-w3m."
264   (mm-setup-w3m)
265   (let ((text (mm-get-part handle))
266         (b (point))
267         (charset (or (mail-content-type-get (mm-handle-type handle) 'charset)
268                      mail-parse-charset)))
269     (save-excursion
270       (insert (if charset (mm-decode-string text charset) text))
271       (save-restriction
272         (narrow-to-region b (point))
273         (unless charset
274           (goto-char (point-min))
275           (when (setq charset (w3m-detect-meta-charset))
276             (delete-region (point-min) (point-max))
277             (insert (mm-decode-string text charset))))
278         (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
279               w3m-force-redisplay)
280           (w3m-region (point-min) (point-max) nil charset))
281         ;; Put the mark meaning this part was rendered by emacs-w3m.
282         (put-text-property (point-min) (point-max)
283                            'mm-inline-text-html-with-w3m t)
284         (when (and mm-inline-text-html-with-w3m-keymap
285                    (boundp 'w3m-minor-mode-map)
286                    w3m-minor-mode-map)
287           (if (and (boundp 'w3m-link-map)
288                    w3m-link-map)
289               (let* ((start (point-min))
290                      (end (point-max))
291                      (on (get-text-property start 'w3m-href-anchor))
292                      (map (copy-keymap w3m-link-map))
293                      next)
294                 (set-keymap-parent map w3m-minor-mode-map)
295                 (while (< start end)
296                   (if on
297                       (progn
298                         (setq next (or (text-property-any start end
299                                                           'w3m-href-anchor nil)
300                                        end))
301                         (put-text-property start next 'keymap map))
302                     (setq next (or (text-property-not-all start end
303                                                           'w3m-href-anchor nil)
304                                    end))
305                     (put-text-property start next 'keymap w3m-minor-mode-map))
306                   (setq start next
307                         on (not on))))
308             (put-text-property (point-min) (point-max)
309                                'keymap w3m-minor-mode-map)))
310         (mm-handle-set-undisplayer
311          handle
312          `(lambda ()
313             (let ((inhibit-read-only t))
314               (delete-region ,(point-min-marker)
315                              ,(point-max-marker)))))))))
316
317 (defvar mm-w3m-standalone-supports-m17n-p (if (featurep 'mule) 'undecided)
318   "*T means the w3m command supports the m17n feature.")
319
320 (defun mm-w3m-standalone-supports-m17n-p ()
321   "Say whether the w3m command supports the m17n feature."
322   (cond ((eq mm-w3m-standalone-supports-m17n-p t) t)
323         ((eq mm-w3m-standalone-supports-m17n-p nil) nil)
324         ((not (featurep 'mule)) (setq mm-w3m-standalone-supports-m17n-p nil))
325         ((condition-case nil
326              (let ((coding-system-for-write 'iso-2022-jp)
327                    (coding-system-for-read 'iso-2022-jp)
328                    (str (mm-decode-coding-string "\
329 \e$B#D#o#e#s!!#w#3#m!!#s#u#p#p#o#r#t!!#m#1#7#n!)\e(B" 'iso-2022-jp)))
330                (mm-with-multibyte-buffer
331                  (insert str)
332                  (call-process-region
333                   (point-min) (point-max) "w3m" t t nil "-dump"
334                   "-T" "text/html" "-I" "iso-2022-jp" "-O" "iso-2022-jp")
335                  (goto-char (point-min))
336                  (search-forward str nil t)))
337            (error nil))
338          (setq mm-w3m-standalone-supports-m17n-p t))
339         (t
340          ;;(message "You had better upgrade your w3m command")
341          (setq mm-w3m-standalone-supports-m17n-p nil))))
342
343 (defun mm-inline-text-html-render-with-w3m-standalone (handle)
344   "Render a text/html part using w3m."
345   (if (mm-w3m-standalone-supports-m17n-p)
346       (let ((source (mm-get-part handle))
347             (charset (or (mail-content-type-get (mm-handle-type handle)
348                                                 'charset)
349                          (symbol-name mail-parse-charset)))
350             cs)
351         (unless (and charset
352                      (setq cs (mm-charset-to-coding-system charset))
353                      (not (eq cs 'ascii)))
354           ;; The default.
355           (setq charset "iso-8859-1"
356                 cs 'iso-8859-1))
357         (mm-insert-inline
358          handle
359          (mm-with-unibyte-buffer
360            (insert source)
361            (mm-enable-multibyte)
362            (let ((coding-system-for-write 'binary)
363                  (coding-system-for-read cs))
364              (call-process-region
365               (point-min) (point-max)
366               "w3m" t t nil "-dump" "-T" "text/html"
367               "-I" charset "-O" charset))
368            (buffer-string))))
369     (mm-inline-render-with-stdin handle nil "w3m" "-dump" "-T" "text/html")))
370
371 (defun mm-links-remove-leading-blank ()
372   ;; Delete the annoying three spaces preceding each line of links
373   ;; output.
374   (goto-char (point-min))
375   (while (re-search-forward "^   " nil t)
376     (delete-region (match-beginning 0) (match-end 0))))
377
378 (defun mm-inline-wash-with-file (post-func cmd &rest args)
379   (let ((file (mm-make-temp-file
380                (expand-file-name "mm" mm-tmp-directory))))
381     (let ((coding-system-for-write 'binary))
382       (write-region (point-min) (point-max) file nil 'silent))
383     (delete-region (point-min) (point-max))
384     (unwind-protect
385         (apply 'call-process cmd nil t nil (mapcar 'eval args))
386       (delete-file file))
387     (and post-func (funcall post-func))))
388
389 (defun mm-inline-wash-with-stdin (post-func cmd &rest args)
390   (let ((coding-system-for-write 'binary))
391     (apply 'call-process-region (point-min) (point-max)
392            cmd t t nil args))
393   (and post-func (funcall post-func)))
394
395 (defun mm-inline-render-with-file (handle post-func cmd &rest args)
396   (let ((source (mm-get-part handle)))
397     (mm-insert-inline
398      handle
399      (mm-with-unibyte-buffer
400        (insert source)
401        (apply 'mm-inline-wash-with-file post-func cmd args)
402        (buffer-string)))))
403
404 (defun mm-inline-render-with-stdin (handle post-func cmd &rest args)
405   (let ((source (mm-get-part handle)))
406     (mm-insert-inline
407      handle
408      (mm-with-unibyte-buffer
409        (insert source)
410        (apply 'mm-inline-wash-with-stdin post-func cmd args)
411        (buffer-string)))))
412
413 (defun mm-inline-render-with-function (handle func &rest args)
414   (let ((source (mm-get-part handle))
415         (charset (or (mail-content-type-get (mm-handle-type handle) 'charset)
416                      mail-parse-charset)))
417     (mm-insert-inline
418      handle
419      (mm-with-multibyte-buffer
420        (insert (if charset
421                    (mm-decode-string source charset)
422                  source))
423        (apply func args)
424        (buffer-string)))))
425
426 (defun mm-inline-text-html (handle)
427   (let* ((func (or mm-inline-text-html-renderer mm-text-html-renderer))
428          (entry (assq func mm-text-html-renderer-alist))
429          (inhibit-read-only t))
430     (if entry
431         (setq func (cdr entry)))
432     (cond
433      ((functionp func)
434       (funcall func handle))
435      (t
436       (apply (car func) handle (cdr func))))))
437
438 (defun mm-inline-text-vcard (handle)
439   (let ((inhibit-read-only t))
440     (mm-insert-inline
441      handle
442      (concat "\n-- \n"
443              (ignore-errors
444                (if (fboundp 'vcard-pretty-print)
445                    (vcard-pretty-print (mm-get-part handle))
446                  (vcard-format-string
447                   (vcard-parse-string (mm-get-part handle)
448                                       'vcard-standard-filter))))))))
449
450 (defun mm-inline-text (handle)
451   (let ((b (point))
452         (type (mm-handle-media-subtype handle))
453         (charset (mail-content-type-get
454                   (mm-handle-type handle) 'charset))
455         (inhibit-read-only t))
456     (if (or (eq charset 'gnus-decoded)
457             ;; This is probably not entirely correct, but
458             ;; makes rfc822 parts with embedded multiparts work.
459             (eq mail-parse-charset 'gnus-decoded))
460         (save-restriction
461           (narrow-to-region (point) (point))
462           (mm-insert-part handle)
463           (goto-char (point-max)))
464       (insert (mm-decode-string (mm-get-part handle) charset)))
465     (when (and mm-fill-flowed
466                (equal type "plain")
467                (equal (cdr (assoc 'format (mm-handle-type handle)))
468                       "flowed"))
469       (save-restriction
470         (narrow-to-region b (point))
471         (goto-char b)
472         (fill-flowed nil (equal (cdr (assoc 'delsp (mm-handle-type handle)))
473                                 "yes"))
474         (goto-char (point-max))))
475     (save-restriction
476       (narrow-to-region b (point))
477       (when (member type '("enriched" "richtext"))
478         (set-text-properties (point-min) (point-max) nil)
479         (ignore-errors
480           (enriched-decode (point-min) (point-max))))
481       (mm-handle-set-undisplayer
482        handle
483        `(lambda ()
484           (let ((inhibit-read-only t))
485             (delete-region ,(point-min-marker)
486                            ,(point-max-marker))))))))
487
488 (defun mm-insert-inline (handle text)
489   "Insert TEXT inline from HANDLE."
490   (let ((b (point)))
491     (insert text)
492     (unless (bolp)
493       (insert "\n"))
494     (mm-handle-set-undisplayer
495      handle
496      `(lambda ()
497         (let ((inhibit-read-only t))
498           (delete-region ,(copy-marker b)
499                          ,(copy-marker (point))))))))
500
501 (defun mm-inline-audio (handle)
502   (message "Not implemented"))
503
504 (defun mm-view-sound-file ()
505   (message "Not implemented"))
506
507 (defun mm-w3-prepare-buffer ()
508   (require 'w3)
509   (let ((url-standalone-mode t)
510         (url-gateway-unplugged t)
511         (w3-honor-stylesheets nil))
512     (w3-prepare-buffer)))
513
514 (defun mm-view-message ()
515   (mm-enable-multibyte)
516   (let (handles)
517     (let (gnus-article-mime-handles)
518       ;; Double decode problem may happen.  See mm-inline-message.
519       (run-hooks 'gnus-article-decode-hook)
520       (gnus-article-prepare-display)
521       (setq handles gnus-article-mime-handles))
522     (when handles
523       (setq gnus-article-mime-handles
524             (mm-merge-handles gnus-article-mime-handles handles))))
525   (fundamental-mode)
526   (goto-char (point-min)))
527
528 (defun mm-inline-message (handle)
529   (let ((b (point))
530         (bolp (bolp))
531         (charset (mail-content-type-get
532                   (mm-handle-type handle) 'charset))
533         gnus-displaying-mime handles)
534     (when (and charset
535                (stringp charset))
536       (setq charset (intern (downcase charset)))
537       (when (eq charset 'us-ascii)
538         (setq charset nil)))
539     (save-excursion
540       (save-restriction
541         (narrow-to-region b b)
542         (mm-insert-part handle)
543         (let (gnus-article-mime-handles
544               ;; disable prepare hook
545               gnus-article-prepare-hook
546               (gnus-newsgroup-charset
547                (unless (eq charset 'gnus-decoded) ;; mm-uu might set it.
548                  (or charset gnus-newsgroup-charset))))
549           (let ((gnus-original-article-buffer (mm-handle-buffer handle)))
550             (run-hooks 'gnus-article-decode-hook))
551           (gnus-article-prepare-display)
552           (setq handles gnus-article-mime-handles))
553         (goto-char (point-min))
554         (unless bolp
555           (insert "\n"))
556         (goto-char (point-max))
557         (unless (bolp)
558           (insert "\n"))
559         (insert "----------\n\n")
560         (when handles
561           (setq gnus-article-mime-handles
562                 (mm-merge-handles gnus-article-mime-handles handles)))
563         (mm-handle-set-undisplayer
564          handle
565          `(lambda ()
566             (let ((inhibit-read-only t))
567               (if (fboundp 'remove-specifier)
568                   ;; This is only valid on XEmacs.
569                   (dolist (prop '(background background-pixmap foreground))
570                     (remove-specifier
571                      (face-property 'default prop) (current-buffer))))
572               (delete-region ,(point-min-marker) ,(point-max-marker)))))))))
573
574 (defun mm-display-inline-fontify (handle mode)
575   (let ((charset (mail-content-type-get (mm-handle-type handle) 'charset))
576         text coding-system)
577     (unless (eq charset 'gnus-decoded)
578       (mm-with-unibyte-buffer
579         (mm-insert-part handle)
580         (mm-decompress-buffer
581          (or (mail-content-type-get (mm-handle-disposition handle) 'name)
582              (mail-content-type-get (mm-handle-disposition handle) 'filename))
583          t t)
584         (unless charset
585           (setq coding-system (mm-find-buffer-file-coding-system)))
586         (setq text (buffer-string))))
587     ;; XEmacs @#$@ version of font-lock refuses to fully turn itself
588     ;; on for buffers whose name begins with " ".  That's why we use
589     ;; `with-current-buffer'/`generate-new-buffer' rather than
590     ;; `with-temp-buffer'.
591     (with-current-buffer (generate-new-buffer "*fontification*")
592       (buffer-disable-undo)
593       (mm-enable-multibyte)
594       (insert (cond ((eq charset 'gnus-decoded)
595                      (with-current-buffer (mm-handle-buffer handle)
596                        (buffer-string)))
597                     (coding-system
598                      (mm-decode-coding-string text coding-system))
599                     (charset
600                      (mm-decode-string text charset))
601                     (t
602                      text)))
603       (require 'font-lock)
604       (let ((font-lock-maximum-size nil)
605             ;; Disable support modes, e.g., jit-lock, lazy-lock, etc.
606             (font-lock-mode-hook nil)
607             (font-lock-support-mode nil)
608             ;; I find font-lock a bit too verbose.
609             (font-lock-verbose nil))
610         (funcall mode)
611         ;; The mode function might have already turned on font-lock.
612         (unless (symbol-value 'font-lock-mode)
613           (font-lock-fontify-buffer)))
614       ;; By default, XEmacs font-lock uses non-duplicable text
615       ;; properties.  This code forces all the text properties
616       ;; to be copied along with the text.
617       (when (featurep 'xemacs)
618         (map-extents (lambda (ext ignored)
619                        (set-extent-property ext 'duplicable t)
620                        nil)
621                      nil nil nil nil nil 'text-prop))
622       (setq text (buffer-string))
623       (kill-buffer (current-buffer)))
624     (mm-insert-inline handle text)))
625
626 ;; Shouldn't these functions check whether the user even wants to use
627 ;; font-lock?  At least under XEmacs, this fontification is pretty
628 ;; much unconditional.  Also, it would be nice to change for the size
629 ;; of the fontified region.
630
631 (defun mm-display-patch-inline (handle)
632   (mm-display-inline-fontify handle 'diff-mode))
633
634 (defun mm-display-elisp-inline (handle)
635   (mm-display-inline-fontify handle 'emacs-lisp-mode))
636
637 (defun mm-display-dns-inline (handle)
638   (mm-display-inline-fontify handle 'dns-mode))
639
640 ;;      id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
641 ;;          us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
642 (defvar mm-pkcs7-signed-magic
643   "\x30\x5c\x28\x80\x5c\x7c\x81\x2e\x5c\x7c\x82\x2e\x2e\x5c\x7c\x83\x2e\x2e\
644 \x2e\x5c\x29\x06\x09\x5c\x2a\x86\x48\x86\xf7\x0d\x01\x07\x02")
645
646 ;;      id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
647 ;;          us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }
648 (defvar mm-pkcs7-enveloped-magic
649   "\x30\x5c\x28\x80\x5c\x7c\x81\x2e\x5c\x7c\x82\x2e\x2e\x5c\x7c\x83\x2e\x2e\
650 \x2e\x5c\x29\x06\x09\x5c\x2a\x86\x48\x86\xf7\x0d\x01\x07\x03")
651
652 (defun mm-view-pkcs7-get-type (handle)
653   (mm-with-unibyte-buffer
654     (mm-insert-part handle)
655     (cond ((looking-at mm-pkcs7-enveloped-magic)
656            'enveloped)
657           ((looking-at mm-pkcs7-signed-magic)
658            'signed)
659           (t
660            (error "Could not identify PKCS#7 type")))))
661
662 (defun mm-view-pkcs7 (handle)
663   (case (mm-view-pkcs7-get-type handle)
664     (enveloped (mm-view-pkcs7-decrypt handle))
665     (signed (mm-view-pkcs7-verify handle))
666     (otherwise (error "Unknown or unimplemented PKCS#7 type"))))
667
668 (defun mm-view-pkcs7-verify (handle)
669   (let ((verified nil))
670     (with-temp-buffer
671       (insert "MIME-Version: 1.0\n")
672       (mm-insert-headers "application/pkcs7-mime" "base64" "smime.p7m")
673       (insert-buffer-substring (mm-handle-buffer handle))
674       (setq verified (smime-verify-region (point-min) (point-max))))
675     (goto-char (point-min))
676     (mm-insert-part handle)
677     (if (search-forward "Content-Type: " nil t)
678         (delete-region (point-min) (match-beginning 0)))
679     (goto-char (point-max))
680     (if (re-search-backward "--\r?\n?" nil t)
681         (delete-region (match-end 0) (point-max)))
682     (unless verified
683       (insert-buffer-substring smime-details-buffer)))
684   (goto-char (point-min))
685   (while (search-forward "\r\n" nil t)
686     (replace-match "\n"))
687   t)
688
689 (defun mm-view-pkcs7-decrypt (handle)
690   (insert-buffer-substring (mm-handle-buffer handle))
691   (goto-char (point-min))
692   (insert "MIME-Version: 1.0\n")
693   (mm-insert-headers "application/pkcs7-mime" "base64" "smime.p7m")
694   (smime-decrypt-region
695    (point-min) (point-max)
696    (if (= (length smime-keys) 1)
697        (cadar smime-keys)
698      (smime-get-key-by-email
699       (gnus-completing-read
700        "Decipher using key"
701        smime-keys nil nil nil (car-safe (car-safe smime-keys))))))
702   (goto-char (point-min))
703   (while (search-forward "\r\n" nil t)
704     (replace-match "\n"))
705   (goto-char (point-min)))
706
707 (provide 'mm-view)
708
709 ;;; mm-view.el ends here