2003-01-15 Kevin Ryde <user42@zip.com.au>
[gnus] / lisp / message.el
index 0eea71c..d1d7fc9 100644 (file)
@@ -1,5 +1,5 @@
 ;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
@@ -174,7 +174,7 @@ Otherwise, most addresses look like `angles', but they look like
   :group 'message-headers
   :type 'boolean)
 
-(defcustom message-syntax-checks 
+(defcustom message-syntax-checks
   (if message-insert-canlock '((sender . disabled)) nil)
   ;; Guess this one shouldn't be easy to customize...
   "*Controls what syntax checks should not be performed on outgoing posts.
@@ -188,11 +188,12 @@ Checks include `subject-cmsg', `multiple-headers', `sendsys',
 `new-text', `quoting-style', `redirected-followup', `signature',
 `approved', `sender', `empty', `empty-headers', `message-id', `from',
 `subject', `shorten-followup-to', `existing-newsgroups',
-`buffer-file-name', `unchanged', `newsgroups', `reply-to'."
+`buffer-file-name', `unchanged', `newsgroups', `reply-to',
+'continuation-headers', and `long-header-lines'."
   :group 'message-news
   :type '(repeat sexp))                        ; Fixme: improve this
 
-(defcustom message-required-headers '((optional . References))
+(defcustom message-required-headers '((optional . References) From)
   "*Headers to be generated or promted for when sending a message.
 Also see `message-required-news-headers' and
 1message-required-mail-headers'."
