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