Sync up with Semi-gnus 6.8.12.
[elisp/gnus.git-] / lisp / message.el
index b220960..33a7a5b 100644 (file)
@@ -3,6 +3,7 @@
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
+;;         Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
 ;; Keywords: mail, news, MIME
 
 ;; This file is part of GNU Emacs.
@@ -182,11 +183,11 @@ shorten-followup-to existing-newsgroups buffer-file-name unchanged."
 (defcustom message-required-news-headers
   '(From Newsgroups Subject Date Message-ID
         (optional . Organization) Lines
-        (optional . X-Newsreader))
+        (optional . User-Agent))
   "*Headers to be generated or prompted for when posting an article.
 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
-X-Newsreader are optional.  If don't you want message to insert some
+User-Agent are optional.  If don't you want message to insert some
 header, remove it from this list."
   :group 'message-news
   :group 'message-headers
@@ -194,10 +195,10 @@ header, remove it from this list."
 
 (defcustom message-required-mail-headers
   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
-        (optional . X-Mailer))
+        (optional . User-Agent))
   "*Headers to be generated or prompted for when mailing a message.
 RFC822 required that From, Date, To, Subject and Message-ID be
-included.  Organization, Lines and X-Mailer are optional."
+included.  Organization, Lines and User-Agent are optional."
   :group 'message-mail
   :group 'message-headers
   :type '(repeat sexp))
@@ -227,6 +228,52 @@ any confusion."
   :group 'message-interface
   :type 'regexp)
 
+(defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
+  "*Regexp matching \"Re: \" in the subject line."
+  :group 'message-various
+  :type 'regexp)
+
+;;; Some sender agents encode the whole subject including leading "Re: ".
+;;; And if followup agent does not decode it for some reason (e.g. unknown
+;;; charset) and just add a new "Re: " in front of the encoded-word, the
+;;; result will contain multiple "Re: "'s.
+(defcustom message-subject-encoded-re-regexp
+  (concat
+   "^[ \t]*"
+   (regexp-quote "=?")
+   "[-!#$%&'*+0-9A-Z^_`a-z{|}~]+" ; charset
+   (regexp-quote "?")
+   "\\("
+   "[Bb]" (regexp-quote "?") ; B encoding
+   "\\(\\(CQk\\|CSA\\|IAk\\|ICA\\)[Jg]\\)*" ; \([ \t][ \t][ \t]\)*
+   "\\("
+   "[Uc][km]U6" ; [Rr][Ee]:
+   "\\|"
+   "\\(C[VX]\\|I[FH]\\)J[Fl]O[g-v]" ; [ \t][Rr][Ee]:
+   "\\|"
+   "\\(CQl\\|CSB\\|IAl\\|ICB\\)[Sy][RZ]T[o-r]" ; [ \t][ \t][Rr][Ee]:
+   "\\)"
+   "\\|"
+   "[Qb]" (regexp-quote "?") ; Q encoding
+   "\\(_\\|=09\\|=20\\)*"
+   "\\([Rr]\\|=[57]2\\)\\([Ee]\\|=[46]5\\)\\(:\\|=3[Aa]\\)"
+   "\\)"
+   )
+  "*Regexp matching \"Re: \" in the subject line.
+Unlike `message-subject-re-regexp', this regexp matches \"Re: \" within
+an encoded-word."
+  :group 'message-various
+  :type 'regexp)
+
+(defcustom message-use-subject-re t
+  "*If t, remove any (buggy) \"Re: \"'s from the subject of the precursor
+and add a new \"Re: \".  If it is nil, use the subject \"as-is\".  If it
+is the symbol `guess', try to detect \"Re: \" within an encoded-word."
+  :group 'message-various
+  :type '(choice (const :tag "off" nil)
+                 (const :tag "on" t)
+                 (const guess)))
+
 ;;;###autoload
 (defcustom message-signature-separator "^-- *$"
   "Regexp matching the signature separator."
@@ -246,7 +293,7 @@ nil means let mailer mail back a message to report errors."
   :type 'boolean)
 
 (defcustom message-generate-new-buffers t
-  "*Non-nil means that a new message buffer will be created whenever `mail-setup' is called.
+  "*Non-nil means that a new message buffer will be created whenever `message-setup' is called.
 If this is a function, call that function with three parameters:  The type,
 the to address and the group name.  (Any of these may be nil.)  The function
 should return the new buffer name."
@@ -297,11 +344,32 @@ If t, use `message-user-organization-file'."
   :type 'boolean)
 
 (defcustom message-included-forward-headers
-  "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-Transfer-Encoding:\\|^Content-Type:\\|^MIME-Version:"
+  "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Mail-Copies-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"
   "*Regexp matching headers to be included in forwarded messages."
   :group 'message-forwarding
   :type 'regexp)
 
+(defcustom message-make-forward-subject-function 
+  'message-forward-subject-author-subject
+ "*A list of functions that are called to generate a subject header for forwarded messages.
+The subject generated by the previous function is passed into each
+successive function.
+
+The provided functions are:
+
+* message-forward-subject-author-subject (Source of article (author or
+      newsgroup)), in brackets followed by the subject
+* message-forward-subject-fwd (Subject of article with 'Fwd:' prepended
+      to it."
+ :group 'message-forwarding
+ :type '(radio (function-item message-forward-subject-author-subject)
+              (function-item message-forward-subject-fwd)))
+
+(defcustom message-wash-forwarded-subjects nil
+  "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
+  :group 'message-forwarding
+  :type 'boolean)
+
 (defcustom message-ignored-resent-headers "^Return-Receipt"
   "*All headers that match this regexp will be deleted when resending a message."
   :group 'message-interface
@@ -373,22 +441,45 @@ always query the user whether to use the value.  If it is the symbol
 `use', always use the value."
   :group 'message-interface
   :type '(choice (const :tag "ignore" nil)
-                (const use)
-                (const ask)))
+                (const :tag "maybe" t)
+                (const :tag "always" use)
+                (const :tag "ask" ask)))
+
+(defcustom message-use-mail-copies-to 'ask
+  "*Specifies what to do with Mail-Copies-To header.
+If nil, always ignore the header.  If it is t, use its value, but
+query before using the value other than \"always\" or \"never\".
+If it is the symbol `ask', always query the user whether to use
+the value.  If it is the symbol `use', always use the value."
+  :group 'message-interface
+  :type '(choice (const :tag "ignore" nil)
+                (const :tag "maybe" t)
+                (const :tag "always" use)
+                (const :tag "ask" ask)))
 
 (defcustom message-use-mail-followup-to 'ask
-  "*Specifies what to do with Mail-Followup-To header."
+  "*Specifies what to do with Mail-Followup-To header.
+If nil, always ignore the header.  If it is the symbol `ask', always
+query the user whether to use the value.  If it is t or the symbol
+`use', always use the value."
   :group 'message-interface
   :type '(choice (const :tag "ignore" nil)
-                (const use)
-                (const ask)))
+                (const :tag "maybe" t)
+                (const :tag "always" use)
+                (const :tag "ask" ask)))
 
