(install): Don't check for the file names.
[elisp/gnus.git-] / lisp / mml.el
index 6cee33d..589988c 100644 (file)
@@ -27,7 +27,8 @@
 (require 'mm-bodies)
 (require 'mm-encode)
 (require 'mm-decode)
 (require 'mm-bodies)
 (require 'mm-encode)
 (require 'mm-decode)
-(eval-when-compile 'cl)
+(require 'mml-sec)
+(eval-when-compile (require 'cl))
 
 (eval-and-compile
   (autoload 'message-make-message-id "message")
 
 (eval-and-compile
   (autoload 'message-make-message-id "message")
@@ -72,6 +73,20 @@ unknown encoding; `use-ascii': always use ASCII for those characters
 with unknown encoding; `multipart': always send messages with more than
 one charsets.")
 
 with unknown encoding; `multipart': always send messages with more than
 one charsets.")
 
+(defvar mml-generate-default-type "text/plain")
+
+(defvar mml-buffer-list nil)
+
+(defun mml-generate-new-buffer (name) 
+  (let ((buf (generate-new-buffer name)))
+    (push buf mml-buffer-list)
+    buf))
+
+(defun mml-destroy-buffers ()
+  (let (kill-buffer-hook)
+    (mapcar 'kill-buffer mml-buffer-list)
+    (setq mml-buffer-list nil)))
+
 (defun mml-parse ()
   "Parse the current buffer as an MML document."
   (goto-char (point-min))
 (defun mml-parse ()
   "Parse the current buffer as an MML document."
   (goto-char (point-min))
@@ -84,7 +99,7 @@ one charsets.")
 
 (defun mml-parse-1 ()
   "Parse the current buffer as an MML document."
 
 (defun mml-parse-1 ()
   "Parse the current buffer as an MML document."
-  (let (struct tag point contents charsets warn use-ascii)
+  (let (struct tag point contents charsets warn use-ascii no-markup-p raw)
     (while (and (not (eobp))
                (not (looking-at "<#/multipart")))
       (cond
     (while (and (not (eobp))
                (not (looking-at "<#/multipart")))
       (cond
@@ -94,26 +109,36 @@ one charsets.")
        (push (nconc (mml-read-tag) (list (cons 'contents (mml-read-part))))
              struct))
        (t
        (push (nconc (mml-read-tag) (list (cons 'contents (mml-read-part))))
              struct))
        (t
-       (if (looking-at "<#part")
-           (setq tag (mml-read-tag))
+       (if (or (looking-at "<#part") (looking-at "<#mml"))
+           (setq tag (mml-read-tag)
+                 no-markup-p nil
+                 warn nil)
          (setq tag (list 'part '(type . "text/plain"))
          (setq tag (list 'part '(type . "text/plain"))
+               no-markup-p t
                warn t))
                warn t))
-       (setq point (point)
-             contents (mml-read-part)
-             charsets (mm-find-mime-charset-region point (point)))
-       (when (memq nil charsets)
+       (setq raw (cdr (assq 'raw tag))
+             point (point)
+             contents (mml-read-part (eq 'mml (car tag)))
+             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
          (if (or (memq 'unknown-encoding mml-confirmation-set)
                  (y-or-n-p
-                  "Warning: You message contains characters with unknown encoding. Really send?"))
+                  "Message contains characters with unknown encoding.  Really send?"))
              (if (setq use-ascii 
                        (or (memq 'use-ascii mml-confirmation-set)
                            (y-or-n-p "Use ASCII as charset?")))
                  (setq charsets (delq nil charsets))
                (setq warn nil))
            (error "Edit your message to remove those characters")))
              (if (setq use-ascii 
                        (or (memq 'use-ascii mml-confirmation-set)
                            (y-or-n-p "Use ASCII as charset?")))
                  (setq charsets (delq nil charsets))
                (setq warn nil))
            (error "Edit your message to remove those characters")))
-       (if (< (length charsets) 2)
-           (push (nconc tag (list (cons 'contents contents)))
-                 struct)
+       (if (or raw
+               (eq 'mml (car tag))
+               (< (length charsets) 2))
+           (if (or (not no-markup-p)
+                   (string-match "[^ \t\r\n]" contents))
+               ;; Don't create blank parts.
+               (push (nconc tag (list (cons 'contents contents)))
+                     struct))
          (let ((nstruct (mml-parse-singlepart-with-multiple-charsets
                          tag point (point) use-ascii)))
            (when (and warn
          (let ((nstruct (mml-parse-singlepart-with-multiple-charsets
                          tag point (point) use-ascii)))
            (when (and warn
@@ -121,7 +146,7 @@ one charsets.")
                       (not
                        (y-or-n-p
                         (format
                       (not
                        (y-or-n-p
                         (format
-                         "Warning: Your message contains %d parts.  Really send? "
+                         "Warning: Your message contains more than %d parts.  Really send? "
                          (length nstruct)))))
              (error "Edit your message to use only one charset"))
            (setq struct (nconc nstruct struct)))))))
                          (length nstruct)))))
              (error "Edit your message to use only one charset"))
            (setq struct (nconc nstruct struct)))))))
@@ -132,56 +157,60 @@ one charsets.")
 (defun mml-parse-singlepart-with-multiple-charsets 
   (orig-tag beg end &optional use-ascii)
   (save-excursion
 (defun mml-parse-singlepart-with-multiple-charsets 
   (orig-tag beg end &optional use-ascii)
   (save-excursion
-    (narrow-to-region beg end)
-    (goto-char (point-min))
-    (let ((current (or (mm-mime-charset (mm-charset-after))
-                      (and use-ascii 'us-ascii)))
-         charset struct space newline paragraph)
-      (while (not (eobp))
-       (cond
-        ;; The charset remains the same.
-        ((or (eq (setq charset (mm-mime-charset (mm-charset-after))) 
-                 'us-ascii)
-             (and use-ascii (not charset))
-             (eq charset current)))
-        ;; The initial charset was ascii.
-        ((eq current 'us-ascii)
-         (setq current charset
-               space nil
-               newline nil
-               paragraph nil))
-        ;; We have a change in charsets.
-        (t
-         (push (append
-                orig-tag
-                (list (cons 'contents
-                            (buffer-substring-no-properties
-                             beg (or paragraph newline space (point))))))
-               struct)
-         (setq beg (or paragraph newline space (point))
-               current charset
-               space nil
-               newline nil
-               paragraph nil)))
-       ;; Compute places where it might be nice to break the part.
-       (cond
-        ((memq (following-char) '(?  ?\t))
-         (setq space (1+ (point))))
-        ((eq (following-char) ?\n)
-         (setq newline (1+ (point))))
-        ((and (eq (following-char) ?\n)
-              (not (bobp))
-              (eq (char-after (1- (point))) ?\n))
-         (setq paragraph (point))))
-       (forward-char 1))
-      ;; Do the final part.
-      (unless (= beg (point))
-       (push (append orig-tag
-                     (list (cons 'contents
-                                 (buffer-substring-no-properties
-                                  beg (point)))))
-             struct))
-      struct)))
+    (save-restriction
+      (narrow-to-region beg end)
+      (goto-char (point-min))
+      (let ((current (or (mm-mime-charset (mm-charset-after))
+                        (and use-ascii 'us-ascii)))
+           charset struct space newline paragraph)
+       (while (not (eobp))
+         (setq charset (mm-mime-charset (mm-charset-after)))
+         (cond
+          ;; The charset remains the same.
+          ((eq charset 'us-ascii))
+          ((or (and use-ascii (not charset))
+               (eq charset current))
+           (setq space nil
+                 newline nil
+                 paragraph nil))
+          ;; The initial charset was ascii.
+          ((eq current 'us-ascii)
+           (setq current charset
+                 space nil
+                 newline nil
+                 paragraph nil))
+          ;; We have a change in charsets.
+          (t
+           (push (append
+                  orig-tag
+                  (list (cons 'contents
+                              (buffer-substring-no-properties
+                               beg (or paragraph newline space (point))))))
+                 struct)
+           (setq beg (or paragraph newline space (point))
+                 current charset
+                 space nil
+                 newline nil
+                 paragraph nil)))
+         ;; Compute places where it might be nice to break the part.
+         (cond
+          ((memq (following-char) '(?  ?\t))
+           (setq space (1+ (point))))
+          ((and (eq (following-char) ?\n)
+                (not (bobp))
+                (eq (char-after (1- (point))) ?\n))
+           (setq paragraph (point)))
+          ((eq (following-char) ?\n)
+           (setq newline (1+ (point)))))
+         (forward-char 1))
+       ;; Do the final part.
+       (unless (= beg (point))
+         (push (append orig-tag
+                       (list (cons 'contents
+                                   (buffer-substring-no-properties
+                                    beg (point)))))
+               struct))
+       struct))))
 
 (defun mml-read-tag ()
   "Read a tag and return the contents."
 
 (defun mml-read-tag ()
   "Read a tag and return the contents."
@@ -190,7 +219,7 @@ one charsets.")
     (setq name (buffer-substring-no-properties
                (point) (progn (forward-sexp 1) (point))))
     (skip-chars-forward " \t\n")
     (setq name (buffer-substring-no-properties
                (point) (progn (forward-sexp 1) (point))))
     (skip-chars-forward " \t\n")
-    (while (not (looking-at ">"))
+    (while (not (looking-at ">[ \t]*\n?"))
       (setq elem (buffer-substring-no-properties
                  (point) (progn (forward-sexp 1) (point))))
       (skip-chars-forward "= \t\n")
       (setq elem (buffer-substring-no-properties
                  (point) (progn (forward-sexp 1) (point))))
       (skip-chars-forward "= \t\n")
@@ -200,26 +229,37 @@ one charsets.")
        (setq val (match-string 1 val)))
       (push (cons (intern elem) val) contents)
       (skip-chars-forward " \t\n"))
        (setq val (match-string 1 val)))
       (push (cons (intern elem) val) contents)
       (skip-chars-forward " \t\n"))
-    (forward-char 1)
-    (skip-chars-forward " \t\n")
+    (goto-char (match-end 0))
+    ;; Don't skip the leading space.
+    ;;(skip-chars-forward " \t\n")
     (cons (intern name) (nreverse contents))))
 
     (cons (intern name) (nreverse contents))))
 
-(defun mml-read-part ()
-  "Return the buffer up till the next part, multipart or closing part or multipart."
-  (let ((beg (point)))
+(defun mml-read-part (&optional mml)
+  "Return the buffer up till the next part, multipart or closing part or multipart.
+If MML is non-nil, return the buffer up till the correspondent mml tag."
+  (let ((beg (point)) (count 1))
     ;; If the tag ended at the end of the line, we go to the next line.
     (when (looking-at "[ \t]*\n")
       (forward-line 1))
     ;; If the tag ended at the end of the line, we go to the next line.
     (when (looking-at "[ \t]*\n")
       (forward-line 1))
-    (if (re-search-forward
-        "<#\\(/\\)?\\(multipart\\|part\\|external\\)." nil t)
-       (prog1
-           (buffer-substring-no-properties beg (match-beginning 0))
-         (if (or (not (match-beginning 1))
-                 (equal (match-string 2) "multipart"))
-             (goto-char (match-beginning 0))
-           (when (looking-at "[ \t]*\n")
-             (forward-line 1))))
-      (buffer-substring-no-properties beg (goto-char (point-max))))))
+    (if mml
+       (progn
+         (while (and (> count 0) (not (eobp)))
+           (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) 
+                                                 (point)
+                                               (match-beginning 0))))
+      (if (re-search-forward
+          "<#\\(/\\)?\\(multipart\\|part\\|external\\|mml\\)." nil t)
+         (prog1
+             (buffer-substring-no-properties beg (match-beginning 0))
+           (if (or (not (match-beginning 1))
+                   (equal (match-string 2) "multipart"))
+               (goto-char (match-beginning 0))
+             (when (looking-at "[ \t]*\n")
+               (forward-line 1))))
+       (buffer-substring-no-properties beg (goto-char (point-max)))))))
 
 (defvar mml-boundary nil)
 (defvar mml-base-boundary "-=-=")
 
 (defvar mml-boundary nil)
 (defvar mml-base-boundary "-=-=")
@@ -228,7 +268,7 @@ one charsets.")
 (defun mml-generate-mime ()
   "Generate a MIME message based on the current MML document."
   (let ((cont (mml-parse))
 (defun mml-generate-mime ()
   "Generate a MIME message based on the current MML document."
   (let ((cont (mml-parse))
-       (mml-multipart-number 0))
+       (mml-multipart-number mml-multipart-number))
     (if (not cont)
        nil
       (with-temp-buffer
     (if (not cont)
        nil
       (with-temp-buffer
@@ -240,106 +280,143 @@ one charsets.")
        (buffer-string)))))
 
 (defun mml-generate-mime-1 (cont)
        (buffer-string)))))
 
 (defun mml-generate-mime-1 (cont)
-  (cond
-   ((eq (car cont) 'part)
-    (let (coded encoding charset filename type)
-      (setq type (or (cdr (assq 'type cont)) "text/plain"))
-      (if (member (car (split-string type "/")) '("text" "message"))
-         (with-temp-buffer
-           (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))
-            (t
-             (save-restriction
-               (narrow-to-region (point) (point))
-               (insert (cdr (assq 'contents cont)))
-               ;; Remove quotes from quoted tags.
-               (goto-char (point-min))
-               (while (re-search-forward
-                       "<#!+/?\\(part\\|multipart\\|external\\)" nil t)
-                 (delete-region (+ (match-beginning 0) 2)
-                                (+ (match-beginning 0) 3))))))
-           (when (string= (car (split-string type "/")) "message")
-             ;; message/rfc822 parts have to have their heads encoded.
-             (save-restriction
-               (message-narrow-to-head)
-               (let ((rfc2047-header-encoding-alist nil))
-                 (mail-encode-encoded-word-buffer))))
-           (setq charset (mm-encode-body))
-           (setq encoding (mm-body-encoding
-                           charset
-                           (if (string= (car (split-string type "/"))
-                                        "message")
-                               '8bit
-                             (cdr (assq 'encoding cont)))))
-           (setq coded (buffer-string)))
-       (mm-with-unibyte-buffer
-         (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")))
-           (let ((coding-system-for-read mm-binary-coding-system))
-             (mm-insert-file-contents filename nil nil nil nil t)))
-          (t
-           (insert (cdr (assq 'contents cont)))))
-         (setq encoding (mm-encode-buffer type)
-               coded (buffer-string))))
-      (mml-insert-mime-headers cont type charset encoding)
-      (insert "\n")
-      (insert coded)))
-   ((eq (car cont) 'external)
-    (insert "Content-Type: message/external-body")
-    (let ((parameters (mml-parameter-string
-                      cont '(expiration size permission)))
-         (name (cdr (assq 'name cont))))
-      (when name
-       (setq name (mml-parse-file-name name))
-       (if (stringp name)
+  (let ((mm-use-ultra-safe-encoding 
+        (or mm-use-ultra-safe-encoding (assq 'sign cont))))
+    (save-restriction
+      (narrow-to-region (point) (point))
+      (cond
+       ((or (eq (car cont) 'part) (eq (car cont) 'mml))
+       (let ((raw (cdr (assq 'raw cont)))
+             coded encoding charset filename type)
+         (setq type (or (cdr (assq 'type cont)) "text/plain"))
+         (if (and (not raw)
+                  (member (car (split-string type "/")) '("text" "message")))
+             (with-temp-buffer
+               (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))
+                ((eq 'mml (car cont))
+                 (insert (cdr (assq 'contents cont))))
+                (t
+                 (save-restriction
+                   (narrow-to-region (point) (point))
+                   (insert (cdr (assq 'contents cont)))
+                   ;; Remove quotes from quoted tags.
+                   (goto-char (point-min))
+                   (while (re-search-forward
+                           "<#!+/?\\(part\\|multipart\\|external\\|mml\\)" nil t)
+                     (delete-region (+ (match-beginning 0) 2)
+                                    (+ (match-beginning 0) 3))))))
+               (cond 
+                ((eq (car cont) 'mml)
+                 (let ((mml-boundary (funcall mml-boundary-function
+                                              (incf mml-multipart-number)))
+                       (mml-generate-default-type "text/plain"))
+                   (mml-to-mime))
+                 (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))))
+                ((string= (car (split-string type "/")) "message")
+                 (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))
+                 (setq encoding (mm-body-encoding
+                                 charset (cdr (assq 'encoding cont))))))
+               (setq coded (buffer-string)))
+           (mm-with-unibyte-buffer
+             (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")))
+               (let ((coding-system-for-read mm-binary-coding-system))
+                 (mm-insert-file-contents filename nil nil nil nil t)))
+              (t
+               (insert (cdr (assq 'contents cont)))))
+             (setq encoding (mm-encode-buffer type)
+                   coded (buffer-string))))
+         (mml-insert-mime-headers cont type charset encoding)
+         (insert "\n")
+         (insert coded)))
+       ((eq (car cont) 'external)
+       (insert "Content-Type: message/external-body")
+       (let ((parameters (mml-parameter-string
+                          cont '(expiration size permission)))
+             (name (cdr (assq 'name cont)))
+             (url (cdr (assq 'url cont))))
+         (when name
+           (setq name (mml-parse-file-name name))
+           (if (stringp name)
+               (mml-insert-parameter
+                (mail-header-encode-parameter "name" name)
+                "access-type=local-file")
+             (mml-insert-parameter
+              (mail-header-encode-parameter
+               "name" (file-name-nondirectory (nth 2 name)))
+              (mail-header-encode-parameter "site" (nth 1 name))
+              (mail-header-encode-parameter
+               "directory" (file-name-directory (nth 2 name))))
+             (mml-insert-parameter
+              (concat "access-type="
+                      (if (member (nth 0 name) '("ftp@" "anonymous@"))
+                          "anon-ftp"
+                        "ftp")))))      
+         (when url
            (mml-insert-parameter
            (mml-insert-parameter
-            (mail-header-encode-parameter "name" name)
-            "access-type=local-file")
-         (mml-insert-parameter
-          (mail-header-encode-parameter
-           "name" (file-name-nondirectory (nth 2 name)))
-          (mail-header-encode-parameter "site" (nth 1 name))
-          (mail-header-encode-parameter
-           "directory" (file-name-directory (nth 2 name))))
-         (mml-insert-parameter
-          (concat "access-type="
-                  (if (member (nth 0 name) '("ftp@" "anonymous@"))
-                      "anon-ftp"
-                    "ftp")))))      
-      (when parameters
-       (mml-insert-parameter-string
-        cont '(expiration size permission))))
-    (insert "\n\n")
-    (insert "Content-Type: " (cdr (assq 'type cont)) "\n")
-    (insert "Content-ID: " (message-make-message-id) "\n")
-    (insert "Content-Transfer-Encoding: "
-           (or (cdr (assq 'encoding cont)) "binary"))
-    (insert "\n\n")
-    (insert (or (cdr (assq 'contents cont))))
-    (insert "\n"))
-   ((eq (car cont) 'multipart)
-    (let* ((type (or (cdr (assq 'type cont)) "mixed"))
-           (handler (assoc type mml-generate-multipart-alist)))
-      (if handler
-          (funcall (cdr handler) cont)
-        ;; No specific handler.  Use default one.
-        (let ((mml-boundary (mml-compute-boundary cont)))
-          (insert (format "Content-Type: multipart/%s; boundary=\"%s\"\n"
-                          type mml-boundary))
-          (setq cont (cddr cont))
-          (while cont
-            (insert "\n--" mml-boundary "\n")
-            (mml-generate-mime-1 (pop cont)))
-          (insert "\n--" mml-boundary "--\n")))))
-   (t
-    (error "Invalid element: %S" cont))))
+            (mail-header-encode-parameter "url" url)
+            "access-type=url"))
+         (when parameters
+           (mml-insert-parameter-string
+            cont '(expiration size permission))))
+       (insert "\n\n")
+       (insert "Content-Type: " (cdr (assq 'type cont)) "\n")
+       (insert "Content-ID: " (message-make-message-id) "\n")
+       (insert "Content-Transfer-Encoding: "
+               (or (cdr (assq 'encoding cont)) "binary"))
+       (insert "\n\n")
+       (insert (or (cdr (assq 'contents cont))))
+       (insert "\n"))
+       ((eq (car cont) 'multipart)
+       (let* ((type (or (cdr (assq 'type cont)) "mixed"))
+              (mml-generate-default-type (if (equal type "digest")
+                                             "message/rfc822"
+                                           "text/plain"))
+              (handler (assoc type mml-generate-multipart-alist)))
+         (if handler
+             (funcall (cdr handler) cont)
+           ;; No specific handler.  Use default one.
+           (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)))
+             (insert "\n--" mml-boundary "--\n")))))
+       (t
+       (error "Invalid element: %S" cont)))
+      (let ((item (assoc (cdr (assq 'sign cont)) mml-sign-alist))
+           sender recipients)
+       (when item
+         (if (setq sender (cdr (assq 'sender cont)))
+             (message-options-set 'message-sender sender))
+         (if (setq recipients (cdr (assq 'recipients cont)))
+             (message-options-set 'message-sender recipients))
+         (funcall (nth 1 item) cont)))
+      (let ((item (assoc (cdr (assq 'encrypt cont)) mml-encrypt-alist))
+           sender recipients)
+       (when item
+         (if (setq sender (cdr (assq 'sender cont)))
+             (message-options-set 'message-sender sender))
+         (if (setq recipients (cdr (assq 'recipients cont)))
+             (message-options-set 'message-sender recipients))
+         (funcall (nth 1 item) cont))))))
 
 (defun mml-compute-boundary (cont)
   "Return a unique boundary that does not exist in CONT."
 
 (defun mml-compute-boundary (cont)
   "Return a unique boundary that does not exist in CONT."
@@ -381,12 +458,6 @@ one charsets.")
            "")
          mml-base-boundary))
 
            "")
          mml-base-boundary))
 
-(defun mml-make-string (num string)
-  (let ((out ""))
-    (while (not (zerop (decf num)))
-      (setq out (concat out string)))
-    out))
-
 (defun mml-insert-mime-headers (cont type charset encoding)
   (let (parameters disposition description)
     (setq parameters
 (defun mml-insert-mime-headers (cont type charset encoding)
   (let (parameters disposition description)
     (setq parameters
@@ -394,7 +465,7 @@ one charsets.")
           cont '(name access-type expiration size permission)))
     (when (or charset
              parameters
           cont '(name access-type expiration size permission)))
     (when (or charset
              parameters
-             (not (equal type "text/plain")))
+             (not (equal type mml-generate-default-type)))
       (when (consp charset)
        (error
         "Can't encode a part with several charsets."))
       (when (consp charset)
        (error
         "Can't encode a part with several charsets."))
@@ -447,13 +518,13 @@ one charsets.")
         (mail-header-encode-parameter
          (symbol-name type) value))))))
 
         (mail-header-encode-parameter
          (symbol-name type) value))))))
 
-(defvar ange-ftp-path-format)
+(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)
 (defvar efs-path-regexp)
 (defun mml-parse-file-name (path)
   (if (if (boundp 'efs-path-regexp)
          (string-match efs-path-regexp path)
-       (if (boundp 'ange-ftp-path-format)
-           (string-match (car ange-ftp-path-format))))
+       (if (boundp 'ange-ftp-name-format)
+           (string-match (car ange-ftp-name-format) path)))
       (list (match-string 1 path) (match-string 2 path)
            (substring path (1+ (match-end 2))))
     path))
       (list (match-string 1 path) (match-string 2 path)
            (substring path (1+ (match-end 2))))
     path))
@@ -483,27 +554,43 @@ one charsets.")
     (if (stringp (car handles))
        (mml-insert-mime handles)
       (mml-insert-mime handles t))
     (if (stringp (car handles))
        (mml-insert-mime handles)
       (mml-insert-mime handles t))
-    (mm-destroy-parts handles)))
+    (mm-destroy-parts handles))
+  (save-restriction
+    (message-narrow-to-head)
+    ;; Remove them, they are confusing.
+    (message-remove-header "Content-Type")
+    (message-remove-header "MIME-Version")
+    (message-remove-header "Content-Transfer-Encoding")))
 
 (defun mml-to-mime ()
   "Translate the current buffer from MML to MIME."
   (message-encode-message-body)
   (save-restriction
     (message-narrow-to-headers-or-head)
 
 (defun mml-to-mime ()
   "Translate the current buffer from MML to MIME."
   (message-encode-message-body)
   (save-restriction
     (message-narrow-to-headers-or-head)
-    (mail-encode-encoded-word-buffer)))
+    (let ((mail-parse-charset message-default-charset))
+      (mail-encode-encoded-word-buffer))))
 
 (defun mml-insert-mime (handle &optional no-markup)
 
 (defun mml-insert-mime (handle &optional no-markup)
-  (let (textp buffer)
+  (let (textp buffer mmlp)
     ;; Determine type and stuff.
     (unless (stringp (car handle))
     ;; Determine type and stuff.
     (unless (stringp (car handle))
-      (unless (setq textp (equal (mm-handle-media-supertype handle)
-                                "text"))
+      (unless (setq textp (equal (mm-handle-media-supertype handle) "text"))
        (save-excursion
        (save-excursion
-         (set-buffer (setq buffer (generate-new-buffer " *mml*")))
-         (mm-insert-part handle))))
-    (unless no-markup
-      (mml-insert-mml-markup handle buffer textp))
+         (set-buffer (setq buffer (mml-generate-new-buffer " *mml*")))
+         (mm-insert-part handle)
+         (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
+                  (equal (mm-handle-media-type handle) "text/plain"))
+       (mml-insert-mml-markup handle buffer textp)))
     (cond
     (cond
+     (mmlp 
+      (insert-buffer buffer)
+      (goto-char (point-max))
+      (insert "<#/mml>\n"))
      ((stringp (car handle))
       (mapcar 'mml-insert-mime (cdr handle))
       (insert "<#/multipart>\n"))
      ((stringp (car handle))
       (mapcar 'mml-insert-mime (cdr handle))
       (insert "<#/multipart>\n"))
@@ -516,12 +603,14 @@ one charsets.")
      (t
       (insert "<#/part>\n")))))
 
      (t
       (insert "<#/part>\n")))))
 
-(defun mml-insert-mml-markup (handle &optional buffer nofile)
+(defun mml-insert-mml-markup (handle &optional buffer nofile mmlp)
   "Take a MIME handle and insert an MML tag."
   (if (stringp (car handle))
       (insert "<#multipart type=" (mm-handle-media-subtype handle)
              ">\n")
   "Take a MIME handle and insert an MML tag."
   (if (stringp (car handle))
       (insert "<#multipart type=" (mm-handle-media-subtype handle)
              ">\n")
-    (insert "<#part type=" (mm-handle-media-type handle))
+    (if mmlp
+       (insert "<#mml type=" (mm-handle-media-type handle))
+      (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) "\""))
     (dolist (elem (append (cdr (mm-handle-type handle))
                          (cdr (mm-handle-disposition handle))))
       (insert " " (symbol-name (car elem)) "=\"" (cdr elem) "\""))
@@ -551,8 +640,14 @@ one charsets.")
 ;;;
 
 (defvar mml-mode-map
 ;;;
 
 (defvar mml-mode-map
-  (let ((map (make-sparse-keymap))
+  (let ((sign (make-sparse-keymap))
+       (encrypt (make-sparse-keymap))
+       (map (make-sparse-keymap))
        (main (make-sparse-keymap)))
        (main (make-sparse-keymap)))
+    (define-key sign "p" 'mml-secure-sign-pgpmime)
+    (define-key sign "s" 'mml-secure-sign-smime)
+    (define-key encrypt "p" 'mml-secure-encrypt-pgpmime)
+    (define-key encrypt "s" 'mml-secure-encrypt-smime)
     (define-key map "f" 'mml-attach-file)
     (define-key map "b" 'mml-attach-buffer)
     (define-key map "e" 'mml-attach-external)
     (define-key map "f" 'mml-attach-file)
     (define-key map "b" 'mml-attach-buffer)
     (define-key map "e" 'mml-attach-external)
@@ -561,7 +656,9 @@ one charsets.")
     (define-key map "p" 'mml-insert-part)
     (define-key map "v" 'mml-validate)
     (define-key map "P" 'mml-preview)
     (define-key map "p" 'mml-insert-part)
     (define-key map "v" 'mml-validate)
     (define-key map "P" 'mml-preview)
-    (define-key map "n" 'mml-narrow-to-part)
+    (define-key map "s" sign)
+    (define-key map "c" encrypt)
+    ;;(define-key map "n" 'mml-narrow-to-part)
     (define-key main "\M-m" map)
     main))
 
     (define-key main "\M-m" map)
     main))
 
@@ -575,7 +672,14 @@ one charsets.")
    ("Insert"
     ["Multipart" mml-insert-multipart t]
     ["Part" mml-insert-part t])
    ("Insert"
     ["Multipart" mml-insert-multipart t]
     ["Part" mml-insert-part t])
-   ["Narrow" mml-narrow-to-part t]
+   ("Security"
+    ("Sign"
+     ["PGP/MIME" mml-secure-sign-pgpmime t]
+     ["S/MIME" mml-secure-sign-smime t])
+    ("Encrypt"
+     ["PGP/MIME" mml-secure-encrypt-pgpmime t]
+     ["S/MIME" mml-secure-encrypt-smime t]))
+   ;;["Narrow" mml-narrow-to-part t]
    ["Quote" mml-quote-region t]
    ["Validate" mml-validate t]
    ["Preview" mml-preview t]))
    ["Quote" mml-quote-region t]
    ["Validate" mml-validate t]
    ["Preview" mml-preview t]))
@@ -588,17 +692,12 @@ one charsets.")
 
 \\{mml-mode-map}"
   (interactive "P")
 
 \\{mml-mode-map}"
   (interactive "P")
-  (if (not (set (make-local-variable 'mml-mode)
-               (if (null arg) (not mml-mode)
-                 (> (prefix-numeric-value arg) 0))))
-      nil
-    (set (make-local-variable 'mml-mode) t)
-    (unless (assq 'mml-mode minor-mode-alist)
-      (push `(mml-mode " MML") minor-mode-alist))
-    (unless (assq 'mml-mode minor-mode-map-alist)
-      (push (cons 'mml-mode mml-mode-map)
-           minor-mode-map-alist)))
-  (run-hooks 'mml-mode-hook))
+  (when (set (make-local-variable 'mml-mode)
+            (if (null arg) (not mml-mode)
+              (> (prefix-numeric-value arg) 0)))
+    (gnus-add-minor-mode 'mml-mode " MML" mml-mode-map)
+    (easy-menu-add mml-menu mml-mode-map)
+    (run-hooks 'mml-mode-hook)))
 
 ;;;
 ;;; Helper functions for reading MIME stuff from the minibuffer and
 
 ;;;
 ;;; Helper functions for reading MIME stuff from the minibuffer and
@@ -618,6 +717,7 @@ one charsets.")
     file))
 
 (defun mml-minibuffer-read-type (name &optional default)
     file))
 
 (defun mml-minibuffer-read-type (name &optional default)
+  (mailcap-parse-mimetypes)
   (let* ((default (or default
                      (mm-default-file-encoding name)
                      ;; Perhaps here we should check what the file
   (let* ((default (or default
                      (mm-default-file-encoding name)
                      ;; Perhaps here we should check what the file
@@ -626,25 +726,7 @@ one charsets.")
                      "application/octet-stream"))
         (string (completing-read
                  (format "Content type (default %s): " default)
                      "application/octet-stream"))
         (string (completing-read
                  (format "Content type (default %s): " default)
-                 (mapcar
-                  'list
-                  (mm-delete-duplicates
-                   (nconc
-                    (mapcar 'cdr mailcap-mime-extensions)
-                    (apply
-                     'nconc
-                     (mapcar
-                      (lambda (l)
-                        (delq nil
-                              (mapcar
-                               (lambda (m)
-                                 (let ((type (cdr (assq 'type (cdr m)))))
-                                   (if (equal (cadr (split-string type "/"))
-                                              "*")
-                                       nil
-                                     type)))
-                               (cdr l))))
-                      mailcap-mime-data))))))))
+                 (mapcar 'list (mailcap-mime-types)))))
     (if (not (equal string ""))
        string
       default)))
     (if (not (equal string ""))
        string
       default)))
@@ -666,7 +748,7 @@ one charsets.")
       (goto-char (point-min))
       ;; Quote parts.
       (while (re-search-forward
       (goto-char (point-min))
       ;; Quote parts.
       (while (re-search-forward
-             "<#/?!*\\(multipart\\|part\\|external\\)" nil t)
+             "<#!*/?\\(multipart\\|part\\|external\\|mml\\)" nil t)
        ;; Insert ! after the #.
        (goto-char (+ (match-beginning 0) 2))
        (insert "!")))))
        ;; Insert ! after the #.
        (goto-char (+ (match-beginning 0) 2))
        (insert "!")))))
@@ -755,10 +837,14 @@ TYPE is the MIME type to use."
 If RAW, don't highlight the article."
   (interactive "P")
   (let ((buf (current-buffer))
 If RAW, don't highlight the article."
   (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 
-                                     (message-fetch-field "Newsgroups"))
+                                     (save-restriction
+                                       (message-narrow-to-headers-or-head)
+                                       (message-fetch-field "Newsgroups")))
                                     message-posting-charset)))
                                     message-posting-charset)))
-    (switch-to-buffer (get-buffer-create 
+    (message-options-set-recipient)
+    (switch-to-buffer (generate-new-buffer
                       (concat (if raw "*Raw MIME preview of "
                                 "*MIME preview of ") (buffer-name))))
     (erase-buffer)
                       (concat (if raw "*Raw MIME preview of "
                                 "*MIME preview of ") (buffer-name))))
     (erase-buffer)
@@ -767,10 +853,17 @@ If RAW, don't highlight the article."
         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
        (replace-match "\n"))
     (mml-to-mime)
         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
        (replace-match "\n"))
     (mml-to-mime)
-    (unless raw
-      (run-hooks 'gnus-article-decode-hook)
-      (let ((gnus-newsgroup-name "dummy"))
-       (gnus-article-prepare-display)))
+    (if raw
+       (when (fboundp 'set-buffer-multibyte)
+         (let ((s (buffer-string)))
+           ;; Insert the content into unibyte buffer.
+           (erase-buffer)
+           (mm-disable-multibyte)
+           (insert s)))
+      (let ((gnus-newsgroup-charset (car message-posting-charset)))
+       (run-hooks 'gnus-article-decode-hook)
+       (let ((gnus-newsgroup-name "dummy"))
+         (gnus-article-prepare-display))))
     (fundamental-mode)
     (setq buffer-read-only t)
     (goto-char (point-min))))
     (fundamental-mode)
     (setq buffer-read-only t)
     (goto-char (point-min))))