Patch by Ed L. Cashin to make gnus-move-split-methods move to
[gnus] / lisp / mml.el
index db11fa8..8c7b849 100644 (file)
@@ -27,7 +27,7 @@
 (require 'mm-bodies)
 (require 'mm-encode)
 (require 'mm-decode)
-(eval-when-compile 'cl)
+(eval-when-compile (require 'cl))
 
 (eval-and-compile
   (autoload 'message-make-message-id "message")
@@ -72,6 +72,15 @@ unknown encoding; `use-ascii': always use ASCII for those characters
 with unknown encoding; `multipart': always send messages with more than
 one charsets.")
 
+(defvar mml-generate-mime-preprocess-function nil
+  "A function called before generating a mime part.
+The function is called with one parameter, which is the part to be 
+generated.")
+
+(defvar mml-generate-mime-postprocess-function nil
+  "A function called after generating a mime part.
+The function is called with one parameter, which is the generated part.")
+
 (defvar mml-generate-default-type "text/plain")
 
 (defvar mml-buffer-list nil)
@@ -98,7 +107,7 @@ one charsets.")
 
 (defun mml-parse-1 ()
   "Parse the current buffer as an MML document."
-  (let (struct tag point contents charsets warn use-ascii no-markup-p)
+  (let (struct tag point contents charsets warn use-ascii no-markup-p raw)
     (while (and (not (eobp))
                (not (looking-at "<#/multipart")))
       (cond
@@ -115,20 +124,24 @@ one charsets.")
          (setq tag (list 'part '(type . "text/plain"))
                no-markup-p t
                warn t))
-       (setq point (point)
+       (setq raw (cdr (assq 'raw tag))
+             point (point)
              contents (mml-read-part (eq 'mml (car tag)))
-             charsets (mm-find-mime-charset-region point (point)))
-       (when (memq nil charsets)
+             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
-                  "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 (< (length charsets) 2)
+       (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.
@@ -274,116 +287,124 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
        (buffer-string)))))
 
 (defun mml-generate-mime-1 (cont)
-  (cond
-   ((or (eq (car cont) 'part) (eq (car cont) 'mml))
-    (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
+  (save-restriction
+    (narrow-to-region (point) (point))
+    (if mml-generate-mime-preprocess-function
+       (funcall mml-generate-mime-preprocess-function cont))
+    (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")))
-             (mm-insert-file-contents filename))
-            ((eq 'mml (car cont))
-             (insert (cdr (assq 'contents cont))))
+             (let ((coding-system-for-read mm-binary-coding-system))
+               (mm-insert-file-contents filename nil nil nil nil t)))
             (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))))
-      (when name
-       (setq name (mml-parse-file-name name))
-       (if (stringp name)
+             (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)
+             (mml-insert-parameter
+              (mail-header-encode-parameter "name" name)
+              "access-type=local-file")
            (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"))
-          (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))))
+            (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"))
+            (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)))
+    (if mml-generate-mime-postprocess-function
+       (funcall mml-generate-mime-postprocess-function cont))))
 
 (defun mml-compute-boundary (cont)
   "Return a unique boundary that does not exist in CONT."
@@ -425,12 +446,6 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
            "")
          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
@@ -491,13 +506,13 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
         (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)
-       (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))
@@ -623,7 +638,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
     (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 "n" 'mml-narrow-to-part)
     (define-key main "\M-m" map)
     main))
 
@@ -637,7 +652,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
    ("Insert"
     ["Multipart" mml-insert-multipart t]
     ["Part" mml-insert-part t])
-   ["Narrow" mml-narrow-to-part t]
+   ;;["Narrow" mml-narrow-to-part t]
    ["Quote" mml-quote-region t]
    ["Validate" mml-validate t]
    ["Preview" mml-preview t]))
@@ -680,6 +695,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
     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
@@ -688,25 +704,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
                      "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)))
@@ -728,7 +726,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
       (goto-char (point-min))
       ;; Quote parts.
       (while (re-search-forward
-             "<#/?!*\\(multipart\\|part\\|external\\|mml\\)" nil t)
+             "<#!*/?\\(multipart\\|part\\|external\\|mml\\)" nil t)
        ;; Insert ! after the #.
        (goto-char (+ (match-beginning 0) 2))
        (insert "!")))))
@@ -832,7 +830,12 @@ If RAW, don't highlight the article."
        (replace-match "\n"))
     (mml-to-mime)
     (if raw
-       (mm-disable-multibyte)
+       (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"))