(message-cite-articles-with-x-no-archive): New
[gnus] / lisp / mml.el
index c86a820..a05c2da 100644 (file)
@@ -1,5 +1,6 @@
 ;;; mml.el --- A package for parsing and validating MML documents
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+;;        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 'gnus-make-local-hook "gnus-util")
   (autoload 'message-fetch-field "message")
+  (autoload 'message-mark-active-p "message")
   (autoload 'fill-flowed-encode "flow-fill")
-  (autoload 'message-posting-charset "message"))
+  (autoload 'message-posting-charset "message")
+  (autoload 'x-dnd-get-local-file-name "x-dnd"))
 
 (defcustom mml-content-type-parameters
   '(name access-type expiration size permission format)
@@ -52,6 +55,12 @@ These parameters are generated in Content-Disposition header if exists."
   :type '(repeat (symbol :tag "Parameter"))
   :group 'message)
 
+(defcustom mml-insert-mime-headers-always nil
+  "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."
+  :type 'boolean
+  :group 'message)
+
 (defvar mml-tweak-type-alist nil
   "A list of (TYPE . FUNCTION) for tweaking MML parts.
 TYPE is a string containing a regexp to match the MIME type.  FUNCTION
@@ -68,7 +77,7 @@ handle to tweak the part.")
 (defvar mml-tweak-sexp-alist
   '((mml-externalize-attachments . mml-tweak-externalize-attachments))
   "A list of (SEXP . FUNCTION) for tweaking MML parts.
-SEXP is a s-expression. If the evaluation of SEXP is non-nil, FUNCTION
+SEXP is an s-expression.  If the evaluation of SEXP is non-nil, FUNCTION
 is called.  FUNCTION is a Lisp function which is called with the MML
 handle to tweak the part.")
 
@@ -123,19 +132,15 @@ one charsets.")
 
 (defun mml-destroy-buffers ()
   (let (kill-buffer-hook)
-    (mapcar 'kill-buffer mml-buffer-list)
+    (mapc 'kill-buffer mml-buffer-list)
     (setq mml-buffer-list nil)))
 
 (defun mml-parse ()
   "Parse the current buffer as an MML document."
   (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)))))
+    (with-syntax-table mml-syntax-table
+      (mml-parse-1))))
 
 (defun mml-parse-1 ()
   "Parse the current buffer as an MML document."
@@ -151,14 +156,14 @@ one charsets.")
        (let* (secure-mode
               (taginfo (mml-read-tag))
               (recipients (cdr (assq 'recipients taginfo)))
+              (sender (cdr (assq 'sender taginfo)))
               (location (cdr (assq 'tag-location taginfo)))
               (mode (cdr (assq 'mode taginfo)))
               (method (cdr (assq 'method taginfo)))
               tags)
          (save-excursion
-           (if
-               (re-search-forward
-                "<#\\(/\\)?\\(multipart\\|part\\|external\\|mml\\)." nil t)
+           (if (re-search-forward
+                "<#/?\\(multipart\\|part\\|external\\|mml\\)." nil t)
                (setq secure-mode "multipart")
              (setq secure-mode "part")))
          (save-excursion
@@ -173,8 +178,10 @@ one charsets.")
                 (setq tags (list "sign" method "encrypt" method))))
          (eval `(mml-insert-tag ,secure-mode
                                 ,@tags
-                                ,(if recipients 'recipients)
-                                ,recipients))
+                                ,(if recipients "recipients")
+                                ,recipients
+                                ,(if sender "sender")
+                                ,sender))
          ;; restart the parse
          (goto-char location)))
        ((looking-at "<#multipart")
@@ -205,12 +212,12 @@ one charsets.")
          (if (or (memq 'unknown-encoding mml-confirmation-set)
                  (message-options-get 'unknown-encoding)
                  (and (y-or-n-p "\
-Message contains characters with unknown encoding.  Really send?")
+Message contains characters with unknown encoding.  Really send? ")
                       (message-options-set 'unknown-encoding t)))
              (if (setq use-ascii
                        (or (memq 'use-ascii mml-confirmation-set)
                            (message-options-get 'use-ascii)
-                           (and (y-or-n-p "Use ASCII as charset?")
+                           (and (y-or-n-p "Use ASCII as charset? ")
                                 (message-options-set 'use-ascii t))))
                  (setq charsets (delq nil charsets))
                (setq warn nil))
@@ -334,7 +341,7 @@ A message part needs to be split into %d charset parts.  Really send? "
   "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.
+    ;; 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 mml
@@ -419,8 +426,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
                                       (+ (match-beginning 0) 3))))))
                  (cond
                   ((eq (car cont) 'mml)
-                   (let ((mml-boundary (funcall mml-boundary-function
-                                                (incf mml-multipart-number)))
+                   (let ((mml-boundary (mml-compute-boundary cont))
                          (mml-generate-default-type "text/plain"))
                      (mml-to-mime))
                    (let ((mm-7bit-chars (concat mm-7bit-chars "\x1b")))
@@ -431,26 +437,27 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
                      ;; ignore 0x1b, it is part of iso-2022-jp
                      (setq encoding (mm-body-7-or-8))))
                   (t
+                   ;; Only perform format=flowed filling on text/plain
+                   ;; parts where there either isn't a format parameter
+                   ;; in the mml tag or it says "flowed" and there
+                   ;; actually are hard newlines in the text.
+                   (let (use-hard-newlines)
+                     (when (and (string= type "text/plain")
+                                (not (string= (cdr (assq 'sign cont)) "pgp"))
+                                (or (null (assq 'format cont))
+                                    (string= (cdr (assq 'format cont))
+                                             "flowed"))
+                                (setq use-hard-newlines
+                                      (text-property-any
+                                       (point-min) (point-max) 'hard 't)))
+                       (fill-flowed-encode)
+                       ;; Indicate that `mml-insert-mime-headers' should
+                       ;; insert a "; format=flowed" string unless the
+                       ;; user has already specified it.
+                       (setq flowed (null (assq 'format cont)))))
                    (setq charset (mm-encode-body charset))
                    (setq encoding (mm-body-encoding
                                    charset (cdr (assq 'encoding cont))))))
-                 ;; Only perform format=flowed filling on text/plain
-                 ;; parts where there either isn't a format parameter
-                 ;; in the mml tag or it says "flowed" and there
-                 ;; actually are hard newlines in the text.
-                 (let (use-hard-newlines)
-                   (when (and (string= type "text/plain")
-                              (or (null (assq 'format cont))
-                                  (string= (cdr (assq 'format cont))
-                                           "flowed"))
-                              (setq use-hard-newlines
-                                    (text-property-any
-                                     (point-min) (point-max) 'hard 't)))
-                     (fill-flowed-encode)
-                     ;; Indicate that `mml-insert-mime-headers' should
-                     ;; insert a "; format=flowed" string unless the
-                     ;; user has already specified it.
-                     (setq flowed (null (assq 'format cont)))))
                  (setq coded (buffer-string)))
                (mml-insert-mime-headers cont type charset encoding flowed)
                (insert "\n")
@@ -519,8 +526,11 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
              (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))
+             (insert (format "Content-Type: multipart/%s; boundary=\"%s\""
+                             type mml-boundary)
+                     (if (cdr (assq 'start cont))
+                         (format "; start=\"%s\"\n" (cdr (assq 'start cont)))
+                       "\n"))
              (let ((cont cont) part)
                (while (setq part (pop cont))
                  ;; Skip `multipart' and attributes.
@@ -536,11 +546,13 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
                                 mml-encrypt-alist))
            sender recipients)
        (when (or sign-item encrypt-item)
-         (if (setq sender (cdr (assq 'sender cont)))
-             (message-options-set 'message-sender sender))
+         (when (setq sender (cdr (assq 'sender cont)))
+           (message-options-set 'mml-sender sender)
+           (message-options-set 'message-sender sender))
          (if (setq recipients (cdr (assq 'recipients cont)))
              (message-options-set 'message-recipients recipients))
-         (let ((style (mml-signencrypt-style (first encrypt-item))))
+         (let ((style (mml-signencrypt-style
+                       (first (or sign-item encrypt-item)))))
            ;; check if: we're both signing & encrypting, both methods
            ;; are the same (why would they be different?!), and that
            ;; the signencrypt style allows for combined operation.
@@ -574,7 +586,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
          (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))
+         (mm-insert-file-contents filename nil nil nil nil t))
         (t
          (insert (cdr (assq 'contents cont)))))
        (goto-char (point-min))
@@ -584,7 +596,7 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
                                      (incf mml-multipart-number)))
          (throw 'not-unique nil))))
      ((eq (car cont) 'multipart)
-      (mapcar 'mml-compute-boundary-1 (cddr cont))))
+      (mapc 'mml-compute-boundary-1 (cddr cont))))
     t))
 
 (defun mml-make-boundary (number)
@@ -595,17 +607,18 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
          mml-base-boundary))
 
 (defun mml-insert-mime-headers (cont type charset encoding flowed)
-  (let (parameters disposition description)
+  (let (parameters id disposition description)
     (setq parameters
          (mml-parameter-string
           cont mml-content-type-parameters))
     (when (or charset
              parameters
              flowed
-             (not (equal type mml-generate-default-type)))
+             (not (equal type mml-generate-default-type))
+             mml-insert-mime-headers-always)
       (when (consp charset)
        (error
-        "Can't encode a part with several charsets."))
+        "Can't encode a part with several charsets"))
       (insert "Content-Type: " type)
       (when charset
        (insert "; " (mail-header-encode-parameter
@@ -616,6 +629,8 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
        (mml-insert-parameter-string
         cont mml-content-type-parameters))
       (insert "\n"))
+    (when (setq id (cdr (assq 'id cont)))
+      (insert "Content-ID: " id "\n"))
     (setq parameters
          (mml-parameter-string
           cont mml-content-disposition-parameters))
@@ -734,7 +749,7 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
        (mml-insert-mml-markup handle buffer textp)))
     (cond
      (mmlp
-      (insert-buffer buffer)
+      (insert-buffer-substring buffer)
       (goto-char (point-max))
       (insert "<#/mml>\n"))
      ((stringp (car handle))
@@ -742,10 +757,12 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
       (insert "<#/multipart>\n"))
      (textp
       (let ((charset (mail-content-type-get
-                     (mm-handle-type handle) 'charset)))
+                     (mm-handle-type handle) 'charset))
+           (start (point)))
        (if (eq charset 'gnus-decoded)
            (mm-insert-part handle)
-         (insert (mm-decode-string (mm-get-part handle) charset))))
+         (insert (mm-decode-string (mm-get-part handle) charset)))
+       (mml-quote-region start (point)))
       (goto-char (point-max)))
      (t
       (insert "<#/part>\n")))))
@@ -753,8 +770,12 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
 (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")
+      (progn
+       (insert "<#multipart type=" (mm-handle-media-subtype handle))
+       (let ((start (mm-handle-multipart-ctl-parameter handle 'start)))
+         (when start
+           (insert " start=\"" start "\"")))
+       (insert ">\n"))
     (if mmlp
        (insert "<#mml type=" (mm-handle-media-type handle))
       (insert "<#part type=" (mm-handle-media-type handle)))
@@ -762,6 +783,8 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
                          (cdr (mm-handle-disposition handle))))
       (unless (symbolp (cdr elem))
        (insert " " (symbol-name (car elem)) "=\"" (cdr elem) "\"")))
+    (when (mm-handle-id handle)
+      (insert " id=\"" (mm-handle-id handle) "\""))
     (when (mm-handle-disposition handle)
       (insert " disposition=" (car (mm-handle-disposition handle))))
     (when buffer
@@ -790,14 +813,22 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
 (defvar mml-mode-map
   (let ((sign (make-sparse-keymap))
        (encrypt (make-sparse-keymap))
+       (signpart (make-sparse-keymap))
+       (encryptpart (make-sparse-keymap))
        (map (make-sparse-keymap))
        (main (make-sparse-keymap)))
     (define-key sign "p" 'mml-secure-message-sign-pgpmime)
     (define-key sign "o" 'mml-secure-message-sign-pgp)
     (define-key sign "s" 'mml-secure-message-sign-smime)
+    (define-key signpart "p" 'mml-secure-sign-pgpmime)
+    (define-key signpart "o" 'mml-secure-sign-pgp)
+    (define-key signpart "s" 'mml-secure-sign-smime)
     (define-key encrypt "p" 'mml-secure-message-encrypt-pgpmime)
     (define-key encrypt "o" 'mml-secure-message-encrypt-pgp)
     (define-key encrypt "s" 'mml-secure-message-encrypt-smime)
+    (define-key encryptpart "p" 'mml-secure-encrypt-pgpmime)
+    (define-key encryptpart "o" 'mml-secure-encrypt-pgp)
+    (define-key encryptpart "s" 'mml-secure-encrypt-smime)
     (define-key map "\C-n" 'mml-unsecure-message)
     (define-key map "f" 'mml-attach-file)
     (define-key map "b" 'mml-attach-buffer)
@@ -808,7 +839,9 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     (define-key map "v" 'mml-validate)
     (define-key map "P" 'mml-preview)
     (define-key map "s" sign)
+    (define-key map "S" signpart)
     (define-key map "c" encrypt)
+    (define-key map "C" encryptpart)
     ;;(define-key map "n" 'mml-narrow-to-part)
     ;; `M-m' conflicts with `back-to-indentation'.
     ;; (define-key main "\M-m" map)
@@ -818,22 +851,32 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
 (easy-menu-define
   mml-menu mml-mode-map ""
   `("Attachments"
-    ["Attach File" mml-attach-file
+    ["Attach File..." mml-attach-file
      ,@(if (featurep 'xemacs) '(t)
         '(:help "Attach a file at point"))]
-    ["Attach Buffer" mml-attach-buffer t]
-    ["Attach External" mml-attach-external t]
-    ["Insert Part" mml-insert-part t]
-    ["Insert Multipart" mml-insert-multipart t]
+    ["Attach Buffer..." mml-attach-buffer t]
+    ["Attach External..." mml-attach-external t]
+    ["Insert Part..." mml-insert-part t]
+    ["Insert Multipart..." mml-insert-multipart t]
     ["PGP/MIME Sign" mml-secure-message-sign-pgpmime t]
     ["PGP/MIME Encrypt" mml-secure-message-encrypt-pgpmime t]