X-Git-Url: http://cgit.sxemacs.org/?p=gnus;a=blobdiff_plain;f=lisp%2Fmailcap.el;h=d8881104af8acb07c5739a91965361eca80b52bb;hp=ba2bfb7f5aca95b65906f73fcbd908dc3de03b15;hb=d20dda5b1d899018825f1f3e7099f22455d83afe;hpb=6012f8d1ee972cfa2dcde9bb1495abbc752ad44a diff --git a/lisp/mailcap.el b/lisp/mailcap.el index ba2bfb7f5..d8881104a 100644 --- a/lisp/mailcap.el +++ b/lisp/mailcap.el @@ -1,34 +1,61 @@ -;;; mailcap.el --- Functions for displaying MIME parts -;; Copyright (C) 1998,99 Free Software Foundation, Inc. +;;; mailcap.el --- MIME media types configuration + +;; Copyright (C) 1998-2015 Free Software Foundation, Inc. ;; Author: William M. Perry ;; Lars Magne Ingebrigtsen -;; Keywords: news, mail +;; Keywords: news, mail, multimedia ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; 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. +;; along with GNU Emacs. If not, see . ;;; Commentary: +;; Provides configuration of MIME media types from directly from Lisp +;; and via the usual mailcap mechanism (RFC 1524). Deals with +;; mime.types similarly. + ;;; Code: (eval-when-compile (require 'cl)) -(require 'mail-parse) -(require 'mm-util) +(autoload 'mail-header-parse-content-type "mail-parse") + +;; `mm-delete-duplicates' is an alias for `delete-dups' in Emacs 22. +(defalias 'mailcap-delete-duplicates + (if (fboundp 'delete-dups) + 'delete-dups + (autoload 'mm-delete-duplicates "mm-util") + 'mm-delete-duplicates)) + +;; `mailcap-replace-in-string' is an alias like `gnus-replace-in-string'. +(eval-and-compile + (cond + ((fboundp 'replace-regexp-in-string) + (defun mailcap-replace-in-string (string regexp newtext &optional literal) + "Replace all matches for REGEXP with NEWTEXT in STRING. +If LITERAL is non-nil, insert NEWTEXT literally. Return a new +string containing the replacements. +This is a compatibility function for different Emacsen." + (replace-regexp-in-string regexp newtext string nil literal))) + ((fboundp 'replace-in-string) + (defalias 'mailcap-replace-in-string 'replace-in-string)))) + +(defgroup mailcap nil + "Definition of viewers for MIME types." + :version "21.1" + :group 'mime) (defvar mailcap-parse-args-syntax-table (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) @@ -37,10 +64,36 @@ (modify-syntax-entry ?{ "(" table) (modify-syntax-entry ?} ")" table) table) - "A syntax table for parsing sgml attributes.") + "A syntax table for parsing SGML attributes.") + +(eval-and-compile + (when (featurep 'xemacs) + (condition-case nil + (require 'lpr) + (error nil)))) +(defvar mailcap-print-command + (mapconcat 'identity + (cons (if (boundp 'lpr-command) + lpr-command + "lpr") + (when (boundp 'lpr-switches) + (if (stringp lpr-switches) + (list lpr-switches) + lpr-switches))) + " ") + "Shell command (including switches) used to print PostScript files.") + +;; Postpone using defcustom for this as it's so big and we essentially +;; have to have two copies of the data around then. Perhaps just +;; customize the Lisp viewers and rely on the normal configuration +;; files for the rest? -- fx (defvar mailcap-mime-data - '(("application" + `(("application" + ("vnd\\.ms-excel" + (viewer . "gnumeric %s") + (test . (getenv "DISPLAY")) + (type . "application/vnd.ms-excel")) ("x-x509-ca-cert" (viewer . ssl-view-site-cert) (test . (fboundp 'ssl-view-site-cert)) @@ -52,23 +105,24 @@ ("octet-stream" (viewer . mailcap-save-binary-file) (non-viewer . t) - (type ."application/octet-stream")) + (type . "application/octet-stream")) ("dvi" - (viewer . "open %s") - (type . "application/dvi") - (test . (eq (mm-device-type) 'ns))) - ("dvi" - (viewer . "xdvi %s") - (test . (eq (mm-device-type) 'x)) + (viewer . "xdvi -safer %s") + (test . (eq window-system 'x)) ("needsx11") - (type . "application/dvi")) + (type . "application/dvi") + ("print" . "dvips -qRP %s")) ("dvi" (viewer . "dvitty %s") (test . (not (getenv "DISPLAY"))) - (type . "application/dvi")) + (type . "application/dvi") + ("print" . "dvips -qRP %s")) ("emacs-lisp" (viewer . mailcap-maybe-eval) (type . "application/emacs-lisp")) + ("x-emacs-lisp" + (viewer . mailcap-maybe-eval) + (type . "application/x-emacs-lisp")) ("x-tar" (viewer . mailcap-save-binary-file) (non-viewer . t) @@ -99,22 +153,61 @@ (type . "application/zip") ("copiousoutput")) ("pdf" - (viewer . "acroread %s") - (type . "application/pdf")) + (viewer . pdf-view-mode) + (type . "application/pdf") + (test . (and (fboundp 'pdf-view-mode) + (eq window-system 'x)))) + ("pdf" + (viewer . doc-view-mode) + (type . "application/pdf") + (test . (and (fboundp 'doc-view-mode) + (eq window-system 'x)))) + ("pdf" + (viewer . "gv -safer %s") + (type . "application/pdf") + (test . window-system) + ("print" . ,(concat "pdf2ps %s - | " mailcap-print-command))) + ("pdf" + (viewer . "gpdf %s") + (type . "application/pdf") + ("print" . ,(concat "pdftops %s - | " mailcap-print-command)) + (test . (eq window-system 'x))) + ("pdf" + (viewer . "xpdf %s") + (type . "application/pdf") + ("print" . ,(concat "pdftops %s - | " mailcap-print-command)) + (test . (eq window-system 'x))) + ("pdf" + (viewer . ,(concat "pdftotext %s -")) + (type . "application/pdf") + ("print" . ,(concat "pdftops %s - | " mailcap-print-command)) + ("copiousoutput")) ("postscript" - (viewer . "open %s") - (type . "application/postscript") - (test . (eq (mm-device-type) 'ns))) + (viewer . "gv -safer %s") + (type . "application/postscript") + (test . window-system) + ("print" . ,(concat mailcap-print-command " %s")) + ("needsx11")) ("postscript" (viewer . "ghostview -dSAFER %s") (type . "application/postscript") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) + ("print" . ,(concat mailcap-print-command " %s")) ("needsx11")) ("postscript" (viewer . "ps2ascii %s") (type . "application/postscript") (test . (not (getenv "DISPLAY"))) - ("copiousoutput"))) + ("print" . ,(concat mailcap-print-command " %s")) + ("copiousoutput")) + ("sieve" + (viewer . sieve-mode) + (test . (fboundp 'sieve-mode)) + (type . "application/sieve")) + ("pgp-keys" + (viewer . "gpg --import --interactive --verbose") + (type . "application/pgp-keys") + ("needsterminal"))) ("audio" ("x-mpeg" (viewer . "maplay %s") @@ -132,55 +225,39 @@ (viewer . vm-mode) (test . (fboundp 'vm-mode)) (type . "message/rfc822")) - ("rfc-*822" - (viewer . w3-mode) - (test . (fboundp 'w3-mode)) - (type . "message/rfc822")) ("rfc-*822" (viewer . view-mode) - (test . (fboundp 'view-mode)) - (type . "message/rfc822")) - ("rfc-*822" - (viewer . fundamental-mode) (type . "message/rfc822"))) ("image" ("x-xwd" (viewer . "xwud -in %s") (type . "image/x-xwd") ("compose" . "xwd -frame > %s") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11")) ("x11-dump" (viewer . "xwud -in %s") (type . "image/x-xwd") ("compose" . "xwd -frame > %s") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11")) ("windowdump" (viewer . "xwud -in %s") (type . "image/x-xwd") ("compose" . "xwd -frame > %s") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11")) - (".*" - (viewer . "aopen %s") - (type . "image/*") - (test . (eq (mm-device-type) 'ns))) (".*" (viewer . "display %s") (type . "image/*") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11")) (".*" (viewer . "ee %s") (type . "image/*") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11"))) ("text" - ("plain" - (viewer . w3-mode) - (test . (fboundp 'w3-mode)) - (type . "text/plain")) ("plain" (viewer . view-mode) (test . (fboundp 'view-mode)) @@ -189,18 +266,18 @@ (viewer . fundamental-mode) (type . "text/plain")) ("enriched" - (viewer . enriched-decode-region) + (viewer . enriched-decode) (test . (fboundp 'enriched-decode)) (type . "text/enriched")) - ("html" - (viewer . mm-w3-prepare-buffer) - (test . (fboundp 'w3-prepare-buffer)) - (type . "text/html"))) + ("dns" + (viewer . dns-mode) + (test . (fboundp 'dns-mode)) + (type . "text/dns"))) ("video" ("mpeg" (viewer . "mpeg_play %s") (type . "video/mpeg") - (test . (eq (mm-device-type) 'x)) + (test . (eq window-system 'x)) ("needsx11"))) ("x-world" ("x-vrml" @@ -225,57 +302,43 @@ Which looks like: (\"plain\" . ))) Where is another assoc list of the various information -related to the mailcap RFC. This is keyed on the lowercase +related to the mailcap RFC 1524. This is keyed on the lowercase attribute name (viewer, test, etc). This looks like: - ((viewer . viewerinfo) - (test . testinfo) - (xxxx . \"string\")) + ((viewer . VIEWERINFO) + (test . TESTINFO) + (xxxx . \"STRING\") + FLAG) -Where viewerinfo specifies how the content-type is viewed. Can be +Where VIEWERINFO specifies how the content-type is viewed. Can be a string, in which case it is run through a shell, with appropriate parameters, or a symbol, in which case the symbol is -funcall'd, with the buffer as an argument. +`funcall'ed, with the buffer as an argument. -testinfo is a list of strings, or nil. If nil, it means the -viewer specified is always valid. If it is a list of strings, -these are used to determine whether a viewer passes the 'test' or -not.") +TESTINFO is a test for the viewer's applicability, or nil. If nil, it +means the viewer is always valid. If it is a Lisp function, it is +called with a list of items from any extra fields from the +Content-Type header as argument to return a boolean value for the +validity. Otherwise, if it is a non-function Lisp symbol or list +whose car is a symbol, it is `eval'led to yield the validity. If it +is a string or list of strings, it represents a shell command to run +to return a true or false shell value for the validity.") +(put 'mailcap-mime-data 'risky-local-variable t) -(defvar mailcap-download-directory nil - "*Where downloaded files should go by default.") +(defcustom mailcap-download-directory nil + "*Directory to which `mailcap-save-binary-file' downloads files by default. +nil means your home directory." + :type '(choice (const :tag "Home directory" nil) + directory) + :group 'mailcap) -(defvar mailcap-temporary-directory - (cond ((fboundp 'temp-directory) (temp-directory)) - ((boundp 'temporary-file-directory) temporary-file-directory) - ("/tmp/")) - "*Where temporary files go.") +(defvar mailcap-poor-system-types + '(ms-dos windows-nt) + "Systems that don't have a Unix-like directory hierarchy.") ;;; ;;; Utility functions ;;; -(defun mailcap-generate-unique-filename (&optional fmt) - "Generate a unique filename in mailcap-temporary-directory." - (if (not fmt) - (let ((base (format "mailcap-tmp.%d" (user-real-uid))) - (fname "") - (x 0)) - (setq fname (format "%s%d" base x)) - (while (file-exists-p - (expand-file-name fname mailcap-temporary-directory)) - (setq x (1+ x) - fname (concat base (int-to-string x)))) - (expand-file-name fname mailcap-temporary-directory)) - (let ((base (concat "mm" (int-to-string (user-real-uid)))) - (fname "") - (x 0)) - (setq fname (format fmt (concat base (int-to-string x)))) - (while (file-exists-p - (expand-file-name fname mailcap-temporary-directory)) - (setq x (1+ x) - fname (format fmt (concat base (int-to-string x))))) - (expand-file-name fname mailcap-temporary-directory)))) - (defun mailcap-save-binary-file () (goto-char (point-min)) (unwind-protect @@ -286,11 +349,42 @@ not.") (write-region (point-min) (point-max) file)) (kill-buffer (current-buffer)))) +(defvar mailcap-maybe-eval-warning + "*** WARNING *** + +This MIME part contains untrusted and possibly harmful content. +If you evaluate the Emacs Lisp code contained in it, a lot of nasty +things can happen. Please examine the code very carefully before you +instruct Emacs to evaluate it. You can browse the buffer containing +the code using \\[scroll-other-window]. + +If you are unsure what to do, please answer \"no\"." + "Text of warning message displayed by `mailcap-maybe-eval'. +Make sure that this text consists only of few text lines. Otherwise, +Gnus might fail to display all of it.") + (defun mailcap-maybe-eval () - "Maybe evaluate a buffer of emacs lisp code." - (if (yes-or-no-p "This is emacs-lisp code, evaluate it? ") - (eval-buffer (current-buffer)) - (emacs-lisp-mode))) + "Maybe evaluate a buffer of Emacs Lisp code." + (let ((lisp-buffer (current-buffer))) + (goto-char (point-min)) + (when + (save-window-excursion + (delete-other-windows) + (let ((buffer (get-buffer-create (generate-new-buffer-name + "*Warning*")))) + (unwind-protect + (with-current-buffer buffer + (insert (substitute-command-keys + mailcap-maybe-eval-warning)) + (goto-char (point-min)) + (display-buffer buffer) + (yes-or-no-p "This is potentially dangerous emacs-lisp code, evaluate it? ")) + (kill-buffer buffer)))) + (eval-buffer (current-buffer))) + (when (buffer-live-p lisp-buffer) + (with-current-buffer lisp-buffer + (emacs-lisp-mode))))) + ;;; ;;; The mailcap parser @@ -305,39 +399,40 @@ not.") (defvar mailcap-parsed-p nil) (defun mailcap-parse-mailcaps (&optional path force) - "Parse out all the mailcaps specified in a unix-style path string PATH. -If FORCE, re-parse even if already parsed." + "Parse out all the mailcaps specified in a path string PATH. +Components of PATH are separated by the `path-separator' character +appropriate for this system. If FORCE, re-parse even if already +parsed. If PATH is omitted, use the value of environment variable +MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus +/usr/local/etc/mailcap." (interactive (list nil t)) (when (or (not mailcap-parsed-p) force) (cond (path nil) ((getenv "MAILCAPS") (setq path (getenv "MAILCAPS"))) - ((memq system-type '(ms-dos ms-windows windows-nt)) - (setq path (mapconcat 'expand-file-name - '("~/mail.cap" "~/etc/mail.cap" "~/.mailcap") - ";"))) - (t (setq path (mapconcat 'expand-file-name - '("~/.mailcap" - "/etc/mailcap:/usr/etc/mailcap" - "/usr/local/etc/mailcap") ":")))) + ((memq system-type mailcap-poor-system-types) + (setq path '("~/.mailcap" "~/mail.cap" "~/etc/mail.cap"))) + (t (setq path + ;; This is per RFC 1524, specifically + ;; with /usr before /usr/local. + '("~/.mailcap" "/etc/mailcap" "/usr/etc/mailcap" + "/usr/local/etc/mailcap")))) (let ((fnames (reverse - (split-string - path (if (memq system-type - '(ms-dos ms-windows windows-nt)) - ";" - ":")))) + (if (stringp path) + (split-string path path-separator t) + path))) fname) (while fnames (setq fname (car fnames)) - (if (and (file-exists-p fname) (file-readable-p fname) + (if (and (file-readable-p fname) (file-regular-p fname)) - (mailcap-parse-mailcap (car fnames))) + (mailcap-parse-mailcap fname)) (setq fnames (cdr fnames)))) - (setq mailcap-parsed-p t))) + (setq mailcap-parsed-p t))) (defun mailcap-parse-mailcap (fname) - ;; Parse out the mailcap file specified by FNAME + "Parse out the mailcap file specified by FNAME." (let (major ; The major mime type (image/audio/etc) minor ; The minor mime type (gif, basic, etc) save-pos ; Misc saved positions used in parsing @@ -348,25 +443,24 @@ If FORCE, re-parse even if already parsed." (insert-file-contents fname) (set-syntax-table mailcap-parse-args-syntax-table) (mailcap-replace-regexp "#.*" "") ; Remove all comments + (mailcap-replace-regexp "\\\\[ \t]*\n" " ") ; And collapse spaces (mailcap-replace-regexp "\n+" "\n") ; And blank lines - (mailcap-replace-regexp "\\\\[ \t\n]+" " ") ; And collapse spaces - (mailcap-replace-regexp (concat (regexp-quote "\\") "[ \t]*\n") "") (goto-char (point-max)) (skip-chars-backward " \t\n") (delete-region (point) (point-max)) - (goto-char (point-min)) - (while (not (eobp)) - (skip-chars-forward " \t\n") + (while (not (bobp)) + (skip-chars-backward " \t\n") + (beginning-of-line) (setq save-pos (point) info nil) (skip-chars-forward "^/; \t\n") (downcase-region save-pos (point)) (setq major (buffer-substring save-pos (point))) - (skip-chars-forward " \t\n") + (skip-chars-forward " \t") (setq minor "") (when (eq (char-after) ?/) (forward-char) - (skip-chars-forward " \t\n") + (skip-chars-forward " \t") (setq save-pos (point)) (skip-chars-forward "^; \t\n") (downcase-region save-pos (point)) @@ -375,14 +469,14 @@ If FORCE, re-parse even if already parsed." ((eq ?* (or (char-after save-pos) 0)) ".*") ((= (point) save-pos) ".*") (t (regexp-quote (buffer-substring save-pos (point))))))) - (skip-chars-forward " \t\n") + (skip-chars-forward " \t") ;;; Got the major/minor chunks, now for the viewers/etc ;;; The first item _must_ be a viewer, according to the - ;;; RFC for mailcap files (#1343) + ;;; RFC for mailcap files (#1524) (setq viewer "") - (when (eq (char-after) ?\;) + (when (eq (char-after) ?\;) (forward-char) - (skip-chars-forward " \t\n") + (skip-chars-forward " \t") (setq save-pos (point)) (skip-chars-forward "^;\n") ;; skip \; @@ -401,17 +495,18 @@ If FORCE, re-parse even if already parsed." (setq viewer (buffer-substring save-pos (point))))) (setq save-pos (point)) (end-of-line) - (unless (equal viewer "") + (unless (equal viewer "") (setq info (nconc (list (cons 'viewer viewer) (cons 'type (concat major "/" (if (string= minor ".*") "*" minor)))) (mailcap-parse-mailcap-extras save-pos (point)))) (mailcap-mailcap-entry-passes-test info) - (mailcap-add-mailcap-entry major minor info)))))) + (mailcap-add-mailcap-entry major minor info)) + (beginning-of-line))))) (defun mailcap-parse-mailcap-extras (st nd) - ;; Grab all the extra stuff from a mailcap entry + "Grab all the extra stuff from a mailcap entry." (let ( name ; From name= value ; its value @@ -451,16 +546,23 @@ If FORCE, re-parse even if already parsed." (skip-chars-forward ";")) (setq done t)))) (setq value (buffer-substring val-pos (point)))) - (setq results (cons (cons name value) results)) + ;; `test' as symbol, others like "copiousoutput" and "needsx11" as + ;; strings + (setq results (cons (cons (if (string-equal name "test") + 'test + name) + value) results)) (skip-chars-forward " \";\n\t")) results))) (defun mailcap-mailcap-entry-passes-test (info) - ;; Return t iff a mailcap entry passes its test clause or no test - ;; clause is present. - (let (status ; Call-process-regions return value - (test (assq 'test info)) ; The test clause - ) + "Replace the test clause of INFO itself with a boolean for some cases. +This function supports only `test -n $DISPLAY' and `test -z $DISPLAY', +replaces them with t or nil. As for others or if INFO has a interactive +spec (needsterm, needsterminal, or needsx11) but DISPLAY is not set, +the test clause will be unchanged." + (let ((test (assq 'test info)) ; The test clause + status) (setq status (and test (split-string (cdr test) " "))) (if (and (or (assoc "needsterm" info) (assoc "needsterminal" info) @@ -487,17 +589,17 @@ If FORCE, re-parse even if already parsed." ;;; (defun mailcap-possible-viewers (major minor) - ;; Return a list of possible viewers from MAJOR for minor type MINOR + "Return a list of possible viewers from MAJOR for minor type MINOR." (let ((exact '()) (wildcard '())) (while major (cond ((equal (car (car major)) minor) (setq exact (cons (cdr (car major)) exact))) - ((and minor (string-match (car (car major)) minor)) + ((and minor (string-match (concat "^" (car (car major)) "$") minor)) (setq wildcard (cons (cdr (car major)) wildcard)))) (setq major (cdr major))) - (nconc (nreverse exact) (nreverse wildcard)))) + (nconc exact wildcard))) (defun mailcap-unescape-mime-test (test type-info) (let (save-pos save-chr subst) @@ -522,18 +624,18 @@ If FORCE, re-parse even if already parsed." (setq save-pos (point)) (skip-chars-forward "%") (setq save-chr (char-after (point))) + ;; Escapes: + ;; %s: name of a file for the body data + ;; %t: content-type + ;; %{