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