+;;; XXX: 'ask and 'use are not implemented yet.
 (defcustom message-use-mail-reply-to 'ask
-  "*Specifies what to do with Mail-Reply-To header."
+  "*Specifies what to do with Mail-Reply-To/Reply-To header.
+If nil, always ignore the header.  If it is t or the symbol `use', use
+its value.  If it is the symbol `ask', always query the user whether to
+use the value.  Not that if \"Reply-To\" is marked as \"broken\", its value
+is never used."
   :group 'message-interface
   :type '(choice (const :tag "ignore" nil)
-                (const use)
-                (const ask)))
+                (const :tag "maybe" t)
+                (const :tag "always" use)
+                (const :tag "ask" ask)))
 
 ;; stuff relating to broken sendmail in MMDF
 (defcustom message-sendmail-f-is-evil nil
@@ -417,6 +508,7 @@ might set this variable to '(\"-f\" \"you@some.where\")."
 (defvar gnus-select-method)
 (defcustom message-post-method
   (cond ((and (boundp 'gnus-post-method)
+             (listp gnus-post-method)
              gnus-post-method)
         gnus-post-method)
        ((boundp 'gnus-select-method)
@@ -485,16 +577,12 @@ Used by `message-yank-original' via `message-yank-cite'."
   :type 'integer)
 
 ;;;###autoload
-(defcustom message-cite-function
-  (if (and (boundp 'mail-citation-hook)
-          mail-citation-hook)
-      mail-citation-hook
-    'message-cite-original)
+(defcustom message-cite-function 'message-cite-original
   "*Function for citing an original message.
-Pre-defined functions include `message-cite-original' and
-`message-cite-original-without-signature'."
+Predefined functions include `message-cite-original' and
+`message-cite-original-without-signature'.
+Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
   :type '(radio (function-item message-cite-original)
-               (function-item message-cite-original-without-signature)
                (function-item sc-cite-original)
                (function :tag "Other"))
   :group 'message-insertion)
@@ -550,8 +638,6 @@ If stringp, use this; if non-nil, use no host name (user name only)."
 
 (defvar message-reply-buffer nil)
 (defvar message-reply-headers nil)
-(defvar message-newsreader nil)
-(defvar message-mailer nil)
 (defvar message-sent-message-via nil)
 (defvar message-checksum nil)
 (defvar message-send-actions nil
@@ -616,6 +702,10 @@ actually occur."
   :group 'message-sending
   :type 'sexp)
 
+;;; XXX: This symbol is overloaded!  See below.
+(defvar message-user-agent nil
+  "String of the form of PRODUCT/VERSION.  Used for User-Agent header field.")
+
 ;; Ignore errors in case this is used in Emacs 19.
 ;; Don't use ignore-errors because this is copied into loaddefs.el.
 ;;;###autoload
@@ -786,7 +876,10 @@ Defaults to `text-mode-abbrev-table'.")
     `((,(concat "^\\([Tt]o:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-to-face nil t))
-      (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|[Mm]ail-[Rr]eply-[Tt]o:\\|[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)
+      (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"
+               "[Mm]ail-[Cc]opies-[Tt]o:\\|"
+               "[Mm]ail-[Rr]eply-[Tt]o:\\|"
+               "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-cc-face nil t))
       (,(concat "^\\([Ss]ubject:\\)" content)
@@ -936,9 +1029,9 @@ The cdr of ech entry is a function for applying the face to a region.")
     (Lines)
     (Expires)
     (Message-ID)
-    (References . message-fill-references)
-    (X-Mailer)
-    (X-Newsreader))
+    ;; (References . message-shorten-references)
+    (References . message-fill-header)
+    (User-Agent))
   "Alist used for formatting headers.")
 
 (eval-and-compile
@@ -954,6 +1047,7 @@ The cdr of ech entry is a function for applying the face to a region.")
   (autoload 'nndraft-request-expire-articles "nndraft")
   (autoload 'gnus-open-server "gnus-int")
   (autoload 'gnus-request-post "gnus-int")
+  (autoload 'gnus-copy-article-buffer "gnus-msg")
   (autoload 'gnus-alive-p "gnus-util")
   (autoload 'rmail-output "rmail"))
 
@@ -1061,7 +1155,7 @@ The cdr of ech entry is a function for applying the face to a region.")
 
 (defun message-strip-subject-re (subject)
   "Remove \"Re:\" from subject lines."
-  (if (string-match "^[Rr][Ee]: *" subject)
+  (if (string-match message-subject-re-regexp subject)
       (substring subject (match-end 0))
     subject))
 
@@ -1071,7 +1165,7 @@ If REGEXP, HEADER is a regular expression.
 If FIRST, only remove the first instance of the header.
 Return the number of headers removed."
   (goto-char (point-min))
-  (let ((regexp (if is-regexp header (concat "^" header ":")))
+  (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
        (number 0)
        (case-fold-search t)
        last)
@@ -1202,8 +1296,8 @@ Return the number of headers removed."
   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
-  (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
-  ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)
+  ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
+  (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)
   (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-n" 'message-goto-newsgroups)
   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
@@ -1252,7 +1346,8 @@ Return the number of headers removed."
    ["Spellcheck" ispell-message t]
    "----"
    ["Send Message" message-send-and-exit t]
-   ["Abort Message" message-dont-send t]))
+   ["Abort Message" message-dont-send t]
+   ["Kill Message" message-kill-buffer t]))
 
 (easy-menu-define
  message-mode-field-menu message-mode-map ""
@@ -1264,8 +1359,9 @@ Return the number of headers removed."
    ["Subject" message-goto-subject t]
    ["Cc" message-goto-cc t]
    ["Reply-To" message-goto-reply-to t]
-   ["Mail-Followup-To" message-goto-mail-followup-to t]
    ["Mail-Reply-To" message-goto-mail-reply-to t]
+   ["Mail-Followup-To" message-goto-mail-followup-to t]
+   ["Mail-Copies-To" message-goto-mail-copies-to t]
    ["Summary" message-goto-summary t]
    ["Keywords" message-goto-keywords t]
    ["Newsgroups" message-goto-newsgroups t]
@@ -1328,23 +1424,21 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
        facemenu-remove-face-function t)
   (make-local-variable 'paragraph-separate)
   (make-local-variable 'paragraph-start)
+  ;; `-- ' precedes the signature.  `-----' appears at the start of the
+  ;; lines that delimit forwarded messages.
+  ;; Lines containing just >= 3 dashes, perhaps after whitespace,
+  ;; are also sometimes used and should be separators.
   (setq paragraph-start
        (concat (regexp-quote mail-header-separator)
-               "$\\|[ \t]*[-_][-_][-_]+$\\|"
-               "-- $\\|"
+               "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
+               "-- $\\|---+$\\|"
+               page-delimiter
                ;;!!! Uhm... shurely this can't be right?
-               "[> " (regexp-quote message-yank-prefix) "]+$\\|"
-               paragraph-start))
-  (setq paragraph-separate
-       (concat (regexp-quote mail-header-separator)
-               "$\\|[ \t]*[-_][-_][-_]+$\\|"
-               "-- $\\|"
-               "[> " (regexp-quote message-yank-prefix) "]+$\\|"
-               paragraph-separate))
+               "[> " (regexp-quote message-yank-prefix) "]+$"))
+  (setq paragraph-separate paragraph-start)
   (make-local-variable 'message-reply-headers)
   (setq message-reply-headers nil)
-  (make-local-variable 'message-newsreader)
-  (make-local-variable 'message-mailer)
+  (make-local-variable 'message-user-agent)
   (make-local-variable 'message-post-method)
   (make-local-variable 'message-sent-message-via)
   (setq message-sent-message-via nil)
@@ -1360,7 +1454,7 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (when (eq message-mail-alias-type 'abbrev)
     (if (fboundp 'mail-abbrevs-setup)
        (mail-abbrevs-setup)
-      (funcall (intern "mail-aliases-setup"))))
+      (mail-aliases-setup)))
   (message-set-auto-save-file-name)
   (unless (string-match "XEmacs" emacs-version)
     (set (make-local-variable 'font-lock-defaults)
@@ -1414,15 +1508,20 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (interactive)
   (message-position-on-field "Reply-To" "Subject"))
 
+(defun message-goto-mail-reply-to ()
+  "Move point to the Mail-Reply-To header."
+  (interactive)
+  (message-position-on-field "Mail-Reply-To" "Subject"))
+
 (defun message-goto-mail-followup-to ()
   "Move point to the Mail-Followup-To header."
   (interactive)
   (message-position-on-field "Mail-Followup-To" "Subject"))
 
-(defun message-goto-mail-reply-to ()
-  "Move point to the Mail-Reply-To header."
+(defun message-goto-mail-copies-to ()
+  "Move point to the Mail-Copies-To header."
   (interactive)
-  (message-position-on-field "Mail-Reply-To" "Subject"))
+  (message-position-on-field "Mail-Copies-To" "Subject"))
 
 (defun message-goto-newsgroups ()
   "Move point to the Newsgroups header."
@@ -1456,13 +1555,22 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (goto-char (point-min))
   (search-forward (concat "\n" mail-header-separator "\n") nil t))
 
+(defun message-goto-eoh ()
+  "Move point to the end of the headers."
+  (interactive)
+  (message-goto-body)
+  (forward-line -2))
+
 (defun message-goto-signature ()
-  "Move point to the beginning of the message signature."
+  "Move point to the beginning of the message signature.
+If there is no signature in the article, go to the end and
+return nil."
   (interactive)
   (goto-char (point-min))
   (if (re-search-forward message-signature-separator nil t)
       (forward-line 1)
-    (goto-char (point-max))))
+    (goto-char (point-max))
+    nil))
 
 \f
 
@@ -1502,16 +1610,17 @@ With the prefix argument FORCE, insert the header anyway."
   (interactive "r")
   (save-excursion
     (goto-char end)
-    (delete-region (point) (progn (message-goto-signature)
-                                 (forward-line -2)
-                                 (point)))
+    (delete-region (point) (if (not (message-goto-signature))
+                              (point)
+                            (forward-line -2)
+                            (point)))
     (insert "\n")
     (goto-char beg)
     (delete-region beg (progn (message-goto-body)
                              (forward-line 2)
                              (point))))
-  (message-goto-signature)
-  (forward-line -2))
+  (when (message-goto-signature)
+    (forward-line -2)))
 
 (defun message-kill-to-signature ()
   "Deletes all text up to the signature."
@@ -1735,6 +1844,7 @@ However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
          (forward-line 1))))
     (goto-char start)))
 
+(defvar gnus-article-copy)
 (defun message-yank-original (&optional arg)
   "Insert the message being replied to, if any.
 Puts point before the text and mark after.
@@ -1749,6 +1859,8 @@ prefix, and don't delete any headers."
   (let ((modified (buffer-modified-p)))
     (when (and message-reply-buffer
               message-cite-function)
+      (gnus-copy-article-buffer)
+      (setq message-reply-buffer gnus-article-copy)
       (delete-windows-on message-reply-buffer t)
       (insert-buffer message-reply-buffer)
       (funcall message-cite-function)
@@ -1756,7 +1868,7 @@ prefix, and don't delete any headers."
       (unless (bolp)
        (insert ?\n))
       (unless modified
-       (setq message-checksum (cons (message-checksum) (buffer-size)))))))
+       (setq message-checksum (message-checksum))))))
 
 (defun message-cite-original-without-signature ()
   "Cite function in the standard Message manner."
@@ -1769,6 +1881,11 @@ prefix, and don't delete any headers."
             (list message-indent-citation-function)))))
     (goto-char end)
     (when (re-search-backward "^-- $" start t)
+      ;; Also peel off any blank lines before the signature.
+      (forward-line -1)
+      (while (looking-at "^[ \t]*$")
+       (forward-line -1))
+      (forward-line 1)
       (delete-region (point) end))
     (goto-char start)
     (while functions
@@ -1780,19 +1897,22 @@ prefix, and don't delete any headers."
 
 (defun message-cite-original ()
   "Cite function in the standard Message manner."
-  (let ((start (point))
-       (functions
-        (when message-indent-citation-function
-          (if (listp message-indent-citation-function)
-              message-indent-citation-function
-            (list message-indent-citation-function)))))
-    (goto-char start)
-    (while functions
-      (funcall (pop functions)))
-    (when message-citation-line-function
-      (unless (bolp)
-       (insert "\n"))
-      (funcall message-citation-line-function))))
+  (if (and (boundp 'mail-citation-hook)
+          mail-citation-hook)
+      (run-hooks 'mail-citation-hook)
+    (let ((start (point))
+         (functions
+          (when message-indent-citation-function
+            (if (listp message-indent-citation-function)
+                message-indent-citation-function
+              (list message-indent-citation-function)))))
+      (goto-char start)
+      (while functions
+       (funcall (pop functions)))
+      (when message-citation-line-function
+       (unless (bolp)
+         (insert "\n"))
+       (funcall message-citation-line-function)))))
 
 (defun message-insert-citation-line ()
   "Function that inserts a simple citation line."
@@ -1855,6 +1975,11 @@ The text will also be indented the normal way."
 ;;; Sending messages
 ;;;
 
+;; Avoid byte-compile warning.
+(defvar message-encoding-buffer nil)
+(defvar message-edit-buffer nil)
+(defvar message-mime-mode nil)
+
 (defun message-send-and-exit (&optional arg)
   "Send message like `message-send', then, if no errors, exit from mail buffer."
   (interactive "P")
@@ -1956,7 +2081,7 @@ the user from the mailer."
        t))))
 
 (defun message-send-via-mail (arg)
-  "Send the current message via mail."  
+  "Send the current message via mail."
   (message-send-mail arg))
 
 (defun message-send-via-news (arg)
@@ -1968,7 +2093,12 @@ the user from the mailer."
   ;; Make sure there's a newline at the end of the message.
   (goto-char (point-max))
   (unless (bolp)
-    (insert "\n")))
+    (insert "\n"))
+  ;; Delete all invisible text.
+  (when (text-property-any (point-min) (point-max) 'invisible t)
+    (put-text-property (point-min) (point-max) 'invisible nil)
+    (unless (yes-or-no-p "Invisible text found and made visible; continue posting?")
+      (error "Invisible text found and made visible"))))
 
 (defun message-add-action (action &rest types)
   "Add ACTION to be performed when doing an exit of type TYPES."
@@ -2038,11 +2168,12 @@ the user from the mailer."
   (let ((errbuf (if message-interactive
                    (generate-new-buffer " sendmail errors")
                  0))
-       resend-to-addresses delimline)
+       resend-addresses delimline)
     (let ((case-fold-search t))
       (save-restriction
        (message-narrow-to-headers)
-       (setq resend-to-addresses (message-fetch-field "resent-to")))
+       ;; XXX: We need to handle Resent-CC/Resent-BCC, too.
+       (setq resend-addresses (message-fetch-field "resent-to")))
       ;; Change header-delimiter to be what sendmail expects.
       (goto-char (point-min))
       (re-search-forward
@@ -2082,8 +2213,8 @@ the user from the mailer."
                     ;; We must not do that for a resend
                     ;; because we would find the original addresses.
                     ;; For a resend, include the specific addresses.
-                    (if resend-to-addresses
-                        (list resend-to-addresses)
+                    (if resend-addresses
+                        (list resend-addresses)
                       '("-t")))))
     (when message-interactive
       (save-excursion
@@ -2101,11 +2232,12 @@ the user from the mailer."
   "Pass the prepared message buffer to qmail-inject.
 Refer to the documentation for the variable `message-send-mail-function'
 to find out how to use this."
-  ;; replace the header delimiter with a blank line
+  ;; replace the header delimiter with a blank line.
   (goto-char (point-min))
   (re-search-forward
    (concat "^" (regexp-quote mail-header-separator) "\n"))
   (replace-match "\n")
+  (backward-char 1)
   (run-hooks 'message-send-mail-hook)
   ;; send the message
   (case
@@ -2158,143 +2290,32 @@ to find out how to use this."
     (mh-send-letter)))
 
 (defun message-send-mail-with-smtp ()
-  "Send the prepared message buffer with SMTP."
-  (require 'smtp)
-  (let ((errbuf (if mail-interactive
-                   (generate-new-buffer " smtp errors")
-                 0))
-       (case-fold-search nil)
-       resend-to-addresses
-       delimline)
-    (unwind-protect
-       (save-excursion
-         (goto-char (point-max))
-         ;; require one newline at the end.
-         (or (= (preceding-char) ?\n)
-             (insert ?\n))
-         ;; Change header-delimiter to be what sendmail expects.
-         (goto-char (point-min))
-         (re-search-forward
-          (concat "^" (regexp-quote mail-header-separator) "\n"))
-         (replace-match "\n")
-         (backward-char 1)
-         (setq delimline (point-marker))
-         (run-hooks 'message-send-mail-hook)
-         ;; (sendmail-synch-aliases)
-          ;; (if mail-aliases
-          ;;     (expand-mail-aliases (point-min) delimline))
-         (goto-char (point-min))
-         ;; ignore any blank lines in the header
-         (while (and (re-search-forward "\n\n\n*" delimline t)
-                     (< (point) delimline))
-           (replace-match "\n"))
-         (let ((case-fold-search t))
-           (goto-char (point-min))
-           (goto-char (point-min))
-           (while (re-search-forward "^Resent-to:" delimline t)
-             (setq resend-to-addresses
-                   (save-restriction
-                     (narrow-to-region (point)
-                                       (save-excursion
-                                         (end-of-line)
-                                         (point)))
-                     (append (mail-parse-comma-list)
-                             resend-to-addresses))))
-;;; Apparently this causes a duplicate Sender.
-;;;        ;; If the From is different than current user, insert Sender.
-;;;        (goto-char (point-min))
-;;;        (and (re-search-forward "^From:"  delimline t)
-;;;             (progn
-;;;               (require 'mail-utils)
-;;;               (not (string-equal
-;;;                     (mail-strip-quoted-names
-;;;                      (save-restriction
-;;;                        (narrow-to-region (point-min) delimline)
-;;;                        (mail-fetch-field "From")))
-;;;                     (user-login-name))))
-;;;             (progn
-;;;               (forward-line 1)
-;;;               (insert "Sender: " (user-login-name) "\n")))
-           ;; Don't send out a blank subject line
-           (goto-char (point-min))
-           (if (re-search-forward "^Subject:[ \t]*\n" delimline t)
-               (replace-match ""))
-           ;; Put the "From:" field in unless for some odd reason
-           ;; they put one in themselves.
-           (goto-char (point-min))
-           (if (not (re-search-forward "^From:" delimline t))
-               (let* ((login user-mail-address)
-                      (fullname (user-full-name)))
-                 (cond ((eq mail-from-style 'angles)
-                        (insert "From: " fullname)
-                        (let ((fullname-start (+ (point-min) 6))
-                              (fullname-end (point-marker)))
-                          (goto-char fullname-start)
-                          ;; Look for a character that cannot appear unquoted
-                          ;; according to RFC 822.
-                          (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
-                                                 fullname-end 1)
-                              (progn
-                                ;; Quote fullname, escaping specials.
-                                (goto-char fullname-start)
-                                (insert "\"")
-                                (while (re-search-forward "[\"\\]"
-                                                          fullname-end 1)
-                                  (replace-match "\\\\\\&" t))
-                                (insert "\""))))
-                        (insert " <" login ">\n"))
-                       ((eq mail-from-style 'parens)
-                        (insert "From: " login " (")
-                        (let ((fullname-start (point)))
-                          (insert fullname)
-                          (let ((fullname-end (point-marker)))
-                            (goto-char fullname-start)
-                            ;; RFC 822 says \ and nonmatching parentheses
-                            ;; must be escaped in comments.
-                            ;; Escape every instance of ()\ ...
-                            (while (re-search-forward "[()\\]" fullname-end 1)
-                              (replace-match "\\\\\\&" t))
-                            ;; ... then undo escaping of matching parentheses,
-                            ;; including matching nested parentheses.
-                            (goto-char fullname-start)
-                            (while (re-search-forward 
-                                    "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
-                                    fullname-end 1)
-                              (replace-match "\\1(\\3)" t)
-                              (goto-char fullname-start))))
-                        (insert ")\n"))
-                       ((null mail-from-style)
-                        (insert "From: " login "\n")))))
-           ;; Insert an extra newline if we need it to work around
-           ;; Sun's bug that swallows newlines.
-           (goto-char (1+ delimline))
-           (if (eval mail-mailer-swallows-blank-line)
-               (newline))
-           ;; Find and handle any FCC fields.
-           (goto-char (point-min))
-           (if (re-search-forward "^FCC:" delimline t)
-               (mail-do-fcc delimline))
-           (if mail-interactive
-               (save-excursion
-                 (set-buffer errbuf)
-                 (erase-buffer))))
-         ;;
-         ;;
-         ;;
-         (let ((recipient-address-list
-                (or resend-to-addresses
-                    (smtp-deduce-address-list (current-buffer)
-                                              (point-min) delimline))))
-           (smtp-do-bcc delimline)
-           
-           (if recipient-address-list
-               (if (not (smtp-via-smtp recipient-address-list
-                                       (current-buffer)))
-                   (error "Sending failed; SMTP protocol error"))
-             (error "Sending failed; no recipients"))
-           ))
-      (if (bufferp errbuf)
-         (kill-buffer errbuf)))))
+  "Send off the prepared buffer with SMTP."
+  (require 'smtp) ; XXX
+  (let ((case-fold-search t)
+       recipients)
+    (save-restriction
+      (message-narrow-to-headers)
+      (setq recipients
+           ;; XXX: Should be replaced by better one.
+           (smtp-deduce-address-list (current-buffer)
+                                     (point-min) (point-max)))
+      ;; Remove BCC lines.
+      (message-remove-header "bcc"))
+    ;; replace the header delimiter with a blank line.
+    (goto-char (point-min))
+    (re-search-forward
+     (concat "^" (regexp-quote mail-header-separator) "\n"))
+    (replace-match "\n")
+    (backward-char 1)
+    (run-hooks 'message-send-mail-hook)
+    (if recipients
+       (let ((result (smtp-via-smtp user-mail-address
+                                    recipients
+                                    (current-buffer))))
+         (unless (eq result t)
+           (error "Sending failed; " result)))
+      (error "Sending failed; no recipients"))))
 
 (defun message-send-news (&optional arg)
   (let ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
@@ -2577,7 +2598,7 @@ to find out how to use this."
    (message-check 'from
      (let* ((case-fold-search t)
            (from (message-fetch-field "from"))
-           (ad (nth 1 (mail-extract-address-components from))))
+           (ad (nth 1 (std11-extract-address-components from))))
        (cond
        ((not from)
         (message "There is no From line.  Posting is denied.")
@@ -2638,8 +2659,7 @@ to find out how to use this."
    (message-check 'new-text
      (or
       (not message-checksum)
-      (not (and (eq (message-checksum) (car message-checksum))
-               (eq (buffer-size) (cdr message-checksum))))
+      (not (eq (message-checksum) message-checksum))
       (y-or-n-p
        "It looks like no new text has been added.  Really post? ")))
    ;; Check the length of the signature.
@@ -2755,6 +2775,16 @@ to find out how to use this."
     (timezone-make-date-arpa-standard
      (current-time-string now) (current-time-zone now))))
 
+(defun message-make-followup-subject (subject)
+  "Make a followup Subject."
+  (cond
+   ((and (eq message-use-subject-re 'guess)
+         (string-match message-subject-encoded-re-regexp subject))
+    subject)
+   (message-use-subject-re
+    (concat "Re: " (message-strip-subject-re subject)))
+   (t subject)))
+
 (defun message-make-message-id ()
   "Make a unique Message-ID."
   (concat "<" (message-unique-id)
@@ -2851,18 +2881,20 @@ to find out how to use this."
 (defun message-make-in-reply-to ()
   "Return the In-Reply-To header for this message."
   (when message-reply-headers
-    (let ((from (mail-header-from message-reply-headers))
+    (let ((mid (mail-header-message-id message-reply-headers))
+         (from (mail-header-from message-reply-headers))
          (date (mail-header-date message-reply-headers)))
-      (when from
-       (let ((stop-pos
-              (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
-         (concat (if (and stop-pos
-                          (not (zerop stop-pos)))
-                     (substring from 0 stop-pos) from)
-                 "'s message of \""
-                 (if (or (not date) (string= date ""))
-                     "(unknown date)" date)
-                 "\""))))))
+      (when mid
+       (concat mid
+               (when from
+                 (let ((stop-pos 
+                        (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
+                   (concat "\n ("
+                           (if stop-pos (substring from 0 stop-pos) from)
+                           "'s message of " 
+                           (if (or (not date) (string= date ""))
+                               "(unknown date)" date)
+                           ")"))))))))
 
 (defun message-make-distribution ()
   "Make a Distribution header."
@@ -2966,7 +2998,7 @@ give as trustworthy answer as possible."
   "Return the pertinent part of `user-mail-address'."
   (when user-mail-address
     (if (string-match " " user-mail-address)
-       (nth 1 (mail-extract-address-components user-mail-address))
+       (nth 1 (std11-extract-address-components user-mail-address))
       user-mail-address)))
 
 (defun message-make-fqdn ()
@@ -3018,9 +3050,7 @@ Headers already prepared in the buffer are not modified."
           (To nil)
           (Distribution (message-make-distribution))
           (Lines (message-make-lines))
-          (X-Newsreader message-newsreader)
-          (X-Mailer (and (not (message-fetch-field "X-Newsreader"))
-                         message-mailer))
+          (User-Agent message-user-agent)
           (Expires (message-make-expires))
           (case-fold-search t)
           header value elem)
@@ -3048,7 +3078,13 @@ Headers already prepared in the buffer are not modified."
              (setq header (car elem)))
          (setq header elem))
        (when (or (not (re-search-forward
-                       (concat "^" (downcase (symbol-name header)) ":")
+                       (concat "^"
+                               (regexp-quote
+                                (downcase
+                                 (if (stringp header)
+                                     header
+                                   (symbol-name header))))
+                               ":")
                        nil t))
                  (progn
                    ;; The header was found.  We insert a space after the
@@ -3090,7 +3126,8 @@ Headers already prepared in the buffer are not modified."
                  (progn
                    ;; This header didn't exist, so we insert it.
                    (goto-char (point-max))
-                   (insert (symbol-name header) ": " value "\n")
+                   (insert (if (stringp header) header (symbol-name header))
+                           ": " value "\n")
                    (forward-line -1))
                ;; The value of this header was empty, so we clear
                ;; totally and insert the new value.
@@ -3110,13 +3147,13 @@ Headers already prepared in the buffer are not modified."
                   (not (message-check-element 'sender))
                   (not (string=
                         (downcase
-                         (cadr (mail-extract-address-components from)))
+                         (cadr (std11-extract-address-components from)))
                         (downcase secure-sender)))
                   (or (null sender)
                       (not
                        (string=
                         (downcase
-                         (cadr (mail-extract-address-components sender)))
+                         (cadr (std11-extract-address-components sender)))
                         (downcase secure-sender)))))
          (goto-char (point-min))
          ;; Rename any old Sender headers to Original-Sender.
@@ -3164,14 +3201,12 @@ Headers already prepared in the buffer are not modified."
        (if (or (= (following-char) ?,)
                (eobp))
            (when (not quoted)
-             (if (and (> (current-column) 78)
-                      last)
-                 (progn
-                   (save-excursion
-                     (goto-char last)
-                     (insert "\n\t"))
-                   (setq last (1+ (point))))
-               (setq last (1+ (point)))))
+             (if last
+                  (save-excursion
+                    (goto-char last)
+                   (looking-at "[ \t]*")
+                    (replace-match "\n " t t)))
+              (setq last (1+ (point))))
          (setq quoted (not quoted)))
        (unless (eobp)
          (forward-char 1))))
@@ -3179,17 +3214,10 @@ Headers already prepared in the buffer are not modified."
     (widen)
     (forward-line 1)))
 
-(defun message-fill-references (header value)
-  (insert (capitalize (symbol-name header))
-         ": "
-         (std11-fill-msg-id-list-string
-          (if (consp value) (car value) value))
-         "\n"))
-
 (defun message-fill-header (header value)
   (let ((begin (point))
-       (fill-column 990)
-       (fill-prefix "\t"))
+       (fill-column 78)
+       (fill-prefix " "))
     (insert (capitalize (symbol-name header))
            ": "
            (if (consp value) (car value) value)
@@ -3207,6 +3235,24 @@ Headers already prepared in the buffer are not modified."
        (replace-match " " t t))
       (goto-char (point-max)))))
 
+(defun message-shorten-references (header references)
+  "Limit REFERENCES to be shorter than 988 characters."
+  (let ((max 988)
+       (cut 4)
+       refs)
+    (nnheader-temp-write nil
+      (insert references)
+      (goto-char (point-min))
+      (while (re-search-forward "<[^>]+>" nil t)
+       (push (match-string 0) refs))
+      (setq refs (nreverse refs))
+      (while (> (length (mapconcat 'identity refs " ")) max)
+       (when (< (length refs) (1+ cut))
+         (decf cut))
+       (setcdr (nthcdr cut refs) (cddr (nthcdr cut refs)))))
+    (insert (capitalize (symbol-name header)) ": "
+           (mapconcat 'identity refs " ") "\n")))
+
 (defun message-position-point ()
   "Move point to where the user probably wants to find it."
   (message-narrow-to-headers)
@@ -3239,7 +3285,7 @@ Headers already prepared in the buffer are not modified."
      (concat "*" type
             (if to
                 (concat " to "
-                        (or (car (mail-extract-address-components to))
+                        (or (car (std11-extract-address-components to))
                             to) "")
               "")
             (if (and group (not (string= group ""))) (concat " on " group) "")
@@ -3250,7 +3296,8 @@ Headers already prepared in the buffer are not modified."
 
 (defun message-pop-to-buffer (name)
   "Pop to buffer NAME, and warn if it already exists and is modified."
-  (let ((buffer (get-buffer name)))
+  (let ((buffer (get-buffer name))
+       (cur (current-buffer)))
     (if (and buffer
             (buffer-name buffer))
        (progn
@@ -3259,9 +3306,9 @@ Headers already prepared in the buffer are not modified."
                     (not (y-or-n-p
                           "Message already being composed; erase? ")))
            (error "Message being composed")))
-      (set-buffer (pop-to-buffer name))))
-  (erase-buffer)
-  (message-mode))
+      (set-buffer (pop-to-buffer name)))
+    (erase-buffer)
+    (message-mode)))
 
 (defun message-do-send-housekeeping ()
   "Kill old message buffers."
@@ -3394,14 +3441,14 @@ Headers already prepared in the buffer are not modified."
                     (Subject . ,(or subject ""))))))
 
 ;;;###autoload
-(defun message-reply (&optional to-address wide ignore-reply-to)
+(defun message-reply (&optional to-address wide)
   "Start editing a reply to the article in the current buffer."
   (interactive)
   (let ((cur (current-buffer))
-       from subject date reply-to to cc
-       references message-id follow-to
        (inhibit-point-motion-hooks t)
-       mft mct never-mct gnus-warning)
+       from date subject mct mft mrt
+        never-mct to cc
+       references message-id follow-to gnus-warning)
     (save-restriction
       (message-narrow-to-head)
       ;; Allow customizations to have their say.
@@ -3416,80 +3463,121 @@ Headers already prepared in the buffer are not modified."
                    (funcall message-wide-reply-to-function)))))
       ;; Find all relevant headers we need.
       (setq from (message-fetch-field "from")
-           date (message-fetch-field "date")
+           date (message-fetch-field "date" t)
            subject (or (message-fetch-field "subject") "none")
+           references (message-fetch-field "references")
+           message-id (message-fetch-field "message-id" t)
            to (message-fetch-field "to")
            cc (message-fetch-field "cc")
-           mct (message-fetch-field "mail-copies-to")
-           mft (message-fetch-field "mail-followup-to")
-           reply-to (or (message-fetch-field "mail-reply-to")
-                         (unless ignore-reply-to (message-fetch-field "reply-to")))
-           references (message-fetch-field "references")
-           message-id (message-fetch-field "message-id" t))
+           mct (when (and wide message-use-mail-copies-to)
+                 (message-fetch-field "mail-copies-to"))
+           mft (when (and wide message-use-mail-followup-to)
+                 (message-fetch-field "mail-followup-to"))
+           mrt (when message-use-mail-reply-to
+                 (or (message-fetch-field "mail-reply-to")
+                     (message-fetch-field "reply-to")))
+           gnus-warning (message-fetch-field "gnus-warning"))
+      (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
+       (setq message-id (match-string 0 gnus-warning)))
       ;; Remove any (buggy) Re:'s that are present and make a
       ;; proper one.
-      (when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
-       (setq subject (substring subject (match-end 0))))
-      (setq subject (concat "Re: " subject))
-
-      (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
-                (string-match "<[^>]+>" gnus-warning))
-       (setq message-id (match-string 0 gnus-warning)))
-
-      ;; Handle special values of Mail-Copies-To.
-      (when mct
-       (cond ((equal (downcase mct) "never")
-              (setq never-mct t)
-              (setq mct nil))
-             ((equal (downcase mct) "always")
-              (setq mct (or reply-to from)))))
-
-      (unless follow-to
-       (cond
-        (to-address
-          (setq follow-to (list (cons 'To to-address)))
-          (when (and wide mct)
-            (push (cons 'Cc mct) follow-to)))
-        ((not wide)
-          (setq follow-to (list (cons 'To (or reply-to from)))))
-        ((and mft message-use-mail-followup-to)
-         (setq follow-to (list (cons 'To mft))))
-        (t
-         (let (ccalist)
-           (save-excursion
-             (message-set-work-buffer)
-             (unless never-mct
-               (insert (or reply-to from "")))
-             (insert (if to (concat (if (bolp) "" ", ") to "") ""))
-             (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
-             (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
-             (goto-char (point-min))
-             (while (re-search-forward "[ \t]+" nil t)
-               (replace-match " " t t))
-             ;; Remove addresses that match `rmail-dont-reply-to-names'.
-             (insert (prog1 (rmail-dont-reply-to (buffer-string))
-                       (erase-buffer)))
-             (goto-char (point-min))
-             ;; Perhaps Mail-Copies-To: never removed the only address?
-             (when (eobp)
-               (insert (or reply-to from "")))
-             (setq ccalist
-                   (mapcar
-                    (lambda (addr)
-                      (cons (mail-strip-quoted-names addr) addr))
-                    (message-tokenize-header (buffer-string))))
-             (let ((s ccalist))
-               (while s
-                 (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
-           (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
-           (when ccalist
-             (let ((ccs (cons 'Cc (mapconcat
-                                   (lambda (addr) (cdr addr)) ccalist ", "))))
-               (when (string-match "^ +" (cdr ccs))
-                 (setcdr ccs (substring (cdr ccs) (match-end 0))))
-               (push ccs follow-to)))))))
+      (setq subject (message-make-followup-subject subject))
       (widen))
 
+    ;; Handle special values of Mail-Copies-To.
+    (when mct
+      (cond
+       ((and (equal (downcase mct) "never")
+            (or (not (eq message-use-mail-copies-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Copies-To: never? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: never'
+directs you not to send your response to the author.")))
+       (setq never-mct t)
+       (setq mct nil))
+       ((and (equal (downcase mct) "always")
+            (or (not (eq message-use-mail-copies-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Copies-To: always? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: always'
+sends a copy of your response to the author.")))
+       (setq mct (or mrt from)))
+       ((and (eq message-use-mail-copies-to 'ask)
+            (not 
+             (message-y-or-n-p
+              (concat "Obey Mail-Copies-To: " mct " ? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: " mct "'
+sends a copy of your response to " (if (string-match "," mct)
+                                      "the specified addresses"
+                                    "that address") ".")))
+       (setq mct nil))
+       ))
+
+    (unless follow-to
+      (cond
+       (to-address (setq follow-to (list (cons 'To to-address))))
+       ((not wide) (setq follow-to (list (cons 'To (or mrt from)))))
+       ;; Handle Mail-Followup-To.
+       ((and mft
+            (or (not (eq message-use-mail-followup-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Followup-To: " mft "? ") t "\
+You should normally obey the Mail-Followup-To: header.
+
+       `Mail-Followup-To: " mft "'
+directs your response to " (if (string-match "," mft)
+                              "the specified addresses"
+                            "that address only") ".
+
+A typical situation where Mail-Followup-To is used is when the author thinks
+that further discussion should take place only in "
+                 (if (string-match "," mft)
+                     "the specified mailing lists"
+                   "that mailing list") ".")))
+       (setq follow-to (list (cons 'To mft)))
+       (when mct
+         (push (cons 'Cc mct) follow-to)))
+       (t
+       (let (ccalist)
+         (save-excursion
+           (message-set-work-buffer)
+           (unless never-mct
+             (insert (or mrt from "")))
+           (insert (if to (concat (if (bolp) "" ", ") to "") ""))
+           (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
+           (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
+           (goto-char (point-min))
+           (while (re-search-forward "[ \t]+" nil t)
+             (replace-match " " t t))
+           ;; Remove addresses that match `rmail-dont-reply-to-names'.
+           (insert (prog1 (rmail-dont-reply-to (buffer-string))
+                     (erase-buffer)))
+           (goto-char (point-min))
+           ;; Perhaps Mail-Copies-To: never removed the only address?
+           (when (eobp)
+             (insert (or mrt from "")))
+           (setq ccalist
+                 (mapcar
+                  (lambda (addr)
+                    (cons (mail-strip-quoted-names addr) addr))
+                  (message-tokenize-header (buffer-string))))
+           (let ((s ccalist))
+             (while s
+               (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
+         (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
+         (when ccalist
+           (let ((ccs (cons 'Cc (mapconcat
+                                 (lambda (addr) (cdr addr)) ccalist ", "))))
+             (when (string-match "^ +" (cdr ccs))
+               (setcdr ccs (substring (cdr ccs) (match-end 0))))
+             (push ccs follow-to)))))))
+
     (message-pop-to-buffer (message-buffer-name
                            (if wide "wide reply" "reply") from
                            (if wide to-address nil)))
@@ -3502,51 +3590,52 @@ Headers already prepared in the buffer are not modified."
        ,@follow-to
        ,@(if (or references message-id)
             `((References . ,(concat (or references "") (and references " ")
-                                     (or message-id ""))))
-          nil))
+                                     (or message-id ""))))))
      cur)))
 
 ;;;###autoload
-(defun message-wide-reply (&optional to-address ignore-reply-to)
+(defun message-wide-reply (&optional to-address)
   "Make a \"wide\" reply to the message in the current buffer."
   (interactive)
-  (message-reply to-address t ignore-reply-to))
+  (message-reply to-address t))
 
 ;;;###autoload
 (defun message-followup (&optional to-newsgroups)
-  "Follow up to the message in the current buffer.
-If TO-NEWSGROUPS, use that as the new Newsgroups line."
+  "Follow up to the message in the current buffer."
   (interactive)
   (let ((cur (current-buffer))
-       from subject date reply-to mct mft
-       references message-id follow-to
        (inhibit-point-motion-hooks t)
+       from date subject mct mft mrt
        (message-this-is-news t)
-       followup-to distribution newsgroups gnus-warning posted-to)
+       followup-to distribution newsgroups posted-to
+       references message-id follow-to gnus-warning)
     (save-restriction
-      (narrow-to-region
-       (goto-char (point-min))
-       (if (search-forward "\n\n" nil t)
-          (1- (point))
-        (point-max)))
+      (message-narrow-to-head)
+      ;; Allow customizations to have their say.
+      ;; This is a followup.
       (when (message-functionp message-followup-to-function)
        (setq follow-to
              (funcall message-followup-to-function)))
+      ;; Find all relevant headers we need.
       (setq from (message-fetch-field "from")
-           date (message-fetch-field "date")
+           date (message-fetch-field "date" t)
            subject (or (message-fetch-field "subject") "none")
            references (message-fetch-field "references")
            message-id (message-fetch-field "message-id" t)
-           followup-to (message-fetch-field "followup-to")
+           followup-to (when message-use-followup-to
+                         (message-fetch-field "followup-to"))
+           distribution (message-fetch-field "distribution")
            newsgroups (message-fetch-field "newsgroups")
            posted-to (message-fetch-field "posted-to")
-           reply-to (or (message-fetch-field "mail-reply-to")
-                         (message-fetch-field "reply-to"))
-           distribution (message-fetch-field "distribution")
-           mct (message-fetch-field "mail-copies-to")
-           mft (message-fetch-field "mail-followup-to"))
-      (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
-                (string-match "<[^>]+>" gnus-warning))
+           mct (when message-use-mail-copies-to
+                 (message-fetch-field "mail-copies-to"))
+           mft (when message-use-mail-followup-to
+                 (message-fetch-field "mail-followup-to"))
+           mrt (when message-use-mail-reply-to
+                 (or (message-fetch-field "mail-reply-to")
+                     (message-fetch-field "reply-to")))
+           gnus-warning (message-fetch-field "gnus-warning"))
+      (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
        (setq message-id (match-string 0 gnus-warning)))
       ;; Remove bogus distribution.
       (when (and (stringp distribution)
@@ -3555,40 +3644,68 @@ If TO-NEWSGROUPS, use that as the new Newsgroups line."
        (setq distribution nil))
       ;; Remove any (buggy) Re:'s that are present and make a
       ;; proper one.
-      (when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
-       (setq subject (substring subject (match-end 0))))
-      (setq subject (concat "Re: " subject))
+      (setq subject (message-make-followup-subject subject))
       (widen))
 
-    (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
-
-    (message-setup
-     `((Subject . ,subject)
-       ,@(cond
-         (to-newsgroups
-          (list (cons 'Newsgroups to-newsgroups)))
-         (follow-to follow-to)
-         ((and followup-to message-use-followup-to)
-          (list
-           (cond
-            ((equal (downcase followup-to) "poster")
-             (if (or (eq message-use-followup-to 'use)
-                     (message-y-or-n-p "Obey Followup-To: poster? " t "\
+    ;; Handle special values of Mail-Copies-To.
+    (when mct
+      (cond
+       ((and (equal (downcase mct) "never")
+            (or (not (eq message-use-mail-copies-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Copies-To: never? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: never'
+directs you not to send your response to the author.")))
+       (setq mct nil))
+       ((and (equal (downcase mct) "always")
+            (or (not (eq message-use-mail-copies-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Copies-To: always? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: always'
+sends a copy of your response to the author.")))
+       (setq mct (or mrt from)))
+       ((and (eq message-use-mail-copies-to 'ask)
+            (not 
+             (message-y-or-n-p
+              (concat "Obey Mail-Copies-To: " mct " ? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+       `Mail-Copies-To: " mct "'
+sends a copy of your response to " (if (string-match "," mct)
+                                      "the specified addresses"
+                                    "that address") ".")))
+       (setq mct nil))
+       ))
+
+    (unless follow-to
+      (cond
+       (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
+       ;; Handle Followup-To.
+       (followup-to
+       (cond
+        ((equal (downcase followup-to) "poster")
+         (if (or (eq message-use-followup-to 'use)
+                 (message-y-or-n-p "Obey Followup-To: poster? " t "\
 You should normally obey the Followup-To: header.
 
-`Followup-To: poster' sends your response via e-mail instead of news.
+       `Followup-To: poster'
+sends your response via e-mail instead of news.
 
-A typical situation where `Followup-To: poster' is used is when the poster
+A typical situation where `Followup-To: poster' is used is when the author
 does not read the newsgroup, so he wouldn't see any replies sent to it."))
-                 (progn
-                   (setq message-this-is-news nil)
-                   (cons 'To (or reply-to from "")))
-               (cons 'Newsgroups newsgroups)))
-            (t
-             (if (or (equal followup-to newsgroups)
-                     (not (eq message-use-followup-to 'ask))
-                     (message-y-or-n-p
-                      (concat "Obey Followup-To: " followup-to "? ") t "\
+             (setq message-this-is-news nil
+                   distribution nil
+                   follow-to (list (cons 'To (or mrt from ""))))
+           (setq follow-to (list (cons 'Newsgroups newsgroups)))))
+        (t
+         (if (or (equal followup-to newsgroups)
+                 (not (eq message-use-followup-to 'ask))
+                 (message-y-or-n-p
+                  (concat "Obey Followup-To: " followup-to "? ") t "\
 You should normally obey the Followup-To: header.
 
        `Followup-To: " followup-to "'
@@ -3603,29 +3720,46 @@ be fragmented and very difficult to follow.
 
 Also, some source/announcement newsgroups are not indented for discussion;
 responses here are directed to other newsgroups."))
-                 (cons 'Newsgroups followup-to)
-               (cons 'Newsgroups newsgroups))))))
-          ((and mft message-use-mail-followup-to)
-           (list (cons 'To mft)))
-         (posted-to
-          `((Newsgroups . ,posted-to)))
-         (t
-          `((Newsgroups . ,newsgroups))))
-       ,@(and distribution (list (cons 'Distribution distribution)))
-       ,@(if (or references message-id)
-            `((References . ,(concat (or references "") (and references " ")
-                                     (or message-id "")))))
-       ,@(when (and mct
-                   (not (equal (downcase mct) "never")))
-          (list (cons 'Cc (if (equal (downcase mct) "always")
-                              (or reply-to from "")
-                            mct)))))
+             (setq follow-to (list (cons 'Newsgroups followup-to)))
+           (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
+       ;; Handle Mail-Followup-To, followup via e-mail.
+       ((and mft
+            (or (not (eq message-use-mail-followup-to 'ask))
+                (message-y-or-n-p
+                 (concat "Obey Mail-Followup-To: " mft "? ") t "\
+You should normally obey the Mail-Followup-To: header.
+
+       `Mail-Followup-To: " mft "'
+directs your response to " (if (string-match "," mft)
+                              "the specified addresses"
+                            "that address only") " instead of news.
+
+A typical situation where Mail-Followup-To is used is when the author thinks
+that further discussion should take place only in "
+                            (if (string-match "," mft)
+                                "the specified mailing lists"
+                              "that mailing list") ".")))
+       (setq message-this-is-news nil
+             distribution nil
+             follow-to (list (cons 'To mft))))
+       (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
+       (t
+       (setq follow-to (list (cons 'Newsgroups newsgroups))))))
 
-     cur)
+    (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
 
     (setq message-reply-headers
-         (vector 0 subject from date message-id references 0 0 ""))))
+         (vector 0 subject from date message-id references 0 0 ""))
 
+    (message-setup
+     `((Subject . ,subject)
+       ,@follow-to
+       ,@(and mct (list (cons 'Cc mct)))
+       ,@(and distribution (list (cons 'Distribution distribution)))
+       ,@(if (or references message-id)
+            `((References . ,(concat (or references "") (and references " ")
+                                     (or message-id ""))))))
+     cur)))
 
 ;;;###autoload
 (defun message-cancel-news ()
@@ -3650,9 +3784,10 @@ responses here are directed to other newsgroups."))
                          (downcase sender)
                          (downcase (message-make-sender))))
                    (string-equal
-                    (downcase (cadr (mail-extract-address-components from)))
-                    (downcase (cadr (mail-extract-address-components
-                                     (message-make-from))))))
+                    (downcase (cadr (std11-extract-address-components
+                                     from)))
+                    (downcase (cadr (std11-extract-address-components
+                                     (message-make-from))))))
          (error "This article is not yours"))
        ;; Make control message.
        (setq buf (set-buffer (get-buffer-create " *message cancel*")))
@@ -3686,7 +3821,7 @@ header line with the old Message-ID."
     ;; Check whether the user owns the article that is to be superseded.
     (unless (string-equal
             (downcase (or (message-fetch-field "sender")
-                          (cadr (mail-extract-address-components
+                          (cadr (std11-extract-address-components
                                  (message-fetch-field "from")))))
             (downcase (message-make-sender)))
       (error "This article is not yours"))
@@ -3724,19 +3859,81 @@ header line with the old Message-ID."
             (insert-file-contents file-name nil)))
          (t (error "message-recover cancelled")))))
 
+;;; Washing Subject:
+
+(defun message-wash-subject (subject)
+  "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
+  (nnheader-temp-write nil
+    (insert-string subject)
+    (goto-char (point-min))
+    ;; strip Re/Fwd stuff off the beginning
+    (while (re-search-forward
+           "\\([Rr][Ee]:\\|[Ff][Ww][Dd]:\\|[Ff][Ww]:\\)" nil t)
+      (replace-match ""))
+
+    ;; and gnus-style forwards [foo@bar.com] subject
+    (goto-char (point-min))
+    (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
+      (replace-match ""))
+
+    ;; and off the end
+    (goto-char (point-max))
+    (while (re-search-backward "([Ff][Ww][Dd])" nil t)
+      (replace-match ""))
+
+    ;; and finally, any whitespace that was left-over
+    (goto-char (point-min))
+    (while (re-search-forward "^[ \t]+" nil t)
+      (replace-match ""))
+    (goto-char (point-max))
+    (while (re-search-backward "[ \t]+$" nil t)
+      (replace-match ""))
+
+    (buffer-string)))
+    
 ;;; Forwarding messages.
 
+(defun message-forward-subject-author-subject (subject)
+  "Generate a subject for a forwarded message.
+The form is: [Source] Subject, where if the original message was mail,
+Source is the sender, and if the original message was news, Source is
+the list of newsgroups is was posted to."
+  (concat "["
+         (or (message-fetch-field
+              (if (message-news-p) "newsgroups" "from"))
+             "(nowhere)")
+         "] " subject))
+
+(defun message-forward-subject-fwd (subject)
+  "Generate a subject for a forwarded message.
+The form is: Fwd: Subject, where Subject is the original subject of
+the message."
+  (concat "Fwd: " subject))
+
 (defun message-make-forward-subject ()
   "Return a Subject header suitable for the message in the current buffer."
   (save-excursion
     (save-restriction
       (current-buffer)
       (message-narrow-to-head)
-      (concat "[" (or (message-fetch-field
-                      (if (message-news-p) "newsgroups" "from"))
-                     "(nowhere)")
-             "] " (or (eword-decode-unstructured-field-body
-                       (message-fetch-field "Subject") ""))))))
+      (let ((funcs message-make-forward-subject-function)
+           (subject (if message-wash-forwarded-subjects
+                        (message-wash-subject
+                         (or (eword-decode-unstructured-field-body
+                              (message-fetch-field "Subject")) ""))
+                      (or (eword-decode-unstructured-field-body
+                           (message-fetch-field "Subject")) ""))))
+       ;; Make sure funcs is a list.
+       (and funcs
+            (not (listp funcs))
+            (setq funcs (list funcs)))
+       ;; Apply funcs in order, passing subject generated by previous
+       ;; func to the next one.
+       (while funcs
+         (when (message-functionp (car funcs))
+           (setq subject (funcall (car funcs) subject)))
+         (setq funcs (cdr funcs)))
+       subject))))
 
 ;;;###autoload
 (defun message-forward (&optional news)
@@ -3881,7 +4078,8 @@ you."
        (same-window-buffer-names nil)
        (same-window-regexps nil))
     (message-pop-to-buffer (message-buffer-name "mail" to)))
-  (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
+  (let ((message-this-is-mail t))
+    (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
 
 ;;;###autoload
 (defun message-mail-other-frame (&optional to subject)
@@ -3893,7 +4091,8 @@ you."
        (same-window-buffer-names nil)
        (same-window-regexps nil))
     (message-pop-to-buffer (message-buffer-name "mail" to)))
-  (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
+  (let ((message-this-is-mail t))
+    (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
 
 ;;;###autoload
 (defun message-news-other-window (&optional newsgroups subject)
@@ -3905,8 +4104,9 @@ you."
        (same-window-buffer-names nil)
        (same-window-regexps nil))
     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
-  (message-setup `((Newsgroups . ,(or newsgroups ""))
-                  (Subject . ,(or subject "")))))
+  (let ((message-this-is-news t))
+    (message-setup `((Newsgroups . ,(or newsgroups ""))
+                    (Subject . ,(or subject ""))))))
 
 ;;;###autoload
 (defun message-news-other-frame (&optional newsgroups subject)
@@ -3918,8 +4118,9 @@ you."
        (same-window-buffer-names nil)
        (same-window-regexps nil))
     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
-  (message-setup `((Newsgroups . ,(or newsgroups ""))
-                  (Subject . ,(or subject "")))))
+  (let ((message-this-is-news t))
+    (message-setup `((Newsgroups . ,(or newsgroups ""))
+                    (Subject . ,(or subject ""))))))
 
 ;;; underline.el