Merge from emacs--devo--0
[gnus] / lisp / message.el
index b9829a8..82dd24c 100644 (file)
@@ -1,27 +1,25 @@
 ;;; message.el --- composing mail and news messages
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 ;;; message.el --- composing mail and news messages
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: mail, news
 
 ;; This file is part of GNU Emacs.
 
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: mail, news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 
 ;;; Commentary:
 
 
 ;;; Code:
 
 
 ;;; Code:
 
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
 (eval-when-compile
-  (require 'cl)
-  (defvar gnus-message-group-art)
-  (defvar gnus-list-identifiers)) ; gnus-sum is required where necessary
+  (require 'cl))
+
 (require 'hashcash)
 (require 'canlock)
 (require 'mailheader)
 (require 'hashcash)
 (require 'canlock)
 (require 'mailheader)
 (require 'rfc822)
 (require 'ecomplete)
 
 (require 'rfc822)
 (require 'ecomplete)
 
+(autoload 'mailclient-send-it "mailclient") ;; Emacs 22 or contrib/
+
+(defvar gnus-message-group-art)
+(defvar gnus-list-identifiers) ; gnus-sum is required where necessary
+(defvar rmail-enable-mime-composing)
+
 (defgroup message '((user-mail-address custom-variable)
                    (user-full-name custom-variable))
   "Mail and news message composing."
 (defgroup message '((user-mail-address custom-variable)
                    (user-full-name custom-variable))
   "Mail and news message composing."
@@ -187,8 +192,8 @@ To disable checking of long signatures, for instance, add
 
 Don't touch this variable unless you really know what you're doing.
 
 
 Don't touch this variable unless you really know what you're doing.
 
-Checks include `approved', `continuation-headers', `control-chars',
-`empty', `existing-newsgroups', `from', `illegible-text',
+Checks include `approved', `bogus-recipient', `continuation-headers',
+`control-chars', `empty', `existing-newsgroups', `from', `illegible-text',
 `invisible-text', `long-header-lines', `long-lines', `message-id',
 `multiple-headers', `new-text', `newsgroups', `quoting-style',
 `repeated-newsgroups', `reply-to', `sender', `sendsys', `shoot',
 `invisible-text', `long-header-lines', `long-lines', `message-id',
 `multiple-headers', `new-text', `newsgroups', `quoting-style',
 `repeated-newsgroups', `reply-to', `sender', `sendsys', `shoot',
@@ -268,7 +273,7 @@ included.  Organization and User-Agent are optional."
   :link '(custom-manual "(message)Mail Headers")
   :type 'regexp)
 
   :link '(custom-manual "(message)Mail Headers")
   :type 'regexp)
 
-(defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:\\|^NNTP-Posting-Date:\\|^X-Trace:\\|^X-Complaints-To:\\|^Cancel-Lock:\\|^Cancel-Key:\\|^X-Hashcash:\\|^X-Payment:\\|^Approved:"
+(defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-ID:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:\\|^NNTP-Posting-Date:\\|^X-Trace:\\|^X-Complaints-To:\\|^Cancel-Lock:\\|^Cancel-Key:\\|^X-Hashcash:\\|^X-Payment:\\|^Approved:"
   "*Header lines matching this regexp will be deleted before posting.
 It's best to delete old Path and Date headers before posting to avoid
 any confusion."
   "*Header lines matching this regexp will be deleted before posting.
 It's best to delete old Path and Date headers before posting to avoid
 any confusion."
@@ -408,9 +413,17 @@ for `message-cross-post-insert-note'."
 
 ;;; End of variables adopted from `message-utils.el'.
 
 
 ;;; End of variables adopted from `message-utils.el'.
 
-(defcustom message-signature-separator "^-- *$"
-  "Regexp matching the signature separator."
-  :type 'regexp
+(defcustom message-signature-separator "^-- $"
+  "Regexp matching the signature separator.
+This variable is used to strip off the signature from quoted text
+when `message-cite-function' is
+`message-cite-original-without-signature'.  Most useful values
+are \"^-- $\" (strict) and \"^-- *$\" (loose; allow missing
+whitespace)."
+  :type '(choice (const :tag "strict" "^-- $")
+                (const :tag "loose" "^-- *$")
+                regexp)
+  :version "22.3" ;; Gnus 5.10.12 (changed default)
   :link '(custom-manual "(message)Various Message Variables")
   :group 'message-various)
 
   :link '(custom-manual "(message)Various Message Variables")
   :group 'message-various)
 
@@ -469,12 +482,11 @@ function
 (defcustom message-kill-buffer-query t
   "*Non-nil means that killing a modified message buffer has to be confirmed.
 This is used by `message-kill-buffer'."
 (defcustom message-kill-buffer-query t
   "*Non-nil means that killing a modified message buffer has to be confirmed.
 This is used by `message-kill-buffer'."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'message-buffers
   :type 'boolean)
 
   :group 'message-buffers
   :type 'boolean)
 
-(eval-when-compile
-  (defvar gnus-local-organization))
+(defvar gnus-local-organization)
 (defcustom message-user-organization
   (or (and (boundp 'gnus-local-organization)
           (stringp gnus-local-organization)
 (defcustom message-user-organization
   (or (and (boundp 'gnus-local-organization)
           (stringp gnus-local-organization)
@@ -555,7 +567,13 @@ Done before generating the new subject of a forward."
   :link '(custom-manual "(message)Forwarding")
   :type 'boolean)
 
   :link '(custom-manual "(message)Forwarding")
   :type 'boolean)
 
-(defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:\\|^>?From "
+(defcustom message-ignored-resent-headers
+  ;; `Delivered-To' needs to be removed because some mailers use it to
+  ;; detect loops, so if you resend a message to an address that ultimately
+  ;; comes back to you (e.g. a mailing-list to which you subscribe, in which
+  ;; case you may be removed from the list on the grounds that mail to you
+  ;; bounced with a "mailing loop" error).
+  "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:\\|^>?From \\|^Delivered-To:"
   "*All headers that match this regexp will be deleted when resending a message."
   :group 'message-interface
   :link '(custom-manual "(message)Resending")
   "*All headers that match this regexp will be deleted when resending a message."
   :group 'message-interface
   :link '(custom-manual "(message)Resending")
@@ -584,21 +602,21 @@ Done before generating the new subject of a forward."
   :type 'regexp)
 
 (defcustom message-cite-prefix-regexp
   :type 'regexp)
 
 (defcustom message-cite-prefix-regexp
-  (if (string-match "[[:digit:]]" "1") ;; support POSIX?
-      "\\([ \t]*[-_.[:word:]]+>+\\|[ \t]*[]>|}+]\\)+"
+  (if (string-match "[[:digit:]]" "1")
+      ;; Support POSIX?  XEmacs 21.5.27 doesn't.
+      "\\([ \t]*[_.[:word:]]+>+\\|[ \t]*[]>|}]\\)+"
     ;; ?-, ?_ or ?. MUST NOT be in syntax entry w.
     (let (non-word-constituents)
       (with-syntax-table text-mode-syntax-table
        (setq non-word-constituents
              (concat
     ;; ?-, ?_ or ?. MUST NOT be in syntax entry w.
     (let (non-word-constituents)
       (with-syntax-table text-mode-syntax-table
        (setq non-word-constituents
              (concat
-              (if (string-match "\\w" "-")  "" "-")
               (if (string-match "\\w" "_")  "" "_")
               (if (string-match "\\w" ".")  "" "."))))
       (if (equal non-word-constituents "")
               (if (string-match "\\w" "_")  "" "_")
               (if (string-match "\\w" ".")  "" "."))))
       (if (equal non-word-constituents "")
-         "\\([ \t]*\\(\\w\\)+>+\\|[ \t]*[]>|}+]\\)+"
+         "\\([ \t]*\\(\\w\\)+>+\\|[ \t]*[]>|}]\\)+"
        (concat "\\([ \t]*\\(\\w\\|["
                non-word-constituents
        (concat "\\([ \t]*\\(\\w\\|["
                non-word-constituents
-               "]\\)+>+\\|[ \t]*[]>|}+]\\)+"))))
+               "]\\)+>+\\|[ \t]*[]>|}]\\)+"))))
   "*Regexp matching the longest possible citation prefix on a line."
   :version "22.1"
   :group 'message-insertion
   "*Regexp matching the longest possible citation prefix on a line."
   :version "22.1"
   :group 'message-insertion
@@ -617,28 +635,37 @@ Done before generating the new subject of a forward."
   :link '(custom-manual "(message)Canceling News")
   :type 'string)
 
   :link '(custom-manual "(message)Canceling News")
   :type 'string)
 
+(defvar smtpmail-default-smtp-server)
+
+(defun message-send-mail-function ()
+  "Return suitable value for the variable `message-send-mail-function'."
+  (cond ((and (require 'sendmail)
+             (boundp 'sendmail-program)
+             sendmail-program
+             (executable-find sendmail-program))
+        'message-send-mail-with-sendmail)
+       ((and (locate-library "smtpmail")
+             (require 'smtpmail)
+             smtpmail-default-smtp-server)
+        'message-smtpmail-send-it)
+       ((locate-library "mailclient")
+        'message-send-mail-with-mailclient)
+       (t
+        (lambda ()
+          (error "Don't know how to send mail.  Please customize `message-send-mail-function'")))))
+
 ;; Useful to set in site-init.el
 ;; Useful to set in site-init.el
-(defcustom message-send-mail-function
-  (let ((program (if (boundp 'sendmail-program)
-                    ;; see paths.el
-                    sendmail-program)))
-    (cond
-     ((and program
-          (string-match "/" program) ;; Skip path
-          (file-executable-p program))
-      'message-send-mail-with-sendmail)
-     ((and program
-          (executable-find program))
-      'message-send-mail-with-sendmail)
-     (t
-      'smtpmail-send-it)))
+(defcustom message-send-mail-function (message-send-mail-function)
   "Function to call to send the current buffer as mail.
 The headers should be delimited by a line whose contents match the
 variable `mail-header-separator'.
 
   "Function to call to send the current buffer as mail.
 The headers should be delimited by a line whose contents match the
 variable `mail-header-separator'.
 
-Valid values include `message-send-mail-with-sendmail' (the default),
+Valid values include `message-send-mail-with-sendmail'
 `message-send-mail-with-mh', `message-send-mail-with-qmail',
 `message-send-mail-with-mh', `message-send-mail-with-qmail',
-`message-smtpmail-send-it', `smtpmail-send-it' and `feedmail-send-it'.
+`message-smtpmail-send-it', `smtpmail-send-it',
+`feedmail-send-it' and `message-send-mail-with-mailclient'.  The
+default is system dependent and determined by the function
+`message-send-mail-function'.
 
 See also `send-mail-function'."
   :type '(radio (function-item message-send-mail-with-sendmail)
 
 See also `send-mail-function'."
   :type '(radio (function-item message-send-mail-with-sendmail)
@@ -647,8 +674,12 @@ See also `send-mail-function'."
                (function-item message-smtpmail-send-it)
                (function-item smtpmail-send-it)
                (function-item feedmail-send-it)
                (function-item message-smtpmail-send-it)
                (function-item smtpmail-send-it)
                (function-item feedmail-send-it)
-               (function :tag "Other"))
+               (function-item message-send-mail-with-mailclient
+                              :tag "Use Mailclient package")
+               (function :tag "Other"))
   :group 'message-sending
   :group 'message-sending
+  :version "23.1" ;; No Gnus
+  :initialize 'custom-initialize-default
   :link '(custom-manual "(message)Mail Variables")
   :group 'message-mail)
 
   :link '(custom-manual "(message)Mail Variables")
   :group 'message-mail)
 
@@ -790,7 +821,7 @@ If this is nil, use `user-mail-address'.  If it is the symbol
 (defcustom message-sendmail-extra-arguments nil
   "Additional arguments to `sendmail-program'."
   ;; E.g. '("-a" "account") for msmtp
 (defcustom message-sendmail-extra-arguments nil
   "Additional arguments to `sendmail-program'."
   ;; E.g. '("-a" "account") for msmtp
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type '(repeat string)
   ;; :link '(custom-manual "(message)Mail Variables")
   :group 'message-sending)
   :type '(repeat string)
   ;; :link '(custom-manual "(message)Mail Variables")
   :group 'message-sending)
@@ -815,9 +846,8 @@ might set this variable to '(\"-f\" \"you@some.where\")."
   :type '(choice (function)
                 (repeat string)))
 
   :type '(choice (function)
                 (repeat string)))
 
-(eval-when-compile
-  (defvar gnus-post-method)
-  (defvar gnus-select-method))
+(defvar gnus-post-method)
+(defvar gnus-select-method)
 (defcustom message-post-method
   (cond ((and (boundp 'gnus-post-method)
              (listp gnus-post-method)
 (defcustom message-post-method
   (cond ((and (boundp 'gnus-post-method)
              (listp gnus-post-method)
@@ -913,7 +943,7 @@ the signature is inserted."
   "*Function called to insert the \"Whomever writes:\" line.
 
 Predefined functions include `message-insert-citation-line' and
   "*Function called to insert the \"Whomever writes:\" line.
 
 Predefined functions include `message-insert-citation-line' and
-`message-insert-formated-citation-line' (see the variable
+`message-insert-formatted-citation-line' (see the variable
 `message-citation-line-format').
 
 Note that Gnus provides a feature where the reader can click on
 `message-citation-line-format').
 
 Note that Gnus provides a feature where the reader can click on
@@ -922,12 +952,12 @@ people who read your message will have to change their Gnus
 configuration.  See the variable `gnus-cite-attribution-suffix'."
   :type '(choice
          (function-item :tag "plain" message-insert-citation-line)
 configuration.  See the variable `gnus-cite-attribution-suffix'."
   :type '(choice
          (function-item :tag "plain" message-insert-citation-line)
-         (function-item :tag "formatted" message-insert-formated-citation-line)
+         (function-item :tag "formatted" message-insert-formatted-citation-line)
          (function :tag "Other"))
   :link '(custom-manual "(message)Insertion Variables")
   :group 'message-insertion)
 
          (function :tag "Other"))
   :link '(custom-manual "(message)Insertion Variables")
   :group 'message-insertion)
 
-(defcustom message-citation-line-format "On %a, %b %d %Y, %N wrote:"
+(defcustom message-citation-line-format "On %a, %b %d %Y, %N wrote:\n"
   "Format of the \"Whomever writes:\" line.
 
 The string is formatted using `format-spec'.  The following
   "Format of the \"Whomever writes:\" line.
 
 The string is formatted using `format-spec'.  The following
@@ -951,7 +981,7 @@ Please also read the note in the documentation of
                 (const :tag "Include date" "On %a, %b %d %Y, %n wrote:")
                 string)
   :link '(custom-manual "(message)Insertion Variables")
                 (const :tag "Include date" "On %a, %b %d %Y, %n wrote:")
                 string)
   :link '(custom-manual "(message)Insertion Variables")
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'message-insertion)
 
 (defcustom message-yank-prefix "> "
   :group 'message-insertion)
 
 (defcustom message-yank-prefix "> "
@@ -986,7 +1016,7 @@ Used by `message-yank-original' via `message-yank-cite'."
   :link '(custom-manual "(message)Insertion Variables")
   :type 'integer)
 
   :link '(custom-manual "(message)Insertion Variables")
   :type 'integer)
 
-(defcustom message-cite-function 'message-cite-original
+(defcustom message-cite-function 'message-cite-original-without-signature
   "*Function for citing an original message.
 Predefined functions include `message-cite-original' and
 `message-cite-original-without-signature'.
   "*Function for citing an original message.
 Predefined functions include `message-cite-original' and
 `message-cite-original-without-signature'.
@@ -996,6 +1026,7 @@ Note that these functions use `mail-citation-hook' if that is non-nil."
                (function-item sc-cite-original)
                (function :tag "Other"))
   :link '(custom-manual "(message)Insertion Variables")
                (function-item sc-cite-original)
                (function :tag "Other"))
   :link '(custom-manual "(message)Insertion Variables")
+  :version "22.3" ;; Gnus 5.10.12 (changed default)
   :group 'message-insertion)
 
 (defcustom message-indent-citation-function 'message-indent-citation
   :group 'message-insertion)
 
 (defcustom message-indent-citation-function 'message-indent-citation
@@ -1121,8 +1152,7 @@ these lines."
           (file-readable-p "/etc/sendmail.cf")
           (let ((buffer (get-buffer-create " *temp*")))
             (unwind-protect
           (file-readable-p "/etc/sendmail.cf")
           (let ((buffer (get-buffer-create " *temp*")))
             (unwind-protect
-                (save-excursion
-                  (set-buffer buffer)
+                (with-current-buffer buffer
                   (insert-file-contents "/etc/sendmail.cf")
                   (goto-char (point-min))
                   (let ((case-fold-search nil))
                   (insert-file-contents "/etc/sendmail.cf")
                   (goto-char (point-min))
                   (let ((case-fold-search nil))
@@ -1204,7 +1234,7 @@ If nil, you might be asked to input the charset."
 (defcustom message-dont-reply-to-names
   (and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
   "*Addresses to prune when doing wide replies.
 (defcustom message-dont-reply-to-names
   (and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
   "*Addresses to prune when doing wide replies.
-This can be a regexp or a list of regexps. Also, a value of nil means
+This can be a regexp or a list of regexps.  Also, a value of nil means
 exclude your own user name only."
   :version "21.1"
   :group 'message
 exclude your own user name only."
   :version "21.1"
   :group 'message
@@ -1213,18 +1243,8 @@ exclude your own user name only."
                 regexp
                 (repeat :tag "Regexp List" regexp)))
 
                 regexp
                 (repeat :tag "Regexp List" regexp)))
 
-;; #### FIXME: this might become a generally usefull function at some point
-;; --dlv.
 (defsubst message-dont-reply-to-names ()
 (defsubst message-dont-reply-to-names ()
-  "Potentially convert a list of regexps into a single one."
-  (cond ((null message-dont-reply-to-names)
-        nil)
-       ((stringp message-dont-reply-to-names)
-        message-dont-reply-to-names)
-       ((listp message-dont-reply-to-names)
-        (mapconcat (lambda (elt) (concat "\\(" elt "\\)"))
-                   message-dont-reply-to-names
-                   "\\|"))))
+  (gmm-regexp-concat message-dont-reply-to-names))
 
 (defvar message-shoot-gnksa-feet nil
   "*A list of GNKSA feet you are allowed to shoot.
 
 (defvar message-shoot-gnksa-feet nil
   "*A list of GNKSA feet you are allowed to shoot.
@@ -1279,7 +1299,7 @@ starting with `not' and followed by regexps."
 (defface message-header-to
   '((((class color)
       (background dark))
 (defface message-header-to
   '((((class color)
       (background dark))
-     (:foreground "green2" :bold t))
+     (:foreground "DarkOliveGreen1" :bold t))
     (((class color)
       (background light))
      (:foreground "MidnightBlue" :bold t))
     (((class color)
       (background light))
      (:foreground "MidnightBlue" :bold t))
@@ -1293,7 +1313,7 @@ starting with `not' and followed by regexps."
 (defface message-header-cc
   '((((class color)
       (background dark))
 (defface message-header-cc
   '((((class color)
       (background dark))
-     (:foreground "green4" :bold t))
+     (:foreground "chartreuse1" :bold t))
     (((class color)
       (background light))
      (:foreground "MidnightBlue"))
     (((class color)
       (background light))
      (:foreground "MidnightBlue"))
@@ -1307,7 +1327,7 @@ starting with `not' and followed by regexps."
 (defface message-header-subject
   '((((class color)
       (background dark))
 (defface message-header-subject
   '((((class color)
       (background dark))
-     (:foreground "green3"))
+     (:foreground "OliveDrab1"))
     (((class color)
       (background light))
      (:foreground "navy blue" :bold t))
     (((class color)
       (background light))
      (:foreground "navy blue" :bold t))
@@ -1335,7 +1355,7 @@ starting with `not' and followed by regexps."
 (defface message-header-other
   '((((class color)
       (background dark))
 (defface message-header-other
   '((((class color)
       (background dark))
-     (:foreground "#b00000"))
+     (:foreground "VioletRed1"))
     (((class color)
       (background light))
      (:foreground "steel blue"))
     (((class color)
       (background light))
      (:foreground "steel blue"))
@@ -1349,7 +1369,7 @@ starting with `not' and followed by regexps."
 (defface message-header-name
   '((((class color)
       (background dark))
 (defface message-header-name
   '((((class color)
       (background dark))
-     (:foreground "DarkGreen"))
+     (:foreground "green"))
     (((class color)
       (background light))
      (:foreground "cornflower blue"))
     (((class color)
       (background light))
      (:foreground "cornflower blue"))
@@ -1363,7 +1383,7 @@ starting with `not' and followed by regexps."
 (defface message-header-xheader
   '((((class color)
       (background dark))
 (defface message-header-xheader
   '((((class color)
       (background dark))
-     (:foreground "blue"))
+     (:foreground "DeepSkyBlue1"))
     (((class color)
       (background light))
      (:foreground "blue"))
     (((class color)
       (background light))
      (:foreground "blue"))
@@ -1377,7 +1397,7 @@ starting with `not' and followed by regexps."
 (defface message-separator
   '((((class color)
       (background dark))
 (defface message-separator
   '((((class color)
       (background dark))
-     (:foreground "blue3"))
+     (:foreground "LightSkyBlue1"))
     (((class color)
       (background light))
      (:foreground "brown"))
     (((class color)
       (background light))
      (:foreground "brown"))
@@ -1391,7 +1411,7 @@ starting with `not' and followed by regexps."
 (defface message-cited-text
   '((((class color)
       (background dark))
 (defface message-cited-text
   '((((class color)
       (background dark))
-     (:foreground "red"))
+     (:foreground "LightPink1"))
     (((class color)
       (background light))
      (:foreground "red"))
     (((class color)
       (background light))
      (:foreground "red"))
@@ -1405,7 +1425,7 @@ starting with `not' and followed by regexps."
 (defface message-mml
   '((((class color)
       (background dark))
 (defface message-mml
   '((((class color)
       (background dark))
-     (:foreground "ForestGreen"))
+     (:foreground "MediumSpringGreen"))
     (((class color)
       (background light))
      (:foreground "ForestGreen"))
     (((class color)
       (background light))
      (:foreground "ForestGreen"))
@@ -1453,13 +1473,13 @@ starting with `not' and followed by regexps."
        (1 'message-header-name)
        (2 'message-header-newsgroups nil t))
       (,(message-font-lock-make-header-matcher
        (1 'message-header-name)
        (2 'message-header-newsgroups nil t))
       (,(message-font-lock-make-header-matcher
-        (concat "^\\([A-Z][^: \n\t]+:\\)" content))
+        (concat "^\\(X-[A-Za-z0-9-]+:\\|In-Reply-To:\\)" content))
        (1 'message-header-name)
        (1 'message-header-name)
-       (2 'message-header-other nil t))
+       (2 'message-header-xheader))
       (,(message-font-lock-make-header-matcher
       (,(message-font-lock-make-header-matcher
-        (concat "^\\(X-[A-Za-z0-9-]+:\\|In-Reply-To:\\)" content))
+        (concat "^\\([A-Z][^: \n\t]+:\\)" content))
        (1 'message-header-name)
        (1 'message-header-name)
-       (2 'message-header-name))
+       (2 'message-header-other nil t))
       ,@(if (and mail-header-separator
                 (not (equal mail-header-separator "")))
            `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
       ,@(if (and mail-header-separator
                 (not (equal mail-header-separator "")))
            `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
@@ -1626,7 +1646,7 @@ functionality to work."
 
 (defcustom message-generate-hashcash (if (executable-find "hashcash") t)
   "*Whether to generate X-Hashcash: headers.
 
 (defcustom message-generate-hashcash (if (executable-find "hashcash") t)
   "*Whether to generate X-Hashcash: headers.
-If `t', always generate hashcash headers.  If `opportunistic',
+If t, always generate hashcash headers.  If `opportunistic',
 only generate hashcash headers if it can be done without the user
 waiting (i.e., only asynchronously).
 
 only generate hashcash headers if it can be done without the user
 waiting (i.e., only asynchronously).
 
@@ -1649,9 +1669,8 @@ You must have the \"hashcash\" binary installed, see `hashcash-path'."
 (defvar message-inserted-headers nil)
 
 ;; Byte-compiler warning
 (defvar message-inserted-headers nil)
 
 ;; Byte-compiler warning
-(eval-when-compile
-  (defvar gnus-active-hashtb)
-  (defvar gnus-read-active-file))
+(defvar gnus-active-hashtb)
+(defvar gnus-read-active-file)
 
 ;;; Regexp matching the delimiter of messages in UNIX mail format
 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
 
 ;;; Regexp matching the delimiter of messages in UNIX mail format
 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
@@ -1716,6 +1735,7 @@ You must have the \"hashcash\" binary installed, see `hashcash-path'."
          "^ *--+ +begin message +--+ *$\\|"
          "^ *---+ +Original message follows +---+ *$\\|"
          "^ *---+ +Undelivered message follows +---+ *$\\|"
          "^ *--+ +begin message +--+ *$\\|"
          "^ *---+ +Original message follows +---+ *$\\|"
          "^ *---+ +Undelivered message follows +---+ *$\\|"
+         "^------ This is a copy of the message, including all the headers. ------ *$\\|"
          "^|? *---+ +Message text follows: +---+ *|?$")
   "A regexp that matches the separator before the text of a failed message.")
 
          "^|? *---+ +Message text follows: +---+ *|?$")
   "A regexp that matches the separator before the text of a failed message.")
 
@@ -1925,8 +1945,7 @@ see `message-narrow-to-headers-or-head'."
   "Evaluate FORMS in the reply buffer, if it exists."
   `(when (and message-reply-buffer
              (buffer-name message-reply-buffer))
   "Evaluate FORMS in the reply buffer, if it exists."
   `(when (and message-reply-buffer
              (buffer-name message-reply-buffer))
-     (save-excursion
-       (set-buffer message-reply-buffer)
+     (with-current-buffer message-reply-buffer
        ,@forms)))
 
 (put 'message-with-reply-buffer 'lisp-indent-function 0)
        ,@forms)))
 
 (put 'message-with-reply-buffer 'lisp-indent-function 0)
@@ -2380,14 +2399,12 @@ Point is left at the beginning of the narrowed-to region."
   (widen)
   (narrow-to-region
    (goto-char (point-min))
   (widen)
   (narrow-to-region
    (goto-char (point-min))
-   (cond
-    ((re-search-forward
-      (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
-     (match-beginning 0))
-    ((search-forward "\n\n" nil t)
-     (1- (point)))
-    (t
-     (point-max))))
+   (if (re-search-forward (concat "\\(\n\\)\n\\|^\\("
+                                 (regexp-quote mail-header-separator)
+                                 "\n\\)")
+                         nil t)
+       (or (match-end 1) (match-beginning 2))
+     (point-max)))
   (goto-char (point-min)))
 
 (defun message-news-p ()
   (goto-char (point-min)))
 
 (defun message-news-p ()
@@ -2469,15 +2486,28 @@ Point is left at the beginning of the narrowed-to region."
     (kill-region start (point))))
 
 
     (kill-region start (point))))
 
 
+(autoload 'Info-goto-node "info")
+(defvar mml2015-use)
+
 (defun message-info (&optional arg)
   "Display the Message manual.
 
 (defun message-info (&optional arg)
   "Display the Message manual.
 
-Prefixed with one \\[universal-argument], display the Emacs MIME manual.
-Prefixed with two \\[universal-argument]'s, display the PGG manual."
+Prefixed with one \\[universal-argument], display the Emacs MIME
+manual.  With two \\[universal-argument]'s, display the EasyPG or
+PGG manual, depending on the value of `mml2015-use'."
   (interactive "p")
   (interactive "p")
-  (cond ((eq arg 16) (Info-goto-node "(pgg)Top"))
-       ((eq arg  4) (Info-goto-node "(emacs-mime)Top"))
-       (t           (Info-goto-node "(message)Top"))))
+  ;; Why not `info', which is in loaddefs.el?
+  (Info-goto-node (format "(%s)Top"
+                         (cond ((eq arg 16)
+                                (require 'mml2015)
+                                mml2015-use)
+                               ((eq arg  4) 'emacs-mime)
+                               ;; `booleanp' only available in Emacs 22+
+                               ((and (not (memq arg '(nil t)))
+                                     (symbolp arg))
+                                arg)
+                               (t
+                                'message)))))
 
 \f
 
 
 \f
 
@@ -2673,9 +2703,8 @@ Prefixed with two \\[universal-argument]'s, display the PGG manual."
 
 (defvar message-tool-bar-map nil)
 
 
 (defvar message-tool-bar-map nil)
 
-(eval-when-compile
-  (defvar facemenu-add-face-function)
-  (defvar facemenu-remove-face-function))
+(defvar facemenu-add-face-function)
+(defvar facemenu-remove-face-function)
 
 ;;; Forbidden properties
 ;;
 
 ;;; Forbidden properties
 ;;
@@ -2888,6 +2917,8 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
   ;; solution would be not to use `define-derived-mode', and run
   ;; `text-mode-hook' ourself at the end of the mode.
   ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-19.
   ;; solution would be not to use `define-derived-mode', and run
   ;; `text-mode-hook' ourself at the end of the mode.
   ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-19.
+  ;; This kludge is unneeded in Emacs>=21 since define-derived-mode is
+  ;; now careful to run parent hooks after the body.  --Stef
   (when auto-fill-function
     (setq auto-fill-function normal-auto-fill-function)))
 
   (when auto-fill-function
     (setq auto-fill-function normal-auto-fill-function)))
 
@@ -3093,8 +3124,7 @@ or in the synonym headers, defined by `message-header-synonyms'."
   (let ((follow-to
         (and message-reply-buffer
              (buffer-name message-reply-buffer)
   (let ((follow-to
         (and message-reply-buffer
              (buffer-name message-reply-buffer)
-             (save-excursion
-               (set-buffer message-reply-buffer)
+             (with-current-buffer message-reply-buffer
                (message-get-reply-headers t)))))
     (save-excursion
       (save-restriction
                (message-get-reply-headers t)))))
     (save-excursion
       (save-restriction
@@ -3346,8 +3376,7 @@ The three allowed values according to RFC 1327 are `high', `normal'
 and `low'."
   (interactive)
   (save-excursion
 and `low'."
   (interactive)
   (save-excursion
-    (let ((valid '("high" "normal" "low"))
-         (new "high")
+    (let ((new "high")
          cur)
       (save-restriction
        (message-narrow-to-headers)
          cur)
       (save-restriction
        (message-narrow-to-headers)
@@ -3529,6 +3558,27 @@ However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
        (forward-line 1))))
   (goto-char start))
 
        (forward-line 1))))
   (goto-char start))
 
+(defun message-remove-blank-cited-lines (&optional remove)
+  "Remove cited lines containing only blanks.
+If REMOVE is non-nil, remove newlines, too.
+
+To use this automatically, you may add this function to
+`gnus-message-setup-hook'."
+  (interactive "P")
+  (let ((citexp
+        (concat
+         "^\\("
+         (when (boundp 'message-yank-cited-prefix)
+           (concat message-yank-cited-prefix "\\|"))
+         message-yank-prefix
+         "\\)+ *\n"
+         )))
+    (gnus-message 8 "removing `%s'" citexp)
+    (save-excursion
+      (message-goto-body)
+      (while (re-search-forward citexp nil t)
+       (replace-match (if remove "" "\n"))))))
+
 (defvar message-cite-reply-above nil
   "If non-nil, start own text above the quote.
 
 (defvar message-cite-reply-above nil
   "If non-nil, start own text above the quote.
 
@@ -3600,7 +3650,7 @@ Really top post? ")))
 (defun message-buffers ()
   "Return a list of active message buffers."
   (let (buffers)
 (defun message-buffers ()
   "Return a list of active message buffers."
   (let (buffers)
-    (save-excursion
+    (save-current-buffer
       (dolist (buffer (buffer-list t))
        (set-buffer buffer)
        (when (and (eq major-mode 'message-mode)
       (dolist (buffer (buffer-list t))
        (set-buffer buffer)
        (when (and (eq major-mode 'message-mode)
@@ -3608,8 +3658,6 @@ Really top post? ")))
          (push (buffer-name buffer) buffers))))
     (nreverse buffers)))
 
          (push (buffer-name buffer) buffers))))
     (nreverse buffers)))
 
-(eval-when-compile (defvar mail-citation-hook))        ; Compiler directive
-
 (defun message-cite-original-1 (strip-signature)
   "Cite an original message.
 If STRIP-SIGNATURE is non-nil, strips off the signature from the
 (defun message-cite-original-1 (strip-signature)
   "Cite an original message.
 If STRIP-SIGNATURE is non-nil, strips off the signature from the
@@ -3676,14 +3724,18 @@ This function uses `mail-citation-hook' if that is non-nil."
   "Cite function in the standard Message manner."
   (message-cite-original-1 nil))
 
   "Cite function in the standard Message manner."
   (message-cite-original-1 nil))
 
-(defun message-insert-formated-citation-line (&optional from date)
-  "Function that inserts a formated citation line.
+(defvar gnus-extract-address-components)
+
+(autoload 'format-spec "format-spec")
+
+(defun message-insert-formatted-citation-line (&optional from date)
+  "Function that inserts a formatted citation line.
 
 See `message-citation-line-format'."
   ;; The optional args are for testing/debugging.  They will disappear later.
   ;; Example:
   ;; (with-temp-buffer
 
 See `message-citation-line-format'."
   ;; The optional args are for testing/debugging.  They will disappear later.
   ;; Example:
   ;; (with-temp-buffer
-  ;;   (message-insert-formated-citation-line
+  ;;   (message-insert-formatted-citation-line
   ;;    "John Doe <john.doe@example.invalid>"
   ;;    (current-time))
   ;;   (buffer-string))
   ;;    "John Doe <john.doe@example.invalid>"
   ;;    (current-time))
   ;;   (buffer-string))
@@ -3760,7 +3812,6 @@ See `message-citation-line-format'."
              (reverse lst)))
           (spec (apply 'format-spec-make flist)))
       (insert (format-spec message-citation-line-format spec)))
              (reverse lst)))
           (spec (apply 'format-spec-make flist)))
       (insert (format-spec message-citation-line-format spec)))
-    (newline)
     (newline)))
 
 (defun message-cite-original-without-signature ()
     (newline)))
 
 (defun message-cite-original-without-signature ()
@@ -4009,6 +4060,20 @@ not have PROP."
        (setq start next)))
     (nreverse regions)))
 
        (setq start next)))
     (nreverse regions)))
 
+(defcustom message-bogus-address-regexp nil ;; "noreply\\|nospam\\|invalid"
+  "Regexp of potentially bogus mail addresses."
+  :version "23.1" ;; No Gnus
+  :group 'message-headers
+  :type '(choice (const :tag "None" nil)
+                (repeat :value-to-internal (lambda (widget value)
+                                             (custom-split-regexp-maybe value))
+                        :match (lambda (widget value)
+                                 (or (stringp value)
+                                     (widget-editable-list-match widget value)))
+                        regexp)
+                (const "noreply\\|nospam\\|invalid")
+                regexp))
+
 (defun message-fix-before-sending ()
   "Do various things to make the message nice before sending it."
   ;; Make sure there's a newline at the end of the message.
 (defun message-fix-before-sending ()
   "Do various things to make the message nice before sending it."
   ;; Make sure there's a newline at the end of the message.
@@ -4037,23 +4102,28 @@ not have PROP."
                 "Invisible text found and made visible; continue sending? ")
          (error "Invisible text found and made visible")))))
   (message-check 'illegible-text
                 "Invisible text found and made visible; continue sending? ")
          (error "Invisible text found and made visible")))))
   (message-check 'illegible-text
-    (let (found choice)
+    (let (char found choice)
       (message-goto-body)
       (message-goto-body)
-      (skip-chars-forward mm-7bit-chars)
-      (while (not (eobp))
-       (when (let ((char (char-after)))
-               (or (< (mm-char-int char) 128)
-                   (and (mm-multibyte-p)
-                        (memq (char-charset char)
-                              '(eight-bit-control eight-bit-graphic
-                                                  control-1))
-                        (not (get-text-property
-                              (point) 'untranslated-utf-8)))))
+      (while (progn
+              (skip-chars-forward mm-7bit-chars)
+              (when (get-text-property (point) 'no-illegible-text)
+                ;; There is a signed or encrypted raw message part
+                ;; that is considered to be safe.
+                (goto-char (or (next-single-property-change
+                                (point) 'no-illegible-text)
+                               (point-max))))
+              (setq char (char-after)))
+       (when (or (< (mm-char-int char) 128)
+                 (and (mm-multibyte-p)
+                      (memq (char-charset char)
+                            '(eight-bit-control eight-bit-graphic
+                                                control-1))
+                      (not (get-text-property
+                            (point) 'untranslated-utf-8))))
          (message-overlay-put (message-make-overlay (point) (1+ (point)))
                               'face 'highlight)
          (setq found t))
          (message-overlay-put (message-make-overlay (point) (1+ (point)))
                               'face 'highlight)
          (setq found t))
-       (forward-char)
-       (skip-chars-forward mm-7bit-chars))
+       (forward-char))
       (when found
        (setq choice
              (gnus-multiple-choice
       (when found
        (setq choice
              (gnus-multiple-choice
@@ -4086,7 +4156,56 @@ not have PROP."
              (when (eq choice ?r)
                (insert message-replacement-char))))
          (forward-char)
              (when (eq choice ?r)
                (insert message-replacement-char))))
          (forward-char)
-         (skip-chars-forward mm-7bit-chars))))))
+         (skip-chars-forward mm-7bit-chars)))))
+  (message-check 'bogus-recipient
+    ;; Warn before sending a mail to an invalid address.
+    (message-check-recipients)))
+
+(defun message-bogus-recipient-p (recipients)
+  "Check if a mail address in RECIPIENTS looks bogus.
+
+RECIPIENTS is a mail header.  Return a list of potentially bogus
+addresses.  If none is found, return nil.
+
+An addresses might be bogus if the domain part is not fully
+qualified, see `message-valid-fqdn-regexp', or if it matches
+`message-bogus-address-regexp'."
+  ;; FIXME: How about "foo@subdomain", when the MTA adds ".domain.tld"?
+  (let (found)
+    (mapc (lambda (address)
+           (setq address (cadr address))
+           (when
+               (or (not
+                    (or
+                     (not (string-match "@" address))
+                     (string-match
+                      (concat ".@.*\\("
+                              message-valid-fqdn-regexp "\\)\\'") address)))
+                   (and (stringp message-bogus-address-regexp)
+                        (string-match message-bogus-address-regexp address)))
+             (push address found)))
+         ;;
+         (mail-extract-address-components recipients t))
+    found))
+
+(defun message-check-recipients ()
+  "Warn before composing or sending a mail to an invalid address.
+
+This function could be useful in `message-setup-hook'."
+  (interactive)
+  (save-restriction
+    (message-narrow-to-headers)
+    (dolist (hdr '("To" "Cc" "Bcc"))
+      (let ((addr (message-fetch-field hdr)))
+       (when (stringp addr)
+         (dolist (bog (message-bogus-recipient-p addr))
+           (and bog
+                (not (y-or-n-p
+                      (format
+                       "Address `%s' might be bogus.  Continue? " bog)))
+                (error "Bogus address."))))))))
+
+(custom-add-option 'message-setup-hook 'message-check-recipients)
 
 (defun message-add-action (action &rest types)
   "Add ACTION to be performed when doing an exit of type TYPES."
 
 (defun message-add-action (action &rest types)
   "Add ACTION to be performed when doing an exit of type TYPES."
@@ -4235,8 +4354,7 @@ not have PROP."
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook))
     (unwind-protect
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook))
     (unwind-protect
-       (save-excursion
-         (set-buffer tembuf)
+       (with-current-buffer tembuf
          (erase-buffer)
          ;; Avoid copying text props (except hard newlines).
          (insert (with-current-buffer mailbuf
          (erase-buffer)
          ;; Avoid copying text props (except hard newlines).
          (insert (with-current-buffer mailbuf
@@ -4381,8 +4499,7 @@ If you always want Gnus to send messages in one piece, set
            (unless (or (null cpr) (and (numberp cpr) (zerop cpr)))
              (error "Sending...failed with exit value %d" cpr)))
          (when message-interactive
            (unless (or (null cpr) (and (numberp cpr) (zerop cpr)))
              (error "Sending...failed with exit value %d" cpr)))
          (when message-interactive
-           (save-excursion
-             (set-buffer errbuf)
+           (with-current-buffer errbuf
              (goto-char (point-min))
              (while (re-search-forward "\n+ *" nil t)
                (replace-match "; "))
              (goto-char (point-min))
              (while (re-search-forward "\n+ *" nil t)
                (replace-match "; "))
@@ -4463,6 +4580,13 @@ manual for details."
   (run-hooks 'message-send-mail-hook)
   (smtpmail-send-it))
 
   (run-hooks 'message-send-mail-hook)
   (smtpmail-send-it))
 
+(defun message-send-mail-with-mailclient ()
+  "Send the prepared message buffer with `mailclient-send-it'.
+This only differs from `smtpmail-send-it' that this command evaluates
+`message-send-mail-hook' just before sending a message."
+  (run-hooks 'message-send-mail-hook)
+  (mailclient-send-it))
+
 (defun message-canlock-generate ()
   "Return a string that is non-trivial to guess.
 Do not use this for anything important, it is cryptographically weak."
 (defun message-canlock-generate ()
   "Return a string that is non-trivial to guess.
 Do not use this for anything important, it is cryptographically weak."
@@ -4545,8 +4669,7 @@ Otherwise, generate and save a value for `canlock-password' first."
                 (message-check-news-syntax)))
          nil
        (unwind-protect
                 (message-check-news-syntax)))
          nil
        (unwind-protect
-           (save-excursion
-             (set-buffer tembuf)
+           (with-current-buffer tembuf
              (buffer-disable-undo)
              (erase-buffer)
              ;; Avoid copying text props (except hard newlines).
              (buffer-disable-undo)
              (erase-buffer)
              ;; Avoid copying text props (except hard newlines).
@@ -4938,7 +5061,7 @@ Otherwise, generate and save a value for `canlock-password' first."
    ;; Check for control characters.
    (message-check 'control-chars
      (if (re-search-forward
    ;; Check for control characters.
    (message-check 'control-chars
      (if (re-search-forward
-         (mm-string-as-multibyte "[\000-\007\013\015-\032\034-\037\200-\237]")
+         (mm-string-to-multibyte "[\000-\007\013\015-\032\034-\037\200-\237]")
          nil t)
         (y-or-n-p
          "The article contains control characters.  Really post? ")
          nil t)
         (y-or-n-p
          "The article contains control characters.  Really post? ")
@@ -4963,12 +5086,16 @@ Otherwise, generate and save a value for `canlock-password' first."
    ;; Check the length of the signature.
    (message-check 'signature
      (goto-char (point-max))
    ;; Check the length of the signature.
    (message-check 'signature
      (goto-char (point-max))
-     (if (> (count-lines (point) (point-max)) 5)
-        (y-or-n-p
-         (format
-          "Your .sig is %d lines; it should be max 4.  Really post? "
-          (1- (count-lines (point) (point-max)))))
-       t))
+     (if (not (re-search-backward message-signature-separator nil t))
+        t
+       (if (>= (count-lines (1+ (point-at-eol)) (point-max)) 5)
+          (if (message-gnksa-enable-p 'signature)
+              (y-or-n-p
+               (format "Signature is excessively long (%d lines).  Really post? "
+                       (count-lines (1+ (point-at-eol)) (point-max))))
+            (message "Denied posting -- Excessive signature.")
+            nil)
+        t)))
    ;; Ensure that text follows last quoted portion.
    (message-check 'quoting-style
      (goto-char (point-max))
    ;; Ensure that text follows last quoted portion.
    (message-check 'quoting-style
      (goto-char (point-max))
@@ -5209,8 +5336,7 @@ In posting styles use `(\"Expires\" (make-expires-date 30))'."
   "Return the References header for this message."
   (when message-reply-headers
     (let ((message-id (mail-header-message-id message-reply-headers))
   "Return the References header for this message."
   (when message-reply-headers
     (let ((message-id (mail-header-message-id message-reply-headers))
-         (references (mail-header-references message-reply-headers))
-         new-references)
+         (references (mail-header-references message-reply-headers)))
       (if (or references message-id)
          (concat (or references "") (and references " ")
                  (or message-id ""))
       (if (or references message-id)
          (concat (or references "") (and references " ")
                  (or message-id ""))
@@ -5231,19 +5357,18 @@ In posting styles use `(\"Expires\" (make-expires-date 30))'."
                   ;; Quote a string containing non-ASCII characters.
                   ;; It will make the RFC2047 encoder cause an error
                   ;; if there are special characters.
                   ;; Quote a string containing non-ASCII characters.
                   ;; It will make the RFC2047 encoder cause an error
                   ;; if there are special characters.
-                  (let ((default-enable-multibyte-characters t))
-                    (with-temp-buffer
-                      (insert (car name))
-                      (goto-char (point-min))
-                      (while (search-forward "\"" nil t)
-                        (when (prog2
-                                  (backward-char)
-                                  (zerop (% (skip-chars-backward "\\\\") 2))
-                                (goto-char (match-beginning 0)))
-                          (insert "\\"))
-                        (forward-char))
-                      ;; Those quotes will be removed by the RFC2047 encoder.
-                      (concat "\"" (buffer-string) "\"")))
+                   (mm-with-multibyte-buffer
+                     (insert (car name))
+                     (goto-char (point-min))
+                     (while (search-forward "\"" nil t)
+                       (when (prog2
+                                 (backward-char)
+                                 (zerop (% (skip-chars-backward "\\\\") 2))
+                               (goto-char (match-beginning 0)))
+                         (insert "\\"))
+                       (forward-char))
+                     ;; Those quotes will be removed by the RFC2047 encoder.
+                     (concat "\"" (buffer-string) "\""))
                 (car name))
             (nth 1 name))
           "'s message of \""
                 (car name))
             (nth 1 name))
           "'s message of \""
@@ -5277,7 +5402,7 @@ In posting styles use `(\"Expires\" (make-expires-date 30))'."
           (concat message-user-path "!" login-name))
          (t login-name))))
 
           (concat message-user-path "!" login-name))
          (t login-name))))
 
-(defun message-make-from (&optional name address )
+(defun message-make-from (&optional name address)
   "Make a From header."
   (let* ((style message-from-style)
         (login (or address (message-make-address)))
   "Make a From header."
   (let* ((style message-from-style)
         (login (or address (message-make-address)))
@@ -5458,8 +5583,7 @@ subscribed address (and not the additional To and Cc header contents)."
                             (mapcar 'funcall
                                     message-subscribed-address-functions))))
     (save-match-data
                             (mapcar 'funcall
                                     message-subscribed-address-functions))))
     (save-match-data
-      (let ((subscribed-lists nil)
-           (list
+      (let ((list
             (loop for recipient in recipients
               when (loop for regexp in mft-regexps
                      when (string-match regexp recipient) return t)
             (loop for recipient in recipients
               when (loop for regexp in mft-regexps
                      when (string-match regexp recipient) return t)
@@ -5480,7 +5604,9 @@ subscribed address (and not the additional To and Cc header contents)."
                        (mapcar 'downcase
                                (mapcar
                                 'car (mail-header-parse-addresses field))))))
                        (mapcar 'downcase
                                (mapcar
                                 'car (mail-header-parse-addresses field))))))
-       (setq ace (downcase (idna-to-ascii rhs)))
+       (setq ace (if (string-match "\\`[[:ascii:]]+\\'" rhs)
+                     rhs
+                   (downcase (idna-to-ascii rhs))))
        (when (and (not (equal rhs ace))
                   (or (not (eq message-use-idna 'ask))
                       (y-or-n-p (format "Replace %s with %s in %s:? "
        (when (and (not (equal rhs ace))
                   (or (not (eq message-use-idna 'ask))
                       (y-or-n-p (format "Replace %s with %s in %s:? "
@@ -5787,8 +5913,10 @@ they are."
     (with-temp-buffer
       (insert references)
       (goto-char (point-min))
     (with-temp-buffer
       (insert references)
       (goto-char (point-min))
-      ;; Cons a list of valid references.
-      (while (re-search-forward "<[^>]+>" nil t)
+      ;; Cons a list of valid references.  GNKSA says we must not include MIDs
+      ;; with whitespace or missing brackets (7.a "Does not propagate broken
+      ;; Message-IDs in original References").
+      (while (re-search-forward "<[^ <]+@[^ <]+>" nil t)
        (push (match-string 0) refs))
       (setq refs (nreverse refs)
            count (length refs)))
        (push (match-string 0) refs))
       (setq refs (nreverse refs)
            count (length refs)))
@@ -5864,7 +5992,7 @@ beginning of header value.  Therefore, repeated calls will toggle point
 between beginning of field and beginning of line."
   (interactive "p")
   (let ((zrs 'zmacs-region-stays))
 between beginning of field and beginning of line."
   (interactive "p")
   (let ((zrs 'zmacs-region-stays))
-    (when (and (interactive-p) (boundp zrs))
+    (when (and (featurep 'xemacs) (interactive-p) (boundp zrs))
       (set zrs t)))
   (if (and message-beginning-of-line
           (message-point-in-header-p))
       (set zrs t)))
   (if (and message-beginning-of-line
           (message-point-in-header-p))
@@ -5933,7 +6061,7 @@ between beginning of field and beginning of line."
             'car-less-than-car)))
          new)))))
 
             'car-less-than-car)))
          new)))))
 
-(defun message-pop-to-buffer (name)
+(defun message-pop-to-buffer (name &optional switch-function)
   "Pop to buffer NAME, and warn if it already exists and is modified."
   (let ((buffer (get-buffer name)))
     (if (and buffer
   "Pop to buffer NAME, and warn if it already exists and is modified."
   (let ((buffer (get-buffer name)))
     (if (and buffer
@@ -5944,14 +6072,16 @@ between beginning of field and beginning of line."
              (progn
                (gnus-select-frame-set-input-focus (window-frame window))
                (select-window window))
              (progn
                (gnus-select-frame-set-input-focus (window-frame window))
                (select-window window))
-           (set-buffer (pop-to-buffer buffer)))
+           (funcall (or switch-function 'pop-to-buffer) buffer)
+           (set-buffer buffer))
          (when (and (buffer-modified-p)
                     (not (prog1
                              (y-or-n-p
                               "Message already being composed; erase? ")
                            (message nil))))
            (error "Message being composed")))
          (when (and (buffer-modified-p)
                     (not (prog1
                              (y-or-n-p
                               "Message already being composed; erase? ")
                            (message nil))))
            (error "Message being composed")))
-      (set-buffer (pop-to-buffer name)))
+      (funcall (or switch-function 'pop-to-buffer) name)
+      (set-buffer name))
     (erase-buffer)
     (message-mode)))
 
     (erase-buffer)
     (message-mode)))
 
@@ -6110,11 +6240,12 @@ are not included."
   (save-restriction
     (message-narrow-to-headers)
     (run-hooks 'message-header-setup-hook))
   (save-restriction
     (message-narrow-to-headers)
     (run-hooks 'message-header-setup-hook))
-  (set-buffer-modified-p nil)
   (setq buffer-undo-list nil)
   (when message-generate-hashcash
     ;; Generate hashcash headers for recipients already known
     (mail-add-payment-async))
   (setq buffer-undo-list nil)
   (when message-generate-hashcash
     ;; Generate hashcash headers for recipients already known
     (mail-add-payment-async))
+  ;; Gnus posting styles are applied via buffer-local `message-setup-hook'
+  ;; values.
   (run-hooks 'message-setup-hook)
   ;; Do this last to give it precedence over posting styles, etc.
   (when (message-mail-p)
   (run-hooks 'message-setup-hook)
   ;; Do this last to give it precedence over posting styles, etc.
   (when (message-mail-p)
@@ -6123,6 +6254,8 @@ are not included."
       (if message-alternative-emails
          (message-use-alternative-email-as-from))))
   (message-position-point)
       (if message-alternative-emails
          (message-use-alternative-email-as-from))))
   (message-position-point)
+  ;; Allow correct handling of `message-checksum' in `message-yank-original':
+  (set-buffer-modified-p nil)
   (undo-boundary))
 
 (defun message-set-auto-save-file-name ()
   (undo-boundary))
 
 (defun message-set-auto-save-file-name ()
@@ -6150,7 +6283,7 @@ are not included."
   "Disassociate the message buffer from the drafts directory."
   (when message-draft-article
     (nndraft-request-expire-articles
   "Disassociate the message buffer from the drafts directory."
   (when message-draft-article
     (nndraft-request-expire-articles
-     (list message-draft-article) "drafts" nil t)))
+     (list message-draft-article) "nndraft:drafts" nil t)))
 
 (defun message-insert-headers ()
   "Generate the headers for the article."
 
 (defun message-insert-headers ()
   "Generate the headers for the article."
@@ -6186,15 +6319,15 @@ is a function used to switch to and display the mail buffer."
   (interactive)
   (let ((message-this-is-mail t) replybuffer)
     (unless (message-mail-user-agent)
   (interactive)
   (let ((message-this-is-mail t) replybuffer)
     (unless (message-mail-user-agent)
-      (funcall
-       (or switch-function 'message-pop-to-buffer)
+      (message-pop-to-buffer
        ;; Search for the existing message buffer if `continue' is non-nil.
        (let ((message-generate-new-buffers
              (when (or (not continue)
                        (eq message-generate-new-buffers 'standard)
                        (functionp message-generate-new-buffers))
                message-generate-new-buffers)))
        ;; Search for the existing message buffer if `continue' is non-nil.
        (let ((message-generate-new-buffers
              (when (or (not continue)
                        (eq message-generate-new-buffers 'standard)
                        (functionp message-generate-new-buffers))
                message-generate-new-buffers)))
-        (message-buffer-name "mail" to))))
+        (message-buffer-name "mail" to))
+       switch-function))
     ;; FIXME: message-mail should do something if YANK-ACTION is not
     ;; insert-buffer.
     (and (consp yank-action) (eq (car yank-action) 'insert-buffer)
     ;; FIXME: message-mail should do something if YANK-ACTION is not
     ;; insert-buffer.
     (and (consp yank-action) (eq (car yank-action) 'insert-buffer)
@@ -6216,6 +6349,29 @@ is a function used to switch to and display the mail buffer."
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
 
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
 
+(defun message-alter-recipients-discard-bogus-full-name (addrcell)
+  "Discard mail address in full names.
+When the full name in reply headers contains the mail
+address (e.g. \"foo@bar <foo@bar>\"), discard full name.
+ADDRCELL is a cons cell where the car is the mail address and the
+cdr is the complete address (full name and mail address)."
+  (if (string-match (concat (regexp-quote (car addrcell)) ".*"
+                           (regexp-quote (car addrcell)))
+                   (cdr addrcell))
+      (cons (car addrcell) (car addrcell))
+    addrcell))
+
+(defcustom message-alter-recipients-function nil
+  "Function called to allow alteration of reply header structures.
+It is called in `message-get-reply-headers' for each recipient.
+The function is called with one parameter, a cons cell ..."
+  :type '(choice (const :tag "None" nil)
+                (const :tag "Discard bogus full name"
+                       message-alter-recipients-discard-bogus-full-name)
+                function)
+  :version "23.1" ;; No Gnus
+  :group 'message-headers)
+
 (defun message-get-reply-headers (wide &optional to-address address-headers)
   (let (follow-to mct never-mct to cc author mft recipients extra)
   ;; Find all relevant headers we need.
 (defun message-get-reply-headers (wide &optional to-address address-headers)
   (let (follow-to mct never-mct to cc author mft recipients extra)
   ;; Find all relevant headers we need.
@@ -6316,7 +6472,11 @@ want to get rid of this query permanently.")))
       (setq recipients
            (mapcar
             (lambda (addr)
       (setq recipients
            (mapcar
             (lambda (addr)
-              (cons (downcase (mail-strip-quoted-names addr)) addr))
+              (if message-alter-recipients-function
+                  (funcall message-alter-recipients-function
+                           (cons (downcase (mail-strip-quoted-names addr))
+                                 addr))
+                (cons (downcase (mail-strip-quoted-names addr)) addr)))
             (message-tokenize-header recipients)))
       ;; Remove first duplicates.  (Why not all duplicates?  Is this a bug?)
       (let ((s recipients))
             (message-tokenize-header recipients)))
       ;; Remove first duplicates.  (Why not all duplicates?  Is this a bug?)
       (let ((s recipients))
@@ -6802,8 +6962,7 @@ the message."
            (setq subject (funcall func subject))))
        subject))))
 
            (setq subject (funcall func subject))))
        subject))))
 
-(eval-when-compile
-  (defvar gnus-article-decoded-p))
+(defvar gnus-article-decoded-p)
 
 
 ;;;###autoload
 
 
 ;;;###autoload
@@ -6858,8 +7017,8 @@ Optional DIGEST will use digest to forward."
          (message-remove-header elem t))))))
 
 (defun message-forward-make-body-mime (forward-buffer)
          (message-remove-header elem t))))))
 
 (defun message-forward-make-body-mime (forward-buffer)
-  (insert "\n\n<#part type=message/rfc822 disposition=inline raw=t>\n")
-  (let ((b (point)) e)
+  (let ((b (point)))
+    (insert "\n\n<#part type=message/rfc822 disposition=inline raw=t>\n")
     (save-restriction
       (narrow-to-region (point) (point))
       (mml-insert-buffer forward-buffer)
     (save-restriction
       (narrow-to-region (point) (point))
       (mml-insert-buffer forward-buffer)
@@ -6867,8 +7026,11 @@ Optional DIGEST will use digest to forward."
       (when (looking-at "From ")
        (replace-match "X-From-Line: "))
       (goto-char (point-max)))
       (when (looking-at "From ")
        (replace-match "X-From-Line: "))
       (goto-char (point-max)))
-    (setq e (point))
-    (insert "<#/part>\n")))
+    (insert "<#/part>\n")
+    ;; Consider there is no illegible text.
+    (add-text-properties
+     b (point)
+     `(no-illegible-text t rear-nonsticky t start-open t))))
 
 (defun message-forward-make-body-mml (forward-buffer)
   (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n")
 
 (defun message-forward-make-body-mml (forward-buffer)
   (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n")
@@ -7014,8 +7176,6 @@ is for the internal use."
        (rmail-msg-restore-non-pruned-header)))
   (message-forward-make-body forward-buffer))
 
        (rmail-msg-restore-non-pruned-header)))
   (message-forward-make-body forward-buffer))
 
-(eval-when-compile (defvar rmail-enable-mime-composing))
-
 ;; Fixme: Should have defcustom.
 ;;;###autoload
 (defun message-insinuate-rmail ()
 ;; Fixme: Should have defcustom.
 ;;;###autoload
 (defun message-insinuate-rmail ()
@@ -7117,7 +7277,7 @@ you."
        (goto-char boundary)
        (when (re-search-backward "^.?From .*\n" nil t)
          (delete-region (match-beginning 0) (match-end 0)))))
        (goto-char boundary)
        (when (re-search-backward "^.?From .*\n" nil t)
          (delete-region (match-beginning 0) (match-end 0)))))
-    (mm-enable-multibyte)
+    (mime-to-mml)
     (save-restriction
       (message-narrow-to-head-1)
       (message-remove-header message-ignored-bounced-headers t)
     (save-restriction
       (message-narrow-to-head-1)
       (message-remove-header message-ignored-bounced-headers t)
@@ -7237,8 +7397,7 @@ which specify the range to operate on."
     (mapcar #'delete-overlay (overlays-in (point-min) (point-max)))))
 
 ;; Support for toolbar
     (mapcar #'delete-overlay (overlays-in (point-min) (point-max)))))
 
 ;; Support for toolbar
-(eval-when-compile
-  (defvar tool-bar-mode))
+(defvar tool-bar-mode)
 
 ;; Note: The :set function in the `message-tool-bar*' variables will only
 ;; affect _new_ message buffers.  We might add a function that walks thru all
 
 ;; Note: The :set function in the `message-tool-bar*' variables will only
 ;; affect _new_ message buffers.  We might add a function that walks thru all
@@ -7267,7 +7426,7 @@ Pre-defined symbols include `message-tool-bar-gnome' and
                 (const :tag "Retro look"  message-tool-bar-retro)
                 (repeat :tag "User defined list" gmm-tool-bar-item)
                 (symbol))
                 (const :tag "Retro look"  message-tool-bar-retro)
                 (repeat :tag "User defined list" gmm-tool-bar-item)
                 (symbol))
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
@@ -7296,14 +7455,14 @@ Pre-defined symbols include `message-tool-bar-gnome' and
 
 See `gmm-tool-bar-from-list' for details on the format of the list."
   :type '(repeat gmm-tool-bar-item)
 
 See `gmm-tool-bar-from-list' for details on the format of the list."
   :type '(repeat gmm-tool-bar-item)
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
 
 (defcustom message-tool-bar-retro
   '(;; Old Emacs 21 icon for consistency.
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
 
 (defcustom message-tool-bar-retro
   '(;; Old Emacs 21 icon for consistency.
-    (message-send-and-exit "gnus/mail_send")
+    (message-send-and-exit "gnus/mail-send")
     (message-kill-buffer "close")
     (message-dont-send "cancel")
     (mml-attach-file "attach" mml-mode-map)
     (message-kill-buffer "close")
     (message-dont-send "cancel")
     (mml-attach-file "attach" mml-mode-map)
@@ -7316,7 +7475,7 @@ See `gmm-tool-bar-from-list' for details on the format of the list."
 
 See `gmm-tool-bar-from-list' for details on the format of the list."
   :type '(repeat gmm-tool-bar-item)
 
 See `gmm-tool-bar-from-list' for details on the format of the list."
   :type '(repeat gmm-tool-bar-item)
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
@@ -7329,7 +7488,7 @@ These items are not displayed on the message mode tool bar.
 
 See `gmm-tool-bar-from-list' for the format of the list."
   :type 'gmm-tool-bar-zap-list
 
 See `gmm-tool-bar-from-list' for the format of the list."
   :type 'gmm-tool-bar-zap-list
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
   :initialize 'custom-initialize-default
   :set 'message-tool-bar-update
   :group 'message)
@@ -7392,6 +7551,8 @@ If nil, the function bound in `text-mode-map' or `global-map' is executed."
   :type '(choice (const nil)
                 function))
 
   :type '(choice (const nil)
                 function))
 
+(declare-function mail-abbrev-in-expansion-header-p "mailabbrev" ())
+
 (defun message-tab ()
   "Complete names according to `message-completion-alist'.
 Execute function specified by `message-tab-body-function' when not in
 (defun message-tab ()
   "Complete names according to `message-completion-alist'.
 Execute function specified by `message-tab-body-function' when not in
@@ -7482,11 +7643,10 @@ The following arguments may contain lists of values."
   (if (and show
           (setq text (message-flatten-list text)))
       (save-window-excursion
   (if (and show
           (setq text (message-flatten-list text)))
       (save-window-excursion
-       (save-excursion
-         (with-output-to-temp-buffer " *MESSAGE information message*"
-           (set-buffer " *MESSAGE information message*")
+        (with-output-to-temp-buffer " *MESSAGE information message*"
+          (with-current-buffer " *MESSAGE information message*"
            (fundamental-mode)          ; for Emacs 20.4+
            (fundamental-mode)          ; for Emacs 20.4+
-           (mapcar 'princ text)
+           (mapc 'princ text)
            (goto-char (point-min))))
        (funcall ask question))
     (funcall ask question)))
            (goto-char (point-min))))
        (funcall ask question))
     (funcall ask question)))
@@ -7507,16 +7667,13 @@ Then clone the local variables and values from the old buffer to the
 new one, cloning only the locals having a substring matching the
 regexp VARSTR."
   (let ((oldbuf (current-buffer)))
 new one, cloning only the locals having a substring matching the
 regexp VARSTR."
   (let ((oldbuf (current-buffer)))
-    (save-excursion
-      (set-buffer (generate-new-buffer name))
+    (with-current-buffer (generate-new-buffer name)
       (message-clone-locals oldbuf varstr)
       (current-buffer))))
 
 (defun message-clone-locals (buffer &optional varstr)
   "Clone the local variables from BUFFER to the current buffer."
       (message-clone-locals oldbuf varstr)
       (current-buffer))))
 
 (defun message-clone-locals (buffer &optional varstr)
   "Clone the local variables from BUFFER to the current buffer."
-  (let ((locals (save-excursion
-                 (set-buffer buffer)
-                 (buffer-local-variables)))
+  (let ((locals (with-current-buffer buffer (buffer-local-variables)))
        (regexp "^gnus\\|^nn\\|^message\\|^sendmail\\|^smtp\\|^user-mail-address"))
     (mapcar
      (lambda (local)
        (regexp "^gnus\\|^nn\\|^message\\|^sendmail\\|^smtp\\|^user-mail-address"))
     (mapcar
      (lambda (local)
@@ -7653,7 +7810,7 @@ From headers in the original article."
                   message-hidden-headers))
        (inhibit-point-motion-hooks t)
        (after-change-functions nil)
                   message-hidden-headers))
        (inhibit-point-motion-hooks t)
        (after-change-functions nil)
-       (end-of-headers 0))
+       (end-of-headers (point-min)))
     (when regexps
       (save-excursion
        (save-restriction
     (when regexps
       (save-excursion
        (save-restriction
@@ -7668,11 +7825,11 @@ From headers in the original article."
                (setq header (buffer-substring begin (point))
                      header-len (- (point) begin))
                (delete-region begin (point))
                (setq header (buffer-substring begin (point))
                      header-len (- (point) begin))
                (delete-region begin (point))
-               (goto-char (1+ end-of-headers))
+               (goto-char end-of-headers)
                (insert header)
                (setq end-of-headers
                      (+ end-of-headers header-len))))))))
                (insert header)
                (setq end-of-headers
                      (+ end-of-headers header-len))))))))
-    (narrow-to-region (1+ end-of-headers) (point-max))))
+    (narrow-to-region end-of-headers (point-max))))
 
 (defun message-hide-header-p (regexps)
   (let ((result nil)
 
 (defun message-hide-header-p (regexps)
   (let ((result nil)
@@ -7700,13 +7857,13 @@ From headers in the original article."
 (defun message-display-abbrev (&optional choose)
   "Display the next possible abbrev for the text before point."
   (interactive (list t))
 (defun message-display-abbrev (&optional choose)
   "Display the next possible abbrev for the text before point."
   (interactive (list t))
-  (when (and (member (char-after (point-at-bol)) '(?C ?T ? ))
+  (when (and (memq (char-after (point-at-bol)) '(?C ?T ?\t ? ))
             (message-point-in-header-p)
             (save-excursion
             (message-point-in-header-p)
             (save-excursion
-              (save-restriction
-                (message-narrow-to-field)
-                (goto-char (point-min))
-                (looking-at "To\\|Cc"))))
+              (beginning-of-line)
+              (while (and (memq (char-after) '(?\t ? ))
+                          (zerop (forward-line -1))))
+              (looking-at "To:\\|Cc:")))
     (let* ((end (point))
           (start (save-excursion
                    (and (re-search-backward "[\n\t ]" nil t)
     (let* ((end (point))
           (start (save-excursion
                    (and (re-search-backward "[\n\t ]" nil t)
@@ -7719,6 +7876,148 @@ From headers in the original article."
        (delete-region start end)
        (insert match)))))
 
        (delete-region start end)
        (insert match)))))
 
+;; To send pre-formatted letters like the example below, you can use
+;; `message-send-form-letter':
+;; --8<---------------cut here---------------start------------->8---
+;; To: alice@invalid.invalid
+;; Subject: Verification of your contact information
+;; From: Contact verification <admin@foo.invalid>
+;; --text follows this line--
+;; Hi Alice,
+;; please verify that your contact information is still valid:
+;; Alice A, A avenue 11, 1111 A town, Austria
+;; ----------next form letter message follows this line----------
+;; To: bob@invalid.invalid
+;; Subject: Verification of your contact information
+;; From: Contact verification <admin@foo.invalid>
+;; --text follows this line--
+;; Hi Bob,
+;; please verify that your contact information is still valid:
+;; Bob, B street 22, 22222 Be town, Belgium
+;; ----------next form letter message follows this line----------
+;; To: charlie@invalid.invalid
+;; Subject: Verification of your contact information
+;; From: Contact verification <admin@foo.invalid>
+;; --text follows this line--
+;; Hi Charlie,
+;; please verify that your contact information is still valid:
+;; Charlie Chaplin, C plaza 33, 33333 C town, Chile
+;; --8<---------------cut here---------------end--------------->8---
+
+;; FIXME: What is the most common term (circular letter, form letter, serial
+;; letter, standard letter) for such kind of letter?  See also
+;; <http://en.wikipedia.org/wiki/Form_letter>
+
+;; FIXME: Maybe extent message-mode's font-lock support to recognize
+;; `message-form-letter-separator', i.e. highlight each message like a single
+;; message.
+
+(defcustom message-form-letter-separator
+  "\n----------next form letter message follows this line----------\n"
+  "Separator for `message-send-form-letter'."
+  ;; :group 'message-form-letter
+  :group 'message-various
+  :version "23.1" ;; No Gnus
+  :type 'string)
+
+(defcustom message-send-form-letter-delay 1
+  "Delay in seconds when sending a message with `message-send-form-letter'.
+Only used when `message-send-form-letter' is called with non-nil
+argument `force'."
+  ;; :group 'message-form-letter
+  :group 'message-various
+  :version "23.1" ;; No Gnus
+  :type 'integer)
+
+(defun message-send-form-letter (&optional force)
+  "Sent all form letter messages from current buffer.
+Unless FORCE, prompt before sending.
+
+The messages are separated by `message-form-letter-separator'.
+Header and body are separated by `mail-header-separator'."
+  (interactive "P")
+  (let ((sent 0) (skipped 0)
+       start end text
+       buff
+       to done)
+    (goto-char (point-min))
+    (while (not done)
+      (setq start (point)
+           end (if (search-forward message-form-letter-separator nil t)
+                   (- (point) (length message-form-letter-separator) -1)
+                 (setq done t)
+                 (point-max)))
+      (setq text
+           (buffer-substring-no-properties start end))
+      (setq buff (generate-new-buffer "*mail - form letter*"))
+      (with-current-buffer buff
+       (insert text)
+       (message-mode)
+       (setq to (message-fetch-field "To"))
+       (switch-to-buffer buff)
+       (when force
+         (sit-for message-send-form-letter-delay))
+       (if (or force
+                 (y-or-n-p (format "Send message to `%s'? " to)))
+           (progn
+             (setq sent (1+ sent))
+             (message-send-and-exit))
+         (message (format "Message to `%s' skipped." to))
+         (setq skipped (1+ skipped)))
+       (when (buffer-live-p buff)
+         (kill-buffer buff))))
+    (message "%s message(s) sent, %s skipped." sent skipped)))
+
+(defun message-replace-header (header new-value &optional after force)
+  "Remove HEADER and insert the NEW-VALUE.
+If AFTER, insert after this header.  If FORCE, insert new field
+even if NEW-VALUE is empty."
+  ;; Similar to `nnheader-replace-header' but for message buffers.
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-headers)
+      (message-remove-header header))
+    (when (or force (> (length new-value) 0))
+      (if after
+         (message-position-on-field header after)
+       (message-position-on-field header))
+      (insert new-value))))
+
+(defcustom message-recipients-without-full-name
+  (list "ding@gnus.org"
+       "bugs@gnus.org"
+       "emacs-devel@gnu.org"
+       "emacs-pretest-bug@gnu.org"
+       "bug-gnu-emacs@gnu.org")
+  "Mail addresses that have no full name.
+Used in `message-simplify-recipients'."
+  ;; Maybe the addresses could be extracted from
+  ;; `gnus-parameter-to-list-alist'?
+  :type '(choice (const :tag "None" nil)
+                (repeat string))
+  :version "23.1" ;; No Gnus
+  :group 'message-headers)
+
+(defun message-simplify-recipients ()
+  (interactive)
+  (dolist (hdr '("Cc" "To"))
+    (message-replace-header
+     hdr
+     (mapconcat
+      (lambda (addrcomp)
+       (if (and message-recipients-without-full-name
+                (string-match
+                 (regexp-opt message-recipients-without-full-name)
+                 (cadr addrcomp)))
+           (cadr addrcomp)
+         (if (car addrcomp)
+             (message-make-from (car addrcomp) (cadr addrcomp))
+           (cadr addrcomp))))
+      (when (message-fetch-field hdr)
+       (mail-extract-address-components
+        (message-fetch-field hdr) t))
+      ", "))))
+
 (when (featurep 'xemacs)
   (require 'messagexmas)
   (message-xmas-redefine))
 (when (featurep 'xemacs)
   (require 'messagexmas)
   (message-xmas-redefine))