Autoload epa-select-keys when compiling.
[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 (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 (mail-content-type-get (mm-handle-type handle) 'charset)))
241     (save-excursion
242       (insert (if charset (mm-decode-string text charset) text))
243       (save-restriction
244         (narrow-to-region b (point))
245         (unless charset
246           (goto-char (point-min))
247           (when (setq charset (w3m-detect-meta-charset))
248             (delete-region (point-min) (point-max))
249             (insert (mm-decode-string text charset))))
250         (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
251               w3m-force-redisplay)
252           (w3m-region (point-min) (point-max) nil charset))
253         (when (and mm-inline-text-html-with-w3m-keymap
254                    (boundp 'w3m-minor-mode-map)
255                    w3m-minor-mode-map)
256           (add-text-properties
257            (point-min) (point-max)
258            (list 'keymap w3m-minor-mode-map
259                  ;; Put the mark meaning this part was rendered by emacs-w3m.
260                  'mm-inline-text-html-with-w3m t)))
261         (mm-handle-set-undisplayer
262          handle
263          `(lambda ()
264             (let (buffer-read-only)
265               (if (functionp 'remove-specifier)
266                   (mapcar (lambda (prop)
267                             (remove-specifier
268                              (face-property 'default prop)
269                              (current-buffer)))
270                           '(background background-pixmap foreground)))
271               (delete-region ,(point-min-marker)
272                              ,(point-max-marker)))))))))
273
274 (defvar mm-w3m-standalone-supports-m17n-p (if (featurep 'mule) 'undecided)
275   "*T means the w3m command supports the m17n feature.")
276
277 (defun mm-w3m-standalone-supports-m17n-p ()
278   "Say whether the w3m command supports the m17n feature."
279   (cond ((eq mm-w3m-standalone-supports-m17n-p t) t)
280         ((eq mm-w3m-standalone-supports-m17n-p nil) nil)
281         ((not (featurep 'mule)) (setq mm-w3m-standalone-supports-m17n-p nil))
282         ((condition-case nil
283              (let ((coding-system-for-write 'iso-2022-jp)
284                    (coding-system-for-read 'iso-2022-jp)
285                    (str (mm-decode-coding-string "\
286 \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)))
287                (mm-with-multibyte-buffer
288                  (insert str)
289                  (call-process-region
290                   (point-min) (point-max) "w3m" t t nil "-dump"
291                   "-T" "text/html" "-I" "iso-2022-jp" "-O" "iso-2022-jp")
292                  (goto-char (point-min))
293                  (search-forward str nil t)))
294            (error nil))
295          (setq mm-w3m-standalone-supports-m17n-p t))
296         (t
297          ;;(message "You had better upgrade your w3m command")
298          (setq mm-w3m-standalone-supports-m17n-p nil))))
299
300 (defun mm-inline-text-html-render-with-w3m-standalone (handle)
301   "Render a text/html part using w3m."
302   (if (mm-w3m-standalone-supports-m17n-p)
303       (let ((source (mm-get-part handle))
304             (charset (mail-content-type-get (mm-handle-type handle) 'charset))
305             cs)
306         (unless (and charset
307                      (setq cs (mm-charset-to-coding-system charset))
308                      (not (eq cs 'ascii)))
309           ;; The default.
310           (setq charset "iso-8859-1"
311                 cs 'iso-8859-1))
312         (mm-insert-inline
313          handle
314          (mm-with-unibyte-buffer
315            (insert source)
316            (mm-enable-multibyte)
317            (let ((coding-system-for-write 'binary)
318                  (coding-system-for-read cs))
319              (call-process-region
320               (point-min) (point-max)
321               "w3m" t t nil "-dump" "-T" "text/html"
322               "-I" charset "-O" charset))
323            (buffer-string))))
324     (mm-inline-render-with-stdin handle nil "w3m" "-dump" "-T" "text/html")))
325
326 (defun mm-links-remove-leading-blank ()
327   ;; Delete the annoying three spaces preceding each line of links
328   ;; output.
329   (goto-char (point-min))
330   (while (re-search-forward "^   " nil t)
331     (delete-region (match-beginning 0) (match-end 0))))
332
333 (defun mm-inline-wash-with-file (post-func cmd &rest args)
334   (let ((file (mm-make-temp-file
335                (expand-file-name "mm" mm-tmp-directory))))
336     (let ((coding-system-for-write 'binary))
337       (write-region (point-min) (point-max) file nil 'silent))
338     (delete-region (point-min) (point-max))
339     (unwind-protect
340         (apply 'call-process cmd nil t nil (mapcar 'eval args))
341       (delete-file file))
342     (and post-func (funcall post-func))))
343
344 (defun mm-inline-wash-with-stdin (post-func cmd &rest args)
345   (let ((coding-system-for-write 'binary))
346     (apply 'call-process-region (point-min) (point-max)
347            cmd t t nil args))
348   (and post-func (funcall post-func)))
349
350 (defun mm-inline-render-with-file (handle post-func cmd &rest args)
351   (let ((source (mm-get-part handle)))
352     (mm-insert-inline
353      handle
354      (mm-with-unibyte-buffer
355        (insert source)
356        (apply 'mm-inline-wash-with-file post-func cmd args)
357        (buffer-string)))))
358
359 (defun mm-inline-render-with-stdin (handle post-func cmd &rest args)
360   (let ((source (mm-get-part handle)))
361     (mm-insert-inline
362      handle
363      (mm-with-unibyte-buffer
364        (insert source)
365        (apply 'mm-inline-wash-with-stdin post-func cmd args)
366        (buffer-string)))))
367
368 (defun mm-inline-render-with-function (handle func &rest args)
369   (let ((source (mm-get-part handle))
370         (charset (mail-content-type-get (mm-handle-type handle) 'charset)))
371     (mm-insert-inline
372      handle
373      (mm-with-multibyte-buffer
374        (insert (if charset
375                    (mm-decode-string source charset)
376                  source))
377        (apply func args)
378        (buffer-string)))))
379
380 (defun mm-inline-text-html (handle)
381   (let* ((func (or mm-inline-text-html-renderer mm-text-html-renderer))
382          (entry (assq func mm-text-html-renderer-alist))
383          buffer-read-only)
384     (if entry
385         (setq func (cdr entry)))
386     (cond
387      ((functionp func)
388       (funcall func handle))
389      (t
390       (apply (car func) handle (cdr func))))))
391
392 (defun mm-inline-text-vcard (handle)
393   (let (buffer-read-only)
394     (mm-insert-inline
395      handle
396      (concat "\n-- \n"
397              (ignore-errors
398                (if (fboundp 'vcard-pretty-print)
399                    (vcard-pretty-print (mm-get-part handle))
400                  (vcard-format-string
401                   (vcard-parse-string (mm-get-part handle)
402                                       'vcard-standard-filter))))))))
403
404 (defun mm-inline-text (handle)
405   (let ((b (point))
406         (type (mm-handle-media-subtype handle))
407         (charset (mail-content-type-get
408                   (mm-handle-type handle) 'charset))
409         buffer-read-only)
410     (if (or (eq charset 'gnus-decoded)
411             ;; This is probably not entirely correct, but
412             ;; makes rfc822 parts with embedded multiparts work.
413             (eq mail-parse-charset 'gnus-decoded))
414         (save-restriction
415           (narrow-to-region (point) (point))
416           (mm-insert-part handle)
417           (goto-char (point-max)))
418       (insert (mm-decode-string (mm-get-part handle) charset)))
419     (when (and mm-fill-flowed
420                (equal type "plain")
421                (equal (cdr (assoc 'format (mm-handle-type handle)))
422                       "flowed"))
423       (save-restriction
424         (narrow-to-region b (point))
425         (goto-char b)
426         (fill-flowed nil (equal (cdr (assoc 'delsp (mm-handle-type handle)))
427                                 "yes"))
428         (goto-char (point-max))))
429     (save-restriction
430       (narrow-to-region b (point))
431       (when (or (equal type "enriched")
432                 (equal type "richtext"))
433         (set-text-properties (point-min) (point-max) nil)
434         (ignore-errors
435           (enriched-decode (point-min) (point-max))))
436       (mm-handle-set-undisplayer
437        handle
438        `(lambda ()
439           (let (buffer-read-only)
440             (delete-region ,(point-min-marker)
441                            ,(point-max-marker))))))))
442
443 (defun mm-insert-inline (handle text)
444   "Insert TEXT inline from HANDLE."
445   (let ((b (point)))
446     (insert text)
447     (unless (bolp)
448       (insert "\n"))
449     (mm-handle-set-undisplayer
450      handle
451      `(lambda ()
452         (let (buffer-read-only)
453           (delete-region ,(set-marker (make-marker) b)
454                          ,(set-marker (make-marker) (point))))))))
455
456 (defun mm-inline-audio (handle)
457   (message "Not implemented"))
458
459 (defun mm-view-sound-file ()
460   (message "Not implemented"))
461
462 (defun mm-w3-prepare-buffer ()
463   (require 'w3)
464   (let ((url-standalone-mode t)
465         (url-gateway-unplugged t)
466         (w3-honor-stylesheets nil))
467     (w3-prepare-buffer)))
468
469 (defun mm-view-message ()
470   (mm-enable-multibyte)
471   (let (handles)
472     (let (gnus-article-mime-handles)
473       ;; Double decode problem may happen.  See mm-inline-message.
474       (run-hooks 'gnus-article-decode-hook)
475       (gnus-article-prepare-display)
476       (setq handles gnus-article-mime-handles))
477     (when handles
478       (setq gnus-article-mime-handles
479             (mm-merge-handles gnus-article-mime-handles handles))))
480   (fundamental-mode)
481   (goto-char (point-min)))
482
483 (defun mm-inline-message (handle)
484   (let ((b (point))
485         (bolp (bolp))
486         (charset (mail-content-type-get
487                   (mm-handle-type handle) 'charset))
488         gnus-displaying-mime handles)
489     (when (and charset
490                (stringp charset))
491       (setq charset (intern (downcase charset)))
492       (when (eq charset 'us-ascii)
493         (setq charset nil)))
494     (save-excursion
495       (save-restriction
496         (narrow-to-region b b)
497         (mm-insert-part handle)
498         (let (gnus-article-mime-handles
499               ;; disable prepare hook
500               gnus-article-prepare-hook
501               (gnus-newsgroup-charset
502                (unless (eq charset 'gnus-decoded) ;; mm-uu might set it.
503                  (or charset gnus-newsgroup-charset))))
504           (let ((gnus-original-article-buffer (mm-handle-buffer handle)))
505             (run-hooks 'gnus-article-decode-hook))
506           (gnus-article-prepare-display)
507           (setq handles gnus-article-mime-handles))
508         (goto-char (point-min))
509         (unless bolp
510           (insert "\n"))
511         (goto-char (point-max))
512         (unless (bolp)
513           (insert "\n"))
514         (insert "----------\n\n")
515         (when handles
516           (setq gnus-article-mime-handles
517                 (mm-merge-handles gnus-article-mime-handles handles)))
518         (mm-handle-set-undisplayer
519          handle
520          `(lambda ()
521             (let (buffer-read-only)
522               (if (fboundp 'remove-specifier)
523                   ;; This is only valid on XEmacs.
524                   (mapcar (lambda (prop)
525                             (remove-specifier
526                              (face-property 'default prop) (current-buffer)))
527                           '(background background-pixmap foreground)))
528               (delete-region ,(point-min-marker) ,(point-max-marker)))))))))
529
530 (defun mm-display-inline-fontify (handle mode)
531   (let ((charset (mail-content-type-get (mm-handle-type handle) 'charset))
532         text coding-system)
533     (unless (eq charset 'gnus-decoded)
534       (mm-with-unibyte-buffer
535         (mm-insert-part handle)
536         (mm-decompress-buffer
537          (or (mail-content-type-get (mm-handle-disposition handle) 'name)
538              (mail-content-type-get (mm-handle-disposition handle) 'filename))
539          t t)
540         (unless charset
541           (setq coding-system (mm-find-buffer-file-coding-system)))
542         (setq text (buffer-string))))
543     ;; XEmacs @#$@ version of font-lock refuses to fully turn itself
544     ;; on for buffers whose name begins with " ".  That's why we use
545     ;; `with-current-buffer'/`generate-new-buffer' rather than
546     ;; `with-temp-buffer'.
547     (with-current-buffer (generate-new-buffer "*fontification*")
548       (buffer-disable-undo)
549       (mm-enable-multibyte)
550       (insert (cond ((eq charset 'gnus-decoded)
551                      (with-current-buffer (mm-handle-buffer handle)
552                        (buffer-string)))
553                     (coding-system
554                      (mm-decode-coding-string text coding-system))
555                     (charset
556                      (mm-decode-string text charset))
557                     (t
558                      text)))
559       (require 'font-lock)
560       (let ((font-lock-maximum-size nil)
561             ;; Disable support modes, e.g., jit-lock, lazy-lock, etc.
562             (font-lock-mode-hook nil)
563             (font-lock-support-mode nil)
564             ;; I find font-lock a bit too verbose.
565             (font-lock-verbose nil))
566         (funcall mode)
567         ;; The mode function might have already turned on font-lock.
568         (unless (symbol-value 'font-lock-mode)
569           (font-lock-fontify-buffer)))
570       ;; By default, XEmacs font-lock uses non-duplicable text
571       ;; properties.  This code forces all the text properties
572       ;; to be copied along with the text.
573       (when (fboundp 'extent-list)
574         (map-extents (lambda (ext ignored)
575                        (set-extent-property ext 'duplicable t)
576                        nil)
577                      nil nil nil nil nil 'text-prop))
578       (setq text (buffer-string))
579       (kill-buffer (current-buffer)))
580     (mm-insert-inline handle text)))
581
582 ;; Shouldn't these functions check whether the user even wants to use
583 ;; font-lock?  At least under XEmacs, this fontification is pretty
584 ;; much unconditional.  Also, it would be nice to change for the size
585 ;; of the fontified region.
586
587 (defun mm-display-patch-inline (handle)
588   (mm-display-inline-fontify handle 'diff-mode))
589
590 (defun mm-display-elisp-inline (handle)
591   (mm-display-inline-fontify handle 'emacs-lisp-mode))
592
593 (defun mm-display-dns-inline (handle)
594   (mm-display-inline-fontify handle 'dns-mode))
595
596 ;;      id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
597 ;;          us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
598 (defvar mm-pkcs7-signed-magic
599   (mm-string-as-unibyte
600    (mapconcat 'char-to-string
601               (list ?\x30 ?\x5c ?\x28 ?\x80 ?\x5c ?\x7c ?\x81 ?\x2e ?\x5c
602                     ?\x7c ?\x82 ?\x2e ?\x2e ?\x5c ?\x7c ?\x83 ?\x2e ?\x2e
603                     ?\x2e ?\x5c ?\x29 ?\x06 ?\x09 ?\x5c ?\x2a ?\x86 ?\x48
604                     ?\x86 ?\xf7 ?\x0d ?\x01 ?\x07 ?\x02) "")))
605
606 ;;      id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
607 ;;          us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }
608 (defvar mm-pkcs7-enveloped-magic
609   (mm-string-as-unibyte
610    (mapconcat 'char-to-string
611               (list ?\x30 ?\x5c ?\x28 ?\x80 ?\x5c ?\x7c ?\x81 ?\x2e ?\x5c
612                     ?\x7c ?\x82 ?\x2e ?\x2e ?\x5c ?\x7c ?\x83 ?\x2e ?\x2e
613                     ?\x2e ?\x5c ?\x29 ?\x06 ?\x09 ?\x5c ?\x2a ?\x86 ?\x48
614                     ?\x86 ?\xf7 ?\x0d ?\x01 ?\x07 ?\x03) "")))
615
616 (defun mm-view-pkcs7-get-type (handle)
617   (mm-with-unibyte-buffer
618     (mm-insert-part handle)
619     (cond ((looking-at mm-pkcs7-enveloped-magic)
620            'enveloped)
621           ((looking-at mm-pkcs7-signed-magic)
622            'signed)
623           (t
624            (error "Could not identify PKCS#7 type")))))
625
626 (defun mm-view-pkcs7 (handle)
627   (case (mm-view-pkcs7-get-type handle)
628     (enveloped (mm-view-pkcs7-decrypt handle))
629     (signed (mm-view-pkcs7-verify handle))
630     (otherwise (error "Unknown or unimplemented PKCS#7 type"))))
631
632 (defun mm-view-pkcs7-verify (handle)
633   (let ((verified nil))
634     (with-temp-buffer
635       (insert "MIME-Version: 1.0\n")
636       (mm-insert-headers "application/pkcs7-mime" "base64" "smime.p7m")
637       (insert-buffer-substring (mm-handle-buffer handle))
638       (setq verified (smime-verify-region (point-min) (point-max))))
639     (goto-char (point-min))
640     (mm-insert-part handle)
641     (if (search-forward "Content-Type: " nil t)
642         (delete-region (point-min) (match-beginning 0)))
643     (goto-char (point-max))
644     (if (re-search-backward "--\r?\n?" nil t)
645         (delete-region (match-end 0) (point-max)))
646     (unless verified
647       (insert-buffer-substring smime-details-buffer)))
648   (goto-char (point-min))
649   (while (search-forward "\r\n" nil t)
650     (replace-match "\n"))
651   t)
652
653 (defun mm-view-pkcs7-decrypt (handle)
654   (insert-buffer-substring (mm-handle-buffer handle))
655   (goto-char (point-min))
656   (insert "MIME-Version: 1.0\n")
657   (mm-insert-headers "application/pkcs7-mime" "base64" "smime.p7m")
658   (smime-decrypt-region
659    (point-min) (point-max)
660    (if (= (length smime-keys) 1)
661        (cadar smime-keys)
662      (smime-get-key-by-email
663       (completing-read
664        (concat "Decipher using key"
665                (if smime-keys (concat "(default " (caar smime-keys) "): ")
666                  ": "))
667        smime-keys nil nil nil nil (car-safe (car-safe smime-keys))))))
668   (goto-char (point-min))
669   (while (search-forward "\r\n" nil t)
670     (replace-match "\n"))
671   (goto-char (point-min)))
672
673 (provide 'mm-view)
674
675 ;;; arch-tag: b60e749a-d05c-47f2-bccd-bdaa59327cb2
676 ;;; mm-view.el ends here