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