;; Author: UMEDA Masanobu <umerin@mse.kyutech.ac.jp>
;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
-;; Maintainer: MORIOKA Tomohiko <morioka@jaist.ac.jp>
;; Created: 1994/08/21 renamed from mime.el
;; Renamed: 1997/2/21 from tm-edit.el
;; Keywords: MIME, multimedia, multilingual, mail, news
;;; Code:
-(require 'emu)
(require 'sendmail)
(require 'mail-utils)
(require 'mel)
(require 'mime-view)
(require 'signature)
(require 'alist)
+(require 'invisible)
;;; @ version
;;;
-(defconst mime-edit-version-string
- `,(concat (car mime-user-interface-version) " "
- (mapconcat #'number-to-string
- (cddr mime-user-interface-version) ".")
- " - \"" (cadr mime-user-interface-version) "\""))
+(eval-and-compile
+ (defconst mime-edit-version
+ (eval-when-compile
+ (concat
+ (mime-product-name mime-user-interface-product) " "
+ (mapconcat #'number-to-string
+ (mime-product-version mime-user-interface-product) ".")
+ " - \"" (mime-product-code-name mime-user-interface-product) "\"")))
+ )
;;; @ variables
("plain"
;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
)
- ("enriched"
- ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
- )
- ("x-latex"
- ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
- )
- ("html"
- ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
- )
- ("css" ;; rfc2318
- ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
- )
- ("xml" ;; rfc2376
- ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
- )
+ ("enriched")
+ ("html")
+ ("css") ; rfc2318
+ ("xml") ; rfc2376
+ ("x-latex")
("x-rot13-47-48")
)
("message"
"*Alist of content-type, subtype, parameters and its values.")
(defcustom mime-file-types
- '(("\\.txt$"
+ '(
+
+ ;; Programming languages
+
+ ("\\.cc$"
+ "application" "octet-stream" (("type" . "C++"))
+ "7bit"
+ "attachment" (("filename" . file))
+ )
+
+ ("\\.el$"
+ "application" "octet-stream" (("type" . "emacs-lisp"))
+ "7bit"
+ "attachment" (("filename" . file))
+ )
+
+ ("\\.lsp$"
+ "application" "octet-stream" (("type" . "common-lisp"))
+ "7bit"
+ "attachment" (("filename" . file))
+ )
+
+ ("\\.pl$"
+ "application" "octet-stream" (("type" . "perl"))
+ "7bit"
+ "attachment" (("filename" . file))
+ )
+
+ ;; Text or translated text
+
+ ("\\.txt$"
"text" "plain" nil
nil
"inline" (("filename" . file))
)
- ("\\.pln$"
+
+ ;; .rc : procmail modules pm-xxxx.rc
+ ;; *rc : other resource files
+
+ ("\\.\\(rc\\|lst\\|log\\|sql\\|mak\\)$\\|\\..*rc$"
"text" "plain" nil
nil
- "inline" (("filename" . file))
+ "attachment" (("filename" . file))
)
- ("\\.rtf$"
- "text" "richtext" nil
- nil
- nil nil)
+
("\\.html$"
"text" "html" nil
nil
nil nil)
+
+ ("\\.diff$\\|\\.patch$"
+ "application" "octet-stream" (("type" . "patch"))
+ nil
+ "attachment" (("filename" . file))
+ )
+
+ ("\\.signature"
+ "text" "plain" nil nil nil nil)
+
+
+ ;; Octect binary text
+
+ ("\\.doc$" ;MS Word
+ "application" "winword" nil
+ "base64"
+ "attachment" (("filename" . file))
+ )
+
+ ("\\.pln$"
+ "text" "plain" nil
+ nil
+ "inline" (("filename" . file))
+ )
("\\.ps$"
"application" "postscript" nil
"quoted-printable"
"attachment" (("filename" . file))
)
+
+ ;; Pure binary
+
("\\.jpg$"
"image" "jpeg" nil
"base64"
"base64"
"attachment" (("filename" . file))
)
- ("\\.el$"
- "application" "octet-stream" (("type" . "emacs-lisp"))
- "7bit"
- "attachment" (("filename" . file))
- )
- ("\\.lsp$"
- "application" "octet-stream" (("type" . "common-lisp"))
- "7bit"
- "attachment" (("filename" . file))
- )
("\\.tar\\.gz$"
"application" "octet-stream" (("type" . "tar+gzip"))
"base64"
"base64"
"attachment" (("filename" . file))
)
- ("\\.diff$"
- "application" "octet-stream" (("type" . "patch"))
- nil
- "attachment" (("filename" . file))
- )
- ("\\.patch$"
- "application" "octet-stream" (("type" . "patch"))
- nil
- "attachment" (("filename" . file))
- )
- ("\\.signature"
- "text" "plain" nil nil nil nil)
+
+ ;; Rest
+
(".*"
"application" "octet-stream" nil
nil
,@(cons
'(const nil)
(mapcar (lambda (cell)
- (list 'item (car cell))
+ (list 'item cell)
)
- mime-file-encoding-method-alist)))
+ (mime-encoding-list))))
;; disposition-type
(choice :tag "Disposition-Type"
(item nil)
(iso-2022-jp 7 "base64")
(iso-2022-kr 7 "base64")
(euc-kr 8 "base64")
- (cn-gb2312 8 "base64")
+ (cn-gb 8 "base64")
(gb2312 8 "base64")
(cn-big5 8 "base64")
(big5 8 "base64")
(make-variable-buffer-local 'mime-transfer-level-string)
+;;; @@ about content transfer encoding
+
+(defvar mime-content-transfer-encoding-priority-list
+ '(nil "8bit" "binary"))
+
;;; @@ about message inserting
;;;
:type 'list)
(defconst mime-edit-split-ignored-field-regexp
- "\\(^Content-\\|^Subject:\\|^Mime-Version:\\|Message-Id:\\)")
+ "\\(^Content-\\|^Subject:\\|^Mime-Version:\\|^Message-Id:\\)")
(defcustom mime-edit-split-blind-field-regexp
"\\(^[BDFbdf]cc:\\|^cc:[ \t]*$\\)"
"*If non-nil, insert User-Agent header field.")
(defvar mime-edit-user-agent-value
- (concat (car mime-user-interface-version)
+ (concat (mime-product-name mime-user-interface-product)
"/"
(mapconcat #'number-to-string
- (cddr mime-user-interface-version) ".")
+ (mime-product-version mime-user-interface-product) ".")
" ("
- (cadr mime-user-interface-version)
+ (mime-product-code-name mime-user-interface-product)
") "
- (car mime-library-version)
+ (mime-product-name mime-library-product)
"/"
(mapconcat #'number-to-string
- (cddr mime-library-version) ".")
+ (mime-product-version mime-library-product) ".")
" ("
- (cadr mime-library-version)
+ (mime-product-code-name mime-library-product)
") "
(if (featurep 'xemacs)
- (concat "XEmacs"
+ (concat (if (featurep 'mule) "MULE")
+ " XEmacs"
(if (string-match "\\s +\\\"" emacs-version)
(concat "/"
(substring emacs-version 0
(match-beginning 0))
- " (" xemacs-codename ")")
- " (" emacs-version ")")
- (if (featurep 'mule) " MULE"))
+ (if (and (boundp 'xemacs-betaname)
+ ;; It does not exist in XEmacs
+ ;; versions prior to 20.3.
+ xemacs-betaname)
+ (concat " " xemacs-betaname)
+ "")
+ " (" xemacs-codename ") ("
+ system-configuration ")")
+ " (" emacs-version ")"))
(let ((ver (if (string-match "\\.[0-9]+$" emacs-version)
(substring emacs-version 0 (match-beginning 0))
emacs-version)))
(if (featurep 'mule)
(if (boundp 'enable-multibyte-characters)
(concat "Emacs/" ver
+ " (" system-configuration ")"
(if enable-multibyte-characters
(concat " MULE/" mule-version)
" (with unibyte mode)")
))))
(concat "MULE/" mule-version
" (based on Emacs " ver ")"))
- ver))))
+ (concat "Emacs/" ver " (" system-configuration ")")))))
"Body of User-Agent field.
If variable `mime-edit-insert-user-agent-field' is not nil, it is
inserted into message header.")
Tspecials means any character that matches with it in header must be quoted.")
(defconst mime-edit-mime-version-value
- (concat "1.0 (generated by " mime-edit-version-string ")")
+ (eval-when-compile
+ (concat "1.0 (generated by " mime-edit-version ")"))
"MIME version number.")
(defconst mime-edit-mime-version-field-for-message/partial
- (concat "MIME-Version: 1.0 (split by " mime-edit-version-string ")\n")
+ (eval-when-compile
+ (concat "MIME-Version: 1.0 (split by " mime-edit-version ")\n"))
"MIME version field for message/partial.")
)
"MIME-edit menubar entry.")
-(cond (running-xemacs
+(cond ((featurep 'xemacs)
;; modified by Pekka Marjola <pema@iki.fi>
;; 1995/9/5 (c.f. [tm-en:69])
(defun mime-edit-define-menu-for-xemacs ()
)))
-(cond (running-xemacs
+(cond ((featurep 'xemacs)
(add-minor-mode 'mime-edit-mode-flag
'((" MIME-Edit " mime-transfer-level-string))
mime-edit-mode-map
(force-mode-line-update)
;; Define menu for XEmacs.
- (if running-xemacs
+ (if (featurep 'xemacs)
(mime-edit-define-menu-for-xemacs)
)
(mime-edit-translate-buffer)))
;; Restore previous state.
(setq mime-edit-mode-flag nil)
- (if (and running-xemacs
+ (if (and (featurep 'xemacs)
(featurep 'menubar))
(delete-menu-item (list mime-edit-menu-title))
)
(insert "\n")
(forward-char -1)
))
- (if (and (member (cadr ret) '("enriched" "richtext"))
- (fboundp 'enriched-mode)
- )
+ (if (and (member (cadr ret) '("enriched"))
+ (fboundp 'enriched-mode))
(enriched-mode t)
(if (boundp 'enriched-mode)
(enriched-mode -1)
(let ((encoding
(completing-read
"What transfer encoding: "
- mime-file-encoding-method-alist nil t nil)))
+ (mime-encoding-alist) nil t nil)))
(mime-edit-insert-tag "audio" "basic" nil)
(mime-edit-define-encoding encoding)
(save-restriction
(setq encoding
(completing-read
"What transfer encoding: "
- mime-file-encoding-method-alist nil t default)
+ (mime-encoding-alist) nil t default)
)
""))
encoding))
(if encoding
(insert "Content-Transfer-Encoding: " encoding "\n"))
(eword-encode-header)
- ))
- t)))
+ )
+ (cons (and contype
+ (downcase contype))
+ (and encoding
+ (downcase encoding))))
+ )))
(defun mime-edit-translate-region (beg end &optional boundary multipart)
(or boundary
(t
;; It's a multipart message.
(goto-char (point-min))
- (and (mime-edit-translate-single-part-tag boundary)
- (while (mime-edit-translate-single-part-tag boundary "\n")))
- ;; Define Content-Type as "multipart/mixed".
- (setq contype
- (concat "multipart/mixed;\n boundary=\"" boundary "\""))
- ;; Content-Transfer-Encoding must be "7bit".
- ;; The following encoding can be `nil', but is
- ;; specified as is since there is no way that a user
- ;; specifies it.
- (setq encoding "7bit")
- ;; Insert the trailer.
- (goto-char (point-max))
- (insert "\n--" boundary "--\n")
- ))
- (list contype encoding boundary nparts)
- ))))
+ (let ((prio mime-content-transfer-encoding-priority-list)
+ part-info nprio)
+ (when (setq part-info
+ (mime-edit-translate-single-part-tag boundary))
+ (and (setq nprio (member (cdr part-info) prio))
+ (setq prio nprio))
+ (while (setq part-info
+ (mime-edit-translate-single-part-tag boundary "\n"))
+ (and (setq nprio (member (cdr part-info) prio))
+ (setq prio nprio))))
+ ;; Define Content-Type as "multipart/mixed".
+ (setq contype
+ (concat "multipart/mixed;\n boundary=\"" boundary "\""))
+ (setq encoding (car prio))
+ ;; Insert the trailer.
+ (goto-char (point-max))
+ (insert "\n--" boundary "--\n")
+ )))
+ (list contype encoding boundary nparts)
+ ))))
(defun mime-edit-normalize-body ()
"Normalize the body part by inserting appropriate message tags."
(while (re-search-forward "\\(\\=\\|[^\r]\\)\n" nil t)
(replace-match "\\1\r\n"))))
(goto-char beg)
- (mime-encode-region beg (mime-edit-content-end) encoding)
+ (mime-encode-region beg (mime-edit-content-end)
+ (or encoding "7bit"))
(mime-edit-define-encoding encoding)
))
(goto-char (mime-edit-content-end))
(let* ((mime-edit-draft-file-name
(or (buffer-file-name)
(make-temp-name
- (expand-file-name "mime-draft" mime-temp-directory))))
+ (expand-file-name "mime-draft" temporary-file-directory))))
(separator mail-header-separator)
(id (concat "\""
(replace-space-with-underline (current-time-string))
"\\):")
"Regexp for deleted header fields when `mime-edit-again' is called.")
-(defun mime-edit-decode-buffer (not-decode-text)
+(defsubst eliminate-top-spaces (string)
+ "Eliminate top sequence of space or tab in STRING."
+ (if (string-match "^[ \t]+" string)
+ (substring string (match-end 0))
+ string))
+
+(defun mime-edit-decode-multipart-in-buffer (content-type not-decode-text)
+ (let* ((subtype (mime-content-type-subtype content-type))
+ (boundary (mime-content-type-parameter content-type "boundary"))
+ (boundary-pat (concat "\n--" (regexp-quote boundary) "[ \t]*\n")))
+ (re-search-forward boundary-pat nil t)
+ (let ((bb (match-beginning 0)) eb tag)
+ (setq tag (format "\n--<<%s>>-{\n" subtype))
+ (goto-char bb)
+ (insert tag)
+ (setq bb (+ bb (length tag)))
+ (re-search-forward
+ (concat "\n--" (regexp-quote boundary) "--[ \t]*\n")
+ nil t)
+ (setq eb (match-beginning 0))
+ (replace-match (format "--}-<<%s>>\n" subtype))
+ (save-restriction
+ (narrow-to-region bb eb)
+ (goto-char (point-min))
+ (while (re-search-forward boundary-pat nil t)
+ (let ((beg (match-beginning 0))
+ end)
+ (delete-region beg (match-end 0))
+ (save-excursion
+ (if (re-search-forward boundary-pat nil t)
+ (setq end (match-beginning 0))
+ (setq end (point-max))
+ )
+ (save-restriction
+ (narrow-to-region beg end)
+ (mime-edit-decode-message-in-buffer
+ (if (eq subtype 'digest)
+ (eval-when-compile
+ (make-mime-content-type 'message 'rfc822))
+ )
+ not-decode-text)
+ (goto-char (point-max))
+ ))))
+ ))
+ (goto-char (point-min))
+ (or (= (point-min) 1)
+ (delete-region (point-min)
+ (if (search-forward "\n\n" nil t)
+ (match-end 0)
+ (point-min)
+ )))
+ ))
+
+(defun mime-edit-decode-single-part-in-buffer (content-type not-decode-text)
+ (let* ((type (mime-content-type-primary-type content-type))
+ (subtype (mime-content-type-subtype content-type))
+ (ctype (format "%s/%s" type subtype))
+ charset
+ (pstr (let ((bytes (+ 14 (length ctype))))
+ (mapconcat (function
+ (lambda (attr)
+ (if (string= (car attr) "charset")
+ (progn
+ (setq charset (cdr attr))
+ "")
+ (let* ((str (concat (car attr)
+ "=" (cdr attr)))
+ (bs (length str)))
+ (setq bytes (+ bytes bs 2))
+ (if (< bytes 76)
+ (concat "; " str)
+ (setq bytes (+ bs 1))
+ (concat ";\n " str)
+ )
+ ))))
+ (mime-content-type-parameters content-type) "")))
+ encoding
+ encoded
+ (limit (save-excursion
+ (if (search-forward "\n\n" nil t)
+ (1- (point))))))
+ (save-excursion
+ (if (re-search-forward
+ "^Content-Transfer-Encoding:" limit t)
+ (let ((beg (match-beginning 0))
+ (hbeg (match-end 0))
+ (end (std11-field-end)))
+ (setq encoding
+ (downcase
+ (eliminate-top-spaces
+ (std11-unfold-string
+ (buffer-substring hbeg end)))))
+ (if (or charset (eq type 'text))
+ (progn
+ (delete-region beg (1+ end))
+ (goto-char (point-min))
+ (if (search-forward "\n\n" nil t)
+ (progn
+ (mime-decode-region
+ (match-end 0)(point-max) encoding)
+ (setq encoded t
+ encoding nil)
+ )))))))
+ (if (or encoded (not not-decode-text))
+ (decode-mime-charset-region (point-min)(point-max)
+ (or charset default-mime-charset))
+ )
+ (let ((he (if (re-search-forward "^$" nil t)
+ (match-end 0)
+ (point-min)
+ )))
+ (if (and (eq type 'text)
+ (eq subtype 'x-rot13-47-48))
+ (mule-caesar-region he (point-max))
+ )
+ (if (= (point-min) 1)
+ (progn
+ (goto-char he)
+ (insert
+ (concat "\n"
+ (mime-create-tag
+ (format "%s/%s%s" type subtype pstr)
+ encoding)))
+ )
+ (delete-region (point-min) he)
+ (insert
+ (mime-create-tag (format "%s/%s%s" type subtype pstr)
+ encoding))
+ ))
+ ))
+
+;;;###autoload
+(defun mime-edit-decode-message-in-buffer (&optional default-content-type
+ not-decode-text)
(save-excursion
(goto-char (point-min))
- (let ((ctl (mime-read-Content-Type)))
+ (let ((ctl (or (mime-read-Content-Type)
+ default-content-type)))
(if ctl
- (let ((type (mime-content-type-primary-type ctl))
- (stype (mime-content-type-subtype ctl))
- (params (mime-content-type-parameters ctl)))
+ (let ((type (mime-content-type-primary-type ctl)))
(cond
- ((and (eq type 'application)(eq stype 'pgp-signature))
+ ((and (eq type 'application)
+ (eq (mime-content-type-subtype ctl) 'pgp-signature))
(delete-region (point-min)(point-max))
)
((eq type 'multipart)
- (let* ((boundary (cdr (assoc "boundary" params)))
- (boundary-pat
- (concat "\n--" (regexp-quote boundary) "[ \t]*\n"))
- )
- (re-search-forward boundary-pat nil t)
- (let ((bb (match-beginning 0)) eb tag)
- (setq tag (format "\n--<<%s>>-{\n" stype))
- (goto-char bb)
- (insert tag)
- (setq bb (+ bb (length tag)))
- (re-search-forward
- (concat "\n--" (regexp-quote boundary) "--[ \t]*\n")
- nil t)
- (setq eb (match-beginning 0))
- (replace-match (format "--}-<<%s>>\n" stype))
- (save-restriction
- (narrow-to-region bb eb)
- (goto-char (point-min))
- (while (re-search-forward boundary-pat nil t)
- (let ((beg (match-beginning 0))
- end)
- (delete-region beg (match-end 0))
- (save-excursion
- (if (re-search-forward boundary-pat nil t)
- (setq end (match-beginning 0))
- (setq end (point-max))
- )
- (save-restriction
- (narrow-to-region beg end)
- (mime-edit-decode-buffer not-decode-text)
- (goto-char (point-max))
- ))))
- ))
- (goto-char (point-min))
- (or (= (point-min) 1)
- (delete-region (point-min)
- (if (search-forward "\n\n" nil t)
- (match-end 0)
- (point-min)
- )))
- ))
+ (mime-edit-decode-multipart-in-buffer ctl not-decode-text)
+ )
(t
- (let* ((ctype (format "%s/%s" type stype))
- charset
- (pstr
- (let ((bytes (+ 14 (length ctype))))
- (mapconcat (function
- (lambda (attr)
- (if (string= (car attr) "charset")
- (progn
- (setq charset (cdr attr))
- "")
- (let* ((str
- (concat (car attr)
- "=" (cdr attr))
- )
- (bs (length str))
- )
- (setq bytes (+ bytes bs 2))
- (if (< bytes 76)
- (concat "; " str)
- (setq bytes (+ bs 1))
- (concat ";\n " str)
- )
- ))))
- params "")))
- encoding
- encoded)
- (save-excursion
- (if (re-search-forward
- "Content-Transfer-Encoding:" nil t)
- (let ((beg (match-beginning 0))
- (hbeg (match-end 0))
- (end (std11-field-end)))
- (setq encoding
- (eliminate-top-spaces
- (std11-unfold-string
- (buffer-substring hbeg end))))
- (if (or charset (eq type 'text))
- (progn
- (delete-region beg (1+ end))
- (goto-char (point-min))
- (if (search-forward "\n\n" nil t)
- (progn
- (mime-decode-region
- (match-end 0)(point-max) encoding)
- (setq encoded t
- encoding nil)
- )))))))
- (if (or encoded (not not-decode-text))
- (decode-mime-charset-region
- (point-min)(point-max)
- (or charset default-mime-charset))
- )
- (let ((he
- (if (re-search-forward "^$" nil t)
- (match-end 0)
- (point-min)
- )))
- (if (= (point-min) 1)
- (progn
- (goto-char he)
- (insert
- (concat "\n"
- (mime-create-tag
- (format "%s/%s%s" type stype pstr)
- encoding)))
- )
- (delete-region (point-min) he)
- (insert
- (mime-create-tag
- (format "%s/%s%s" type stype pstr)
- encoding))
- ))
- ))))
+ (mime-edit-decode-single-part-in-buffer ctl not-decode-text)
+ )))
(or not-decode-text
(decode-mime-charset-region (point-min) (point-max)
- default-mime-charset)
- )
- ))))
+ default-mime-charset))
+ )
+ (save-restriction
+ (std11-narrow-to-header)
+ (goto-char (point-min))
+ (while (re-search-forward mime-edit-again-ignored-field-regexp nil t)
+ (delete-region (match-beginning 0) (1+ (std11-field-end)))
+ ))
+ (mime-decode-header-in-buffer (not not-decode-text))
+ )))
+;;;###autoload
(defun mime-edit-again (&optional not-decode-text no-separator not-turn-on)
"Convert current buffer to MIME-Edit buffer and turn on MIME-Edit mode.
Content-Type and Content-Transfer-Encoding header fields will be
nil t)
(replace-match "\n\n")
)
- (mime-edit-decode-buffer not-decode-text)
+ (mime-edit-decode-message-in-buffer nil not-decode-text)
(goto-char (point-min))
- (save-restriction
- (std11-narrow-to-header)
- (goto-char (point-min))
- (while (re-search-forward mime-edit-again-ignored-field-regexp nil t)
- (delete-region (match-beginning 0) (1+ (std11-field-end)))
- ))
(or no-separator
(and (re-search-forward "^$")
(replace-match mail-header-separator)