(gnus-summary-from-or-to-or-newsgroups): Check
[gnus] / lisp / mm-decode.el
index 921c108..99076cc 100644 (file)
@@ -1,7 +1,7 @@
 ;;; mm-decode.el --- Functions for decoding MIME things
 
 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005 Free Software Foundation, Inc.
+;;   2005, 2006 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
@@ -35,6 +35,7 @@
 (eval-and-compile
   (autoload 'mm-inline-partial "mm-partial")
   (autoload 'mm-inline-external-body "mm-extern")
+  (autoload 'mm-extern-cache-contents "mm-extern")
   (autoload 'mm-insert-inline "mm-view"))
 
 (defvar gnus-current-window-configuration)
@@ -279,11 +280,13 @@ before the external MIME handler is invoked."
     "application/x-emacs-lisp"
     "application/pgp-signature" "application/x-pkcs7-signature"
     "application/pkcs7-signature" "application/x-pkcs7-mime"
-    "application/pkcs7-mime")
+    "application/pkcs7-mime"
+    ;; Mutt still uses this even though it has already been withdrawn.
+    "application/pgp")
   "List of media types that are to be displayed inline.
 See also `mm-inline-media-tests', which says how to display a media
 type inline."
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-keep-viewer-alive-types
@@ -292,20 +295,21 @@ type inline."
   "List of media types for which the external viewer will not be killed
 when selecting a different article."
   :version "22.1"
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-automatic-display
-  '("text/plain" "text/enriched" "text/richtext" "text/html"
-    "text/x-gnus-verbatim"
+  '("text/plain" "text/enriched" "text/richtext" "text/html" "text/x-verbatim"
     "text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
     "message/rfc822" "text/x-patch" "text/dns" "application/pgp-signature"
     "application/emacs-lisp" "application/x-emacs-lisp"
     "application/x-pkcs7-signature"
     "application/pkcs7-signature" "application/x-pkcs7-mime"
-    "application/pkcs7-mime")
+    "application/pkcs7-mime"
+    ;; Mutt still uses this even though it has already been withdrawn.
+    "application/pgp")
   "A list of MIME types to be displayed automatically."
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-attachment-override-types '("text/x-vcard"
@@ -314,17 +318,17 @@ when selecting a different article."
                                          "application/pkcs7-signature"
                                          "application/x-pkcs7-signature")
   "Types to have \"attachment\" ignored if they can be displayed inline."
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-inline-override-types nil
   "Types to be treated as attachments even if they can be displayed inline."
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-automatic-external-display nil
   "List of MIME type regexps that will be displayed externally automatically."
-  :type '(repeat string)
+  :type '(repeat regexp)
   :group 'mime-display)
 
 (defcustom mm-discouraged-alternatives nil
@@ -336,8 +340,13 @@ for instance, text/html parts are very unwanted, and text/richtext are
 somewhat unwanted, then the value of this variable should be set
 to:
 
- (\"text/html\" \"text/richtext\")"
-  :type '(repeat string)
+ (\"text/html\" \"text/richtext\")
+
+Adding \"image/.*\" might also be useful.  Spammers use it as the
+prefered part of multipart/alternative messages.  See also
+`gnus-buttonized-mime-types', to which adding \"multipart/alternative\"
+enables you to choose manually one of two types those mails include."
+  :type '(repeat regexp) ;; See `mm-preferred-alternative-precedence'.
   :group 'mime-display)
 
 (defcustom mm-tmp-directory
@@ -526,13 +535,13 @@ Postpone undisplaying of viewers for types in
                  loose-mime
                  (mail-fetch-field "mime-version"))
          (setq ct (mail-fetch-field "content-type")
-               ctl (ignore-errors (mail-header-parse-content-type ct))
+               ctl (and ct (mail-header-parse-content-type ct))
                cte (mail-fetch-field "content-transfer-encoding")
                cd (mail-fetch-field "content-disposition")
                description (mail-fetch-field "content-description")
                id (mail-fetch-field "content-id"))
          (unless from
-               (setq from (mail-fetch-field "from")))
+           (setq from (mail-fetch-field "from")))
          ;; FIXME: In some circumstances, this code is running within
          ;; an unibyte macro.  mail-extract-address-components
          ;; creates unibyte buffers. This `if', though not a perfect
@@ -545,7 +554,7 @@ Postpone undisplaying of viewers for types in
           (list mm-dissect-default-type)
           (and cte (intern (downcase (mail-header-strip cte))))
           no-strict-mime
-          (and cd (ignore-errors (mail-header-parse-content-disposition cd)))
+          (and cd (mail-header-parse-content-disposition cd))
           description)
        (setq type (split-string (car ctl) "/"))
        (setq subtype (cadr type)
@@ -578,8 +587,7 @@ Postpone undisplaying of viewers for types in
             ctl
             (and cte (intern (downcase (mail-header-strip cte))))
             no-strict-mime
-            (and cd (ignore-errors
-                      (mail-header-parse-content-disposition cd)))
+            (and cd (mail-header-parse-content-disposition cd))
             description id)
            ctl))))
        (when id
@@ -756,7 +764,18 @@ external if displayed external."
                          (gnus-map-function mm-file-name-rewrite-functions
                                             (file-name-nondirectory filename))
                          dir))
-           (setq file (mm-make-temp-file (expand-file-name "mm." dir))))
+           ;; Use nametemplate (defined in RFC1524) if it is specified
+           ;; in mailcap.
+           (let ((suffix (cdr (assoc "nametemplate" mime-info))))
+             (if (and suffix
+                      (string-match "\\`%s\\(\\..+\\)\\'" suffix))
+                 (setq suffix (match-string 1 suffix))
+               ;; Otherwise, use a suffix according to
+               ;; `mailcap-mime-extensions'.
+               (setq suffix (car (rassoc (mm-handle-media-type handle)
+                                         mailcap-mime-extensions))))
+             (setq file (mm-make-temp-file (expand-file-name "mm." dir)
+                                           nil suffix))))
          (let ((coding-system-for-write mm-binary-coding-system))
            (write-region (point-min) (point-max) file nil 'nomesg))
          (message "Viewing with %s" method)
@@ -814,6 +833,9 @@ external if displayed external."
                   (ignore-errors (kill-buffer buffer))))))
            'inline)
           (t
+           ;; Deleting the temp file should be postponed for some wrappers,
+           ;; shell scripts, and so on, which might exit right after having
+           ;; started a viewer command as a background job.
            (let ((command (mm-mailcap-command
                            method file (mm-handle-type handle))))
              (unwind-protect
@@ -825,24 +847,38 @@ external if displayed external."
                                   shell-command-switch command)
                    (set-process-sentinel
                     (get-buffer-process buffer)
-                    `(lambda (process state)
-                       (when (eq 'exit (process-status process))
-                         ;; Don't use `ignore-errors'.
-                         (condition-case nil
-                             (delete-file ,file)
-                           (error))
-                         (condition-case nil
-                             (delete-directory ,(file-name-directory file))
-                           (error))
-                         (condition-case nil
-                             (kill-buffer ,buffer)
-                           (error))
-                         (condition-case nil
-                             ,(macroexpand (list 'mm-handle-set-undisplayer
-                                                 (list 'quote handle)
-                                                 nil))
-                           (error))
-                         (message "Displaying %s...done" ,command)))))
+                    (lexical-let ;; Don't use `let'.
+                        ;; Function used to remove temp file and directory.
+                        ((fn `(lambda nil
+                                ;; Don't use `ignore-errors'.
+                                (condition-case nil
+                                    (delete-file ,file)
+                                  (error))
+                                (condition-case nil
+                                    (delete-directory
+                                     ,(file-name-directory file))
+                                  (error))))
+                         ;; Form uses to kill the process buffer and
+                         ;; remove the undisplayer.
+                         (fm `(progn
+                                (kill-buffer ,buffer)
+                                ,(macroexpand
+                                  (list 'mm-handle-set-undisplayer
+                                        (list 'quote handle)
+                                        nil))))
+                         ;; Message to be issued when the process exits.
+                         (done (format "Displaying %s...done" command))
+                         ;; In particular, the timer object (which is
+                         ;; a vector in Emacs but is a list in XEmacs)
+                         ;; requires that it is lexically scoped.
+                         (timer (run-at-time 2.0 nil 'ignore)))
+                      (lambda (process state)
+                        (when (eq 'exit (process-status process))
+                          (if (memq timer timer-list)
+                              (timer-set-function timer fn)
+                            (funcall fn))
+                          (ignore-errors (eval fm))
+                          (message "%s" done))))))
                (mm-handle-set-external-undisplayer
                 handle (cons file buffer)))
              (message "Displaying %s..." command))
@@ -1041,19 +1077,44 @@ external if displayed external."
 ;;; Functions for outputting parts
 ;;;
 
-(defun mm-get-part (handle)
-  "Return the contents of HANDLE as a string."
-  (mm-with-unibyte-buffer
-    (insert (with-current-buffer (mm-handle-buffer handle)
-             (mm-with-unibyte-current-buffer
-               (buffer-string))))
-    (mm-decode-content-transfer-encoding
-     (mm-handle-encoding handle)
-     (mm-handle-media-type handle))
-    (buffer-string)))
-
-(defun mm-insert-part (handle)
-  "Insert the contents of HANDLE in the current buffer."
+(defmacro mm-with-part (handle &rest forms)
+  "Run FORMS in the temp buffer containing the contents of HANDLE."
+  `(let* ((handle ,handle)
+         ;; The multibyteness of the temp buffer should be turned on
+         ;; if inserting a multibyte string.  Contrarily, the buffer's
+         ;; multibyteness should be off if inserting a unibyte string,
+         ;; especially if a string contains 8bit data.
+         (default-enable-multibyte-characters
+           (with-current-buffer (mm-handle-buffer handle)
+             (mm-multibyte-p))))
+     (with-temp-buffer
+       (insert-buffer-substring (mm-handle-buffer handle))
+       (mm-disable-multibyte)
+       (mm-decode-content-transfer-encoding
+       (mm-handle-encoding handle)
+       (mm-handle-media-type handle))
+       ,@forms)))
+(put 'mm-with-part 'lisp-indent-function 1)
+(put 'mm-with-part 'edebug-form-spec '(body))
+
+(defun mm-get-part (handle &optional no-cache)
+  "Return the contents of HANDLE as a string.
+If NO-CACHE is non-nil, cached contents of a message/external-body part
+are ignored."
+  (if (and (not no-cache)
+          (equal (mm-handle-media-type handle) "message/external-body"))
+      (progn
+       (unless (mm-handle-cache handle)
+         (mm-extern-cache-contents handle))
+       (with-current-buffer (mm-handle-buffer (mm-handle-cache handle))
+         (buffer-string)))
+    (mm-with-part handle
+      (buffer-string))))
+
+(defun mm-insert-part (handle &optional no-cache)
+  "Insert the contents of HANDLE in the current buffer.
+If NO-CACHE is non-nil, cached contents of a message/external-body part
+are ignored."
   (save-excursion
     (insert
      (cond ((eq (mail-content-type-get (mm-handle-type handle) 'charset)
@@ -1061,9 +1122,9 @@ external if displayed external."
            (with-current-buffer (mm-handle-buffer handle)
              (buffer-string)))
           ((mm-multibyte-p)
-           (mm-string-as-multibyte (mm-get-part handle)))
+           (mm-string-as-multibyte (mm-get-part handle no-cache)))
           (t
-           (mm-get-part handle))))))
+           (mm-get-part handle no-cache))))))
 
 (defun mm-file-name-delete-whitespace (file-name)
   "Remove all whitespace characters from FILE-NAME."
@@ -1106,18 +1167,19 @@ string if you do not like underscores."
 (defun mm-save-part (handle &optional prompt)
   "Write HANDLE to a file.
 PROMPT overrides the default one used to ask user for a file name."
-  (let* ((name (mail-content-type-get (mm-handle-type handle) 'name))
-        (filename (mail-content-type-get
-                   (mm-handle-disposition handle) 'filename))
-        file)
+  (let ((filename (or (mail-content-type-get
+                      (mm-handle-disposition handle) 'filename)
+                     (mail-content-type-get
+                      (mm-handle-type handle) 'name)))
+       file)
     (when filename
       (setq filename (gnus-map-function mm-file-name-rewrite-functions
                                        (file-name-nondirectory filename))))
     (setq file
          (mm-with-multibyte
-           (read-file-name (or prompt "Save MIME part to: ")
-                           (or mm-default-directory default-directory)
-                           nil nil (or filename name ""))))
+          (read-file-name (or prompt "Save MIME part to: ")
+                          (or mm-default-directory default-directory)
+                          nil nil (or filename ""))))
     (setq mm-default-directory (file-name-directory file))
     (and (or (not (file-exists-p file))
             (yes-or-no-p (format "File %s already exists; overwrite? "
@@ -1271,8 +1333,8 @@ be determined."
     ;; out to a file, and then create a file
     ;; specifier.
     (let ((file (mm-make-temp-file
-                (expand-file-name "emm.xbm"
-                                  mm-tmp-directory))))
+                (expand-file-name "emm" mm-tmp-directory)
+                nil ".xbm")))
       (unwind-protect
          (progn
            (write-region (point-min) (point-max) file)
@@ -1359,9 +1421,8 @@ If RECURSIVE, search recursively."
        (save-excursion
          (save-restriction
            (narrow-to-region start (1- (point)))
-           (when (let ((ctl (ignore-errors
-                              (mail-header-parse-content-type
-                               (mail-fetch-field "content-type")))))
+           (when (let* ((ct (mail-fetch-field "content-type"))
+                        (ctl (and ct (mail-header-parse-content-type ct))))
                    (if notp
                        (not (equal (car ctl) type))
                      (equal (car ctl) type)))
@@ -1372,9 +1433,8 @@ If RECURSIVE, search recursively."
       (save-excursion
        (save-restriction
          (narrow-to-region start end)
-         (when (let ((ctl (ignore-errors
-                            (mail-header-parse-content-type
-                             (mail-fetch-field "content-type")))))
+         (when (let* ((ct (mail-fetch-field "content-type"))
+                      (ctl (and ct (mail-header-parse-content-type ct))))
                  (if notp
                      (not (equal (car ctl) type))
                    (equal (car ctl) type)))