;;; mml.el --- A package for parsing and validating MML documents
-;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; This file is part of GNU Emacs.
(eval-and-compile
(autoload 'message-make-message-id "message")
(autoload 'gnus-setup-posting-charset "gnus-msg")
+ (autoload 'gnus-add-minor-mode "gnus-ems")
(autoload 'message-fetch-field "message")
(autoload 'message-posting-charset "message"))
(defvar mml-generate-multipart-alist nil
"*Alist of multipart generation functions.
Each entry has the form (NAME . FUNCTION), where
-NAME is a string containing the name of the part (without the
+NAME is a string containing the name of the part (without the
leading \"/multipart/\"),
FUNCTION is a Lisp function which is called to generate the part.
(defvar mml-buffer-list nil)
-(defun mml-generate-new-buffer (name)
+(defun mml-generate-new-buffer (name)
(let ((buf (generate-new-buffer name)))
(push buf mml-buffer-list)
buf))
(defun mml-parse ()
"Parse the current buffer as an MML document."
- (goto-char (point-min))
- (let ((table (syntax-table)))
- (unwind-protect
- (progn
- (set-syntax-table mml-syntax-table)
- (mml-parse-1))
- (set-syntax-table table))))
+ (save-excursion
+ (goto-char (point-min))
+ (let ((table (syntax-table)))
+ (unwind-protect
+ (progn
+ (set-syntax-table mml-syntax-table)
+ (mml-parse-1))
+ (set-syntax-table table)))))
(defun mml-parse-1 ()
"Parse the current buffer as an MML document."
(setq raw (cdr (assq 'raw tag))
point (point)
contents (mml-read-part (eq 'mml (car tag)))
- charsets (if raw nil
+ charsets (if raw nil
(mm-find-mime-charset-region point (point))))
(when (and (not raw) (memq nil charsets))
(if (or (memq 'unknown-encoding mml-confirmation-set)
- (y-or-n-p
- "Message contains characters with unknown encoding. Really send?"))
- (if (setq use-ascii
+ (prog1 (y-or-n-p
+ "\
+Message contains characters with unknown encoding. Really send?")
+ (set (make-local-variable 'mml-confirmation-set)
+ (push 'unknown-encoding mml-confirmation-set))))
+ (if (setq use-ascii
(or (memq 'use-ascii mml-confirmation-set)
(y-or-n-p "Use ASCII as charset?")))
(setq charsets (delq nil charsets))
(when (and warn
(not (memq 'multipart mml-confirmation-set))
(not
- (y-or-n-p
- (format
- "Warning: Your message contains more than %d parts. Really send? "
- (length nstruct)))))
+ (prog1 (y-or-n-p
+ (format
+ "\
+A message part needs to be split into %d charset parts. Really send? "
+ (length nstruct)))
+ (set (make-local-variable 'mml-confirmation-set)
+ (push 'multipart mml-confirmation-set)))))
(error "Edit your message to use only one charset"))
(setq struct (nconc nstruct struct)))))))
(unless (eobp)
(forward-line 1))
(nreverse struct)))
-(defun mml-parse-singlepart-with-multiple-charsets
+(defun mml-parse-singlepart-with-multiple-charsets
(orig-tag beg end &optional use-ascii)
(save-excursion
(save-restriction
(defun mml-read-tag ()
"Read a tag and return the contents."
- (let (contents name elem val)
+ (let ((orig-point (point))
+ contents name elem val)
(forward-char 2)
(setq name (buffer-substring-no-properties
(point) (progn (forward-sexp 1) (point))))
(goto-char (match-end 0))
;; Don't skip the leading space.
;;(skip-chars-forward " \t\n")
+ ;; Put the tag location into the returned contents
+ (setq contents (append (list (cons 'tag-location orig-point)) contents))
(cons (intern name) (nreverse contents))))
(defun mml-read-part (&optional mml)
(if (re-search-forward "<#\\(/\\)?mml." nil t)
(setq count (+ count (if (match-beginning 1) -1 1)))
(goto-char (point-max))))
- (buffer-substring-no-properties beg (if (> count 0)
+ (buffer-substring-no-properties beg (if (> count 0)
(point)
(match-beginning 0))))
(if (re-search-forward
(buffer-string)))))
(defun mml-generate-mime-1 (cont)
- (let ((mm-use-ultra-safe-encoding
+ (let ((mm-use-ultra-safe-encoding
(or mm-use-ultra-safe-encoding (assq 'sign cont))))
(save-restriction
(narrow-to-region (point) (point))
(if (and (not raw)
(member (car (split-string type "/")) '("text" "message")))
(with-temp-buffer
+ (setq charset (mm-charset-to-coding-system
+ (cdr (assq 'charset cont))))
+ (when (eq charset 'ascii)
+ (setq charset nil))
(cond
((cdr (assq 'buffer cont))
(insert-buffer-substring (cdr (assq 'buffer cont))))
((and (setq filename (cdr (assq 'filename cont)))
(not (equal (cdr (assq 'nofile cont)) "yes")))
- (mm-insert-file-contents filename))
+ (let ((coding-system-for-read charset))
+ (mm-insert-file-contents filename)))
((eq 'mml (car cont))
(insert (cdr (assq 'contents cont))))
(t
;; Remove quotes from quoted tags.
(goto-char (point-min))
(while (re-search-forward
- "<#!+/?\\(part\\|multipart\\|external\\|mml\\)" nil t)
+ "<#!+/?\\(part\\|multipart\\|external\\|mml\\)"
+ nil t)
(delete-region (+ (match-beginning 0) 2)
(+ (match-beginning 0) 3))))))
- (cond
+ (cond
((eq (car cont) 'mml)
(let ((mml-boundary (funcall mml-boundary-function
(incf mml-multipart-number)))
(let ((mm-7bit-chars (concat mm-7bit-chars "\x1b")))
;; ignore 0x1b, it is part of iso-2022-jp
(setq encoding (mm-body-7-or-8))))
- (t
- (setq charset (mm-encode-body))
+ (t
+ (setq charset (mm-encode-body charset))
(setq encoding (mm-body-encoding
charset (cdr (assq 'encoding cont))))))
(setq coded (buffer-string)))
(insert "Content-Type: message/external-body")
(let ((parameters (mml-parameter-string
cont '(expiration size permission)))
- (name (cdr (assq 'name cont))))
+ (name (cdr (assq 'name cont)))
+ (url (cdr (assq 'url cont))))
(when name
(setq name (mml-parse-file-name name))
(if (stringp name)
(concat "access-type="
(if (member (nth 0 name) '("ftp@" "anonymous@"))
"anon-ftp"
- "ftp")))))
+ "ftp")))))
+ (when url
+ (mml-insert-parameter
+ (mail-header-encode-parameter "url" url)
+ "access-type=url"))
(when parameters
(mml-insert-parameter-string
cont '(expiration size permission))))
(let ((mml-boundary (mml-compute-boundary cont)))
(insert (format "Content-Type: multipart/%s; boundary=\"%s\"\n"
type mml-boundary))
- ;; Skip `multipart' and `type' elements.
- (setq cont (cddr cont))
- (while cont
- (insert "\n--" mml-boundary "\n")
- (mml-generate-mime-1 (pop cont)))
+ (let ((cont cont) part)
+ (while (setq part (pop cont))
+ ;; Skip `multipart' and attributes.
+ (when (and (consp part) (consp (cdr part)))
+ (insert "\n--" mml-boundary "\n")
+ (mml-generate-mime-1 part))))
(insert "\n--" mml-boundary "--\n")))))
(t
(error "Invalid element: %S" cont)))
(mail-header-encode-parameter
(symbol-name type) value))))))
-(defvar ange-ftp-name-format)
-(defvar efs-path-regexp)
+(eval-when-compile
+ (defvar ange-ftp-name-format)
+ (defvar efs-path-regexp))
(defun mml-parse-file-name (path)
(if (if (boundp 'efs-path-regexp)
(string-match efs-path-regexp path)
;;; Transforming MIME to MML
;;;
-(defun mime-to-mml ()
- "Translate the current buffer (which should be a message) into MML."
+(defun mime-to-mml (&optional handles)
+ "Translate the current buffer (which should be a message) into MML.
+If HANDLES is non-nil, use it instead reparsing the buffer."
;; First decode the head.
(save-restriction
(message-narrow-to-head)
(mail-decode-encoded-word-region (point-min) (point-max)))
- (let ((handles (mm-dissect-buffer t)))
- (goto-char (point-min))
- (search-forward "\n\n" nil t)
- (delete-region (point) (point-max))
- (if (stringp (car handles))
- (mml-insert-mime handles)
- (mml-insert-mime handles t))
- (mm-destroy-parts handles))
+ (unless handles
+ (setq handles (mm-dissect-buffer t)))
+ (goto-char (point-min))
+ (search-forward "\n\n" nil t)
+ (delete-region (point) (point-max))
+ (if (stringp (car handles))
+ (mml-insert-mime handles)
+ (mml-insert-mime handles t))
+ (mm-destroy-parts handles)
(save-restriction
(message-narrow-to-head)
;; Remove them, they are confusing.
(save-excursion
(set-buffer (setq buffer (mml-generate-new-buffer " *mml*")))
(mm-insert-part handle)
- (if (setq mmlp (equal (mm-handle-media-type handle)
+ (if (setq mmlp (equal (mm-handle-media-type handle)
"message/rfc822"))
(mime-to-mml)))))
(if mmlp
(equal (mm-handle-media-type handle) "text/plain"))
(mml-insert-mml-markup handle buffer textp)))
(cond
- (mmlp
+ (mmlp
(insert-buffer buffer)
(goto-char (point-max))
(insert "<#/mml>\n"))
(mapcar 'mml-insert-mime (cdr handle))
(insert "<#/multipart>\n"))
(textp
- (let ((text (mm-get-part handle))
- (charset (mail-content-type-get
+ (let ((charset (mail-content-type-get
(mm-handle-type handle) 'charset)))
- (insert (mm-decode-string text charset)))
+ (if (eq charset 'gnus-decoded)
+ (mm-insert-part handle)
+ (insert (mm-decode-string (mm-get-part handle) charset))))
(goto-char (point-max)))
(t
(insert "<#/part>\n")))))
(insert "<#part type=" (mm-handle-media-type handle)))
(dolist (elem (append (cdr (mm-handle-type handle))
(cdr (mm-handle-disposition handle))))
- (insert " " (symbol-name (car elem)) "=\"" (cdr elem) "\""))
+ (unless (symbolp (cdr elem))
+ (insert " " (symbol-name (car elem)) "=\"" (cdr elem) "\"")))
(when (mm-handle-disposition handle)
(insert " disposition=" (car (mm-handle-disposition handle))))
(when buffer
(define-key map "s" sign)
(define-key map "c" encrypt)
;;(define-key map "n" 'mml-narrow-to-part)
- (define-key main "\M-m" map)
+ ;; `M-m' conflicts with `back-to-indentation'.
+ ;; (define-key main "\M-m" map)
+ (define-key main "\C-c\C-m" map)
main))
(easy-menu-define
(interactive "P")
(let ((buf (current-buffer))
(message-options message-options)
- (message-posting-charset (or (gnus-setup-posting-charset
+ (message-posting-charset (or (gnus-setup-posting-charset
(save-restriction
(message-narrow-to-headers-or-head)
(message-fetch-field "Newsgroups")))