* lisp/gnus.el (gnus-version-number): Update to 6.10.017.
[elisp/gnus.git-] / lisp / mm-decode.el
index 9d0a44b..48b0496 100644 (file)
 
 ;;; Code:
 
+(require 'drums)
+(require 'mailcap)
+(require 'mm-bodies)
+
+(defvar mm-inline-media-tests
+  '(("image/jpeg" mm-inline-image (featurep 'jpeg))
+    ("image/png" mm-inline-image (featurep 'png))
+    ("image/gif" mm-inline-image (featurep 'gif))
+    ("image/tiff" mm-inline-image (featurep 'tiff))
+    ("image/xbm" mm-inline-image (eq (device-type) 'x))
+    ("image/xpm" mm-inline-image (featurep 'xpm))
+    ("text/plain" mm-inline-text t)
+    ("text/html" mm-inline-text (featurep 'w3))
+    )
+  "Alist of media types/test that say whether the media types can be displayed inline.")
+
+(defvar mm-user-display-methods
+  '(("image/.*" . inline)
+    ("text/.*" . inline)))
+
+(defvar mm-user-automatic-display
+  '("text/plain" "image/gif"))
+
+(defvar mm-tmp-directory "/tmp/"
+  "Where mm will store its temporary files.")
+
+;;; Internal variables.
+
+(defvar mm-dissection-list nil)
+
+(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)
+      (save-restriction
+       (drums-narrow-to-header)
+       (when (and (or no-strict-mime
+                      (mail-fetch-field "mime-version"))
+                  (setq ct (mail-fetch-field "content-type")))
+         (setq ctl (drums-parse-content-type ct))
+         (setq cte (mail-fetch-field "content-transfer-encoding"))))
+      (when ctl
+       (setq type (split-string (car ctl) "/"))
+       (setq subtype (cadr type)
+             type (pop type))
+       (cond
+        ((equal type "multipart")
+         (mm-dissect-multipart ctl))
+        (t
+         (mm-dissect-singlepart ctl (and cte (intern cte))
+                                no-strict-mime)))))))
+
+(defun mm-dissect-singlepart (ctl cte &optional force)
+  (when (or force
+           (not (equal "text/plain" (car ctl))))
+    (let ((res (list (list (mm-copy-to-buffer) ctl cte nil))))
+      (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))
+  (let ((boundary (concat "\n--" (drums-content-type-get ctl 'boundary)))
+       start parts end)
+    (while (search-forward boundary nil t)
+      (forward-line -1)
+      (when start
+       (save-excursion
+         (save-restriction
+           (narrow-to-region start (point))
+           (setq parts (nconc (mm-dissect-buffer t) parts)))))
+      (forward-line 2)
+      (setq start (point)))
+    (nreverse parts)))
+
+(defun mm-copy-to-buffer ()
+  "Copy the contents of the current buffer to a fresh buffer."
+  (save-excursion
+    (let ((obuf (current-buffer))
+         beg)
+      (goto-char (point-min))
+      (search-forward "\n\n" nil t)
+      (setq beg (point))
+      (set-buffer (generate-new-buffer " *mm*"))
+      (insert-buffer-substring obuf beg)
+      (current-buffer))))
+
+(defun mm-display-part (handle)
+  "Display the MIME part represented by HANDLE."
+  (save-excursion
+    (mailcap-parse-mailcaps)
+    (if (nth 3 handle)
+       (mm-remove-part handle)
+      (let* ((type (caadr 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))
+         (mm-display-external handle (or user-method method)))))))
+
+(defun mm-display-external (handle method)
+  "Display HANDLE using METHOD."
+  (mm-with-unibyte-buffer
+    (insert-buffer-substring (car handle))
+    (mm-decode-content-transfer-encoding (nth 2 handle))
+    (if (functionp method)
+       (let ((cur (current-buffer)))
+         (switch-to-buffer (generate-new-buffer "*mm*"))
+         (insert-buffer-substring cur)
+         (funcall method)
+         (setcar (nthcdr 3 handle) (current-buffer)))
+      (let* ((file (make-temp-name (expand-file-name "emm." mm-tmp-directory)))
+            process)
+       (write-region (point-min) (point-max)
+                     file nil 'nomesg nil 'no-conversion)
+       (setq process
+             (start-process "*display*" nil shell-file-name
+                            "-c" (format method file)))
+       (setcar (nthcdr 3 handle) (cons file process))
+       (message "Displaying %s..." (format method file))))))
+
+(defun mm-remove-part (handle)
+  "Remove the displayed MIME part represented by HANDLE."
+  (let ((object (nth 3 handle)))
+    (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 ()
+         (kill-process (cdr object))
+       (error nil)))
+     ((bufferp object)
+      (when (buffer-live-p object)
+       (kill-buffer object))))
+    (setcar (nthcdr 3 handle) nil)))
+
+(defun mm-display-inline (handle)
+  (let* ((type (caadr handle))
+        (function (cadr (assoc type mm-inline-media-tests))))
+    (funcall function handle)))
+        
+(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."
+  (mm-remove-part handle)
+  (when (buffer-live-p (car handle))
+    (kill-buffer (car 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 displaying various formats inline
+;;;
+
+(defun mm-inline-image (handle)
+  (let ((type (cadr (split-string (caadr handle) "/")))
+       image)
+    (mm-with-unibyte-buffer
+      (insert-buffer-substring (car handle))
+      (mm-decode-content-transfer-encoding (nth 2 handle))
+      (setq image (make-image-specifier
+                  (vector (intern type) :data (buffer-string)))))
+    (let ((annot (make-annotation image nil 'text)))
+      (set-extent-property annot 'mm t)
+      (set-extent-property annot 'duplicable t)
+      (setcar (nthcdr 3 handle) annot))))
+
+(defun mm-inline-text (handle)
+  (let ((type (cadr (split-string (caadr handle) "/")))
+       text buffer-read-only)
+    (mm-with-unibyte-buffer
+      (insert-buffer-substring (car handle))
+      (mm-decode-content-transfer-encoding (nth 2 handle))
+      (setq text (buffer-string)))
+    (cond
+     ((equal type "plain")
+      (let ((b (point)))
+       (insert text)
+       (setcar
+        (nthcdr 3 handle)
+        `(lambda ()
+           (let (buffer-read-only)
+             (delete-region ,(set-marker (make-marker) b)
+                            ,(set-marker (make-marker) (point)))))))))))
+                                   
+    
 (provide 'mm-decode)
 
 ;; mm-decode.el ends here