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