emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] Changes to emacs/lisp/gnus/mm-decode.el [lexbind]


From: Miles Bader
Subject: [Emacs-diffs] Changes to emacs/lisp/gnus/mm-decode.el [lexbind]
Date: Wed, 15 Sep 2004 20:37:22 -0400

Index: emacs/lisp/gnus/mm-decode.el
diff -c emacs/lisp/gnus/mm-decode.el:1.12.2.2 
emacs/lisp/gnus/mm-decode.el:1.12.2.3
*** emacs/lisp/gnus/mm-decode.el:1.12.2.2       Tue Oct 14 23:34:51 2003
--- emacs/lisp/gnus/mm-decode.el        Thu Sep 16 00:12:16 2004
***************
*** 1,5 ****
! ;;; mm-decode.el --- functions for decoding MIME things
! ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
  
  ;; Author: Lars Magne Ingebrigtsen <address@hidden>
  ;;    MORIOKA Tomohiko <address@hidden>
--- 1,6 ----
! ;;; mm-decode.el --- Functions for decoding MIME things
! ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
! ;;        Free Software Foundation, Inc.
  
  ;; Author: Lars Magne Ingebrigtsen <address@hidden>
  ;;    MORIOKA Tomohiko <address@hidden>
***************
*** 27,46 ****
  (require 'mail-parse)
  (require 'mailcap)
  (require 'mm-bodies)
! (eval-when-compile (require 'cl))
  
  (eval-and-compile
    (autoload 'mm-inline-partial "mm-partial")
    (autoload 'mm-insert-inline "mm-view"))
  
  (defgroup mime-display ()
    "Display of MIME in mail and news articles."
!   :link '(custom-manual "(emacs-mime)Customization")
    :version "21.1"
    :group 'mail
    :group 'news
    :group 'multimedia)
  
  ;;; Convenience macros.
  
  (defmacro mm-handle-buffer (handle)
--- 28,59 ----
  (require 'mail-parse)
  (require 'mailcap)
  (require 'mm-bodies)
! (eval-when-compile (require 'cl)
!                  (require 'term))
  
  (eval-and-compile
+   (autoload 'executable-find "executable")
    (autoload 'mm-inline-partial "mm-partial")
+   (autoload 'mm-inline-external-body "mm-extern")
    (autoload 'mm-insert-inline "mm-view"))
  
+ (add-hook 'gnus-exit-gnus-hook 'mm-destroy-postponed-undisplay-list)
+ 
  (defgroup mime-display ()
    "Display of MIME in mail and news articles."
!   :link '(custom-manual "(emacs-mime)Display Customization")
    :version "21.1"
    :group 'mail
    :group 'news
    :group 'multimedia)
  
+ (defgroup mime-security ()
+   "MIME security in mail and news articles."
+   :link '(custom-manual "(emacs-mime)Display Customization")
+   :group 'mail
+   :group 'news
+   :group 'multimedia)
+ 
  ;;; Convenience macros.
  
  (defmacro mm-handle-buffer (handle)
***************
*** 71,84 ****
    `(setcar (nthcdr 6 ,handle) ,contents))
  (defmacro mm-handle-id (handle)
    `(nth 7 ,handle))
  (defmacro mm-make-handle (&optional buffer type encoding undisplayer
                                    disposition description cache
                                    id)
    `(list ,buffer ,type ,encoding ,undisplayer
         ,disposition ,description ,cache ,id))
  
  (defcustom mm-inline-media-tests
!   '(("image/jpeg"
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'jpeg handle)))
--- 84,177 ----
    `(setcar (nthcdr 6 ,handle) ,contents))
  (defmacro mm-handle-id (handle)
    `(nth 7 ,handle))
+ (defmacro mm-handle-multipart-original-buffer (handle)
+   `(get-text-property 0 'buffer (car ,handle)))
+ (defmacro mm-handle-multipart-from (handle)
+   `(get-text-property 0 'from (car ,handle)))
+ (defmacro mm-handle-multipart-ctl-parameter (handle parameter)
+   `(get-text-property 0 ,parameter (car ,handle)))
+ 
  (defmacro mm-make-handle (&optional buffer type encoding undisplayer
                                    disposition description cache
                                    id)
    `(list ,buffer ,type ,encoding ,undisplayer
         ,disposition ,description ,cache ,id))
  
+ (defcustom mm-text-html-renderer
+   (cond ((locate-library "w3") 'w3)
+       ((executable-find "w3m") (if (locate-library "w3m")
+                                    'w3m
+                                  'w3m-standalone))
+       ((executable-find "links") 'links)
+       ((executable-find "lynx") 'lynx)
+       (t 'html2text))
+   "Render of HTML contents.
+ It is one of defined renderer types, or a rendering function.
+ The defined renderer types are:
+ `w3'   : use Emacs/W3;
+ `w3m'  : use emacs-w3m;
+ `w3m-standalone': use w3m;
+ `links': use links;
+ `lynx' : use lynx;
+ `html2text' : use html2text;
+ nil    : use external viewer."
+   :type '(choice (const w3)
+                (const w3m)
+                (const w3m-standalone)
+                (const links)
+                (const lynx)
+                (const html2text)
+                (const nil)
+                (function))
+   :version "21.3"
+   :group 'mime-display)
+ 
+ (defvar mm-inline-text-html-renderer nil
+   "Function used for rendering inline HTML contents.
+ It is suggested to customize `mm-text-html-renderer' instead.")
+ 
+ (defcustom mm-inline-text-html-with-images nil
+   "If non-nil, Gnus will allow retrieving images in HTML contents with
+ the <img> tags.  It has no effect on Emacs/w3.  See also the
+ documentation for the `mm-w3m-safe-url-regexp' variable."
+   :type 'boolean
+   :group 'mime-display)
+ 
+ (defcustom mm-w3m-safe-url-regexp "\\`cid:"
+   "Regexp matching URLs which are considered to be safe.
+ Some HTML mails might contain a nasty trick used by spammers, using
+ the <img> tag which is far more evil than the [Click Here!] button.
+ It is most likely intended to check whether the ominous spam mail has
+ reached your eyes or not, in which case the spammer knows for sure
+ that your email address is valid.  It is done by embedding an
+ identifier string into a URL that you might automatically retrieve
+ when displaying the image.  The default value is \"\\\\`cid:\" which only
+ matches parts embedded to the Multipart/Related type MIME contents and
+ Gnus will never connect to the spammer's site arbitrarily.  You may
+ set this variable to nil if you consider all urls to be safe."
+   :type '(choice (regexp :tag "Regexp")
+                (const :tag "All URLs are safe" nil))
+   :group 'mime-display)
+ 
+ (defcustom mm-inline-text-html-with-w3m-keymap t
+   "If non-nil, use emacs-w3m command keys in the article buffer."
+   :type 'boolean
+   :group 'mime-display)
+ 
+ (defcustom mm-enable-external t
+   "Indicate whether external MIME handlers should be used.
+ 
+ If t, all defined external MIME handlers are used.  If nil, files are saved by
+ `mailcap-save-binary-file'.  If it is the symbol `ask', you are prompted
+ before the external MIME handler is invoked."
+   :version "21.4"
+   :type '(choice (const :tag "Always" t)
+                (const :tag "Never" nil)
+                (const :tag "Ask" ask))
+   :group 'mime-display)
+ 
  (defcustom mm-inline-media-tests
!   '(("image/p?jpeg"
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'jpeg handle)))
***************
*** 106,112 ****
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'xpm handle)))
!     ("image/x-pixmap"
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'xpm handle)))
--- 199,205 ----
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'xpm handle)))
!     ("image/x-xpixmap"
       mm-inline-image
       (lambda (handle)
         (mm-valid-and-fit-image-p 'xpm handle)))
***************
*** 125,142 ****
       (lambda (handle)
         (locate-library "diff-mode")))
      ("application/emacs-lisp" mm-display-elisp-inline identity)
      ("text/html"
!      mm-inline-text
       (lambda (handle)
!        (locate-library "w3")))
      ("text/x-vcard"
!      mm-inline-text
       (lambda (handle)
         (or (featurep 'vcard)
           (locate-library "vcard"))))
      ("message/delivery-status" mm-inline-text identity)
      ("message/rfc822" mm-inline-message identity)
      ("message/partial" mm-inline-partial identity)
      ("text/.*" mm-inline-text identity)
      ("audio/wav" mm-inline-audio
       (lambda (handle)
--- 218,238 ----
       (lambda (handle)
         (locate-library "diff-mode")))
      ("application/emacs-lisp" mm-display-elisp-inline identity)
+     ("application/x-emacs-lisp" mm-display-elisp-inline identity)
      ("text/html"
!      mm-inline-text-html
       (lambda (handle)
!        (or mm-inline-text-html-renderer
!          mm-text-html-renderer)))
      ("text/x-vcard"
!      mm-inline-text-vcard
       (lambda (handle)
         (or (featurep 'vcard)
           (locate-library "vcard"))))
      ("message/delivery-status" mm-inline-text identity)
      ("message/rfc822" mm-inline-message identity)
      ("message/partial" mm-inline-partial identity)
+     ("message/external-body" mm-inline-external-body identity)
      ("text/.*" mm-inline-text identity)
      ("audio/wav" mm-inline-audio
       (lambda (handle)
***************
*** 148,167 ****
         (and (or (featurep 'nas-sound) (featurep 'native-sound))
            (device-sound-enabled-p))))
      ("application/pgp-signature" ignore identity)
      ("multipart/alternative" ignore identity)
      ("multipart/mixed" ignore identity)
!     ("multipart/related" ignore identity))
    "Alist of media types/tests saying whether types can be displayed inline."
!   :type '(repeat (list (string :tag "MIME type")
                       (function :tag "Display function")
                       (function :tag "Display test")))
    :group 'mime-display)
  
  (defcustom mm-inlined-types
    '("image/.*" "text/.*" "message/delivery-status" "message/rfc822"
!     "message/partial" "application/emacs-lisp"
!     "application/pgp-signature")
!   "List of media types that are to be displayed inline."
    :type '(repeat string)
    :group 'mime-display)
  
--- 244,285 ----
         (and (or (featurep 'nas-sound) (featurep 'native-sound))
            (device-sound-enabled-p))))
      ("application/pgp-signature" ignore identity)
+     ("application/x-pkcs7-signature" ignore identity)
+     ("application/pkcs7-signature" ignore identity)
+     ("application/x-pkcs7-mime" ignore identity)
+     ("application/pkcs7-mime" ignore identity)
      ("multipart/alternative" ignore identity)
      ("multipart/mixed" ignore identity)
!     ("multipart/related" ignore identity)
!     ;; Disable audio and image
!     ("audio/.*" ignore ignore)
!     ("image/.*" ignore ignore)
!     ;; Default to displaying as text
!     (".*" mm-inline-text mm-readable-p))
    "Alist of media types/tests saying whether types can be displayed inline."
!   :type '(repeat (list (regexp :tag "MIME type")
                       (function :tag "Display function")
                       (function :tag "Display test")))
    :group 'mime-display)
  
  (defcustom mm-inlined-types
    '("image/.*" "text/.*" "message/delivery-status" "message/rfc822"
!     "message/partial" "message/external-body" "application/emacs-lisp"
!     "application/x-emacs-lisp"
!     "application/pgp-signature" "application/x-pkcs7-signature"
!     "application/pkcs7-signature" "application/x-pkcs7-mime"
!     "application/pkcs7-mime")
!   "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)
!   :group 'mime-display)
! 
! (defcustom mm-keep-viewer-alive-types
!   '("application/postscript" "application/msword" "application/vnd.ms-excel"
!     "application/pdf" "application/x-dvi")
!   "List of media types for which the external viewer will not be killed
! when selecting a different article."
    :type '(repeat string)
    :group 'mime-display)
  
***************
*** 169,180 ****
    '("text/plain" "text/enriched" "text/richtext" "text/html"
      "text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
      "message/rfc822" "text/x-patch" "application/pgp-signature"
!     "application/emacs-lisp")
    "A list of MIME types to be displayed automatically."
    :type '(repeat string)
    :group 'mime-display)
  
! (defcustom mm-attachment-override-types '("text/x-vcard")
    "Types to have \"attachment\" ignored if they can be displayed inline."
    :type '(repeat string)
    :group 'mime-display)
--- 287,305 ----
    '("text/plain" "text/enriched" "text/richtext" "text/html"
      "text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
      "message/rfc822" "text/x-patch" "application/pgp-signature"
!     "application/emacs-lisp" "application/x-emacs-lisp"
!     "application/x-pkcs7-signature"
!     "application/pkcs7-signature" "application/x-pkcs7-mime"
!     "application/pkcs7-mime")
    "A list of MIME types to be displayed automatically."
    :type '(repeat string)
    :group 'mime-display)
  
! (defcustom mm-attachment-override-types '("text/x-vcard"
!                                         "application/pkcs7-mime"
!                                         "application/x-pkcs7-mime"
!                                         "application/pkcs7-signature"
!                                         "application/x-pkcs7-signature")
    "Types to have \"attachment\" ignored if they can be displayed inline."
    :type '(repeat string)
    :group 'mime-display)
***************
*** 202,229 ****
    :type '(repeat string)
    :group 'mime-display)
  
! (defvar mm-tmp-directory
!   (cond ((fboundp 'temp-directory) (temp-directory))
!       ((boundp 'temporary-file-directory) temporary-file-directory)
!       ("/tmp/"))
!   "Where mm will store its temporary files.")
  
  (defcustom mm-inline-large-images nil
    "If non-nil, then all images fit in the buffer."
    :type 'boolean
    :group 'mime-display)
  
  ;;; Internal variables.
  
- (defvar mm-dissection-list nil)
  (defvar mm-last-shell-command "")
  (defvar mm-content-id-alist nil)
  
  ;; According to RFC2046, in particular, in a digest, the default
  ;; Content-Type value for a body part is changed from "text/plain" to
  ;; "message/rfc822".
  (defvar mm-dissect-default-type "text/plain")
  
  (defvar mm-viewer-completion-map
    (let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
      (set-keymap-parent map minibuffer-local-completion-map)
--- 327,451 ----
    :type '(repeat string)
    :group 'mime-display)
  
! (defcustom mm-tmp-directory
!   (if (fboundp 'temp-directory)
!       (temp-directory)
!     (if (boundp 'temporary-file-directory)
!       temporary-file-directory
!       "/tmp/"))
!   "Where mm will store its temporary files."
!   :type 'directory
!   :group 'mime-display)
  
  (defcustom mm-inline-large-images nil
    "If non-nil, then all images fit in the buffer."
    :type 'boolean
    :group 'mime-display)
  
+ (defvar mm-file-name-rewrite-functions
+   '(mm-file-name-delete-control mm-file-name-delete-gotchas)
+   "*List of functions used for rewriting file names of MIME parts.
+ Each function takes a file name as input and returns a file name.
+ 
+ Ready-made functions include
+ `mm-file-name-delete-control'
+ `mm-file-name-delete-gotchas'
+ `mm-file-name-delete-whitespace',
+ `mm-file-name-trim-whitespace',
+ `mm-file-name-collapse-whitespace',
+ `mm-file-name-replace-whitespace',
+ `capitalize', `downcase', `upcase', and
+ `upcase-initials'.")
+ 
+ (defvar mm-path-name-rewrite-functions nil
+   "*List of functions for rewriting the full file names of MIME parts.
+ This is used when viewing parts externally, and is meant for
+ transforming the absolute name so that non-compliant programs can find
+ the file where it's saved.
+ 
+ Each function takes a file name as input and returns a file name.")
+ 
+ (defvar mm-file-name-replace-whitespace nil
+   "String used for replacing whitespace characters; default is `\"_\"'.")
+ 
+ (defcustom mm-default-directory nil
+   "The default directory where mm will save files.
+ If not set, `default-directory' will be used."
+   :type '(choice directory (const :tag "Default" nil))
+   :group 'mime-display)
+ 
+ (defcustom mm-attachment-file-modes 384
+   "Set the mode bits of saved attachments to this integer."
+   :type 'integer
+   :group 'mime-display)
+ 
+ (defcustom mm-external-terminal-program "xterm"
+   "The program to start an external terminal."
+   :type 'string
+   :group 'mime-display)
+ 
  ;;; Internal variables.
  
  (defvar mm-last-shell-command "")
  (defvar mm-content-id-alist nil)
+ (defvar mm-postponed-undisplay-list nil)
  
  ;; According to RFC2046, in particular, in a digest, the default
  ;; Content-Type value for a body part is changed from "text/plain" to
  ;; "message/rfc822".
  (defvar mm-dissect-default-type "text/plain")
  
+ (autoload 'mml2015-verify "mml2015")
+ (autoload 'mml2015-verify-test "mml2015")
+ (autoload 'mml-smime-verify "mml-smime")
+ (autoload 'mml-smime-verify-test "mml-smime")
+ 
+ (defvar mm-verify-function-alist
+   '(("application/pgp-signature" mml2015-verify "PGP" mml2015-verify-test)
+     ("application/x-gnus-pgp-signature" mm-uu-pgp-signed-extract-1 "PGP"
+      mm-uu-pgp-signed-test)
+     ("application/pkcs7-signature" mml-smime-verify "S/MIME"
+      mml-smime-verify-test)
+     ("application/x-pkcs7-signature" mml-smime-verify "S/MIME"
+      mml-smime-verify-test)))
+ 
+ (defcustom mm-verify-option 'never
+   "Option of verifying signed parts.
+ `never', not verify; `always', always verify;
+ `known', only verify known protocols.  Otherwise, ask user."
+   :type '(choice (item always)
+                (item never)
+                (item :tag "only known protocols" known)
+                (item :tag "ask" nil))
+   :group 'mime-security)
+ 
+ (autoload 'mml2015-decrypt "mml2015")
+ (autoload 'mml2015-decrypt-test "mml2015")
+ 
+ (defvar mm-decrypt-function-alist
+   '(("application/pgp-encrypted" mml2015-decrypt "PGP" mml2015-decrypt-test)
+     ("application/x-gnus-pgp-encrypted" mm-uu-pgp-encrypted-extract-1 "PGP"
+      mm-uu-pgp-encrypted-test)))
+ 
+ (defcustom mm-decrypt-option nil
+   "Option of decrypting encrypted parts.
+ `never', not decrypt; `always', always decrypt;
+ `known', only decrypt known protocols.  Otherwise, ask user."
+   :type '(choice (item always)
+                (item never)
+                (item :tag "only known protocols" known)
+                (item :tag "ask" nil))
+   :group 'mime-security)
+ 
+ (defvar mm-viewer-completion-map
+   (let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
+     (set-keymap-parent map minibuffer-local-completion-map)
+     map)
+   "Keymap for input viewer with completion.")
+ 
+ ;; Should we bind other key to minibuffer-complete-word?
+ (define-key mm-viewer-completion-map " " 'self-insert-command)
+ 
  (defvar mm-viewer-completion-map
    (let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
      (set-keymap-parent map minibuffer-local-completion-map)
***************
*** 235,254 ****
  
  ;;; The functions.
  
! (defun mm-dissect-buffer (&optional no-strict-mime)
    "Dissect the current buffer and return a list of MIME handles."
    (save-excursion
!     (let (ct ctl type subtype cte cd description id result)
        (save-restriction
        (mail-narrow-to-head)
        (when (or no-strict-mime
                  (mail-fetch-field "mime-version"))
          (setq ct (mail-fetch-field "content-type")
                ctl (ignore-errors (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"))))
        (when cte
        (setq cte (mail-header-strip cte)))
        (if (or (not ctl)
--- 457,528 ----
  
  ;;; The functions.
  
! (defun mm-alist-to-plist (alist)
!   "Convert association list ALIST into the equivalent property-list form.
! The plist is returned.  This converts from
! 
! \((a . 1) (b . 2) (c . 3))
! 
! into
! 
! \(a 1 b 2 c 3)
! 
! The original alist is not modified.  See also `destructive-alist-to-plist'."
!   (let (plist)
!     (while alist
!       (let ((el (car alist)))
!       (setq plist (cons (cdr el) (cons (car el) plist))))
!       (setq alist (cdr alist)))
!     (nreverse plist)))
! 
! (defun mm-keep-viewer-alive-p (handle)
!   "Say whether external viewer for HANDLE should stay alive."
!   (let ((types mm-keep-viewer-alive-types)
!       (type (mm-handle-media-type handle))
!       ty)
!     (catch 'found
!       (while (setq ty (pop types))
!       (when (string-match ty type)
!         (throw 'found t))))))
! 
! (defun mm-handle-set-external-undisplayer (handle function)
!   "Set the undisplayer for HANDLE to FUNCTION.
! Postpone undisplaying of viewers for types in
! `mm-keep-viewer-alive-types'."
!   (if (mm-keep-viewer-alive-p handle)
!       (let ((new-handle (copy-sequence handle)))
!       (mm-handle-set-undisplayer new-handle function)
!       (mm-handle-set-undisplayer handle nil)
!       (push new-handle mm-postponed-undisplay-list))
!     (mm-handle-set-undisplayer handle function)))
! 
! (defun mm-destroy-postponed-undisplay-list ()
!   (when mm-postponed-undisplay-list
!     (message "Destroying external MIME viewers")
!     (mm-destroy-parts mm-postponed-undisplay-list)))
! 
! (defun mm-dissect-buffer (&optional no-strict-mime loose-mime)
    "Dissect the current buffer and return a list of MIME handles."
    (save-excursion
!     (let (ct ctl type subtype cte cd description id result from)
        (save-restriction
        (mail-narrow-to-head)
        (when (or no-strict-mime
+                 loose-mime
                  (mail-fetch-field "mime-version"))
          (setq ct (mail-fetch-field "content-type")
                ctl (ignore-errors (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")
!               from (mail-fetch-field "from")
!               id (mail-fetch-field "content-id"))
!         ;; 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
!         ;; solution, avoids most of them.
!         (if from
!             (setq from (cadr (mail-extract-address-components from))))))
        (when cte
        (setq cte (mail-header-strip cte)))
        (if (or (not ctl)
***************
*** 270,286 ****
          ((equal type "multipart")
           (let ((mm-dissect-default-type (if (equal subtype "digest")
                                              "message/rfc822"
!                                           "text/plain")))
             (cons (car ctl) (mm-dissect-multipart ctl))))
          (t
!          (mm-dissect-singlepart
!           ctl
!           (and cte (intern (downcase (mail-header-remove-whitespace
!                                       (mail-header-remove-comments
!                                        cte)))))
!           no-strict-mime
!           (and cd (ignore-errors (mail-header-parse-content-disposition cd)))
!           description id))))
        (when id
          (when (string-match " *<\\(.*\\)> *" id)
            (setq id (match-string 1 id)))
--- 544,577 ----
          ((equal type "multipart")
           (let ((mm-dissect-default-type (if (equal subtype "digest")
                                              "message/rfc822"
!                                           "text/plain"))
!                (start (cdr (assq 'start (cdr ctl)))))
!            (add-text-properties 0 (length (car ctl))
!                                 (mm-alist-to-plist (cdr ctl)) (car ctl))
! 
!            ;; what really needs to be done here is a way to link a
!            ;; MIME handle back to it's parent MIME handle (in a multilevel
!            ;; MIME article).  That would probably require changing
!            ;; the mm-handle API so we simply store the multipart buffert
!            ;; name as a text property of the "multipart/whatever" string.
!            (add-text-properties 0 (length (car ctl))
!                                 (list 'buffer (mm-copy-to-buffer)
!                                       'from from
!                                       'start start)
!                                 (car ctl))
             (cons (car ctl) (mm-dissect-multipart ctl))))
          (t
!          (mm-possibly-verify-or-decrypt
!           (mm-dissect-singlepart
!            ctl
!            (and cte (intern (downcase (mail-header-remove-whitespace
!                                        (mail-header-remove-comments
!                                         cte)))))
!            no-strict-mime
!            (and cd (ignore-errors
!                      (mail-header-parse-content-disposition cd)))
!            description id)
!           ctl))))
        (when id
          (when (string-match " *<\\(.*\\)> *" id)
            (setq id (match-string 1 id)))
***************
*** 292,307 ****
            (if (equal "text/plain" (car ctl))
                (assoc 'format ctl)
              t))
!     (let ((res (mm-make-handle
!               (mm-copy-to-buffer) ctl cte nil cdl description nil id)))
!       (push (car res) mm-dissection-list)
!       res)))
! 
! (defun mm-remove-all-parts ()
!   "Remove all MIME handles."
!   (interactive)
!   (mapcar 'mm-remove-part mm-dissection-list)
!   (setq mm-dissection-list nil))
  
  (defun mm-dissect-multipart (ctl)
    (goto-char (point-min))
--- 583,590 ----
            (if (equal "text/plain" (car ctl))
                (assoc 'format ctl)
              t))
!     (mm-make-handle
!      (mm-copy-to-buffer) ctl cte nil cdl description nil id)))
  
  (defun mm-dissect-multipart (ctl)
    (goto-char (point-min))
***************
*** 321,334 ****
          (save-restriction
            (narrow-to-region start (point))
            (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
!       (forward-line 2)
        (setq start (point)))
      (when (and start (< start end))
        (save-excursion
        (save-restriction
          (narrow-to-region start end)
          (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
!     (nreverse parts)))
  
  (defun mm-copy-to-buffer ()
    "Copy the contents of the current buffer to a fresh buffer."
--- 604,619 ----
          (save-restriction
            (narrow-to-region start (point))
            (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
!       (end-of-line 2)
!       (or (looking-at boundary)
!         (forward-line 1))
        (setq start (point)))
      (when (and start (< start end))
        (save-excursion
        (save-restriction
          (narrow-to-region start end)
          (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
!     (mm-possibly-verify-or-decrypt (nreverse parts) ctl)))
  
  (defun mm-copy-to-buffer ()
    "Copy the contents of the current buffer to a fresh buffer."
***************
*** 342,347 ****
--- 627,642 ----
        (insert-buffer-substring obuf beg)
        (current-buffer))))
  
+ (defun mm-display-parts (handle &optional no-default)
+   (if (stringp (car handle))
+       (mapcar 'mm-display-parts (cdr handle))
+     (if (bufferp (car handle))
+       (save-restriction
+         (narrow-to-region (point) (point))
+         (mm-display-part handle)
+         (goto-char (point-max)))
+       (mapcar 'mm-display-parts handle))))
+ 
  (defun mm-display-part (handle &optional no-default)
    "Display the MIME part represented by HANDLE.
  Returns nil if the part is removed; inline if displayed inline;
***************
*** 351,358 ****
      (if (mm-handle-displayed-p handle)
        (mm-remove-part handle)
        (let* ((type (mm-handle-media-type handle))
!            (method (mailcap-mime-info type)))
!       (if (mm-inlined-p handle)
            (progn
              (forward-line 1)
              (mm-display-inline handle)
--- 646,660 ----
      (if (mm-handle-displayed-p handle)
        (mm-remove-part handle)
        (let* ((type (mm-handle-media-type handle))
!            (method (mailcap-mime-info type))
!            (filename (or (mail-content-type-get
!                           (mm-handle-disposition handle) 'filename)
!                          (mail-content-type-get
!                           (mm-handle-type handle) 'name)
!                          "<file>"))
!            (external mm-enable-external))
!       (if (and (mm-inlinable-p handle)
!                (mm-inlined-p handle))
            (progn
              (forward-line 1)
              (mm-display-inline handle)
***************
*** 365,372 ****
                  (forward-line 1)
                  (mm-insert-inline handle (mm-get-part handle))
                  'inline)
!             (mm-display-external
!              handle (or method 'mailcap-save-binary-file)))))))))
  
  (defun mm-display-external (handle method)
    "Display HANDLE using METHOD."
--- 667,693 ----
                  (forward-line 1)
                  (mm-insert-inline handle (mm-get-part handle))
                  'inline)
!             (if (and method ;; If nil, we always use "save".
!                      (stringp method) ;; 'mailcap-save-binary-file
!                      (or (eq mm-enable-external t)
!                          (and (eq mm-enable-external 'ask)
!                               (y-or-n-p
!                                (concat
!                                 "Display part (" type
!                                 ") using external program"
!                                 ;; Can non-string method ever happen?
!                                 (if (stringp method)
!                                     (concat
!                                      " \"" (format method filename) "\"")
!                                   "")
!                                 "? ")))))
!                 (setq external t)
!               (setq external nil))
!             (if external
!                 (mm-display-external
!                  handle (or method 'mailcap-save-binary-file))
!               (mm-display-external
!                handle 'mailcap-save-binary-file)))))))))
  
  (defun mm-display-external (handle method)
    "Display HANDLE using METHOD."
***************
*** 383,392 ****
                (when win
                  (select-window win)))
              (switch-to-buffer (generate-new-buffer " *mm*")))
            (mm-set-buffer-file-coding-system mm-binary-coding-system)
            (insert-buffer-substring cur)
            (goto-char (point-min))
!           (message "Viewing with %s" method)
            (let ((mm (current-buffer))
                  (non-viewer (assq 'non-viewer
                                    (mailcap-mime-info
--- 704,715 ----
                (when win
                  (select-window win)))
              (switch-to-buffer (generate-new-buffer " *mm*")))
+           (buffer-disable-undo)
            (mm-set-buffer-file-coding-system mm-binary-coding-system)
            (insert-buffer-substring cur)
            (goto-char (point-min))
!           (when method
!             (message "Viewing with %s" method))
            (let ((mm (current-buffer))
                  (non-viewer (assq 'non-viewer
                                    (mailcap-mime-info
***************
*** 400,409 ****
                  (mm-handle-set-undisplayer handle mm)))))
        ;; The function is a string to be executed.
        (mm-insert-part handle)
!       (let* ((dir (mm-make-temp-file
                     (expand-file-name "emm." mm-tmp-directory) 'dir))
!              (filename (mail-content-type-get
!                         (mm-handle-disposition handle) 'filename))
               (mime-info (mailcap-mime-info
                           (mm-handle-media-type handle) t))
               (needsterm (or (assoc "needsterm" mime-info)
--- 723,735 ----
                  (mm-handle-set-undisplayer handle mm)))))
        ;; The function is a string to be executed.
        (mm-insert-part handle)
!       (let* ((dir (mm-make-temp-file
                     (expand-file-name "emm." mm-tmp-directory) 'dir))
!              (filename (or
!                         (mail-content-type-get
!                          (mm-handle-disposition handle) 'filename)
!                         (mail-content-type-get
!                          (mm-handle-type handle) 'name)))
               (mime-info (mailcap-mime-info
                           (mm-handle-media-type handle) t))
               (needsterm (or (assoc "needsterm" mime-info)
***************
*** 413,478 ****
          ;; We create a private sub-directory where we store our files.
          (set-file-modes dir 448)
          (if filename
!             (setq file (expand-file-name (file-name-nondirectory filename)
!                                          dir))
            (setq file (mm-make-temp-file (expand-file-name "mm." dir))))
          (let ((coding-system-for-write mm-binary-coding-system))
            (write-region (point-min) (point-max) file nil 'nomesg))
          (message "Viewing with %s" method)
!         (cond (needsterm
!                (unwind-protect
!                    (start-process "*display*" nil
!                                   "xterm"
!                                   "-e" shell-file-name
!                                   shell-command-switch
!                                   (mm-mailcap-command
!                                    method file (mm-handle-type handle)))
!                  (mm-handle-set-undisplayer handle (cons file buffer)))
!                (message "Displaying %s..." (format method file))
!                'external)
!               (copiousoutput
!                (with-current-buffer outbuf
!                  (forward-line 1)
!                  (mm-insert-inline
!                   handle
!                   (unwind-protect
!                       (progn
!                         (call-process shell-file-name nil
!                                       (setq buffer
!                                             (generate-new-buffer "*mm*"))
!                                       nil
!                                       shell-command-switch
!                                       (mm-mailcap-command
!                                        method file (mm-handle-type handle)))
!                         (if (buffer-live-p buffer)
!                             (save-excursion
!                               (set-buffer buffer)
!                               (buffer-string))))
!                     (progn
!                       (ignore-errors (delete-file file))
!                       (ignore-errors (delete-directory
!                                       (file-name-directory file)))
!                       (ignore-errors (kill-buffer buffer))))))
!                'inline)
!               (t
!                (unwind-protect
!                    (start-process "*display*"
!                                   (setq buffer
!                                         (generate-new-buffer "*mm*"))
!                                   shell-file-name
!                                   shell-command-switch
!                                   (mm-mailcap-command
!                                    method file (mm-handle-type handle)))
!                  (mm-handle-set-undisplayer handle (cons file buffer)))
!                (message "Displaying %s..." (format method file))
!                'external)))))))
  
  (defun mm-mailcap-command (method file type-list)
    (let ((ctl (cdr type-list))
        (beg 0)
        (uses-stdin t)
        out sub total)
!     (while (string-match "%{\\([^}]+\\)}\\|%s\\|%t\\|%%" method beg)
        (push (substring method beg (match-beginning 0)) out)
        (setq beg (match-end 0)
            total (match-string 0 method)
--- 739,827 ----
          ;; We create a private sub-directory where we store our files.
          (set-file-modes dir 448)
          (if filename
!             (setq file (expand-file-name
!                         (gnus-map-function mm-file-name-rewrite-functions
!                                            (file-name-nondirectory filename))
!                         dir))
            (setq file (mm-make-temp-file (expand-file-name "mm." dir))))
          (let ((coding-system-for-write mm-binary-coding-system))
            (write-region (point-min) (point-max) file nil 'nomesg))
          (message "Viewing with %s" method)
!         (cond
!          (needsterm
!           (let ((command (mm-mailcap-command
!                           method file (mm-handle-type handle))))
!             (unwind-protect
!                 (if window-system
!                     (start-process "*display*" nil
!                                    mm-external-terminal-program
!                                    "-e" shell-file-name
!                                    shell-command-switch command)
!                   (require 'term)
!                   (require 'gnus-win)
!                   (set-buffer
!                    (setq buffer
!                          (make-term "display"
!                                     shell-file-name
!                                     nil
!                                     shell-command-switch command)))
!                   (term-mode)
!                   (term-char-mode)
!                   (set-process-sentinel
!                    (get-buffer-process buffer)
!                    `(lambda (process state)
!                       (if (eq 'exit (process-status process))
!                           (gnus-configure-windows
!                            ',gnus-current-window-configuration))))
!                   (gnus-configure-windows 'display-term))
!               (mm-handle-set-external-undisplayer handle (cons file buffer)))
!             (message "Displaying %s..." command))
!           'external)
!          (copiousoutput
!           (with-current-buffer outbuf
!             (forward-line 1)
!             (mm-insert-inline
!              handle
!              (unwind-protect
!                  (progn
!                    (call-process shell-file-name nil
!                                  (setq buffer
!                                        (generate-new-buffer " *mm*"))
!                                  nil
!                                  shell-command-switch
!                                  (mm-mailcap-command
!                                   method file (mm-handle-type handle)))
!                    (if (buffer-live-p buffer)
!                        (save-excursion
!                          (set-buffer buffer)
!                          (buffer-string))))
!                (progn
!                  (ignore-errors (delete-file file))
!                  (ignore-errors (delete-directory
!                                  (file-name-directory file)))
!                  (ignore-errors (kill-buffer buffer))))))
!           'inline)
!          (t
!           (let ((command (mm-mailcap-command
!                           method file (mm-handle-type handle))))
!             (unwind-protect
!                 (start-process "*display*"
!                                (setq buffer
!                                      (generate-new-buffer " *mm*"))
!                                shell-file-name
!                                shell-command-switch command)
!               (mm-handle-set-external-undisplayer
!                handle (cons file buffer)))
!             (message "Displaying %s..." command))
!           'external)))))))
  
  (defun mm-mailcap-command (method file type-list)
    (let ((ctl (cdr type-list))
        (beg 0)
        (uses-stdin t)
        out sub total)
!     (while (string-match "%{\\([^}]+\\)}\\|'%s'\\|\"%s\"\\|%s\\|%t\\|%%"
!                        method beg)
        (push (substring method beg (match-beginning 0)) out)
        (setq beg (match-end 0)
            total (match-string 0 method)
***************
*** 480,497 ****
        (cond
         ((string= total "%%")
        (push "%" out))
!        ((string= total "%s")
        (setq uses-stdin nil)
!       (push (mm-quote-arg file) out))
         ((string= total "%t")
        (push (mm-quote-arg (car type-list)) out))
         (t
        (push (mm-quote-arg (or (cdr (assq (intern sub) ctl)) "")) out))))
      (push (substring method beg (length method)) out)
!     (if uses-stdin
!       (progn
!         (push "<" out)
!         (push (mm-quote-arg file) out)))
      (mapconcat 'identity (nreverse out) "")))
  
  (defun mm-remove-parts (handles)
--- 829,851 ----
        (cond
         ((string= total "%%")
        (push "%" out))
!        ((or (string= total "%s")
!           ;; We do our own quoting.
!           (string= total "'%s'")
!           (string= total "\"%s\""))
        (setq uses-stdin nil)
!       (push (mm-quote-arg
!              (gnus-map-function mm-path-name-rewrite-functions file)) out))
         ((string= total "%t")
        (push (mm-quote-arg (car type-list)) out))
         (t
        (push (mm-quote-arg (or (cdr (assq (intern sub) ctl)) "")) out))))
      (push (substring method beg (length method)) out)
!     (when uses-stdin
!       (push "<" out)
!       (push (mm-quote-arg
!            (gnus-map-function mm-path-name-rewrite-functions file))
!           out))
      (mapconcat 'identity (nreverse out) "")))
  
  (defun mm-remove-parts (handles)
***************
*** 503,510 ****
        (while (setq handle (pop handles))
        (cond
         ((stringp handle)
!         ;; Do nothing.
!         )
         ((and (listp handle)
               (stringp (car handle)))
          (mm-remove-parts (cdr handle)))
--- 857,864 ----
        (while (setq handle (pop handles))
        (cond
         ((stringp handle)
!         (when (buffer-live-p (get-text-property 0 'buffer handle))
!           (kill-buffer (get-text-property 0 'buffer handle))))
         ((and (listp handle)
               (stringp (car handle)))
          (mm-remove-parts (cdr handle)))
***************
*** 520,530 ****
        (while (setq handle (pop handles))
        (cond
         ((stringp handle)
!         ;; Do nothing.
!         )
         ((and (listp handle)
               (stringp (car handle)))
!         (mm-destroy-parts (cdr handle)))
         (t
          (mm-destroy-part handle)))))))
  
--- 874,884 ----
        (while (setq handle (pop handles))
        (cond
         ((stringp handle)
!         (when (buffer-live-p (get-text-property 0 'buffer handle))
!           (kill-buffer (get-text-property 0 'buffer handle))))
         ((and (listp handle)
               (stringp (car handle)))
!         (mm-destroy-parts handle))
         (t
          (mm-destroy-part handle)))))))
  
***************
*** 543,551 ****
          (funcall object))
         ;; Externally displayed part.
         ((consp object)
          (ignore-errors (delete-file (car object)))
!         (ignore-errors (delete-directory (file-name-directory (car object))))
!         (ignore-errors (kill-buffer (cdr object))))
         ((bufferp object)
          (when (buffer-live-p object)
            (kill-buffer object)))))
--- 897,914 ----
          (funcall object))
         ;; Externally displayed part.
         ((consp object)
+         (condition-case ()
+             (while (get-buffer-process (cdr object))
+               (interrupt-process (get-buffer-process (cdr object)))
+               (message "Waiting for external displayer to die...")
+               (sit-for 1))
+           (quit)
+           (error))
+         (ignore-errors (and (cdr object) (kill-buffer (cdr object))))
+         (message "Waiting for external displayer to die...done")
          (ignore-errors (delete-file (car object)))
!         (ignore-errors (delete-directory (file-name-directory
!                                           (car object)))))
         ((bufferp object)
          (when (buffer-live-p object)
            (kill-buffer object)))))
***************
*** 562,567 ****
--- 925,942 ----
      (when (string-match (car elem) type)
        (return elem))))
  
+ (defun mm-automatic-display-p (handle)
+   "Say whether the user wants HANDLE to be displayed automatically."
+   (let ((methods mm-automatic-display)
+       (type (mm-handle-media-type handle))
+       method result)
+     (while (setq method (pop methods))
+       (when (and (not (mm-inline-override-p handle))
+                (string-match method type))
+       (setq result t
+             methods nil)))
+     result))
+ 
  (defun mm-inlinable-p (handle)
    "Say whether HANDLE can be displayed inline."
    (let ((alist mm-inline-media-tests)
***************
*** 575,602 ****
        (pop alist))
      test))
  
- (defun mm-automatic-display-p (handle)
-   "Say whether the user wants HANDLE to be displayed automatically."
-   (let ((methods mm-automatic-display)
-       (type (mm-handle-media-type handle))
-       method result)
-     (while (setq method (pop methods))
-       (when (and (not (mm-inline-override-p handle))
-                (string-match method type)
-                (mm-inlinable-p handle))
-       (setq result t
-             methods nil)))
-     result))
- 
  (defun mm-inlined-p (handle)
!   "Say whether the user wants HANDLE to be displayed automatically."
    (let ((methods mm-inlined-types)
        (type (mm-handle-media-type handle))
        method result)
      (while (setq method (pop methods))
        (when (and (not (mm-inline-override-p handle))
!                (string-match method type)
!                (mm-inlinable-p handle))
        (setq result t
              methods nil)))
      result))
--- 950,963 ----
        (pop alist))
      test))
  
  (defun mm-inlined-p (handle)
!   "Say whether the user wants HANDLE to be displayed inline."
    (let ((methods mm-inlined-types)
        (type (mm-handle-media-type handle))
        method result)
      (while (setq method (pop methods))
        (when (and (not (mm-inline-override-p handle))
!                (string-match method type))
        (setq result t
              methods nil)))
      result))
***************
*** 650,656 ****
  (defun mm-get-part (handle)
    "Return the contents of HANDLE as a string."
    (mm-with-unibyte-buffer
!     (mm-insert-part handle)
      (buffer-string)))
  
  (defun mm-insert-part (handle)
--- 1011,1022 ----
  (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)
***************
*** 659,681 ****
      (save-excursion
        (if (member (mm-handle-media-supertype handle) '("text" "message"))
          (with-temp-buffer
!           (insert-buffer-substring (mm-handle-buffer handle))
!           (mm-decode-content-transfer-encoding
!            (mm-handle-encoding handle)
!            (mm-handle-media-type handle))
!           (let ((temp (current-buffer)))
!             (set-buffer cur)
!             (insert-buffer-substring temp)))
        (mm-with-unibyte-buffer
          (insert-buffer-substring (mm-handle-buffer handle))
!         (mm-decode-content-transfer-encoding
!          (mm-handle-encoding handle)
!          (mm-handle-media-type handle))
!         (let ((temp (current-buffer)))
!           (set-buffer cur)
!           (insert-buffer-substring temp)))))))
  
! (defvar mm-default-directory nil)
  
  (defun mm-save-part (handle)
    "Write HANDLE to a file."
--- 1025,1085 ----
      (save-excursion
        (if (member (mm-handle-media-supertype handle) '("text" "message"))
          (with-temp-buffer
!           (insert-buffer-substring (mm-handle-buffer handle))
!           (prog1
!               (mm-decode-content-transfer-encoding
!                (mm-handle-encoding handle)
!                (mm-handle-media-type handle))
!             (let ((temp (current-buffer)))
!               (set-buffer cur)
!               (insert-buffer-substring temp))))
        (mm-with-unibyte-buffer
          (insert-buffer-substring (mm-handle-buffer handle))
!         (prog1
!             (mm-decode-content-transfer-encoding
!              (mm-handle-encoding handle)
!              (mm-handle-media-type handle))
!           (let ((temp (current-buffer)))
!             (set-buffer cur)
!             (insert-buffer-substring temp))))))))
  
! (defun mm-file-name-delete-whitespace (file-name)
!   "Remove all whitespace characters from FILE-NAME."
!   (while (string-match "\\s-+" file-name)
!     (setq file-name (replace-match "" t t file-name)))
!   file-name)
! 
! (defun mm-file-name-trim-whitespace (file-name)
!   "Remove leading and trailing whitespace characters from FILE-NAME."
!   (when (string-match "\\`\\s-+" file-name)
!     (setq file-name (substring file-name (match-end 0))))
!   (when (string-match "\\s-+\\'" file-name)
!     (setq file-name (substring file-name 0 (match-beginning 0))))
!   file-name)
! 
! (defun mm-file-name-collapse-whitespace (file-name)
!   "Collapse multiple whitespace characters in FILE-NAME."
!   (while (string-match "\\s-\\s-+" file-name)
!     (setq file-name (replace-match " " t t file-name)))
!   file-name)
! 
! (defun mm-file-name-replace-whitespace (file-name)
!   "Replace whitespace characters in FILE-NAME with underscores.
! Set the option `mm-file-name-replace-whitespace' to any other
! string if you do not like underscores."
!   (let ((s (or mm-file-name-replace-whitespace "_")))
!     (while (string-match "\\s-" file-name)
!       (setq file-name (replace-match s t t file-name))))
!   file-name)
! 
! (defun mm-file-name-delete-control (filename)
!   "Delete control characters from FILENAME."
!   (gnus-replace-in-string filename "[\x00-\x1f\x7f]" ""))
! 
! (defun mm-file-name-delete-gotchas (filename)
!   "Delete shell gotchas from FILENAME."
!   (setq filename (gnus-replace-in-string filename "[<>|]" ""))
!   (gnus-replace-in-string filename "^[.-]+" ""))
  
  (defun mm-save-part (handle)
    "Write HANDLE to a file."
***************
*** 684,712 ****
                    (mm-handle-disposition handle) 'filename))
         file)
      (when filename
!       (setq filename (file-name-nondirectory filename)))
      (setq file
!         (read-file-name "Save MIME part to: "
!                         (expand-file-name
!                          (or filename name "")
!                          (or mm-default-directory default-directory))))
      (setq mm-default-directory (file-name-directory file))
!     (when (or (not (file-exists-p file))
!             (yes-or-no-p (format "File %s already exists; overwrite? "
!                                  file)))
!       (mm-save-part-to-file handle file))))
  
  (defun mm-save-part-to-file (handle file)
    (mm-with-unibyte-buffer
      (mm-insert-part handle)
      (let ((coding-system-for-write 'binary)
          ;; Don't re-compress .gz & al.  Arguably we should make
          ;; `file-name-handler-alist' nil, but that would chop
          ;; ange-ftp, which is reasonable to use here.
          (inhibit-file-name-operation 'write-region)
          (inhibit-file-name-handlers
           (cons 'jka-compr-handler inhibit-file-name-handlers)))
!       (write-region (point-min) (point-max) file))))
  
  (defun mm-pipe-part (handle)
    "Pipe HANDLE to a process."
--- 1088,1123 ----
                    (mm-handle-disposition handle) 'filename))
         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 "Save MIME part to: "
!                           (or mm-default-directory default-directory)
!                           nil nil (or filename name ""))))
      (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? "
!                                 file)))
!        (progn
!          (mm-save-part-to-file handle file)
!          file))))
  
  (defun mm-save-part-to-file (handle file)
    (mm-with-unibyte-buffer
      (mm-insert-part handle)
      (let ((coding-system-for-write 'binary)
+         (current-file-modes (default-file-modes))
          ;; Don't re-compress .gz & al.  Arguably we should make
          ;; `file-name-handler-alist' nil, but that would chop
          ;; ange-ftp, which is reasonable to use here.
          (inhibit-file-name-operation 'write-region)
          (inhibit-file-name-handlers
           (cons 'jka-compr-handler inhibit-file-name-handlers)))
!       (set-default-file-modes mm-attachment-file-modes)
!       (unwind-protect
!         (write-region (point-min) (point-max) file)
!       (set-default-file-modes current-file-modes)))))
  
  (defun mm-pipe-part (handle)
    "Pipe HANDLE to a process."
***************
*** 715,721 ****
          (read-string "Shell command on MIME part: " mm-last-shell-command)))
      (mm-with-unibyte-buffer
        (mm-insert-part handle)
!       (shell-command-on-region (point-min) (point-max) command nil))))
  
  (defun mm-interactively-view-part (handle)
    "Display HANDLE using METHOD."
--- 1126,1133 ----
          (read-string "Shell command on MIME part: " mm-last-shell-command)))
      (mm-with-unibyte-buffer
        (mm-insert-part handle)
!       (let ((coding-system-for-write 'binary))
!       (shell-command-on-region (point-min) (point-max) command nil)))))
  
  (defun mm-interactively-view-part (handle)
    "Display HANDLE using METHOD."
***************
*** 768,773 ****
--- 1180,1214 ----
    "Return the handle(s) referred to by ID."
    (cdr (assoc id mm-content-id-alist)))
  
+ (defconst mm-image-type-regexps
+   '(("/\\*.*XPM.\\*/" . xpm)
+     ("P[1-6]" . pbm)
+     ("GIF8" . gif)
+     ("\377\330" . jpeg)
+     ("\211PNG\r\n" . png)
+     ("#define" . xbm)
+     ("\\(MM\0\\*\\)\\|\\(II\\*\0\\)" . tiff)
+     ("%!PS" . postscript))
+   "Alist of (REGEXP . IMAGE-TYPE) pairs used to auto-detect image types.
+ When the first bytes of an image file match REGEXP, it is assumed to
+ be of image type IMAGE-TYPE.")
+ 
+ ;; Steal from image.el. image-type-from-data suffers multi-line matching bug.
+ (defun mm-image-type-from-buffer ()
+   "Determine the image type from data in the current buffer.
+ Value is a symbol specifying the image type or nil if type cannot
+ be determined."
+   (let ((types mm-image-type-regexps)
+       type)
+     (goto-char (point-min))
+     (while (and types (null type))
+       (let ((regexp (car (car types)))
+           (image-type (cdr (car types))))
+       (when (looking-at regexp)
+         (setq type image-type))
+       (setq types (cdr types))))
+     type))
+ 
  (defun mm-get-image (handle)
    "Return an image instance based on HANDLE."
    (let ((type (mm-handle-media-subtype handle))
***************
*** 788,818 ****
          (prog1
              (setq spec
                    (ignore-errors
!                    ;; Avoid testing `make-glyph' since W3 may define
!                    ;; a bogus version of it.
                      (if (fboundp 'create-image)
!                         (create-image (buffer-string) (intern type) 'data-p)
!                       (cond
!                        ((equal type "xbm")
!                         ;; xbm images require special handling, since
!                         ;; the only way to create glyphs from these
!                         ;; (without a ton of work) is to write them
!                         ;; out to a file, and then create a file
!                         ;; specifier.
!                         (let ((file (mm-make-temp-file
!                                      (expand-file-name "emm.xbm"
!                                                        mm-tmp-directory))))
!                           (unwind-protect
!                               (progn
!                                 (write-region (point-min) (point-max) file)
!                                 (make-glyph (list (cons 'x file))))
!                             (ignore-errors
!                              (delete-file file)))))
!                        (t
!                         (make-glyph
!                          (vector (intern type) :data (buffer-string))))))))
            (mm-handle-set-cache handle spec))))))
  
  (defun mm-image-fit-p (handle)
    "Say whether the image in HANDLE will fit the current window."
    (let ((image (mm-get-image handle)))
--- 1229,1268 ----
          (prog1
              (setq spec
                    (ignore-errors
!                     ;; Avoid testing `make-glyph' since W3 may define
!                     ;; a bogus version of it.
                      (if (fboundp 'create-image)
!                         (create-image (buffer-string)
!                                       (or (mm-image-type-from-buffer)
!                                           (intern type))
!                                       'data-p)
!                       (mm-create-image-xemacs type))))
            (mm-handle-set-cache handle spec))))))
  
+ (defun mm-create-image-xemacs (type)
+   (cond
+    ((equal type "xbm")
+     ;; xbm images require special handling, since
+     ;; the only way to create glyphs from these
+     ;; (without a ton of work) is to write them
+     ;; out to a file, and then create a file
+     ;; specifier.
+     (let ((file (mm-make-temp-file
+                (expand-file-name "emm.xbm"
+                                  mm-tmp-directory))))
+       (unwind-protect
+         (progn
+           (write-region (point-min) (point-max) file)
+           (make-glyph (list (cons 'x file))))
+       (ignore-errors
+         (delete-file file)))))
+    (t
+     (make-glyph
+      (vector
+       (or (mm-image-type-from-buffer)
+         (intern type))
+       :data (buffer-string))))))
+ 
  (defun mm-image-fit-p (handle)
    "Say whether the image in HANDLE will fit the current window."
    (let ((image (mm-get-image handle)))
***************
*** 848,853 ****
--- 1298,1485 ----
    (and (mm-valid-image-format-p format)
         (mm-image-fit-p handle)))
  
+ (defun mm-find-part-by-type (handles type &optional notp recursive)
+   "Search in HANDLES for part with TYPE.
+ If NOTP, returns first non-matching part.
+ If RECURSIVE, search recursively."
+   (let (handle)
+     (while handles
+       (if (and recursive (stringp (caar handles)))
+         (if (setq handle (mm-find-part-by-type (cdar handles) type
+                                                notp recursive))
+             (setq handles nil))
+       (if (if notp
+               (not (equal (mm-handle-media-type (car handles)) type))
+             (equal (mm-handle-media-type (car handles)) type))
+           (setq handle (car handles)
+                 handles nil)))
+       (setq handles (cdr handles)))
+     handle))
+ 
+ (defun mm-find-raw-part-by-type (ctl type &optional notp)
+   (goto-char (point-min))
+   (let* ((boundary (concat "--" (mm-handle-multipart-ctl-parameter ctl
+                                                                  'boundary)))
+        (close-delimiter (concat "^" (regexp-quote boundary) "--[ \t]*$"))
+        start
+        (end (save-excursion
+               (goto-char (point-max))
+               (if (re-search-backward close-delimiter nil t)
+                   (match-beginning 0)
+                 (point-max))))
+        result)
+     (setq boundary (concat "^" (regexp-quote boundary) "[ \t]*$"))
+     (while (and (not result)
+               (re-search-forward boundary end t))
+       (goto-char (match-beginning 0))
+       (when start
+       (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")))))
+                   (if notp
+                       (not (equal (car ctl) type))
+                     (equal (car ctl) type)))
+             (setq result (buffer-string))))))
+       (forward-line 1)
+       (setq start (point)))
+     (when (and (not result) start)
+       (save-excursion
+       (save-restriction
+         (narrow-to-region start end)
+         (when (let ((ctl (ignore-errors
+                            (mail-header-parse-content-type
+                             (mail-fetch-field "content-type")))))
+                 (if notp
+                     (not (equal (car ctl) type))
+                   (equal (car ctl) type)))
+           (setq result (buffer-string))))))
+     result))
+ 
+ (defvar mm-security-handle nil)
+ 
+ (defsubst mm-set-handle-multipart-parameter (handle parameter value)
+   ;; HANDLE could be a CTL.
+   (when handle
+     (put-text-property 0 (length (car handle)) parameter value
+                      (car handle))))
+ 
+ (defun mm-possibly-verify-or-decrypt (parts ctl)
+   (let ((type (car ctl))
+       (subtype (cadr (split-string (car ctl) "/")))
+       (mm-security-handle ctl) ;; (car CTL) is the type.
+       protocol func functest)
+     (cond
+      ((or (equal type "application/x-pkcs7-mime")
+         (equal type "application/pkcs7-mime"))
+       (with-temp-buffer
+       (when (and (cond
+                   ((eq mm-decrypt-option 'never) nil)
+                   ((eq mm-decrypt-option 'always) t)
+                   ((eq mm-decrypt-option 'known) t)
+                   (t (y-or-n-p
+                       (format "Decrypt (S/MIME) part? "))))
+                  (mm-view-pkcs7 parts))
+         (setq parts (mm-dissect-buffer t)))))
+      ((equal subtype "signed")
+       (unless (and (setq protocol
+                        (mm-handle-multipart-ctl-parameter ctl 'protocol))
+                  (not (equal protocol "multipart/mixed")))
+       ;; The message is broken or draft-ietf-openpgp-multsig-01.
+       (let ((protocols mm-verify-function-alist))
+         (while protocols
+           (if (and (or (not (setq functest (nth 3 (car protocols))))
+                        (funcall functest parts ctl))
+                    (mm-find-part-by-type parts (caar protocols) nil t))
+               (setq protocol (caar protocols)
+                     protocols nil)
+             (setq protocols (cdr protocols))))))
+       (setq func (nth 1 (assoc protocol mm-verify-function-alist)))
+       (when (cond
+            ((eq mm-verify-option 'never) nil)
+            ((eq mm-verify-option 'always) t)
+            ((eq mm-verify-option 'known)
+             (and func
+                  (or (not (setq functest
+                                 (nth 3 (assoc protocol
+                                               mm-verify-function-alist))))
+                      (funcall functest parts ctl))))
+            (t
+             (y-or-n-p
+              (format "Verify signed (%s) part? "
+                      (or (nth 2 (assoc protocol mm-verify-function-alist))
+                          (format "protocol=%s" protocol))))))
+       (save-excursion
+         (if func
+             (funcall func parts ctl)
+           (mm-set-handle-multipart-parameter
+            mm-security-handle 'gnus-details
+            (format "Unknown sign protocol (%s)" protocol))))))
+      ((equal subtype "encrypted")
+       (unless (setq protocol
+                   (mm-handle-multipart-ctl-parameter ctl 'protocol))
+       ;; The message is broken.
+       (let ((parts parts))
+         (while parts
+           (if (assoc (mm-handle-media-type (car parts))
+                      mm-decrypt-function-alist)
+               (setq protocol (mm-handle-media-type (car parts))
+                     parts nil)
+             (setq parts (cdr parts))))))
+       (setq func (nth 1 (assoc protocol mm-decrypt-function-alist)))
+       (when (cond
+            ((eq mm-decrypt-option 'never) nil)
+            ((eq mm-decrypt-option 'always) t)
+            ((eq mm-decrypt-option 'known)
+             (and func
+                  (or (not (setq functest
+                                 (nth 3 (assoc protocol
+                                               mm-decrypt-function-alist))))
+                      (funcall functest parts ctl))))
+            (t
+             (y-or-n-p
+              (format "Decrypt (%s) part? "
+                      (or (nth 2 (assoc protocol mm-decrypt-function-alist))
+                          (format "protocol=%s" protocol))))))
+       (save-excursion
+         (if func
+             (setq parts (funcall func parts ctl))
+           (mm-set-handle-multipart-parameter
+            mm-security-handle 'gnus-details
+            (format "Unknown encrypt protocol (%s)" protocol))))))
+      (t nil))
+     parts))
+ 
+ (defun mm-multiple-handles (handles)
+   (and (listp handles)
+        (> (length handles) 1)
+        (or (listp (car handles))
+          (stringp (car handles)))))
+ 
+ (defun mm-complicated-handles (handles)
+   (and (listp (car handles))
+        (> (length handles) 1)))
+ 
+ (defun mm-merge-handles (handles1 handles2)
+   (append
+    (if (listp (car handles1))
+        handles1
+      (list handles1))
+    (if (listp (car handles2))
+        handles2
+      (list handles2))))
+ 
+ (defun mm-readable-p (handle)
+   "Say whether the content of HANDLE is readable."
+   (and (< (with-current-buffer (mm-handle-buffer handle)
+           (buffer-size)) 10000)
+        (mm-with-unibyte-buffer
+        (mm-insert-part handle)
+        (and (eq (mm-body-7-or-8) '7bit)
+             (not (mm-long-lines-p 76))))))
+ 
  (provide 'mm-decode)
  
  ;;; arch-tag: 4f35d360-56b8-4030-9388-3ed82d359b9b




reply via email to

[Prev in Thread] Current Thread [Next in Thread]