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