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