Synch with Oort Gnus.
[elisp/gnus.git-] / lisp / message.el
index b3d7570..0b159ed 100644 (file)
@@ -1,5 +1,5 @@
-;;; message.el --- composing mail and news messages  -*- coding: iso-latin-1 -*-
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
+;;; message.el --- composing mail and news messages
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 (eval-when-compile
   (require 'cl)
   (require 'smtp)
-  (defvar gnus-list-identifiers))      ; gnus-sum is required where necessary
+  (defvar gnus-list-identifiers)) ; gnus-sum is required where necessary
+(eval-and-compile
+  (if (boundp 'MULE)
+      (progn
+       (require 'base64)
+       (require 'canlock-om))
+    (require 'canlock)))
 (require 'mailheader)
 (require 'nnheader)
 ;; This is apparently necessary even though things are autoloaded:
@@ -54,6 +60,9 @@
   (require 'mml))
 
 (require 'rfc822)
+(eval-and-compile
+  (autoload 'sha1 "sha1-el")
+  (autoload 'customize-save-variable "cus-edit"));; for Mule 2.
 
 (defgroup message '((user-mail-address custom-variable)
                    (user-full-name custom-variable))
@@ -151,6 +160,11 @@ mailbox format."
   :group 'message-sending
   :type '(repeat (symbol :tag "Type")))
 
+(defcustom message-fcc-externalize-attachments nil
+  "If non-nil, attachments are included as external parts in Fcc copies."
+  :type 'boolean
+  :group 'message-sending)
+
 (defcustom message-courtesy-message
   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
   "*This is inserted at the start of a mailed copy of a posted message.
@@ -235,20 +249,20 @@ included.  Organization, Lines and User-Agent are optional."
   :type 'sexp)
 
 (defcustom message-ignored-news-headers
-  "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:"
+  "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:"
   "*Regexp of headers to be removed unconditionally before posting."
   :group 'message-news
   :group 'message-headers
   :type 'regexp)
 
 (defcustom message-ignored-mail-headers
-  "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:\\|^X-Draft-From:"
+  "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:"
   "*Regexp of headers to be removed unconditionally before mailing."
   :group 'message-mail
   :group 'message-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:"
+(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:"
   "*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."
@@ -349,14 +363,21 @@ should return the new buffer name."
   :type 'boolean)
 
 (defcustom message-kill-buffer-query-function 'yes-or-no-p
-  "*A function called to query the user whether to kill buffer anyway or not.
-If it is t, the buffer will be killed peremptorily."
+  "*Function used to prompt user whether to kill the message buffer.  If
+it is t, the buffer will be killed unconditionally."
   :type '(radio (function-item yes-or-no-p)
                (function-item y-or-n-p)
                (function-item nnheader-Y-or-n-p)
                (function :tag "Other" t))
   :group 'message-buffers)
 
+(defcustom message-kill-buffer-and-remove-file t
+  "*Non-nil means that the associated file will be removed before
+removing the message buffer.  However, it is treated as nil when the
+command `message-mimic-kill-buffer' is used."
+  :group 'message-buffers
+  :type 'boolean)
+
 (eval-when-compile
   (defvar gnus-local-organization))
 (defcustom message-user-organization
@@ -453,9 +474,22 @@ The provided functions are:
 
 (defcustom message-cite-prefix-regexp
   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
-      "\\([ \t]*[-_.[:word:]]+>+\\|[ \t]*[]>~|:}+]\\)+"
+      "\\([ \t]*[-_.[:word:]]+>+\\|[ \t]*[]>»|:}+]\\)+"
     ;; ?-, ?_ or ?. MUST NOT be in syntax entry w.
-    "\\([ \t]*\\(\\w\\|[-_.]\\)+>+\\|[ \t]*[]>~|:}+]\\)+")
+    (let ((old-table (syntax-table))
+         non-word-constituents)
+      (set-syntax-table text-mode-syntax-table)
+      (setq non-word-constituents
+           (concat
+            (if (string-match "\\w" "-")  "" "-")
+            (if (string-match "\\w" "_")  "" "_")
+            (if (string-match "\\w" ".")  "" ".")))
+      (set-syntax-table old-table)
+      (if (equal non-word-constituents "")
+         "\\([ \t]*\\(\\w\\)+>+\\|[ \t]*[]>»|:}+]\\)+"
+       (concat "\\([ \t]*\\(\\w\\|["
+               non-word-constituents
+               "]\\)+>+\\|[ \t]*[]>»|:}+]\\)+"))))
   "*Regexp matching the longest possible citation prefix on a line."
   :group 'message-insertion
   :type 'regexp)
@@ -576,6 +610,13 @@ conjunction with `message-subscribed-regexps' and
   :group 'message-interface
   :type '(repeat sexp))
 
+(defcustom message-subscribed-address-file nil
+  "*A file containing addresses the user is subscribed to.
+If nil, do not look at any files to determine list subscriptions.  If
+non-nil, each line of this file should be a mailing list address."
+  :group 'message-interface
+  :type 'string)
+
 (defcustom message-subscribed-addresses nil
   "*Specifies a list of addresses the user is subscribed to.
 If nil, do not use any predefined list subscriptions.  This list of
@@ -592,6 +633,16 @@ regular expressions can be used in conjuction with
   :group 'message-interface
   :type '(repeat regexp))
 
+(defcustom message-allow-no-recipients 'ask
+  "Specifies what to do when there are no recipients other than Gcc/Fcc.
+If it is the symbol `always', the posting is allowed.  If it is the
+symbol `never', the posting is not allowed.  If it is the symbol
+`ask', you are prompted."
+  :group 'message-interface
+  :type '(choice (const always)
+                (const never)
+                (const ask)))
+
 (defcustom message-sendmail-f-is-evil nil
   "*Non-nil means don't add \"-f username\" to the sendmail command line.
 Doing so would be even more evil than leaving it out."
@@ -827,7 +878,10 @@ If stringp, use this; if non-nil, use no host name (user name only)."
                 (sexp :tag "none" :format "%t" t)))
 
 (defvar message-reply-buffer nil)
-(defvar message-reply-headers nil)
+(defvar message-reply-headers nil
+  "The headers of the current replied article.
+It is a vector of the following headers:
+\[number subject from date id references chars lines xref extra].")
 (defvar message-sent-message-via nil)
 (defvar message-checksum nil)
 (defvar message-send-actions nil
@@ -1217,6 +1271,19 @@ The first matched address (not primary one) is used in the From field."
   :type '(choice (const :tag "Always use primary" nil)
                 regexp))
 
+(defcustom message-hierarchical-addresses nil
+  "A list of hierarchical mail address definitions.
+
+Inside each entry, the first address is the \"top\" address, and
+subsequent addresses are subaddresses; this is used to indicate that
+mail sent to the first address will automatically be delivered to the
+subaddresses.  So if the first address appears in the recipient list
+for a message, the subaddresses will be removed (if present) before
+the mail is sent.  All addresses in this structure should be
+downcased."
+  :group 'message-headers
+  :type '(repeat (repeat string)))
+
 (defcustom message-mail-user-agent nil
   "Like `mail-user-agent'.
 Except if it is nil, use Gnus native MUA; if it is t, use
@@ -1247,6 +1314,11 @@ no, only reply back to the author."
   :group 'message-headers
   :type 'boolean)
 
+(defcustom message-insert-canlock t
+  "Whether to insert a Cancel-Lock header in news postings."
+  :group 'message-headers
+  :type 'boolean)
+
 ;;; Internal variables.
 
 (defvar message-sending-message "Sending...")
@@ -1715,6 +1787,7 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c?" 'describe-mode)
 
   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
+  (define-key message-mode-map "\C-c\C-f\C-o" 'message-goto-from)
   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
   (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)
@@ -1728,6 +1801,8 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
+  (define-key message-mode-map "\C-c\C-f\C-i" 'message-insert-or-toggle-importance)
+  (define-key message-mode-map "\C-c\C-f\C-a" 'message-gen-unsubscribed-mft)
   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
   (define-key message-mode-map "\C-c\C-fc" 'message-goto-mail-copies-to)
@@ -1735,6 +1810,9 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
 
+  (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
+  (define-key message-mode-map "\C-c\M-n" 'message-insert-disposition-notification-to)
+
   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
   (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer)
   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
@@ -1756,6 +1834,7 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
   ;;(define-key message-mode-map "\M-q" 'message-fill-paragraph)
 
+  (define-key message-mode-map "\C-a" 'message-beginning-of-line)
   (define-key message-mode-map "\t" 'message-tab)
   (define-key message-mode-map "\M-;" 'comment-region)
 
@@ -1776,6 +1855,16 @@ Point is left at the beginning of the narrowed-to region."
    ["Kill To Signature" message-kill-to-signature t]
    ["Newline and Reformat" message-newline-and-reformat t]
    ["Rename buffer" message-rename-buffer t]
+   ["Flag As Important" message-insert-importance-high
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help "Mark this message as important"))]
+   ["Flag As Unimportant" message-insert-importance-low
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help "Mark this message as unimportant"))]
+   ["Request Receipt"
+    message-insert-disposition-notification-to
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help "Request a Disposition Notification of this article"))]
    ["Spellcheck" ispell-message
     ,@(if (featurep 'xemacs) '(t)
        '(:help "Spellcheck this message"))]
@@ -1803,6 +1892,7 @@ Point is left at the beginning of the narrowed-to region."
    ["Fetch Newsgroups" message-insert-newsgroups t]
    "----"
    ["To" message-goto-to t]
+   ["From" message-goto-from t]
    ["Subject" message-goto-subject t]
    ["Cc" message-goto-cc t]
    ["Reply-To" message-goto-reply-to t]
@@ -1824,6 +1914,63 @@ Point is left at the beginning of the narrowed-to region."
   (defvar facemenu-add-face-function)
   (defvar facemenu-remove-face-function))
 
+;;; Forbidden properties
+;;
+;; We use `after-change-functions' to keep special text properties
+;; that interfer with the normal function of message mode out of the
+;; buffer.
+
+(defcustom message-strip-special-text-properties t
+  "Strip special properties from the message buffer.
+
+Emacs has a number of special text properties which can break message
+composing in various ways.  If this option is set, message will strip
+these properties from the message composition buffer.  However, some
+packages requires these properties to be present in order to work.
+If you use one of these packages, turn this option off, and hope the
+message composition doesn't break too bad."
+  :group 'message-various
+  :type 'boolean)
+
+(defconst message-forbidden-properties
+  ;; No reason this should be clutter up customize.  We make it a
+  ;; property list (rather than a list of property symbols), to be
+  ;; directly useful for `remove-text-properties'.
+  '(field nil read-only nil intangible nil invisible nil
+         mouse-face nil modification-hooks nil insert-in-front-hooks nil
+         insert-behind-hooks nil point-entered nil point-left nil)
+  ;; Other special properties:
+  ;; category, face, display: probably doesn't do any harm.
+  ;; fontified: is used by font-lock.
+  ;; syntax-table, local-map: I dunno.
+  ;; We need to add XEmacs names to the list.
+  "Property list of with properties.forbidden in message buffers.
+The values of the properties are ignored, only the property names are used.")
+
+(defun message-tamago-not-in-use-p (pos)
+  "Return t when tamago version 4 is not in use at the cursor position.
+Tamago version 4 is a popular input method for writing Japanese text.
+It uses the properties `intangible', `invisible', `modification-hooks'
+and `read-only' when translating ascii or kana text to kanji text.
+These properties are essential to work, so we should never strip them."
+  (not (and (boundp 'egg-modefull-mode)
+           (symbol-value 'egg-modefull-mode)
+           (or (memq (get-text-property pos 'intangible)
+                     '(its-part-1 its-part-2))
+               (get-text-property pos 'egg-end)
+               (get-text-property pos 'egg-lang)
+               (get-text-property pos 'egg-start)))))
+
+(defun message-strip-forbidden-properties (begin end &optional old-length)
+  "Strip forbidden properties between BEGIN and END, ignoring the third arg.
+This function is intended to be called from `after-change-functions'.
+See also `message-forbidden-properties'."
+  (when (and message-strip-special-text-properties
+            (message-tamago-not-in-use-p begin)
+            ;; Check whether the invisible MIME part is not inserted.
+            (not (text-property-any begin end 'mime-edit-invisible t)))
+    (remove-text-properties begin end message-forbidden-properties)))
+
 ;;;###autoload
 (define-derived-mode message-mode text-mode "Message"
   "Major mode for editing mail and news to be sent.
@@ -1838,6 +1985,7 @@ C-c C-f  move to a header field (and create it if there isn't):
         C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
         C-c C-f C-f  move to Followup-To
         C-c C-f C-m  move to Mail-Followup-To
+        C-c C-f C-i  cycle through Importance values
         C-c C-f c    move to Mail-Copies-To
 C-c C-t  `message-insert-to' (add a To header to a news followup)
 C-c C-n  `message-insert-newsgroups' (add a Newsgroup header to a news reply)
@@ -1850,7 +1998,10 @@ C-c C-e  `message-elide-region' (elide the text between point and mark).
 C-c C-v  `message-delete-not-region' (remove the text outside the region).
 C-c C-z  `message-kill-to-signature' (kill the text up to the signature).
 C-c C-r  `message-caesar-buffer-body' (rot13 the message body).
+C-c C-u  `message-insert-or-toggle-importance'  (insert or cycle importance).
+C-c M-n  `message-insert-disposition-notification-to'  (request receipt).
 M-RET    `message-newline-and-reformat' (break the line and reformat)."
+  (setq local-abbrev-table text-mode-abbrev-table)
   (set (make-local-variable 'message-reply-buffer) nil)
   (make-local-variable 'message-send-actions)
   (make-local-variable 'message-exit-actions)
@@ -1885,12 +2036,19 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
        (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
   (easy-menu-add message-mode-menu message-mode-map)
   (easy-menu-add message-mode-field-menu message-mode-map)
+  ;; make-local-hook is harmless though obsolete in Emacs 21.
+  ;; Emacs 20 and XEmacs need make-local-hook.
+  (make-local-hook 'after-change-functions)
+  ;; Mmmm... Forbidden properties...
+  (add-hook 'after-change-functions 'message-strip-forbidden-properties
+           nil 'local)
   ;; Allow mail alias things.
   (when (eq message-mail-alias-type 'abbrev)
     (if (fboundp 'mail-abbrevs-setup)
        (mail-abbrevs-setup)
       (mail-aliases-setup)))
-  (message-set-auto-save-file-name)
+  (unless buffer-file-name
+    (message-set-auto-save-file-name))
   (set (make-local-variable 'indent-tabs-mode) nil)) ;No tabs for indentation.
 
 (defun message-setup-fill-variables ()
@@ -1948,6 +2106,11 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
   (interactive)
   (message-position-on-field "To"))
 
+(defun message-goto-from ()
+  "Move point to the From header."
+  (interactive)
+  (message-position-on-field "From"))
+
 (defun message-goto-subject ()
   "Move point to the Subject header."
   (interactive)
@@ -2055,6 +2218,26 @@ return nil."
     (goto-char (point-max))
     nil))
 
+(defun message-gen-unsubscribed-mft (&optional include-cc)
+  "Insert a reasonable MFT header in a post to an unsubscribed list.
+When making original posts to a mailing list you are not subscribed to,
+you have to type in a MFT header by hand.  The contents, usually, are
+the addresses of the list and your own address.  This function inserts
+such a header automatically.  It fetches the contents of the To: header
+in the current mail buffer, and appends the current user-mail-address.
+
+If the optional argument `include-cc' is non-nil, the addresses in the
+Cc: header are also put into the MFT."
+
+  (interactive)
+  (message-remove-header "Mail-Followup-To")
+  (let* ((cc (and include-cc (message-fetch-field "Cc")))
+        (tos (if cc
+                 (concat (message-fetch-field "To") "," cc)
+               (message-fetch-field "To"))))
+    (message-goto-mail-followup-to)
+    (insert (concat tos ", " user-mail-address))))
+
 \f
 
 (defun message-insert-to (&optional force)
@@ -2211,10 +2394,14 @@ Prefix arg means justify as well."
       (if not-break
          (setq point nil)
        (if bolp
-           (insert "\n")
-         (insert "\n\n"))
+           (newline)
+         (newline)
+         (newline))
        (setq point (point))
-       (insert "\n\n")
+       ;; (newline 2) doesn't mark both newline's as hard, so call
+       ;; newline twice. -jas
+       (newline)
+       (newline)
        (delete-region (point) (re-search-forward "[ \t]*"))
        (when (and quoted (not bolp))
          (insert quoted leading-space)))
@@ -2235,15 +2422,19 @@ Prefix arg means justify as well."
     (message-newline-and-reformat arg t)
     t))
 
+;; Is it better to use `mail-header-end'?
+(defun message-point-in-header-p ()
+  "Return t if point is in the header."
+  (save-excursion
+    (let ((p (point)))
+      (goto-char (point-min))
+      (not (re-search-forward
+           (concat "^" (regexp-quote mail-header-separator) "\n")
+           p t)))))
+
 (defun message-do-auto-fill ()
   "Like `do-auto-fill', but don't fill in message header."
-  (when (> (point) (save-excursion 
-                    (goto-char (point-min))
-                    (if (re-search-forward
-                         (concat "^" (regexp-quote mail-header-separator)
-                                 "\n") nil t)
-                        (match-beginning 0)
-                      (point-max))))
+  (unless (message-point-in-header-p)
     (do-auto-fill)))
 
 (defun message-insert-signature (&optional force)
@@ -2285,6 +2476,52 @@ Prefix arg means justify as well."
       (goto-char (point-max))
       (or (bolp) (insert "\n")))))
 
+(defun message-insert-importance-high ()
+  "Insert header to mark message as important."
+  (interactive)
+  (save-excursion
+    (message-remove-header "Importance")
+    (message-goto-eoh)
+    (insert "Importance: high\n")))
+
+(defun message-insert-importance-low ()
+  "Insert header to mark message as unimportant."
+  (interactive)
+  (save-excursion
+    (message-remove-header "Importance")
+    (message-goto-eoh)
+    (insert "Importance: low\n")))
+
+(defun message-insert-or-toggle-importance ()
+  "Insert a \"Importance: high\" header, or cycle through the header values.
+The three allowed values according to RFC 1327 are `high', `normal'
+and `low'."
+  (interactive)
+  (save-excursion
+    (let ((valid '("high" "normal" "low"))
+         (new "high")
+         cur)
+      (when (setq cur (message-fetch-field "Importance"))
+       (message-remove-header "Importance")
+       (setq new (cond ((string= cur "high")
+                        "low")
+                       ((string= cur "low")
+                        "normal")
+                       (t
+                        "high"))))
+      (message-goto-eoh)
+      (insert (format "Importance: %s\n" new)))))
+
+(defun message-insert-disposition-notification-to ()
+  "Request a disposition notification (return receipt) to this message.
+Note that this should not be used in newsgroups."
+  (interactive)
+  (save-excursion
+    (message-remove-header "Disposition-Notification-To")
+    (message-goto-eoh)
+    (insert (format "Disposition-Notification-To: %s\n"
+                   (or (message-fetch-field "From") (message-make-from))))))
+
 (defun message-elide-region (b e)
   "Elide the text in the region.
 An ellipsis (from `message-elide-ellipsis') will be inserted where the
@@ -2710,7 +2947,8 @@ The text will also be indented the normal way."
       t)))
 
 (defun message-dont-send ()
-  "Don't send the message you have been editing."
+  "Don't send the message you have been editing.
+Instead, just auto-save the buffer and then bury it."
   (interactive)
   (message-save-drafts)
   (let ((actions message-postpone-actions)
@@ -2728,27 +2966,40 @@ The text will also be indented the normal way."
            (funcall message-kill-buffer-query-function
                     "The buffer modified; kill anyway? "))
     (let ((actions message-kill-actions)
+         (draft-article message-draft-article)
+         (auto-save-file-name buffer-auto-save-file-name)
+         (file-name buffer-file-name)
+         (modified (buffer-modified-p))
          (frame (selected-frame))
          (org-frame message-original-frame))
       (setq buffer-file-name nil)
       (kill-buffer (current-buffer))
+      (when (and message-kill-buffer-and-remove-file
+                (or (and auto-save-file-name
+                         (file-exists-p auto-save-file-name))
+                    (and file-name
+                         (file-exists-p file-name)))
+                (yes-or-no-p (format "Remove the backup file%s? "
+                                     (if modified " too" ""))))
+       (ignore-errors
+         (delete-file auto-save-file-name))
+       (let ((message-draft-article draft-article))
+         (message-disassociate-draft)))
       (message-do-actions actions)
       (message-delete-frame frame org-frame)))
   (message ""))
 
 (defun message-mimic-kill-buffer ()
-  "Kill the current buffer with query."
+  "Kill the current buffer with query.  This is an imitation for
+`kill-buffer', but it will delete a message frame."
   (interactive)
-  (unless (eq 'message-mode major-mode)
-    (error "%s must be invoked from a message buffer." this-command))
-  (let ((command this-command)
-       (bufname (read-buffer (format "Kill buffer: (default %s) "
-                                     (buffer-name)))))
-    (if (or (not bufname)
-           (string-equal bufname "")
-           (string-equal bufname (buffer-name)))
-       (message-kill-buffer)
-      (message "%s must be invoked only for the current buffer." command))))
+  (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
+                                     (buffer-name))))
+       message-kill-buffer-and-remove-file)
+    (when (or (not bufname)
+             (string-equal bufname "")
+             (string-equal bufname (buffer-name)))
+      (message-kill-buffer))))
 
 (defun message-delete-frame (frame org-frame)
   "Delete frame for editing message."
@@ -2805,13 +3056,13 @@ It should typically alter the sending method in some way or other."
          (message-mime-mode mime-edit-mode-flag)
          (alist message-send-method-alist)
          (success t)
-         elem sent
+         elem sent dont-barf-on-no-method
          (message-options message-options))
       (message-options-set-recipient)
       (save-excursion
        (set-buffer message-encoding-buffer)
        (erase-buffer)
-       ;; ;; Avoid copying text props.
+       ;; ;; Avoid copying text props (except hard newlines).
        ;; T-gnus change: copy all text props from the editing buffer
        ;; into the encoding buffer.
        (insert-buffer message-edit-buffer)
@@ -2830,10 +3081,24 @@ It should typically alter the sending method in some way or other."
                             (error "Denied posting -- multiple copies")))
                       (setq success (funcall (caddr elem) arg)))
              (setq sent t)))))
-      (unless (or sent (not success))
+      (unless
+         (or sent
+             (not success)
+             (let ((fcc (message-fetch-field "Fcc"))
+                   (gcc (message-fetch-field "Gcc")))
+               (when (or fcc gcc)
+                 (or (eq message-allow-no-recipients 'always)
+                     (and (not (eq message-allow-no-recipients 'never))
+                          (setq dont-barf-on-no-method
+                                (gnus-y-or-n-p
+                                 (format "No receiver, perform %s anyway? "
+                                         (cond ((and fcc gcc) "Fcc and Gcc")
+                                               (fcc "Fcc")
+                                               (t "Gcc"))))))))))
        (error "No methods specified to send by"))
       (prog1
-         (when (and success sent)
+         (when (or dont-barf-on-no-method
+                   (and success sent))
            (message-do-fcc)
            (save-excursion
              (run-hooks 'message-sent-hook))
@@ -2886,6 +3151,17 @@ used to distinguish whether the invisible text is a MIME part or not."
                                     '(invisible t mime-edit-invisible t))
              (put-text-property start end 'invisible t))))))
 
+(defun message-text-with-property (prop)
+  "Return a list of all points where the text has PROP."
+  (let ((points nil)
+       (point (point-min)))
+    (save-excursion
+      (while (< point (point-max))
+       (when (get-text-property point prop)
+         (push point points))
+       (incf point)))
+    (nreverse points)))
+
 (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.
@@ -2919,7 +3195,46 @@ used to distinguish whether the invisible text is a MIME part or not."
        (set-window-start (selected-window) (gnus-point-at-bol))
        (unless (yes-or-no-p
                 "Invisible text found and made visible; continue posting? ")
-         (error "Invisible text found and made visible"))))))
+         (error "Invisible text found and made visible")))))
+  (message-check 'illegible-text
+    (let ((mm-7bit-chars "\x20-\x7f\r\n\t\x7\x8\xb\xc\x1f\x1b")
+         found choice)
+      (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)))))
+         (add-text-properties (point) (1+ (point)) '(highlight t))
+         (setq found t))
+       (forward-char)
+       (skip-chars-forward mm-7bit-chars))
+      (when found
+       (setq choice
+             (gnus-multiple-choice
+              "Illegible text found. Continue posting? "
+              '((?d "Remove and continue posting")
+                (?r "Replace with dots and continue posting")
+                (?e "Continue editing"))))
+       (if (eq choice ?e)
+         (error "Illegible text found"))
+       (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)))))
+           (delete-char 1)
+           (if (eq choice ?r)
+               (insert ".")))
+         (forward-char)
+         (skip-chars-forward mm-7bit-chars))))))
 
 (defun message-add-action (action &rest types)
   "Add ACTION to be performed when doing an exit of type TYPES."
@@ -3065,23 +3380,27 @@ This sub function is for exclusive use of `message-send-mail'."
         (case-fold-search nil)
         (news (message-news-p))
         (message-this-is-mail t)
+        (headers message-required-mail-headers)
         failure)
     (save-restriction
       (message-narrow-to-headers)
-      ;; Insert some headers.
-      (let ((message-deletable-headers
-            (if news nil message-deletable-headers)))
-       (message-generate-headers message-required-mail-headers))
       ;; Generate the Mail-Followup-To header if the header is not there...
       (if (and (or message-subscribed-regexps
                   message-subscribed-addresses
+                  message-subscribed-address-file
                   message-subscribed-address-functions)
               (not (mail-fetch-field "mail-followup-to")))
-         (message-generate-headers
-          `(("Mail-Followup-To" . ,(message-make-mft))))
+         (setq headers
+               (cons
+                (cons "Mail-Followup-To" (message-make-mft))
+                message-required-mail-headers))
        ;; otherwise, delete the MFT header if the field is empty
        (when (equal "" (mail-fetch-field "mail-followup-to"))
-         (message-remove-header "Mail-Followup-To")))
+         (message-remove-header "^Mail-Followup-To:")))
+      ;; Insert some headers.
+      (let ((message-deletable-headers
+            (if news nil message-deletable-headers)))
+       (message-generate-headers headers))
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook))
     (if (not (message-check-mail-syntax))
@@ -3092,6 +3411,9 @@ This sub function is for exclusive use of `message-send-mail'."
          (save-excursion
            (set-buffer tembuf)
            (erase-buffer)
+           ;; ;; Avoid copying text props (except hard newlines).
+           ;; T-gnus change: copy all text props from the editing buffer
+           ;; into the encoding buffer.
            (insert-buffer message-encoding-buffer)
            ;; Remove some headers.
            (save-restriction
@@ -3100,7 +3422,6 @@ This sub function is for exclusive use of `message-send-mail'."
 ;;           ;; We (re)generate the Lines header.
 ;;           (when (memq 'Lines message-required-mail-headers)
 ;;             (message-generate-headers '(Lines)))
-             ;; Remove some headers.
              (message-remove-header message-ignored-mail-headers t))
            (goto-char (point-max))
            ;; require one newline at the end.
@@ -3314,6 +3635,27 @@ This sub function is for exclusive use of `message-send-news'."
        nil)
      (not (funcall message-send-news-function method)))))
 
+(defun message-canlock-generate ()
+  "Return a string that is non-trival to guess.
+Do not use this for anything important, it is cryptographically weak."
+  (sha1 (concat (message-unique-id)
+               (format "%x%x%x" (random) (random t) (random))
+               (prin1-to-string (recent-keys))
+               (prin1-to-string (garbage-collect)))))
+
+(defun message-canlock-password ()
+  "The password used by message for cancel locks.
+This is the value of `canlock-password', if that option is non-nil.
+Otherwise, generate and save a value for `canlock-password' first."
+  (unless canlock-password
+    (customize-save-variable 'canlock-password (message-canlock-generate)))
+  canlock-password)
+
+(defun message-insert-canlock ()
+  (when message-insert-canlock
+    (message-canlock-password)
+    (canlock-insert-header)))
+
 (defun message-send-news (&optional arg)
   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
         (case-fold-search nil)
@@ -3352,6 +3694,7 @@ This sub function is for exclusive use of `message-send-news'."
       (message-narrow-to-headers)
       ;; Insert some headers.
       (message-generate-headers message-required-news-headers)
+      (message-insert-canlock)
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook))
     ;; Note: This check will be disabled by the ".*" default value for
@@ -3405,7 +3748,7 @@ This sub function is for exclusive use of `message-send-news'."
     (backward-char 1)
     (run-hooks 'message-send-news-hook)
     (gnus-open-server method)
-    (message "Sending news with %s..." (gnus-server-string method))
+    (message "Sending news via %s..." (gnus-server-string method))
     (gnus-request-post method)
     ))
 
@@ -3502,7 +3845,7 @@ This sub function is for exclusive use of `message-send-news'."
                   (zerop
                    (length
                     (setq to (completing-read
-                              "Followups to: (default all groups) "
+                              "Followups to (default: no Followup-To header) "
                               (mapcar (lambda (g) (list g))
                                       (cons "poster"
                                             (message-tokenize-header
@@ -3850,7 +4193,8 @@ This sub function is for exclusive use of `message-send-news'."
   (let ((case-fold-search t)
        (coding-system-for-write 'raw-text)
        (output-coding-system 'raw-text)
-       list file)
+       list file
+       (mml-externalize-attachments message-fcc-externalize-attachments))
     (save-excursion
       (save-restriction
        (message-narrow-to-headers)
@@ -4003,7 +4347,7 @@ If NOW, use that time instead."
             (aset user (match-beginning 0) ?_))
           user)
        (message-number-base36 (user-uid) -1))
-     (message-number-base36 (+ (car   tm)
+     (message-number-base36 (+ (car tm)
                               (lsh (% message-unique-id-char 25) 16)) 4)
      (message-number-base36 (+ (nth 1 tm)
                               (lsh (/ message-unique-id-char 25) 16)) 4)
@@ -4120,16 +4464,6 @@ If NOW, use that time instead."
                         (aset tmp (1- (match-end 0)) ?-))
                       (string-match "[\\()]" tmp)))))
        (insert fullname)
-       (goto-char (point-min))
-       ;; Look for a character that cannot appear unquoted
-       ;; according to RFC 822.
-       (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
-         ;; Quote fullname, escaping specials.
-         (goto-char (point-min))
-         (insert "\"")
-         (while (re-search-forward "[\"\\]" nil 1)
-           (replace-match "\\\\\\&" t))
-         (insert "\""))
        (insert " <" login ">"))
        (t                              ; 'parens or default
        (insert login " (")
@@ -4191,7 +4525,7 @@ give as trustworthy answer as possible."
       (match-string 1 user-mail))
      ;; Default to this bogus thing.
      (t
-      (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
+      (concat system-name ".i-did-not-set--mail-host-address--so-tickle-me")))))
 
 (defun message-make-host-name ()
   "Return the name of the host."
@@ -4210,9 +4544,25 @@ give as trustworthy answer as possible."
         (recipients
          (mapcar 'mail-strip-quoted-names
                  (message-tokenize-header msg-recipients)))
+        (file-regexps
+         (if message-subscribed-address-file
+             (let (begin end item re)
+               (save-excursion
+                 (with-temp-buffer
+                   (insert-file-contents message-subscribed-address-file)
+                   (while (not (eobp))
+                     (setq begin (point))
+                     (forward-line 1)
+                     (setq end (point))
+                     (if (bolp) (setq end (1- end)))
+                     (setq item (regexp-quote (buffer-substring begin end)))
+                     (if re (setq re (concat re "\\|" item))
+                       (setq re (concat "\\`\\(" item))))
+                   (and re (list (concat re "\\)\\'"))))))))
         (mft-regexps (apply 'append message-subscribed-regexps
                             (mapcar 'regexp-quote
                                     message-subscribed-addresses)
+                            file-regexps
                             (mapcar 'funcall
                                     message-subscribed-address-functions))))
     (save-match-data
@@ -4346,6 +4696,8 @@ Headers already prepared in the buffer are not modified."
                    (goto-char (point-max))
                    (insert (if (stringp header) header (symbol-name header))
                            ": " value)
+                   ;; We check whether the value was ended by a
+                   ;; newline.  If now, we insert one.
                    (unless (bolp)
                      (insert "\n"))
                    (forward-line -1))
@@ -4542,6 +4894,19 @@ than 988 characters long, and if they are not, trim them until they are."
       (forward-line 2)))
    (sit-for 0)))
 
+(defun message-beginning-of-line (&optional n)
+  "Move point to beginning of header value or to beginning of line."
+  (interactive "p")
+  (if (message-point-in-header-p)
+      (let* ((here (point))
+            (bol (progn (beginning-of-line n) (point)))
+            (eol (gnus-point-at-eol))
+            (eoh (re-search-forward ": *" eol t)))
+       (if (or (not eoh) (equal here eoh))
+           (goto-char bol)
+         (goto-char eoh)))
+    (beginning-of-line n)))
+
 (defun message-buffer-name (type &optional to group)
   "Return a new (unique) buffer name based on TYPE and TO."
   (cond
@@ -4629,7 +4994,7 @@ than 988 characters long, and if they are not, trim them until they are."
            to group)
        (if (not (or (null name)
                     (string-equal name "mail")
-                    (string-equal name "news")))
+                    (string-equal name "posting")))
            (setq name (concat "*sent " name "*"))
          (message-narrow-to-headers)
          (setq to (message-fetch-field "to"))
@@ -4641,7 +5006,7 @@ than 988 characters long, and if they are not, trim them until they are."
                             (or (car (mail-extract-address-components to))
                                 to) "*"))
                 ((and group (not (string= group "")))
-                 (concat "*sent news on " group "*"))
+                 (concat "*sent posting on " group "*"))
                 (t "*sent mail*"))))
        (unless (string-equal name (buffer-name))
          (rename-buffer name t)))))
@@ -4684,15 +5049,11 @@ than 988 characters long, and if they are not, trim them until they are."
                              headers)
                      nil switch-function yank-action actions)))))
 
-;;;(defvar mc-modes-alist)
 (defun message-setup-1 (headers &optional replybuffer actions)
-;;;   (when (and (boundp 'mc-modes-alist)
-;;;         (not (assq 'message-mode mc-modes-alist)))
-;;;     (push '(message-mode (encrypt . mc-encrypt-message)
-;;;                     (sign . mc-sign-message))
-;;;      mc-modes-alist))
-  (when actions
-    (setq message-send-actions actions))
+  (dolist (action actions)
+    (condition-case nil
+       (add-to-list 'message-send-actions
+                    `(apply ',(car action) ',(cdr action)))))
   (setq message-reply-buffer
        (or (message-get-parameter 'reply-buffer)
            replybuffer))
@@ -4828,7 +5189,7 @@ OTHER-HEADERS is an alist of header/value pairs."
   "Start editing a news article to be sent."
   (interactive)
   (let ((message-this-is-news t))
-    (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
+    (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups))
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
 
@@ -4936,16 +5297,34 @@ responses here are directed to other addresses.")))
       ;; Perhaps "Mail-Copies-To: never" removed the only address?
       (if (string-equal recipients "")
          (setq recipients author))
-      ;; Convert string to a list of (("foo@bar" . "Name <foo@bar>") ...).
+      ;; Convert string to a list of (("foo@bar" . "Name <Foo@BAR>") ...).
       (setq recipients
            (mapcar
             (lambda (addr)
-              (cons (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))
        (while s
          (setq recipients (delq (assoc (car (pop s)) s) recipients))))
+
+      ;; Remove hierarchical lists that are contained within each other,
+      ;; if message-hierarchical-addresses is defined.
+      (when message-hierarchical-addresses
+       (let ((plain-addrs (mapcar 'car recipients))
+             subaddrs recip)
+         (while plain-addrs
+           (setq subaddrs (assoc (car plain-addrs)
+                                 message-hierarchical-addresses)
+                 plain-addrs (cdr plain-addrs))
+           (when subaddrs
+             (setq subaddrs (cdr subaddrs))
+             (while subaddrs
+               (setq recip (assoc (car subaddrs) recipients)
+                     subaddrs (cdr subaddrs))
+               (if recip
+                   (setq recipients (delq recip recipients))))))))
+
       ;; Build the header alist.  Allow the user to be asked whether
       ;; or not to reply to all recipients in a wide reply.
       (setq follow-to (list (cons 'To (cdr (pop recipients)))))
@@ -5205,15 +5584,31 @@ If ARG, allow editing of the cancellation message."
              message-id (message-fetch-field "message-id" t)
              distribution (message-fetch-field "distribution")))
       ;; Make sure that this article was written by the user.
-      (unless (or (message-gnksa-enable-p 'cancel-messages)
-                 (and sender
-                      (string-equal
-                       (downcase sender)
-                       (downcase (message-make-sender))))
-                 (string-equal
-                  (downcase (cadr (std11-extract-address-components from)))
-                  (downcase (cadr (std11-extract-address-components
-                                   (message-make-from))))))
+      (unless (or
+              ;; Canlock-logic as suggested by Per Abrahamsen
+              ;; <abraham@dina.kvl.dk>
+              ;;
+              ;; IF article has cancel-lock THEN
+              ;;   IF we can verify it THEN
+              ;;     issue cancel
+              ;;   ELSE
+              ;;     error: cancellock: article is not yours
+              ;; ELSE
+              ;;   Use old rules, comparing sender...
+              (if (message-fetch-field "Cancel-Lock")
+                  (if (null (canlock-verify))
+                      t
+                    (error "Failed to verify Cancel-lock: This article is not yours"))
+                nil)
+              (message-gnksa-enable-p 'cancel-messages)
+              (and sender
+                   (string-equal
+                    (downcase sender)
+                    (downcase (message-make-sender))))
+              (string-equal
+               (downcase (cadr (std11-extract-address-components from)))
+               (downcase (cadr (std11-extract-address-components
+                                (message-make-from))))))
        (error "This article is not yours"))
       (when (yes-or-no-p "Do you really want to cancel this article? ")
        ;; Make control message.
@@ -5255,15 +5650,31 @@ header line with the old Message-ID."
        (sender (message-fetch-field "sender"))
        (from (message-fetch-field "from")))
     ;; Check whether the user owns the article that is to be superseded.
-    (unless (or (message-gnksa-enable-p 'cancel-messages)
-               (and sender
-                    (string-equal
-                     (downcase sender)
-                     (downcase (message-make-sender))))
-               (string-equal
-                (downcase (cadr (std11-extract-address-components from)))
-                (downcase (cadr (std11-extract-address-components
-                                 (message-make-from))))))
+    (unless (or
+            ;; Canlock-logic as suggested by Per Abrahamsen
+            ;; <abraham@dina.kvl.dk>
+            ;;
+            ;; IF article has cancel-lock THEN
+            ;;   IF we can verify it THEN
+            ;;     issue cancel
+            ;;   ELSE
+            ;;     error: cancellock: article is not yours
+            ;; ELSE
+            ;;   Use old rules, comparing sender...
+            (if (message-fetch-field "Cancel-Lock")
+                (if (null (canlock-verify))
+                    t
+                  (error "Failed to verify Cancel-lock: This article is not yours"))
+              nil)
+            (message-gnksa-enable-p 'cancel-messages)
+            (and sender
+                 (string-equal
+                  (downcase sender)
+                  (downcase (message-make-sender))))
+            (string-equal
+             (downcase (cadr (std11-extract-address-components from)))
+             (downcase (cadr (std11-extract-address-components
+                              (message-make-from))))))
       (error "This article is not yours"))
     ;; Get a normal message buffer.
     (message-pop-to-buffer (message-buffer-name "supersede"))
@@ -5311,7 +5722,7 @@ header line with the old Message-ID."
   "Remove junk like \"Re:\", \"(fwd)\", etc. added to subject string SUBJECT.
 Previous forwarders, replyers, etc. may add it."
   (with-temp-buffer
-    (insert-string subject)
+    (insert subject)
     (goto-char (point-min))
     ;; strip Re/Fwd stuff off the beginning
     (while (re-search-forward
@@ -5405,6 +5816,8 @@ Optional NEWS will use news to forward instead of mail."
 (defun message-forward-make-body (forward-buffer)
   ;; Put point where we want it before inserting the forwarded
   ;; message.
+  ;; Note that this function definition for T-gnus is totally different
+  ;; from the original Gnus."
   (if message-forward-before-signature
       (message-goto-body)
     (goto-char (point-max)))
@@ -5432,9 +5845,12 @@ Optional NEWS will use news to forward instead of mail."
 
 ;;;###autoload
 (defun message-forward-rmail-make-body (forward-buffer)
-  (with-current-buffer forward-buffer
-    (let (rmail-enable-mime)
-      (rmail-toggle-header 0)))
+  (save-window-excursion
+    (set-buffer forward-buffer)
+    ;; Rmail doesn't have rmail-msg-restore-non-pruned-header in Emacs
+    ;; 20.  FIXIT, or we drop support for rmail in Emacs 20.
+    (if (rmail-msg-is-pruned)
+       (rmail-msg-restore-non-pruned-header)))
   (message-forward-make-body forward-buffer))
 
 ;;;###autoload
@@ -5592,7 +6008,7 @@ you."
        (special-display-regexps nil)
        (same-window-buffer-names nil)
        (same-window-regexps nil))
-    (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
+    (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
   (let ((message-this-is-news t))
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
@@ -5606,7 +6022,7 @@ you."
        (special-display-regexps nil)
        (same-window-buffer-names nil)
        (same-window-regexps nil))
-    (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
+    (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
   (let ((message-this-is-news t))
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
@@ -5671,35 +6087,55 @@ which specify the range to operate on."
                   (tool-bar-add-item-from-menu
                    'message-dont-send "cancel" message-mode-map)
                   (tool-bar-add-item-from-menu
-                   'mml-attach-file "attach" message-mode-map)
+                   'mime-edit-insert-file "attach" message-mode-map)
                   (tool-bar-add-item-from-menu
                    'ispell-message "spell" message-mode-map)
+                  (tool-bar-add-item-from-menu
+                   'message-insert-importance-high "important"
+                   message-mode-map)
+                  (tool-bar-add-item-from-menu
+                   'message-insert-importance-low "unimportant"
+                   message-mode-map)
+                  (tool-bar-add-item-from-menu
+                   'message-insert-disposition-notification-to "receipt"
+                   message-mode-map)
                   tool-bar-map)))))
 
 ;;; Group name completion.
 
-(defvar message-newgroups-header-regexp
+(defcustom message-newgroups-header-regexp
   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
-  "Regexp that match headers that lists groups.")
+  "Regexp that match headers that lists groups."
+  :group 'message
+  :type 'regexp)
 
-(defvar message-completion-alist
+(defcustom message-completion-alist
   (list (cons message-newgroups-header-regexp 'message-expand-group)
        '("^\\(Resent-\\)?\\(To\\|B?Cc\\):" . message-expand-name))
-  "Alist of (RE . FUN).  Use FUN for completion on header lines matching RE.")
+  "Alist of (RE . FUN).  Use FUN for completion on header lines matching RE."
+  :group 'message
+  :type '(alist :key-type regexp :value-type function))
 
-(defvar message-tab-body-function 'indent-relative
-  "*Function to execute when `message-tab' (TAB) is executed in the body.")
+(defcustom message-tab-body-function nil
+  "*Function to execute when `message-tab' (TAB) is executed in the body.
+If nil, the function bound in `text-mode-map' or `global-map' is executed."
+  :group 'message
+  :type 'function)
 
 (defun message-tab ()
   "Complete names according to `message-completion-alist'.
-Do an `indent-relative' if not in those headers."
+Execute function specified by `message-tab-body-function' when not in
+those headers."
   (interactive)
   (let ((alist message-completion-alist))
     (while (and alist
                (let ((mail-abbrev-mode-regexp (caar alist)))
                  (not (mail-abbrev-in-expansion-header-p))))
       (setq alist (cdr alist)))
-    (funcall (or (cdar alist) message-tab-body-function))))
+    (funcall (or (cdar alist) message-tab-body-function
+                (lookup-key text-mode-map "\t")
+                (lookup-key global-map "\t")
+                'indent-relative))))
 
 (defun message-expand-group ()
   "Expand the group name under point."
@@ -5902,27 +6338,28 @@ regexp varstr."
        (message-narrow-to-headers-or-head)
        (message-remove-first-header "Content-Type")
        (message-remove-first-header "Content-Transfer-Encoding"))
-      ;; We always make sure that the message has a Content-Type header.
-      ;; This is because some broken MTAs and MUAs get awfully confused
-      ;; when confronted with a message with a MIME-Version header and
-      ;; without a Content-Type header.  For instance, Solaris'
-      ;; /usr/bin/mail.
+      ;; We always make sure that the message has a Content-Type
+      ;; header.  This is because some broken MTAs and MUAs get
+      ;; awfully confused when confronted with a message with a
+      ;; MIME-Version header and without a Content-Type header.  For
+      ;; instance, Solaris' /usr/bin/mail.
       (unless content-type-p
        (goto-char (point-min))
-       (re-search-forward "^MIME-Version:")
-       (forward-line 1)
-       (insert "Content-Type: text/plain; charset=us-ascii\n")))))
+       ;; For unknown reason, MIME-Version doesn't exist.
+       (when (re-search-forward "^MIME-Version:" nil t)
+         (forward-line 1)
+         (insert "Content-Type: text/plain; charset=us-ascii\n"))))))
 
-(defun message-read-from-minibuffer (prompt)
+(defun message-read-from-minibuffer (prompt &optional initial-contents)
   "Read from the minibuffer while providing abbrev expansion."
   (if (fboundp 'mail-abbrevs-setup)
       (let ((mail-abbrev-mode-regexp "")
            (minibuffer-setup-hook 'mail-abbrevs-setup)
            (minibuffer-local-map message-minibuffer-local-map))
-       (read-from-minibuffer prompt))
+       (read-from-minibuffer prompt initial-contents))
     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook)
          (minibuffer-local-map message-minibuffer-local-map))
-      (read-string prompt))))
+      (read-string prompt initial-contents))))
 
 (defun message-use-alternative-email-as-from ()
   (require 'mail-utils)
@@ -5987,7 +6424,9 @@ regexp varstr."
       (insert-buffer buffer)
       (setq message-reply-headers reply-headers)
       (message-generate-headers '((optional . In-Reply-To)))
-      (mime-edit-translate-buffer))
+      (let ((mime-header-encode-method-alist
+            '((eword-encode-unstructured-field-body))))
+       (mime-edit-translate-buffer)))
     (set-buffer-modified-p nil))
   (message "Saving %s...done" buffer-file-name))