- (while (re-search-forward mm-encoded-word-regexp nil t)
- (insert (mm-decode-word
- (prog1
- (match-string 0)
- (delete-region (match-beginning 0) (match-end 0)))))))))
-
-(defun mm-decode-words-string (string)
- "Decode the quoted-printable-encoded STRING and return the results."
- (with-temp-buffer
- (insert string)
- (inline
- (mm-decode-words-region (point-min) (point-max)))
- (buffer-string)))
-
-(defun mm-decode-word (word)
- "Decode WORD and return it if it is an encoded word.
-Return WORD if not."
- (if (not (string-match mm-encoded-word-regexp word))
- word
- (or
- (condition-case nil
- (mm-decode-text
- (match-string 1 word)
- (upcase (match-string 2 word))
- (match-string 3 word))
- (error word))
- word)))
-
-(eval-and-compile
- (if (fboundp 'decode-coding-string)
- (fset 'mm-decode-coding-string 'decode-coding-string)
- (fset 'mm-decode-coding-string (lambda (s a) s))))
-
-(eval-and-compile
- (if (fboundp 'coding-system-list)
- (fset 'mm-coding-system-list 'coding-system-list)
- (fset 'mm-coding-system-list 'ignore)))
-
-(defun mm-decode-text (charset encoding string)
- "Decode STRING as an encoded text.
-Valid ENCODINGs are \"B\" and \"Q\".
-If your Emacs implementation can't decode CHARSET, it returns nil."
- (let ((cs (mm-charset-to-coding-system charset)))
- (when cs
- (mm-decode-coding-string
- (cond
- ((equal "B" encoding)
- (base64-decode string))
- ((equal "Q" encoding)
- (quoted-printable-decode-string
- (nnheader-replace-chars-in-string string ?_ ? )))
- (t (error "Invalid encoding: %s" encoding)))
- cs))))
-
-(defvar mm-charset-coding-system-alist
- (let ((rest
- '((us-ascii . iso-8859-1)
- (gb2312 . cn-gb-2312)
- (iso-2022-jp-2 . iso-2022-7bit-ss2)
- (x-ctext . ctext)))
- (systems (mm-coding-system-list))
- dest)
- (while rest
- (let ((pair (car rest)))
- (unless (memq (car pair) systems)
- (setq dest (cons pair dest))))
- (setq rest (cdr rest)))
- dest)
- "Charset/coding system alist.")
-
-(defun mm-charset-to-coding-system (charset &optional lbt)
- "Return coding-system corresponding to CHARSET.
-CHARSET is a symbol naming a MIME charset.
-If optional argument LBT (`unix', `dos' or `mac') is specified, it is
-used as the line break code type of the coding system."
- (when (stringp charset)
- (setq charset (intern (downcase charset))))
- (setq charset
- (or (cdr (assq charset mm-charset-coding-system-alist))
- charset))
- (when lbt
- (setq charset (intern (format "%s-%s" charset lbt))))
- (cond
- ;; Running in a non-MULE environment.
- ((and (null (mm-coding-system-list))
- (eq charset 'iso-8859-1))
- charset)
- ;; Check to see whether we can handle this charset.
- ((memq charset (mm-coding-system-list))
- charset)
- ;; Nope.
- (t
- nil)))
+ (search-forward "\n\n" nil t)
+ (setq beg (point))
+ (set-buffer (generate-new-buffer " *mm*"))
+ (insert-buffer-substring obuf beg)
+ (current-buffer))))
+
+(defun mm-inlinable-part-p (type)
+ "Say whether TYPE can be displayed inline."
+ (eq (mm-user-method type) 'inline))
+
+(defun mm-display-part (handle &optional no-default)
+ "Display the MIME part represented by HANDLE."
+ (save-excursion
+ (mailcap-parse-mailcaps)
+ (if (mm-handle-displayed-p handle)
+ (mm-remove-part handle)
+ (let* ((type (car (mm-handle-type handle)))
+ (method (mailcap-mime-info type))
+ (user-method (mm-user-method type)))
+ (if (eq user-method 'inline)
+ (progn
+ (forward-line 1)
+ (mm-display-inline handle))
+ (when (or user-method
+ method
+ (not no-default))
+ (mm-display-external
+ handle (or user-method method 'mailcap-save-binary-file))))))))
+
+(defun mm-display-external (handle method)
+ "Display HANDLE using METHOD."
+ (mm-with-unibyte-buffer
+ (insert-buffer-substring (mm-handle-buffer handle))
+ (mm-decode-content-transfer-encoding
+ (mm-handle-encoding handle) (car (mm-handle-type handle)))
+ (if (functionp method)
+ (let ((cur (current-buffer)))
+ (if (eq method 'mailcap-save-binary-file)
+ (set-buffer (generate-new-buffer "*mm*"))
+ (let ((win (get-buffer-window cur t)))
+ (when win
+ (select-window win)))
+ (switch-to-buffer (generate-new-buffer "*mm*")))
+ (buffer-disable-undo)
+ (mm-set-buffer-file-coding-system 'no-conversion)
+ (insert-buffer-substring cur)
+ (message "Viewing with %s" method)
+ (let ((mm (current-buffer)))
+ (unwind-protect
+ (funcall method)
+ (mm-handle-set-undisplayer handle mm))))
+ (let* ((dir (make-temp-name (expand-file-name "emm." mm-tmp-directory)))
+ (filename (mail-content-type-get
+ (mm-handle-disposition handle) 'filename))
+ (needsterm (assoc "needsterm"
+ (mailcap-mime-info
+ (car (mm-handle-type handle)) t)))
+ process file)
+ ;; We create a private sub-directory where we store our files.
+ (make-directory dir)
+ (set-file-modes dir 448)
+ (if filename
+ (setq file (expand-file-name (file-name-nondirectory filename)
+ dir))
+ (setq file (make-temp-name (expand-file-name "mm." dir))))
+ (write-region (point-min) (point-max)
+ file nil 'nomesg nil 'no-conversion)
+ (message "Viewing with %s" method)
+ (unwind-protect
+ (setq process
+ (if needsterm
+ (start-process "*display*" nil
+ "xterm"
+ "-e" shell-file-name "-c"
+ (format method
+ (mm-quote-arg file)))
+ (start-process "*display*" (generate-new-buffer "*mm*")
+ shell-file-name
+ "-c" (format method
+ (mm-quote-arg file)))))
+ (mm-handle-set-undisplayer handle (cons file process)))
+ (message "Displaying %s..." (format method file))))))
+
+(defun mm-remove-parts (handles)
+ "Remove the displayed MIME parts represented by HANDLE."
+ (if (and (listp handles)
+ (bufferp (car handles)))
+ (mm-remove-part handles)
+ (let (handle)
+ (while (setq handle (pop handles))
+ (cond
+ ((stringp handle)
+ )
+ ((and (listp handle)
+ (stringp (car handle)))
+ (mm-remove-parts (cdr handle)))
+ (t
+ (mm-remove-part handle)))))))
+
+(defun mm-destroy-parts (handles)
+ "Remove the displayed MIME parts represented by HANDLE."
+ (if (and (listp handles)
+ (bufferp (car handles)))
+ (mm-destroy-part handles)
+ (let (handle)
+ (while (setq handle (pop handles))
+ (cond
+ ((stringp handle)
+ )
+ ((and (listp handle)
+ (stringp (car handle)))
+ (mm-destroy-parts (cdr handle)))
+ (t
+ (mm-destroy-part handle)))))))
+
+(defun mm-remove-part (handle)
+ "Remove the displayed MIME part represented by HANDLE."
+ (when (listp handle)
+ (let ((object (mm-handle-undisplayer handle)))
+ (condition-case ()
+ (cond
+ ;; Internally displayed part.
+ ((mm-annotationp object)
+ (delete-annotation object))
+ ((or (functionp object)
+ (and (listp object)
+ (eq (car object) 'lambda)))
+ (funcall object))
+ ;; Externally displayed part.
+ ((consp object)
+ (condition-case ()
+ (delete-file (car object))
+ (error nil))
+ (condition-case ()
+ (delete-directory (file-name-directory (car object)))
+ (error nil))
+ (condition-case ()
+ (kill-process (cdr object))
+ (error nil)))
+ ((bufferp object)
+ (when (buffer-live-p object)
+ (kill-buffer object))))
+ (error nil))
+ (mm-handle-set-undisplayer handle nil))))
+
+(defun mm-display-inline (handle)
+ (let* ((type (car (mm-handle-type handle)))
+ (function (cadr (assoc type mm-inline-media-tests))))
+ (funcall function handle)
+ (goto-char (point-min))))
+
+(defun mm-inlinable-p (type)
+ "Say whether TYPE can be displayed inline."
+ (let ((alist mm-inline-media-tests)
+ test)
+ (while alist
+ (when (equal type (caar alist))
+ (setq test (caddar alist)
+ alist nil)
+ (setq test (eval test)))
+ (pop alist))
+ test))
+
+(defun mm-user-method (type)
+ "Return the user-defined method for TYPE."
+ (let ((methods mm-user-display-methods)
+ method result)
+ (while (setq method (pop methods))
+ (when (string-match (car method) type)
+ (when (or (not (eq (cdr method) 'inline))
+ (mm-inlinable-p type))
+ (setq result (cdr method)
+ methods nil))))
+ result))
+
+(defun mm-automatic-display-p (type)
+ "Return the user-defined method for TYPE."
+ (let ((methods mm-user-automatic-display)
+ method result)
+ (while (setq method (pop methods))
+ (when (string-match method type)
+ (setq result t
+ methods nil)))
+ result))
+
+(defun add-mime-display-method (type method)
+ "Make parts of TYPE be displayed with METHOD.
+This overrides entries in the mailcap file."
+ (push (cons type method) mm-user-display-methods))
+
+(defun mm-destroy-part (handle)
+ "Destroy the data structures connected to HANDLE."
+ (when (listp handle)
+ (mm-remove-part handle)
+ (when (buffer-live-p (mm-handle-buffer handle))
+ (kill-buffer (mm-handle-buffer handle)))))
+
+(defun mm-handle-displayed-p (handle)
+ "Say whether HANDLE is displayed or not."
+ (mm-handle-undisplayer handle))
+
+(defun mm-quote-arg (arg)
+ "Return a version of ARG that is safe to evaluate in a shell."
+ (let ((pos 0) new-pos accum)
+ ;; *** bug: we don't handle newline characters properly
+ (while (setq new-pos (string-match "[!`\"$\\& \t{} ]" arg pos))
+ (push (substring arg pos new-pos) accum)
+ (push "\\" accum)
+ (push (list (aref arg new-pos)) accum)
+ (setq pos (1+ new-pos)))
+ (if (= pos 0)
+ arg
+ (apply 'concat (nconc (nreverse accum) (list (substring arg pos)))))))
+
+;;;
+;;; Functions for outputting parts
+;;;
+
+(defun mm-get-part (handle)
+ "Return the contents of HANDLE as a string."
+ (mm-with-unibyte-buffer
+ (insert-buffer-substring (mm-handle-buffer handle))
+ (mm-decode-content-transfer-encoding
+ (mm-handle-encoding handle)
+ (car (mm-handle-type handle)))
+ (buffer-string)))
+
+(defvar mm-default-directory nil)
+
+(defun mm-save-part (handle)
+ "Write HANDLE to a file."
+ (let* ((name (mail-content-type-get (mm-handle-type handle) 'name))
+ (filename (mail-content-type-get
+ (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))
+ (mm-with-unibyte-buffer
+ (insert-buffer-substring (mm-handle-buffer handle))
+ (mm-decode-content-transfer-encoding
+ (mm-handle-encoding handle)
+ (car (mm-handle-type handle)))
+ (when (or (not (file-exists-p file))
+ (yes-or-no-p (format "File %s already exists; overwrite? "
+ file)))
+ (write-region (point-min) (point-max) file)))))
+
+(defun mm-pipe-part (handle)
+ "Pipe HANDLE to a process."
+ (let* ((name (mail-content-type-get (mm-handle-type handle) 'name))
+ (command
+ (read-string "Shell command on MIME part: " mm-last-shell-command)))
+ (mm-with-unibyte-buffer
+ (insert-buffer-substring (mm-handle-buffer handle))
+ (mm-decode-content-transfer-encoding
+ (mm-handle-encoding handle)
+ (car (mm-handle-type handle)))
+ (shell-command-on-region (point-min) (point-max) command nil))))
+
+(defun mm-interactively-view-part (handle)
+ "Display HANDLE using METHOD."
+ (let* ((type (car (mm-handle-type handle)))
+ (methods
+ (mapcar (lambda (i) (list (cdr (assoc 'viewer i))))
+ (mailcap-mime-info type 'all)))
+ (method (completing-read "Viewer: " methods)))
+ (mm-display-external (copy-sequence handle) method)))
+
+(defun mm-preferred-alternative (handles &optional preferred)
+ "Say which of HANDLES are preferred."
+ (let ((prec (if preferred (list preferred) mm-alternative-precedence))
+ p h result type)
+ (while (setq p (pop prec))
+ (setq h handles)
+ (while h
+ (setq type
+ (if (stringp (caar h))
+ (caar h)
+ (car (mm-handle-type (car h)))))
+ (when (and (equal p type)
+ (mm-automatic-display-p type)
+ (or (stringp (caar h))
+ (not (mm-handle-disposition (car h)))
+ (equal (car (mm-handle-disposition (car h)))
+ "inline")))
+ (setq result (car h)
+ h nil
+ prec nil))
+ (pop h)))
+ result))
+
+(defun mm-get-content-id (id)
+ "Return the handle(s) referred to by ID."
+ (cdr (assoc id mm-content-id-alist)))