@@ -200,6 +201,12 @@ Also see `message-required-news-headers' and
   :group 'message-headers
   :type '(repeat sexp))
 
+(defcustom message-draft-headers '(References From)
+  "*Headers to be generated when saving a draft message."
+  :group 'message-news
+  :group 'message-headers
+  :type '(repeat sexp))
+
 (defcustom message-required-news-headers
   '(From Newsgroups Subject Date Message-ID
         (optional . Organization)
@@ -322,7 +329,7 @@ Archives \(such as groups.googgle.com\) respect this header."
 ;;;###autoload
 (defcustom message-archive-note
   "X-No-Archive: Yes - save http://groups.google.com/"
-  "Note to insert why you wouldn't want this posting archived. 
+  "Note to insert why you wouldn't want this posting archived.
 If nil, don't insert any text in the body."
   :type 'string
   :group 'message-various)
@@ -331,40 +338,40 @@ If nil, don't insert any text in the body."
 ;; inspired by JoH-followup-to by Jochem Huhman <joh  at gmx.de>
 ;; new suggestions by R. Weikusat <rw at another.de>
 
-(defvar message-xpost-old-target nil
+(defvar message-cross-post-old-target nil
   "Old target for cross-posts or follow-ups.")
-(make-variable-buffer-local 'message-xpost-old-target)
+(make-variable-buffer-local 'message-cross-post-old-target)
 
 ;;;###autoload
-(defcustom message-xpost-default t
-  "When non-nil `message-xpost-fup2' will normally perform a crosspost.
-If nil, `message-xpost-fup2' will only do a followup.  Note that you
-can explicitly override this setting by calling `message-xpost-fup2'
-with a prefix."
+(defcustom message-cross-post-default t
+  "When non-nil `message-cross-post-followup-to' will perform a crosspost.
+If nil, `message-cross-post-followup-to' will only do a followup.  Note that
+you can explicitly override this setting by calling
+`message-cross-post-followup-to' with a prefix."
   :type 'boolean
   :group 'message-various)
 
 ;;;###autoload
-(defcustom message-xpost-note
+(defcustom message-cross-post-note
   "Crosspost & Followup-To: "
-  "Note to insert before signature to notify of xpost and follow-up."
+  "Note to insert before signature to notify of cross-post and follow-up."
   :type 'string
   :group 'message-various)
 
 ;;;###autoload
-(defcustom message-fup2-note
+(defcustom message-followup-to-note
   "Followup-To: "
   "Note to insert before signature to notify of follow-up only."
   :type 'string
   :group 'message-various)
 
 ;;;###autoload
-(defcustom message-xpost-note-function
-  'message-xpost-insert-note
-  "Function to use to insert note about Crosspost or Followup-To.  
+(defcustom message-cross-post-note-function
+  'message-cross-post-insert-note
+  "Function to use to insert note about Crosspost or Followup-To.
 The function will be called with four arguments.  The function should not only
 insert a note, but also ensure old notes are deleted.  See the documentation
-for `message-xpost-insert-note'. "
+for `message-cross-post-insert-note'."
   :type 'function
   :group 'message-various)
 
@@ -676,12 +683,16 @@ variable isn't used."
   "*If non-nil, generate all required headers before composing.
 The variables `message-required-news-headers' and
 `message-required-mail-headers' specify which headers to generate.
+This can also be a list of headers that should be generated before
+composing.
 
 Note that the variable `message-deletable-headers' specifies headers which
 are to be deleted and then re-generated before sending, so this variable
 will not have a visible effect for those headers."
   :group 'message-headers
-  :type 'boolean)
+  :type '(choice (const :tag "None" nil)
+                 (const :tag "All" t)
+                 (repeat (sexp :tag "Header"))))
 
 (defcustom message-setup-hook nil
   "Normal hook, run each time a new outgoing message is initialized.
@@ -792,6 +803,12 @@ If nil, don't insert a signature."
   :type '(choice file (const :tags "None" nil))
   :group 'message-insertion)
 
+;;;###autoload
+(defcustom message-signature-insert-empty-line t
+  "*If non-nil, insert an empty line before the signature separator."
+  :type 'boolean
+  :group 'message-insertion)
+
 (defcustom message-distribution-function nil
   "*Function called to return a Distribution header."
   :group 'message-news
@@ -944,12 +961,12 @@ A value of nil means exclude your own user name only."
   "*A list of GNKSA feet you are allowed to shoot.
 Gnus gives you all the opportunity you could possibly want for
 shooting yourself in the foot.  Also, Gnus allows you to shoot the
-feet of Good Net-Keeping Seal of Approval. The following are foot
+feet of Good Net-Keeping Seal of Approval.  The following are foot
 candidates:
 `empty-article'     Allow you to post an empty article;
 `quoted-text-only'  Allow you to post quoted text only;
 `multiple-copies'   Allow you to post multiple copies;
-`cancel-messages'   Allow you to cancel or supersede messages from 
+`cancel-messages'   Allow you to cancel or supersede messages from
                     your other email addresses.")
 
 (defsubst message-gnksa-enable-p (feature)
@@ -1503,7 +1520,9 @@ is used by default."
 (defun message-fetch-reply-field (header)
   "Fetch field HEADER from the message we're replying to."
   (message-with-reply-buffer
-    (message-fetch-field header)))
+    (save-restriction
+      (mail-narrow-to-head)
+      (message-fetch-field header))))
 
 (defun message-set-work-buffer ()
   (if (get-buffer " *message work*")
@@ -1544,13 +1563,13 @@ is used by default."
 ;;; Start of functions adopted from `message-utils.el'.
 
 (defun message-strip-subject-trailing-was (subject)
-  "Remove trailing \"(Was: <old subject>)\" from subject lines.   
+  "Remove trailing \"(Was: <old subject>)\" from SUBJECT lines.
 Leading \"Re: \" is not stripped by this function.  Use the function
 `message-strip-subject-re' for this."
   (let* ((query message-subject-trailing-was-query)
         (new) (found))
     (setq found
-         (string-match 
+         (string-match
           (if (eq query 'ask)
               message-subject-trailing-was-ask-regexp
             message-subject-trailing-was-regexp)
@@ -1562,7 +1581,7 @@ Leading \"Re: \" is not stripped by this function.  Use the function
       (if (eq query 'ask)
          (if (message-y-or-n-p
               "Strip `(was: <old subject>)' in subject? " t
-              (concat 
+              (concat
                "Strip `(was: <old subject>)' in subject "
                "and use the new one instead?\n\n"
                "Current subject is:   \""
@@ -1579,7 +1598,7 @@ Leading \"Re: \" is not stripped by this function.  Use the function
 
 ;;;###autoload
 (defun message-change-subject (new-subject)
-  "Ask for new Subject: header, append (was: <Old Subject>)."
+  "Ask for NEW-SUBJECT header, append (was: <Old Subject>)."
   (interactive
    (list
     (read-from-minibuffer "New subject: ")))
@@ -1589,7 +1608,7 @@ Leading \"Re: \" is not stripped by this function.  Use the function
         (save-excursion
           (let ((old-subject (message-fetch-field "Subject")))
             (cond ((not old-subject)
-                   (error "No current subject."))
+                   (error "No current subject"))
                   ((not (string-match
                          (concat "^[ \t]*"
                                  (regexp-quote new-subject)
@@ -1619,7 +1638,7 @@ See `message-mark-insert-begin' and `message-mark-insert-end'."
 
 ;;;###autoload
 (defun message-mark-insert-file (file)
-  "Inserts FILE at point, marking it with enclosing tags.
+  "Insert FILE at point, marking it with enclosing tags.
 See `message-mark-insert-begin' and `message-mark-insert-end'."
   (interactive "fFile to insert: ")
     ;; reverse insertion to get correct result.
@@ -1651,7 +1670,7 @@ body, set  `message-archive-note' to nil."
       (message-sort-headers)))
 
 ;;;###autoload
-(defun message-xpost-fup2-header (target-group)
+(defun message-cross-post-followup-to-header (target-group)
   "Mangles FollowUp-To and Newsgroups header to point to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
   (interactive
@@ -1666,19 +1685,19 @@ With prefix-argument just set Follow-Up, don't cross-post."
   (message-goto-newsgroups)
   (beginning-of-line)
   ;; if we already did a crosspost before, kill old target
-  (if (and message-xpost-old-target
+  (if (and message-cross-post-old-target
           (re-search-forward
-           (regexp-quote (concat "," message-xpost-old-target))
+           (regexp-quote (concat "," message-cross-post-old-target))
            nil t))
       (replace-match ""))
   ;; unless (followup is to poster or user explicitly asked not
   ;; to cross-post, or target-group is already in Newsgroups)
   ;; add target-group to Newsgroups line.
   (cond ((and (or
-              ;; def: xpost, req:no
-              (and message-xpost-default (not current-prefix-arg))  
-              ;; def: no-xpost, req:yes
-              (and (not message-xpost-default) current-prefix-arg))
+              ;; def: cross-post, req:no
+              (and message-cross-post-default (not current-prefix-arg))
+              ;; def: no-cross-post, req:yes
+              (and (not message-cross-post-default) current-prefix-arg))
              (not (string-match "poster" target-group))
              (not (string-match (regexp-quote target-group)
                                 (message-fetch-field "Newsgroups"))))
@@ -1692,13 +1711,14 @@ With prefix-argument just set Follow-Up, don't cross-post."
                                 "[ \t]*$")
                         (message-fetch-field "Newsgroups")))
       (insert (concat "\nFollowup-To: " target-group)))
-  (setq message-xpost-old-target target-group))
+  (setq message-cross-post-old-target target-group))
 
 ;;;###autoload
-(defun message-xpost-insert-note (target-group xpost in-old old-groups)
+(defun message-cross-post-insert-note (target-group cross-post in-old
+                                                   old-groups)
   "Insert a in message body note about a set Followup or Crosspost.
 If there have been previous notes, delete them.  TARGET-GROUP specifies the
-group to Followup-To.  When XPOST is t, insert note about
+group to Followup-To.  When CROSS-POST is t, insert note about
 crossposting.  IN-OLD specifies whether TARGET-GROUP is a member of
 OLD-GROUPS.  OLD-GROUPS lists the old-groups the posting would have
 been made to before the user asked for a Crosspost."
@@ -1709,26 +1729,26 @@ been made to before the user asked for a Crosspost."
               nil t))) ; just search in body
     (message-goto-signature)
     (while (re-search-backward
-           (concat "^" (regexp-quote message-xpost-note) ".*")
+           (concat "^" (regexp-quote message-cross-post-note) ".*")
            head t)
       (message-delete-line))
     (message-goto-signature)
     (while (re-search-backward
-           (concat "^" (regexp-quote message-fup2-note) ".*")
+           (concat "^" (regexp-quote message-followup-to-note) ".*")
            head t)
       (message-delete-line))
     ;; insert new note
     (if (message-goto-signature)
        (re-search-backward message-signature-separator))
     (if (or in-old
-           (not xpost)
+           (not cross-post)
            (string-match "^[ \t]*poster[ \t]*$" target-group))
-       (insert (concat message-fup2-note target-group "\n"))
-      (insert (concat message-xpost-note target-group "\n")))))
+       (insert (concat message-followup-to-note target-group "\n"))
+      (insert (concat message-cross-post-note target-group "\n")))))
 
 ;;;###autoload
-(defun message-xpost-fup2 (target-group)
-  "Crossposts message and sets Followup-To to TARGET-GROUP.
+(defun message-cross-post-followup-to (target-group)
+  "Crossposts message and set Followup-To to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
   (interactive
    (list ; Completion based on Gnus
@@ -1744,11 +1764,11 @@ With prefix-argument just set Follow-Up, don't cross-post."
         (save-excursion
           (let* ((old-groups (message-fetch-field "Newsgroups"))
                  (in-old (string-match
-                          (regexp-quote target-group) 
+                          (regexp-quote target-group)
                           (or old-groups ""))))
             ;; check whether target exactly matches old Newsgroups
             (cond ((not old-groups)
-                   (error "No current newsgroup."))
+                   (error "No current newsgroup"))
                   ((or (not in-old)
                        (not (string-match
                              (concat "^[ \t]*"
@@ -1756,13 +1776,13 @@ With prefix-argument just set Follow-Up, don't cross-post."
                                      "[ \t]*$")
                              old-groups)))
                    ;; yes, Newsgroups line must change
-                   (message-xpost-fup2-header target-group)
-                   ;; insert note whether we do xpost or fup2
-                   (funcall message-xpost-note-function
+                   (message-cross-post-followup-to-header target-group)
+                   ;; insert note whether we do cross-post or followup-to
+                   (funcall message-cross-post-note-function
                             target-group
-                            (if (or (and message-xpost-default
+                            (if (or (and message-cross-post-default
                                          (not current-prefix-arg))
-                                    (and (not message-xpost-default)
+                                    (and (not message-cross-post-default)
                                          current-prefix-arg)) t)
                             in-old old-groups))))))))
 
@@ -1896,6 +1916,13 @@ Point is left at the beginning of the narrowed-to region."
                   (message-fetch-field "cc")
                   (message-fetch-field "bcc")))))))
 
+(defun message-subscribed-p ()
+  "Say whether we need to insert a MFT header."
+  (or message-subscribed-regexps
+      message-subscribed-addresses
+      message-subscribed-address-file
+      message-subscribed-address-functions))
+
 (defun message-next-header ()
   "Go to the beginning of the next header."
   (beginning-of-line)
@@ -1968,30 +1995,33 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
-  (define-key message-mode-map "\C-c\C-f\C-i" 'message-insert-or-toggle-importance)
-  (define-key message-mode-map "\C-c\C-f\C-a" 'message-gen-unsubscribed-mft)
+  (define-key message-mode-map "\C-c\C-f\C-i"
+    'message-insert-or-toggle-importance)
+  (define-key message-mode-map "\C-c\C-f\C-a"
+    'message-generate-unsubscribed-mail-followup-to)
 
   ;; modify headers (and insert notes in body)
   (define-key message-mode-map "\C-c\C-fs"    'message-change-subject)
   ;;
-  (define-key message-mode-map "\C-c\C-fx"    'message-xpost-fup2)
-  ;; prefix+message-xpost-fup2 = same w/o xpost
+  (define-key message-mode-map "\C-c\C-fx"    'message-cross-post-followup-to)
+  ;; prefix+message-cross-post-followup-to = same w/o cross-post
   (define-key message-mode-map "\C-c\C-ft"    'message-reduce-to-to-cc)
   (define-key message-mode-map "\C-c\C-fa"    'message-add-archive-header)
   ;; mark inserted text
   (define-key message-mode-map "\C-c\M-m" 'message-mark-inserted-region)
   (define-key message-mode-map "\C-c\M-f" 'message-mark-insert-file)
-  
+
   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
 
   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
-  (define-key message-mode-map "\C-c\C-p" 'message-insert-wide-reply)
+  (define-key message-mode-map "\C-c\C-fw" 'message-insert-wide-reply)
   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
   (define-key message-mode-map "\C-c\C-l" 'message-to-list-only)
 
   (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
-  (define-key message-mode-map "\C-c\M-n" 'message-insert-disposition-notification-to)
+  (define-key message-mode-map "\C-c\M-n"
+    'message-insert-disposition-notification-to)
 
   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
   (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer)
@@ -2023,7 +2053,6 @@ Point is left at the beginning of the narrowed-to region."
 (easy-menu-define
   message-mode-menu message-mode-map "Message Menu."
   `("Message"
-    ["Sort Headers" message-sort-headers t]
     ["Yank Original" message-yank-original t]
     ["Fill Yanked Message" message-fill-yanked-message t]
     ["Insert Signature" message-insert-signature t]
@@ -2034,24 +2063,14 @@ Point is left at the beginning of the narrowed-to region."
     ["Kill To Signature" message-kill-to-signature t]
     ["Newline and Reformat" message-newline-and-reformat t]
     ["Rename buffer" message-rename-buffer t]
-    ["Flag As Important" message-insert-importance-high
-     ,@(if (featurep 'xemacs) '(t)
-        '(:help "Mark this message as important"))]
-    ["Flag As Unimportant" message-insert-importance-low
-     ,@(if (featurep 'xemacs) '(t)
-        '(:help "Mark this message as unimportant"))]
-    ["Request Receipt"
-     message-insert-disposition-notification-to
-     ,@(if (featurep 'xemacs) '(t)
-        '(:help "Request a Disposition Notification of this article"))]
     ["Spellcheck" ispell-message
      ,@(if (featurep 'xemacs) '(t)
         '(:help "Spellcheck this message"))]
     "----"
-    ["Insert Region Marked" message-mark-inserted-region 
+    ["Insert Region Marked" message-mark-inserted-region
      ,@(if (featurep 'xemacs) '(t)
         '(:help "Mark region with enclosing tags"))]
-    ["Insert File Marked..." message-mark-insert-file 
+    ["Insert File Marked..." message-mark-insert-file
      ,@(if (featurep 'xemacs) '(t)
         '(:help "Insert file at point marked with enclosing tags"))]
     "----"
@@ -2070,7 +2089,7 @@ Point is left at the beginning of the narrowed-to region."
 
 (easy-menu-define
   message-mode-field-menu message-mode-map ""
-  '("Field"
+  `("Field"
     ["Fetch To" message-insert-to t]
     ["Fetch Newsgroups" message-insert-newsgroups t]
     "----"
@@ -2082,14 +2101,24 @@ Point is left at the beginning of the narrowed-to region."
     ["Bcc" message-goto-bcc t]
     ["Fcc" message-goto-fcc t]
     ["Reply-To" message-goto-reply-to t]
+    ["Flag As Important" message-insert-importance-high
+     ,@(if (featurep 'xemacs) '(t)
+        '(:help "Mark this message as important"))]
+    ["Flag As Unimportant" message-insert-importance-low
+     ,@(if (featurep 'xemacs) '(t)
+        '(:help "Mark this message as unimportant"))]
+    ["Request Receipt"
+     message-insert-disposition-notification-to
+     ,@(if (featurep 'xemacs) '(t)
+        '(:help "Request a receipt notification"))]
     "----"
     ;; (typical) news stuff
     ["Summary" message-goto-summary t]
     ["Keywords" message-goto-keywords t]
     ["Newsgroups" message-goto-newsgroups t]
     ["Followup-To" message-goto-followup-to t]
-    ;; ["Followup-To (with note in body)" message-xpost-fup2 t]
-    ["Crosspost / Followup-To..." message-xpost-fup2 t]
+    ;; ["Followup-To (with note in body)" message-cross-post-followup-to t]
+    ["Crosspost / Followup-To..." message-cross-post-followup-to t]
     ["Distribution" message-goto-distribution t]
     ["X-No-Archive:" message-add-archive-header t ]
     "----"
@@ -2098,8 +2127,9 @@ Point is left at the beginning of the narrowed-to region."
     ["Mail-Followup-To" message-goto-mail-followup-to t]
     ["Reduce To: to Cc:" message-reduce-to-to-cc t]
     "----"
-    ["Body" message-goto-body t]
-    ["Signature" message-goto-signature t]))
+    ["Sort Headers" message-sort-headers t]
+    ["Goto Body" message-goto-body t]
+    ["Goto Signature" message-goto-signature t]))
 
 (defvar message-tool-bar-map nil)
 
@@ -2396,15 +2426,15 @@ return nil."
     (goto-char (point-max))
     nil))
 
-(defun message-gen-unsubscribed-mft (&optional include-cc)
+(defun message-generate-unsubscribed-mail-followup-to (&optional include-cc)
   "Insert a reasonable MFT header in a post to an unsubscribed list.
 When making original posts to a mailing list you are not subscribed to,
 you have to type in a MFT header by hand.  The contents, usually, are
 the addresses of the list and your own address.  This function inserts
 such a header automatically.  It fetches the contents of the To: header
-in the current mail buffer, and appends the current user-mail-address.
+in the current mail buffer, and appends the current `user-mail-address'.
 
-If the optional argument `include-cc' is non-nil, the addresses in the
+If the optional argument INCLUDE-CC is non-nil, the addresses in the
 Cc: header are also put into the MFT."
 
   (interactive "P")
@@ -2661,7 +2691,9 @@ Prefix arg means justify as well."
       ;; Insert the signature.
       (unless (bolp)
        (insert "\n"))
-      (insert "\n-- \n")
+      (when message-signature-insert-empty-line
+       (insert "\n"))
+      (insert "-- \n")
       (if (eq signature t)
          (insert-file-contents message-signature-file)
        (insert signature))
@@ -3104,6 +3136,7 @@ It should typically alter the sending method in some way or other."
       (when (funcall (cadr elem))
        (when (and (or (not (memq (car elem)
                                  message-sent-message-via))
+                      (not (message-fetch-field "supersedes"))
                       (if (or (message-gnksa-enable-p 'multiple-copies)
                               (not (eq (car elem) 'news)))
                           (y-or-n-p
@@ -3197,7 +3230,7 @@ It should typically alter the sending method in some way or other."
                (or (< (mm-char-int char) 128)
                    (and (mm-multibyte-p)
                         (memq (char-charset char)
-                              '(eight-bit-control eight-bit-graphic 
+                              '(eight-bit-control eight-bit-graphic
                                                   control-1)))))
          (add-text-properties (point) (1+ (point)) '(highlight t))
          (setq found t))
@@ -3205,7 +3238,7 @@ It should typically alter the sending method in some way or other."
        (skip-chars-forward mm-7bit-chars))
       (when found
        (setq choice
-             (gnus-multiple-choice 
+             (gnus-multiple-choice
               "Illegible text found. Continue posting? "
               '((?d "Remove and continue posting")
                 (?r "Replace with dots and continue posting")
@@ -3343,14 +3376,11 @@ It should typically alter the sending method in some way or other."
     (save-restriction
       (message-narrow-to-headers)
       ;; Generate the Mail-Followup-To header if the header is not there...
-      (if (and (or message-subscribed-regexps
-                  message-subscribed-addresses
-                  message-subscribed-address-file
-                  message-subscribed-address-functions)
+      (if (and (message-subscribed-p)
               (not (mail-fetch-field "mail-followup-to")))
          (setq headers
                (cons
-                (cons "Mail-Followup-To" (message-make-mft))
+                (cons "Mail-Followup-To" (message-make-mail-followup-to))
                 message-required-mail-headers))
        ;; otherwise, delete the MFT header if the field is empty
        (when (equal "" (mail-fetch-field "mail-followup-to"))
@@ -3384,6 +3414,7 @@ It should typically alter the sending method in some way or other."
          ;; require one newline at the end.
          (or (= (preceding-char) ?\n)
              (insert ?\n))
+         (message-cleanup-headers)
          (when
              (save-restriction
                (message-narrow-to-headers)
@@ -3748,6 +3779,24 @@ Otherwise, generate and save a value for `canlock-password' first."
         (y-or-n-p
          "The control code \"cmsg\" is in the subject.  Really post? ")
        t))
+   ;; Check long header lines.
+   (message-check 'long-header-lines
+     (let ((start (point))
+          (header nil)
+          (length 0)
+          found)
+       (while (and (not found)
+                  (re-search-forward "^\\([^ \t:]+\\): " nil t))
+        (if (> (- (point) (match-beginning 0)) 998)
+            (setq found t
+                  length (- (point) (match-beginning 0)))
+          (setq header (match-string-no-properties 1)))
+        (setq start (match-beginning 0))
+        (forward-line 1))
+       (if found
+          (y-or-n-p (format "Your %s header is too long (%d).  Really post? "
+                            header length))
+        t)))
    ;; Check for multiple identical headers.
    (message-check 'multiple-headers
      (let (found)
@@ -3881,6 +3930,18 @@ Otherwise, generate and save a value for `canlock-password' first."
           (if (= (length errors) 1) "this" "these")
           (if (= (length errors) 1) "" "s")
           (mapconcat 'identity errors ", ")))))))
+   ;; Check continuation headers.
+   (message-check 'continuation-headers
+     (goto-char (point-min))
+     (let ((do-posting t))
+       (while (re-search-forward "^[^ \t\n][^:\n]*$" nil t)
+        (if (y-or-n-p "Fix continuation lines? ")
+            (progn
+              (goto-char (match-beginning 0))
+              (insert " "))
+          (unless (y-or-n-p "Send anyway? ")
+            (setq do-posting nil))))
+       do-posting))
    ;; Check the Newsgroups & Followup-To headers for syntax errors.
    (message-check 'valid-newsgroups
      (let ((case-fold-search t)
@@ -4439,7 +4500,7 @@ give as trustworthy answer as possible."
   "Send a message to the list only.
 Remove all addresses but the list address from To and Cc headers."
   (interactive)
-  (let ((listaddr (message-make-mft t)))
+  (let ((listaddr (message-make-mail-followup-to t)))
     (when listaddr
       (save-excursion
        (message-remove-header "to")
@@ -4447,10 +4508,10 @@ Remove all addresses but the list address from To and Cc headers."
        (message-position-on-field "To" "X-Draft-From")
        (insert listaddr)))))
 
-(defun message-make-mft (&optional only-show-subscribed)
-  "Return the Mail-Followup-To header. If passed the optional
-argument `only-show-subscribed' only return the subscribed address (and
-not the additional To and Cc header contents)."
+(defun message-make-mail-followup-to (&optional only-show-subscribed)
+  "Return the Mail-Followup-To header.
+If passed the optional argument ONLY-SHOW-SUBSCRIBED only return the
+subscribed address (and not the additional To and Cc header contents)."
   (let* ((case-fold-search t)
         (to (message-fetch-field "To"))
         (cc (message-fetch-field "cc"))
@@ -4512,6 +4573,7 @@ Headers already prepared in the buffer are not modified."
           (User-Agent message-newsreader)
           (Expires (message-make-expires))
           (case-fold-search t)
+          (optionalp nil)
           header value elem)
       ;; First we remove any old generated headers.
       (let ((headers message-deletable-headers))
@@ -4533,7 +4595,8 @@ Headers already prepared in the buffer are not modified."
        (setq elem (pop headers))
        (if (consp elem)
            (if (eq (car elem) 'optional)
-               (setq header (cdr elem))
+               (setq header (cdr elem)
+                     optionalp t)
              (setq header (car elem)))
          (setq header elem))
        (when (or (not (re-search-forward
@@ -4549,26 +4612,32 @@ Headers already prepared in the buffer are not modified."
                    ;; The header was found.  We insert a space after the
                    ;; colon, if there is none.
                    (if (/= (char-after) ? ) (insert " ") (forward-char 1))
-                   ;; Find out whether the header is empty...
+                   ;; Find out whether the header is empty.
                    (looking-at "[ \t]*\n[^ \t]")))
          ;; So we find out what value we should insert.
          (setq value
                (cond
-                ((and (consp elem) (eq (car elem) 'optional))
+                ((and (consp elem)
+                      (eq (car elem) 'optional))
                  ;; This is an optional header.  If the cdr of this
                  ;; is something that is nil, then we do not insert
                  ;; this header.
                  (setq header (cdr elem))
-                 (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
-                     (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
+                 (or (and (message-functionp (cdr elem))
+                          (funcall (cdr elem)))
+                     (and (boundp (cdr elem))
+                          (symbol-value (cdr elem)))))
                 ((consp elem)
                  ;; The element is a cons.  Either the cdr is a
                  ;; string to be inserted verbatim, or it is a
                  ;; function, and we insert the value returned from
                  ;; this function.
-                 (or (and (stringp (cdr elem)) (cdr elem))
-                     (and (fboundp (cdr elem)) (funcall (cdr elem)))))
-                ((and (boundp header) (symbol-value header))
+                 (or (and (stringp (cdr elem))
+                          (cdr elem))
+                     (and (message-functionp (cdr elem))
+                          (funcall (cdr elem)))))
+                ((and (boundp header)
+                      (symbol-value header))
                  ;; The element is a symbol.  We insert the value
                  ;; of this symbol, if any.
                  (symbol-value header))
@@ -4585,17 +4654,25 @@ Headers already prepared in the buffer are not modified."
                  (progn
                    ;; This header didn't exist, so we insert it.
                    (goto-char (point-max))
-                   (insert (if (stringp header) header (symbol-name header))
-                           ": " value)
-                   ;; We check whether the value was ended by a
-                   ;; newline.  If now, we insert one.
-                   (unless (bolp)
-                     (insert "\n"))
-                   (forward-line -1))
+                   (let ((formatter
+                          (cdr (assq header message-header-format-alist))))
+                     (if formatter
+                         (funcall formatter header value)
+                       (insert (if (stringp header)
+                                   header (symbol-name header))
+                               ": " value))
+                     ;; We check whether the value was ended by a
+                     ;; newline.  If now, we insert one.
+                     (unless (bolp)
+                       (insert "\n"))
+                     (forward-line -1)))
                ;; The value of this header was empty, so we clear
                ;; totally and insert the new value.
                (delete-region (point) (gnus-point-at-eol))
-               (insert value))
+               ;; If the header is optional, and the header was
+               ;; empty, we con't insert it anyway.
+               (unless optionalp
+                 (insert value)))
              ;; Add the deletable property to the headers that require it.
              (and (memq header message-deletable-headers)
                   (progn (beginning-of-line) (looking-at "[^:]+: "))
@@ -4925,6 +5002,31 @@ than 988 characters long, and if they are not, trim them until they are."
                              headers)
                      nil switch-function yank-action actions)))))
 
+(defun message-headers-to-generate (headers included-headers excluded-headers)
+  "Return a list that includes all headers from HEADERS.
+If INCLUDED-HEADERS is a list, just include those headers.  If if is
+t, include all headers.  In any case, headers from EXCLUDED-HEADERS
+are not included."
+  (let ((result nil)
+       header-name)
+    (dolist (header headers)
+      (setq header-name (cond
+                        ((and (consp header)
+                              (eq (car header) 'optional))
+                         ;; On the form (optional . Header)
+                         (cdr header))
+                        ((consp header)
+                         ;; On the form (Header . function)
+                         (car header))
+                        (t
+                         ;; Just a Header.
+                         header)))
+      (when (and (not (memq header-name excluded-headers))
+                (or (eq included-headers t)
+                    (memq header-name included-headers)))
+       (push header result)))
+    (nreverse result)))
+
 (defun message-setup-1 (headers &optional replybuffer actions)
   (dolist (action actions)
     (condition-case nil
@@ -4959,18 +5061,22 @@ than 988 characters long, and if they are not, trim them until they are."
       (or (bolp) (insert ?\n)))
     (when message-generate-headers-first
       (message-generate-headers
-       (delq 'Lines
-            (delq 'Subject
-                  (copy-sequence message-required-news-headers))))))
+       (message-headers-to-generate
+       (append message-required-news-headers
+               message-required-headers)
+       message-generate-headers-first
+       '(Lines Subject)))))
   (when (message-mail-p)
     (when message-default-mail-headers
       (insert message-default-mail-headers)
       (or (bolp) (insert ?\n)))
     (when message-generate-headers-first
       (message-generate-headers
-       (delq 'Lines
-            (delq 'Subject
-                  (copy-sequence message-required-mail-headers))))))
+       (message-headers-to-generate
+       (append message-required-mail-headers
+               message-required-headers)
+       message-generate-headers-first
+       '(Lines Subject)))))
   (run-hooks 'message-signature-setup-hook)
   (message-insert-signature)
   (save-restriction
@@ -5299,7 +5405,7 @@ If TO-NEWSGROUPS, use that as the new Newsgroups line."
 
     (setq message-reply-headers
          (vector 0 subject from date message-id references 0 0 ""))
-    
+
     (message-setup
      `((Subject . ,subject)
        ,@(cond
@@ -5633,7 +5739,7 @@ Optional DIGEST will use digest to forward."
             (mm-disable-multibyte-mule4)
             (insert
              (with-current-buffer forward-buffer
-               (mm-string-as-unibyte (buffer-string))))
+               (mm-with-unibyte-current-buffer-mule4 (buffer-string))))
             (mm-enable-multibyte-mule4)
             (mime-to-mml)
             (goto-char (point-min))
@@ -5896,7 +6002,7 @@ which specify the range to operate on."
 (defun message-tool-bar-map ()
   (or message-tool-bar-map
       (setq message-tool-bar-map
-           (and 
+           (and
             (condition-case nil (require 'tool-bar) (error nil))
             (fboundp 'tool-bar-add-item-from-menu)
             tool-bar-mode
@@ -5917,6 +6023,9 @@ which specify the range to operate on."
                'mml-attach-file "attach" tool-bar-map mml-mode-map)
               (message-tool-bar-local-item-from-menu
                'ispell-message "spell" tool-bar-map message-mode-map)
+              (message-tool-bar-local-item-from-menu
+               'mml-preview "preview"
+               tool-bar-map mml-mode-map)
               (message-tool-bar-local-item-from-menu
                'message-insert-importance-high "important"
                tool-bar-map message-mode-map)
@@ -5939,9 +6048,9 @@ which specify the range to operate on."
 (defcustom message-completion-alist
   (list (cons message-newgroups-header-regexp 'message-expand-group)
        '("^\\(Resent-\\)?\\(To\\|B?Cc\\):" . message-expand-name)
-       '("^\\(Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):" 
+       '("^\\(Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):"
          . message-expand-name)
-       '("^\\(Disposition-Notification-To\\|Return-Receipt-To\\):" 
+       '("^\\(Disposition-Notification-To\\|Return-Receipt-To\\):"
          . message-expand-name))
   "Alist of (RE . FUN).  Use FUN for completion on header lines matching RE."
   :group 'message