;;; mml.el --- A package for parsing and validating MML documents
;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-;; 2007, 2008 Free Software Foundation, Inc.
+;; 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; This file is part of GNU Emacs.
(require 'mm-decode)
(require 'mml-sec)
(eval-when-compile (require 'cl))
+(eval-when-compile
+ (when (featurep 'xemacs)
+ (require 'easy-mmode))) ; for `define-minor-mode'
(autoload 'message-make-message-id "message")
-(autoload 'gnus-setup-posting-charset "gnus-msg")
+(declare-function gnus-setup-posting-charset "gnus-msg" (group))
(autoload 'gnus-make-local-hook "gnus-util")
+(autoload 'gnus-completing-read "gnus-util")
(autoload 'message-fetch-field "message")
(autoload 'message-mark-active-p "message")
(autoload 'message-info "message")
,dispositions))))
:group 'message)
-(defcustom mml-insert-mime-headers-always nil
+(defcustom mml-insert-mime-headers-always t
"If non-nil, always put Content-Type: text/plain at top of empty parts.
It is necessary to work against a bug in certain clients."
- :version "22.1"
+ :version "24.1"
:type 'boolean
:group 'message)
(let* (secure-mode
(taginfo (mml-read-tag))
(keyfile (cdr (assq 'keyfile taginfo)))
- (certfile (cdr (assq 'certfile taginfo)))
+ (certfiles (delq nil (mapcar (lambda (tag)
+ (if (eq (car-safe tag) 'certfile)
+ (cdr tag)))
+ taginfo)))
(recipients (cdr (assq 'recipients taginfo)))
(sender (cdr (assq 'sender taginfo)))
(location (cdr (assq 'tag-location taginfo)))
,@tags
,(if keyfile "keyfile")
,keyfile
- ,(if certfile "certfile")
- ,certfile
+ ,@(apply #'append
+ (mapcar (lambda (certfile)
+ (list "certfile" certfile))
+ certfiles))
,(if recipients "recipients")
,recipients
,(if sender "sender")
(skip-chars-forward "= \t\n")
(setq val (buffer-substring-no-properties
(point) (progn (forward-sexp 1) (point))))
- (when (string-match "^\"\\(.*\\)\"$" val)
- (setq val (match-string 1 val)))
+ (when (string-match "\\`\"" val)
+ (setq val (read val))) ;; inverse of prin1 in mml-insert-tag
(push (cons (intern elem) val) contents)
(skip-chars-forward " \t\n"))
(goto-char (match-end 0))
;; `m-g-d-t' will be bound to "message/rfc822"
;; when encoding an article to be forwarded.
(mml-generate-default-type "text/plain"))
- (mml-to-mime))
+ (mml-to-mime)
+ ;; Update handle so mml-compute-boundary can
+ ;; detect collisions with the nested parts.
+ (setcdr (assoc 'contents cont) (buffer-string)))
(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))))
(unless raw
(setq charset (mm-encode-body charset))))
(insert contents)))))
- (setq encoding (mm-encode-buffer type)
+ (if (setq encoding (cdr (assq 'encoding cont)))
+ (setq encoding (intern (downcase encoding))))
+ (setq encoding (mm-encode-buffer type encoding)
coded (mm-string-as-multibyte (buffer-string))))
(mml-insert-mime-headers cont type charset encoding nil)
(insert "\n" coded))))
(defun mml-compute-boundary-1 (cont)
(let (filename)
(cond
- ((eq (car cont) 'part)
+ ((member (car cont) '(part mml))
(with-temp-buffer
(cond
((cdr (assq 'buffer cont))
;; Determine type and stuff.
(unless (stringp (car handle))
(unless (setq textp (equal (mm-handle-media-supertype handle) "text"))
- (save-excursion
- (set-buffer (setq buffer (mml-generate-new-buffer " *mml*")))
- (mm-insert-part handle 'no-cache)
- (if (setq mmlp (equal (mm-handle-media-type handle)
- "message/rfc822"))
- (mime-to-mml)))))
+ (with-current-buffer (setq buffer (mml-generate-new-buffer " *mml*"))
+ (if (eq (mail-content-type-get (mm-handle-type handle) 'charset)
+ 'gnus-decoded)
+ ;; A part that mm-uu dissected from a non-MIME message
+ ;; because of `gnus-article-emulate-mime'.
+ (progn
+ (mm-enable-multibyte)
+ (insert-buffer-substring (mm-handle-buffer handle)))
+ (mm-insert-part handle 'no-cache)
+ (if (setq mmlp (equal (mm-handle-media-type handle)
+ "message/rfc822"))
+ (mime-to-mml))))))
(if mmlp
(mml-insert-mml-markup handle nil t t)
(unless (and no-markup
,@(if (featurep 'xemacs) '(t)
'(:help "Display the EasyPG manual"))]))
-(defvar mml-mode nil
- "Minor mode for editing MML.")
-
-(defun mml-mode (&optional arg)
+(define-minor-mode mml-mode
"Minor mode for editing MML.
MML is the MIME Meta Language, a minor mode for composing MIME articles.
See Info node `(emacs-mime)Composing'.
\\{mml-mode-map}"
- (interactive "P")
- (when (set (make-local-variable 'mml-mode)
- (if (null arg) (not mml-mode)
- (> (prefix-numeric-value arg) 0)))
- (add-minor-mode 'mml-mode " MML" mml-mode-map)
+ :lighter " MML" :keymap mml-mode-map
+ (when mml-mode
(easy-menu-add mml-menu mml-mode-map)
(when (boundp 'dnd-protocol-alist)
(set (make-local-variable 'dnd-protocol-alist)
- (append mml-dnd-protocol-alist dnd-protocol-alist)))
- (run-hooks 'mml-mode-hook)))
+ (append mml-dnd-protocol-alist dnd-protocol-alist)))))
;;;
;;; Helper functions for reading MIME stuff from the minibuffer and
(error "Permission denied: %s" file))
file))
+(declare-function mailcap-parse-mimetypes "mailcap" (&optional path force))
+(declare-function mailcap-mime-types "mailcap" ())
+
(defun mml-minibuffer-read-type (name &optional default)
+ (require 'mailcap)
(mailcap-parse-mimetypes)
(let* ((default (or default
(mm-default-file-encoding name)
;; looks like, and offer text/plain if it looks
;; like text/plain.
"application/octet-stream"))
- (string (completing-read
- (format "Content type (default %s): " default)
- (mapcar 'list (mailcap-mime-types)))))
+ (string (gnus-completing-read
+ "Content type"
+ (mailcap-mime-types)
+ nil nil nil default)))
(if (not (equal string ""))
string
default)))
(defun mml-minibuffer-read-disposition (type &optional default filename)
(unless default
(setq default (mml-content-disposition type filename)))
- (let ((disposition (completing-read
- (format "Disposition (default %s): " default)
- '(("attachment") ("inline") (""))
- nil t nil nil default)))
+ (let ((disposition (gnus-completing-read
+ "Disposition"
+ '("attachment" "inline")
+ t nil nil default)))
(if (not (equal disposition ""))
disposition
default)))
(description (mml-minibuffer-read-description))
(disposition (mml-minibuffer-read-disposition type nil file)))
(list file type description disposition)))
- (save-excursion
- (unless (message-in-body-p) (goto-char (point-max)))
+ ;; Don't move point if this command is invoked inside the message header.
+ (let ((head (unless (message-in-body-p)
+ (prog1
+ (point)
+ (goto-char (point-max))))))
(mml-insert-empty-tag 'part
'type type
- 'filename file
+ ;; icicles redefines read-file-name and returns a
+ ;; string w/ text properties :-/
+ 'filename (mm-substring-no-properties file)
'disposition (or disposition "attachment")
- 'description description)))
+ 'description description)
+ (when head
+ (unless (prog1
+ (pos-visible-in-window-p)
+ (goto-char head))
+ (message "The file \"%s\" has been attached at the end of the message"
+ (file-name-nondirectory file))))))
(defun mml-dnd-attach-file (uri action)
"Attach a drag and drop file.
(description (mml-minibuffer-read-description))
(disposition (mml-minibuffer-read-disposition type nil)))
(list buffer type description disposition)))
- (save-excursion
- (unless (message-in-body-p) (goto-char (point-max)))
+ ;; Don't move point if this command is invoked inside the message header.
+ (let ((head (unless (message-in-body-p)
+ (prog1
+ (point)
+ (goto-char (point-max))))))
(mml-insert-empty-tag 'part 'type type 'buffer buffer
'disposition disposition
- 'description description)))
+ 'description description)
+ (when head
+ (unless (prog1
+ (pos-visible-in-window-p)
+ (goto-char head))
+ (message
+ "The buffer \"%s\" has been attached at the end of the message"
+ buffer)))))
(defun mml-attach-external (file &optional type description)
"Attach an external file into the buffer.
(type (mml-minibuffer-read-type file))
(description (mml-minibuffer-read-description)))
(list file type description)))
- (save-excursion
- (unless (message-in-body-p) (goto-char (point-max)))
+ ;; Don't move point if this command is invoked inside the message header.
+ (let ((head (unless (message-in-body-p)
+ (prog1
+ (point)
+ (goto-char (point-max))))))
(mml-insert-empty-tag 'external 'type type 'name file
- 'disposition "attachment" 'description description)))
+ 'disposition "attachment" 'description description)
+ (when head
+ (unless (prog1
+ (pos-visible-in-window-p)
+ (goto-char head))
+ (message "The file \"%s\" has been attached at the end of the message"
+ (file-name-nondirectory file))))))
(defun mml-insert-multipart (&optional type)
- (interactive (list (completing-read "Multipart type (default mixed): "
- '(("mixed") ("alternative") ("digest") ("parallel")
- ("signed") ("encrypted"))
- nil nil "mixed")))
+ (interactive (if (message-in-body-p)
+ (list (gnus-completing-read "Multipart type"
+ '("mixed" "alternative"
+ "digest" "parallel"
+ "signed" "encrypted")
+ nil "mixed"))
+ (error "Use this command in the message body")))
(or type
(setq type "mixed"))
(mml-insert-empty-tag "multipart" 'type type)
(forward-line -1))
(defun mml-insert-part (&optional type)
- (interactive
- (list (mml-minibuffer-read-type "")))
- (mml-insert-tag 'part 'type type 'disposition "inline")
- (forward-line -1))
+ (interactive (if (message-in-body-p)
+ (list (mml-minibuffer-read-type ""))
+ (error "Use this command in the message body")))
+ (mml-insert-tag 'part 'type type 'disposition "inline"))
(declare-function message-subscribed-p "message" ())
(declare-function message-make-mail-followup-to "message"
(setq mml-preview-buffer (generate-new-buffer
(concat (if raw "*Raw MIME preview of "
"*MIME preview of ") (buffer-name))))
+ (require 'gnus-msg) ; for gnus-setup-posting-charset
(save-excursion
(let* ((buf (current-buffer))
(message-options message-options)
(provide 'mml)
-;; arch-tag: 583c96cf-1ffe-451b-a5e5-4733ae9ddd12
;;; mml.el ends here