;;; mm-decode.el --- Functions for decoding MIME things
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
-;; Free Software Foundation, Inc.
+
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;; 2005, 2006 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
(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"))
+(defvar gnus-current-window-configuration)
+
(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)Customization")
+ :link '(custom-manual "(emacs-mime)Display Customization")
:version "21.1"
:group 'mail
:group 'news
(defgroup mime-security ()
"MIME security in mail and news articles."
- :link '(custom-manual "(emacs-mime)Customization")
+ :link '(custom-manual "(emacs-mime)Display Customization")
:group 'mail
:group 'news
:group 'multimedia)
(defcustom mm-text-html-renderer
(cond ((locate-library "w3") 'w3)
- ((locate-library "w3m") 'w3m)
- ((executable-find "w3m") 'w3m-standalone)
+ ((executable-find "w3m") (if (locate-library "w3m")
+ 'w3m
+ 'w3m-standalone))
((executable-find "links") 'links)
((executable-find "lynx") 'lynx)
(t 'html2text))
`lynx' : use lynx;
`html2text' : use html2text;
nil : use external viewer."
+ :version "22.1"
:type '(choice (const w3)
(const w3m)
(const w3m-standalone)
(const html2text)
(const nil)
(function))
- :version "21.3"
:group 'mime-display)
(defvar mm-inline-text-html-renderer 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."
+ :version "22.1"
:type 'boolean
:group 'mime-display)
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."
+ :version "22.1"
: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."
+ :version "22.1"
:type 'boolean
:group 'mime-display)
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"
+ :version "22.1"
:type '(choice (const :tag "Always" t)
(const :tag "Never" nil)
(const :tag "Ask" ask))
("text/richtext" mm-inline-text identity)
("text/x-patch" mm-display-patch-inline
(lambda (handle)
- (locate-library "diff-mode")))
+ ;; If the diff-mode.el package is installed, the function is
+ ;; autoloaded. Checking (locate-library "diff-mode") would be trying
+ ;; to cater to broken installations. OTOH checking the function
+ ;; makes it possible to install another package which provides an
+ ;; alternative implementation of diff-mode. --Stef
+ (fboundp 'diff-mode)))
("application/emacs-lisp" mm-display-elisp-inline identity)
("application/x-emacs-lisp" mm-display-elisp-inline identity)
+ ("text/dns" mm-display-dns-inline identity)
("text/html"
mm-inline-text-html
(lambda (handle)
"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
"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)
+ :version "22.1"
+ :type '(repeat regexp)
:group 'mime-display)
(defcustom mm-automatic-display
- '("text/plain" "text/enriched" "text/richtext" "text/html"
+ '("text/plain" "text/enriched" "text/richtext" "text/html" "text/x-verbatim"
"text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
- "message/rfc822" "text/x-patch" "application/pgp-signature"
+ "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"
"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
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
(defcustom mm-attachment-file-modes 384
"Set the mode bits of saved attachments to this integer."
+ :version "22.1"
:type 'integer
:group 'mime-display)
(defcustom mm-external-terminal-program "xterm"
"The program to start an external terminal."
+ :version "22.1"
:type 'string
:group 'mime-display)
"Option of verifying signed parts.
`never', not verify; `always', always verify;
`known', only verify known protocols. Otherwise, ask user."
+ :version "22.1"
:type '(choice (item always)
(item never)
(item :tag "only known protocols" known)
"Option of decrypting encrypted parts.
`never', not decrypt; `always', always decrypt;
`known', only decrypt known protocols. Otherwise, ask user."
+ :version "22.1"
:type '(choice (item always)
(item never)
(item :tag "only known protocols" known)
(defvar mm-viewer-completion-map
(let ((map (make-sparse-keymap 'mm-viewer-completion-map)))
(set-keymap-parent map minibuffer-local-completion-map)
+ ;; Should we bind other key to minibuffer-complete-word?
+ (define-key map " " 'self-insert-command)
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)
+ ;; Should we bind other key to minibuffer-complete-word?
+ (define-key map " " 'self-insert-command)
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)
-
;;; The functions.
(defun mm-alist-to-plist (alist)
(message "Destroying external MIME viewers")
(mm-destroy-parts mm-postponed-undisplay-list)))
-(defun mm-dissect-buffer (&optional no-strict-mime loose-mime)
+(defun mm-dissect-buffer (&optional no-strict-mime loose-mime from)
"Dissect the current buffer and return a list of MIME handles."
(save-excursion
- (let (ct ctl type subtype cte cd description id result from)
+ (let (ct ctl type subtype cte cd description id result)
(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))
+ 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")
- from (mail-fetch-field "from")
id (mail-fetch-field "content-id"))
+ (unless 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
;; 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)
(not (string-match "/" (car ctl))))
(mm-dissect-singlepart
(list mm-dissect-default-type)
- (and cte (intern (downcase (mail-header-remove-whitespace
- (mail-header-remove-comments
- cte)))))
+ (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)
;; 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
+ ;; the mm-handle API so we simply store the multipart buffer
;; 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))))
+ (cons (car ctl) (mm-dissect-multipart ctl from))))
(t
(mm-possibly-verify-or-decrypt
(mm-dissect-singlepart
ctl
- (and cte (intern (downcase (mail-header-remove-whitespace
- (mail-header-remove-comments
- cte)))))
+ (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
(mm-make-handle
(mm-copy-to-buffer) ctl cte nil cdl description nil id)))
-(defun mm-dissect-multipart (ctl)
+(defun mm-dissect-multipart (ctl from)
(goto-char (point-min))
(let* ((boundary (concat "\n--" (mail-content-type-get ctl 'boundary)))
(close-delimiter (concat (regexp-quote boundary) "--[ \t]*$"))
(save-excursion
(save-restriction
(narrow-to-region start (point))
- (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
+ (setq parts (nconc (list (mm-dissect-buffer t nil from)) parts)))))
(end-of-line 2)
(or (looking-at boundary)
(forward-line 1))
(save-excursion
(save-restriction
(narrow-to-region start end)
- (setq parts (nconc (list (mm-dissect-buffer t)) parts)))))
+ (setq parts (nconc (list (mm-dissect-buffer t nil from)) parts)))))
(mm-possibly-verify-or-decrypt (nreverse parts) ctl)))
(defun mm-copy-to-buffer ()
(goto-char (point-min))
(search-forward-regexp "^\n" nil t)
(setq beg (point))
- (set-buffer (generate-new-buffer " *mm*"))
+ (set-buffer
+ ;; Preserve the data's unibyteness (for url-insert-file-contents).
+ (let ((default-enable-multibyte-characters (mm-multibyte-p)))
+ (generate-new-buffer " *mm*")))
(insert-buffer-substring obuf beg)
(current-buffer))))
(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)
(mm-mailcap-command
method file (mm-handle-type handle)))
(if (buffer-live-p buffer)
- (save-excursion
- (set-buffer buffer)
+ (with-current-buffer buffer
(buffer-string))))
(progn
(ignore-errors (delete-file file))
(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
- (start-process "*display*"
- (setq buffer
- (generate-new-buffer " *mm*"))
- shell-file-name
- shell-command-switch command)
+ (progn
+ (start-process "*display*"
+ (setq buffer
+ (generate-new-buffer " *mm*"))
+ shell-file-name
+ shell-command-switch command)
+ (set-process-sentinel
+ (get-buffer-process buffer)
+ (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))
(string= total "'%s'")
(string= total "\"%s\""))
(setq uses-stdin nil)
- (push (mm-quote-arg
+ (push (shell-quote-argument
(gnus-map-function mm-path-name-rewrite-functions file)) out))
((string= total "%t")
- (push (mm-quote-arg (car type-list)) out))
+ (push (shell-quote-argument (car type-list)) out))
(t
- (push (mm-quote-arg (or (cdr (assq (intern sub) ctl)) "")) out))))
+ (push (shell-quote-argument (or (cdr (assq (intern sub) ctl)) "")) out))))
(push (substring method beg (length method)) out)
(when uses-stdin
(push "<" out)
- (push (mm-quote-arg
+ (push (shell-quote-argument
(gnus-map-function mm-path-name-rewrite-functions file))
out))
(mapconcat 'identity (nreverse out) "")))
(defun mm-insert-part (handle)
"Insert the contents of HANDLE in the current buffer."
- (let ((cur (current-buffer)))
- (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))))))))
+ (save-excursion
+ (insert
+ (cond ((eq (mail-content-type-get (mm-handle-type handle) 'charset)
+ 'gnus-decoded)
+ (with-current-buffer (mm-handle-buffer handle)
+ (buffer-string)))
+ ((mm-multibyte-p)
+ (mm-string-as-multibyte (mm-get-part handle)))
+ (t
+ (mm-get-part handle))))))
(defun mm-file-name-delete-whitespace (file-name)
"Remove all whitespace characters from FILE-NAME."
(setq filename (gnus-replace-in-string filename "[<>|]" ""))
(gnus-replace-in-string filename "^[.-]+" ""))
-(defun mm-save-part (handle)
- "Write HANDLE to a file."
+(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))
(setq filename (gnus-map-function mm-file-name-rewrite-functions
(file-name-nondirectory filename))))
(setq file
- (read-file-name "Save MIME part to: "
- (or mm-default-directory default-directory)
- nil nil (or filename name "")))
+ (mm-with-multibyte
+ (read-file-name (or prompt "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? "
;; 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)
(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)))
(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)))
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)))
(provide 'mm-decode)
+;; arch-tag: 4f35d360-56b8-4030-9388-3ed82d359b9b
;;; mm-decode.el ends here