Synch to Gnus 200311102021.
[elisp/gnus.git-] / lisp / message.el
index 431da3f..fed132e 100644 (file)
 
 (require 'rfc822)
 (eval-and-compile
+  (autoload 'customize-save-variable "cus-edit") ;; for Mule 2.
   (autoload 'sha1 "sha1-el")
   (autoload 'gnus-find-method-for-group "gnus")
   (autoload 'nnvirtual-find-group-art "nnvirtual")
-  (autoload 'customize-save-variable "cus-edit")) ;; for Mule 2.
+  (autoload 'gnus-group-decoded-name "gnus-group"))
 
 (defgroup message '((user-mail-address custom-variable)
                    (user-full-name custom-variable))
@@ -178,7 +179,7 @@ If the string contains the format spec \"%s\", the Newsgroups
 the article has been posted to will be inserted there.
 If this variable is nil, no such courtesy message will be added."
   :group 'message-sending
-  :type 'string)
+  :type '(radio (string :format "%t: %v\n" :size 0) (const nil)))
 
 (defcustom message-ignored-bounced-headers
   "^\\(Received\\|Return-Path\\|Delivered-To\\):"
@@ -253,7 +254,7 @@ few false positives here."
 (defcustom message-archive-header
   "X-No-Archive: Yes\n"
   "Header to insert when you don't want your article to be archived.
-Archives \(such as groups.googgle.com\) respect this header."
+Archives \(such as groups.google.com\) respect this header."
   :type 'string
   :group 'message-various)
 
@@ -262,7 +263,8 @@ Archives \(such as groups.googgle.com\) respect this header."
   "X-No-Archive: Yes - save http://groups.google.com/"
   "Note to insert why you wouldn't want this posting archived.
 If nil, don't insert any text in the body."
-  :type 'string
+  :type '(radio (string :format "%t: %v\n" :size 0)
+               (const nil))
   :group 'message-various)
 
 ;;; Crossposts and Followups
@@ -410,7 +412,7 @@ included.  Organization and User-Agent are optional."
   :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:\\|^Cancel-Lock:\\|^Cancel-Key:\\|^X-Hashcash:"
+(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:"
   "*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."
@@ -566,22 +568,23 @@ If t, use `message-user-organization-file'."
   :type 'regexp)
 
 (defcustom message-make-forward-subject-function
-  'message-forward-subject-name-subject
+  #'message-forward-subject-name-subject
   "*List of functions called to generate subject headers for forwarded messages.
 The subject generated by the previous function is passed into each
 successive function.
 
 The provided functions are:
 
-* `message-forward-subject-author-subject' (Source of article (author or
-      newsgroup)), in brackets followed by the subject
-* `message-forward-subject-name-subject' (Source of article (name of author
-      or newsgroup)), in brackets followed by the subject
-* `message-forward-subject-fwd' (Subject of article with 'Fwd:' prepended
+* `message-forward-subject-author-subject' Source of article (author or
+      newsgroup), in brackets followed by the subject
+* `message-forward-subject-name-subject' Source of article (name of author
+      or newsgroup), in brackets followed by the subject
+* `message-forward-subject-fwd' Subject of article with 'Fwd:' prepended
       to it."
   :group 'message-forwarding
   :type '(radio (function-item message-forward-subject-author-subject)
                (function-item message-forward-subject-fwd)
+               (function-item message-forward-subject-name-subject)
                (repeat :tag "List of functions" function)))
 
 (defcustom message-forward-as-mime t
@@ -591,12 +594,19 @@ Otherwise, directly inline the old message in the forwarded message."
   :group 'message-forwarding
   :type 'boolean)
 
-(defcustom message-forward-show-mml t
-  "*Non-nil means show forwarded messages as mml.
-Otherwise, forwarded messages are unchanged."
+(defcustom message-forward-show-mml 'best
+  "*Non-nil means show forwarded messages as MML (decoded from MIME).
+Otherwise, forwarded messages are unchanged.
+Can also be the symbol `best' to indicate that MML should be
+used, except when it is a bad idea to use MML.  One example where
+it is a bad idea is when forwarding a signed or encrypted
+message, because converting MIME to MML would invalidate the
+digital signature."
   :version "21.1"
   :group 'message-forwarding
-  :type 'boolean)
+  :type '(choice (const :tag "use MML" t)
+                (const :tag "don't use MML " nil)
+                (const :tag "use MML when appropriate" best)))
 
 (defcustom message-forward-before-signature t
   "*Non-nil means put forwarded message before signature, else after."
@@ -758,7 +768,7 @@ query the user whether to use the value.  If it is t or the symbol
 
 (defcustom message-subscribed-address-functions nil
   "*Specifies functions for determining list subscription.
-If nil, do not attempt to determine list subscribtion with functions.
+If nil, do not attempt to determine list subscription with functions.
 If non-nil, this variable contains a list of functions which return
 regular expressions to match lists.  These functions can be used in
 conjunction with `message-subscribed-regexps' and
@@ -771,12 +781,13 @@ conjunction with `message-subscribed-regexps' and
 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)
+  :type '(radio (file :format "%t: %v\n" :size 0)
+               (const nil)))
 
 (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
-addresses can be used in conjuction with
+addresses can be used in conjunction with
 `message-subscribed-address-functions' and `message-subscribed-regexps'."
   :group 'message-interface
   :type '(repeat string))
@@ -784,7 +795,7 @@ addresses can be used in conjuction with
 (defcustom message-subscribed-regexps nil
   "*Specifies a list of addresses the user is subscribed to.
 If nil, do not use any predefined list subscriptions.  This list of
-regular expressions can be used in conjuction with
+regular expressions can be used in conjunction with
 `message-subscribed-address-functions' and `message-subscribed-addresses'."
   :group 'message-interface
   :type '(repeat regexp))
@@ -1208,7 +1219,7 @@ candidates:
 (defcustom message-hidden-headers nil
   "Regexp of headers to be hidden when composing new messages.
 This can also be a list of regexps to match headers.  Or a list
-starting with `not' and followed by regexps.."
+starting with `not' and followed by regexps."
   :group 'message
   :type '(repeat regexp))
 
@@ -1527,7 +1538,8 @@ no, only reply back to the author."
   "*Domain part of Messsage-Ids."
   :group 'message-headers
   :link '(custom-manual "(message)News Headers")
-  :type 'string)
+  :type '(radio (const :format "%v  " nil)
+               (string :format "FQDN: %v\n" :size 0)))
 
 (defcustom message-use-idna (and (condition-case nil (require 'idna)
                                   (file-error))
@@ -1549,6 +1561,7 @@ no, only reply back to the author."
 (defvar message-draft-article nil)
 (defvar message-mime-part nil)
 (defvar message-posting-charset nil)
+(defvar message-inserted-headers nil)
 
 ;; Byte-compiler warning
 (eval-when-compile
@@ -1775,8 +1788,8 @@ is used by default."
 
 (defun message-fetch-field (header &optional not-all)
   "The same as `mail-fetch-field', only remove all newlines.
-Note that the buffer should be narrowed to the headers; see
-function `message-narrow-to-headers-or-head'."
+The buffer is expected to be narrowed to just the header of the message;
+see `message-narrow-to-headers-or-head'."
   (let* ((inhibit-point-motion-hooks t)
         (case-fold-search t)
         (value (mail-fetch-field header nil (not not-all))))
@@ -1786,6 +1799,13 @@ function `message-narrow-to-headers-or-head'."
       (set-text-properties 0 (length value) nil value)
       value)))
 
+(defun message-field-value (header &optional not-all)
+  "The same as `message-fetch-field', only narrow to the headers first."
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-headers-or-head)
+      (message-fetch-field header not-all))))
+
 (defun message-narrow-to-field ()
   "Narrow the buffer to the header on the current line."
   (beginning-of-line)
@@ -1895,6 +1915,7 @@ Leading \"Re: \" is not stripped by this function.  Use the function
 ;;;###autoload
 (defun message-change-subject (new-subject)
   "Ask for NEW-SUBJECT header, append (was: <Old Subject>)."
+  ;; <URL:http://www.landfield.com/usefor/drafts/draft-ietf-usefor-useage--1.02.unpaged>
   (interactive
    (list
     (read-from-minibuffer "New subject: ")))
@@ -1902,7 +1923,10 @@ Leading \"Re: \" is not stripped by this function.  Use the function
                       (zerop (string-width new-subject))
                       (string-match "^[ \t]*$" new-subject))))
         (save-excursion
-          (let ((old-subject (message-fetch-field "Subject")))
+          (let ((old-subject
+                 (save-restriction
+                   (message-narrow-to-headers)
+                   (message-fetch-field "Subject"))))
             (cond ((not old-subject)
                    (error "No current subject"))
                   ((not (string-match
@@ -1926,7 +1950,7 @@ Leading \"Re: \" is not stripped by this function.  Use the function
 See `message-mark-insert-begin' and `message-mark-insert-end'."
   (interactive "r")
   (save-excursion
-    ; add to the end of the region first, otherwise end would be invalid
+    ;; add to the end of the region first, otherwise end would be invalid
     (goto-char end)
     (insert message-mark-insert-end)
     (goto-char beg)
@@ -2088,19 +2112,26 @@ With prefix-argument just set Follow-Up, don't cross-post."
 (defun message-reduce-to-to-cc ()
  "Replace contents of To: header with contents of Cc: or Bcc: header."
  (interactive)
- (let ((cc-content (message-fetch-field "cc"))
+ (let ((cc-content
+       (save-restriction (message-narrow-to-headers)
+                         (message-fetch-field "cc")))
        (bcc nil))
    (if (and (not cc-content)
-           (setq cc-content (message-fetch-field "bcc")))
+           (setq cc-content
+                 (save-restriction
+                   (message-narrow-to-headers)
+                   (message-fetch-field "bcc"))))
        (setq bcc t))
    (cond (cc-content
          (save-excursion
            (message-goto-to)
            (message-delete-line)
            (insert (concat "To: " cc-content "\n"))
-           (message-remove-header (if bcc
-                                      "bcc"
-                                    "cc")))))))
+           (save-restriction
+             (message-narrow-to-headers)
+             (message-remove-header (if bcc
+                                        "bcc"
+                                      "cc"))))))))
 
 ;;; End of functions adopted from `message-utils.el'.
 
@@ -2399,9 +2430,6 @@ Point is left at the beginning of the narrowed-to region."
 (easy-menu-define
  message-mode-field-menu message-mode-map ""
  `("Field"
-   ["Fetch To" message-insert-to t]
-   ["Fetch Newsgroups" message-insert-newsgroups t]
-   "----"
    ["To" message-goto-to t]
    ["From" message-goto-from t]
    ["Subject" message-goto-subject t]
@@ -2425,6 +2453,7 @@ Point is left at the beginning of the narrowed-to region."
    ["Summary" message-goto-summary t]
    ["Keywords" message-goto-keywords t]
    ["Newsgroups" message-goto-newsgroups t]
+   ["Fetch Newsgroups" message-insert-newsgroups t]
    ["Followup-To" message-goto-followup-to t]
    ;; ["Followup-To (with note in body)" message-cross-post-followup-to t]
    ["Crosspost / Followup-To..." message-cross-post-followup-to t]
@@ -2432,10 +2461,21 @@ Point is left at the beginning of the narrowed-to region."
    ["X-No-Archive:" message-add-archive-header t ]
    "----"
    ;; (typical) mailing-lists stuff
+   ["Fetch To" message-insert-to
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help "Insert a To header that points to the author."))]
+   ["Fetch To and Cc" message-insert-wide-reply
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help
+         "Insert To and Cc headers as if you were doing a wide reply."))]
+   "----"
    ["Send to list only" message-to-list-only t]
    ["Mail-Followup-To" message-goto-mail-followup-to t]
    ["Mail-Reply-To" message-goto-mail-reply-to t]
    ["Mail-Copies-To" message-goto-mail-copies-to t]
+   ["Unsubscribed list post" message-generate-unsubscribed-mail-followup-to
+    ,@(if (featurep 'xemacs) '(t)
+       '(:help "Insert a reasonable `Mail-Followup-To:' header."))]
    ["Reduce To: to Cc:" message-reduce-to-to-cc t]
    "----"
    ["Sort Headers" message-sort-headers t]
@@ -2550,11 +2590,12 @@ C-c M-f  `message-mark-insert-file' (insert file marked with enclosing tags).
 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)
-  (make-local-variable 'message-kill-actions)
-  (make-local-variable 'message-postpone-actions)
-  (make-local-variable 'message-draft-article)
+  (set (make-local-variable 'message-inserted-headers) nil)
+  (set (make-local-variable 'message-send-actions) nil)
+  (set (make-local-variable 'message-exit-actions) nil)
+  (set (make-local-variable 'message-kill-actions) nil)
+  (set (make-local-variable 'message-postpone-actions) nil)
+  (set (make-local-variable 'message-draft-article) nil)
   (setq buffer-offer-save t)
   (set (make-local-variable 'facemenu-add-face-function)
        (lambda (face end)
@@ -2781,11 +2822,14 @@ If the optional argument INCLUDE-CC is non-nil, the addresses in the
 Cc: header are also put into the MFT."
 
   (interactive "P")
-  (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"))))
+  (let* (cc tos)
+    (save-restriction
+      (message-narrow-to-headers)
+      (message-remove-header "Mail-Followup-To")
+      (setq cc (and include-cc (message-fetch-field "Cc")))
+      (setq tos (if cc
+                   (concat (message-fetch-field "To") "," cc)
+                 (message-fetch-field "To"))))
     (message-goto-mail-followup-to)
     (insert (concat tos ", " user-mail-address))))
 
@@ -2793,22 +2837,25 @@ Cc: header are also put into the MFT."
 
 (defun message-insert-to (&optional force)
   "Insert a To header that points to the author of the article being replied to.
-If the original author requested not to be sent mail, the function signals
-an error.
-With the prefix argument FORCE, insert the header anyway."
+If the original author requested not to be sent mail, don't insert unless the
+prefix FORCE is given."
   (interactive "P")
-  (let ((co (message-fetch-reply-field "mail-copies-to")))
-    (when (and (null force)
-              co
-              (or (equal (downcase co) "never")
-                  (equal (downcase co) "nobody")))
-      (error "The user has requested not to have copies sent via mail")))
-  (message-carefully-insert-headers
-   (list (cons 'To
-              (or (message-fetch-reply-field "mail-reply-to")
-                  (message-fetch-reply-field "reply-to")
-                  (message-fetch-reply-field "from")
-                  "")))))
+  (let* ((mct (message-fetch-reply-field "mail-copies-to"))
+         (dont (and mct (or (equal (downcase mct) "never")
+                           (equal (downcase mct) "nobody"))))
+         (to (or (message-fetch-reply-field "mail-reply-to")
+                 (message-fetch-reply-field "reply-to")
+                 (message-fetch-reply-field "from"))))
+    (when (and dont to)
+      (gnus-message
+       3
+       (if force
+          "Ignoring the user request not to have copies sent via mail"
+        "Complying with the user request not to have copies sent via mail")))
+    (when (and force (not to))
+      (error "No mail address in the article"))
+    (when (and to (or force (not dont)))
+      (message-carefully-insert-headers (list (cons 'To to))))))
 
 (defun message-insert-wide-reply ()
   "Insert To and Cc headers as if you were doing a wide reply."
@@ -3050,7 +3097,9 @@ Prefix arg means justify as well."
   "Insert header to mark message as important."
   (interactive)
   (save-excursion
-    (message-remove-header "Importance")
+    (save-restriction
+      (message-narrow-to-headers)
+      (message-remove-header "Importance"))
     (message-goto-eoh)
     (insert "Importance: high\n")))
 
@@ -3058,7 +3107,9 @@ Prefix arg means justify as well."
   "Insert header to mark message as unimportant."
   (interactive)
   (save-excursion
-    (message-remove-header "Importance")
+    (save-restriction
+      (message-narrow-to-headers)
+      (message-remove-header "Importance"))
     (message-goto-eoh)
     (insert "Importance: low\n")))
 
@@ -3071,14 +3122,16 @@ and `low'."
     (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"))))
+      (save-restriction
+       (message-narrow-to-headers)
+       (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)))))
 
@@ -3087,10 +3140,14 @@ and `low'."
 Note that this should not be used in newsgroups."
   (interactive)
   (save-excursion
-    (message-remove-header "Disposition-Notification-To")
+    (save-restriction
+      (message-narrow-to-headers)
+      (message-remove-header "Disposition-Notification-To"))
     (message-goto-eoh)
     (insert (format "Disposition-Notification-To: %s\n"
-                   (or (message-fetch-field "From") (message-make-from))))))
+                   (or (message-field-value "Reply-to")
+                       (message-field-value "From")
+                       (message-make-from))))))
 
 (defun message-elide-region (b e)
   "Elide the text in the region.
@@ -3318,8 +3375,9 @@ be added to the \"References\" field."
                             refs)
                ;; If the References field has been changed, we make it
                ;; visible in the header.
-               (mail-header-set-message-id message-reply-headers nil)
-               (mail-header-set-references message-reply-headers nil)
+               (when message-reply-headers
+                 (mail-header-set-message-id message-reply-headers nil)
+                 (mail-header-set-references message-reply-headers nil))
                (widen)
                (message-narrow-to-headers)
                (if (let ((case-fold-search t))
@@ -3655,7 +3713,7 @@ It should typically alter the sending method in some way or other."
                  (when (and
                         (or (not (memq (car elem)
                                        message-sent-message-via))
-                            (not (message-fetch-field "supersedes"))
+                            (message-fetch-field "supersedes")
                             (if (or (message-gnksa-enable-p 'multiple-copies)
                                     (not (eq (car elem) 'news)))
                                 (y-or-n-p
@@ -3761,8 +3819,10 @@ used to distinguish whether the invisible text is a MIME part or not."
       (dolist (point points)
        (add-text-properties point (1+ point)
                             '(invisible nil intangible nil)))))
-  ;; Make invisible text visible except for the mime parts which may
-  ;; be inserted by the MIME-Edit.
+  ;; Make invisible text visible except for mime parts which may be
+  ;; inserted by the MIME-Edit.
+  ;; It doesn't seem as if this is useful, since the invisible property
+  ;; is clobbered by an after-change hook anyhow.
   (message-check 'invisible-text
     ;; FIXME T-gnus: It should also detect invisible overlays.
     (let (from
@@ -3793,52 +3853,56 @@ used to distinguish whether the invisible text is a MIME part or not."
        (unless (yes-or-no-p
                 "Invisible text found and made visible; continue sending? ")
          (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)
-                        (> (length (mm-find-mime-charset-region
-                                    (point) (point-max)))
-                           1))))
-         (message-overlay-put (message-make-overlay (point) (1+ (point)))
-                              'face 'highlight)
-         (setq found t))
-       (forward-char)
-       (skip-chars-forward mm-7bit-chars))
-      (when found
-       (setq choice
-             (gnus-multiple-choice
-              "Non-printable characters found.  Continue sending?"
-              '((?d "Remove non-printable characters and send")
-                (?r "Replace non-printable characters with dots and send")
-                (?i "Ignore non-printable characters and send")
-                (?e "Continue editing"))))
-       (if (eq choice ?e)
-         (error "Non-printable characters"))
-       (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)
-                          ;; Fixme: Wrong for Emacs 22 and for things
-                          ;; like undecable utf-8.  Should at least
-                          ;; use find-coding-systems-region.
-                          (memq (char-charset char)
-                                '(eight-bit-control eight-bit-graphic
-                                                    control-1)))))
-           (if (eq choice ?i)
-               (message-kill-all-overlays)
-             (delete-char 1)
-             (when (eq choice ?r)
-               (insert "."))))
-         (forward-char)
-         (skip-chars-forward mm-7bit-chars))))))
+;; The following check is needless to T-gnus since T-gnus determines
+;; a MIME charset forcibly (even if it cannot be determined properly,
+;; the value of the `default-mime-charset-for-write' variable is used).
+;;  (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)
+;;                      (> (length (mm-find-mime-charset-region
+;;                                  (point) (point-max)))
+;;                         1))))
+;;       (message-overlay-put (message-make-overlay (point) (1+ (point)))
+;;                            'face 'highlight)
+;;       (setq found t))
+;;     (forward-char)
+;;     (skip-chars-forward mm-7bit-chars))
+;;      (when found
+;;     (setq choice
+;;           (gnus-multiple-choice
+;;            "Non-printable characters found.  Continue sending?"
+;;            '((?d "Remove non-printable characters and send")
+;;              (?r "Replace non-printable characters with dots and send")
+;;              (?i "Ignore non-printable characters and send")
+;;              (?e "Continue editing"))))
+;;     (if (eq choice ?e)
+;;       (error "Non-printable characters"))
+;;     (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)
+;;                        ;; Fixme: Wrong for Emacs 22 and for things
+;;                        ;; like undecable utf-8.  Should at least
+;;                        ;; use find-coding-systems-region.
+;;                        (memq (char-charset char)
+;;                              '(eight-bit-control eight-bit-graphic
+;;                                                  control-1)))))
+;;         (if (eq choice ?i)
+;;             (message-kill-all-overlays)
+;;           (delete-char 1)
+;;           (when (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."
@@ -4122,7 +4186,7 @@ This sub function is for exclusive use of `message-send-mail'."
                         (if resend-to-addresses
                             (list resend-to-addresses)
                           '("-t")))))))
-           (unless (or (null cpr) (zerop cpr))
+           (unless (or (null cpr) (and (numberp cpr) (zerop cpr)))
              (error "Sending...failed with exit value %d" cpr)))
          (when message-interactive
            (save-excursion
@@ -4265,7 +4329,7 @@ documentation for the function `mail-source-touch-pop'."
   (smtpmail-send-it))
 
 (defun message-canlock-generate ()
-  "Return a string that is non-trival to guess.
+  "Return a string that is non-trivial to guess.
 Do not use this for anything important, it is cryptographically weak."
   (let (sha1-maximum-internal-length)
     (sha1 (concat (message-unique-id)
@@ -4654,7 +4718,7 @@ Otherwise, generate and save a value for `canlock-password' first."
         nil)
        ((or (not (string-match
                   "@[^\\.]*\\."
-                  (setq ad (nth 1 (mail-extract-address-components
+                  (setq ad (nth 1 (std11-extract-address-components
                                    from))))) ;larsi@ifi
             (string-match "\\.\\." ad) ;larsi@ifi..uio
             (string-match "@\\." ad)   ;larsi@.ifi.uio
@@ -4687,7 +4751,7 @@ Otherwise, generate and save a value for `canlock-password' first."
                  reply-to)))
        ((or (not (string-match
                   "@[^\\.]*\\."
-                  (setq ad (nth 1 (mail-extract-address-components
+                  (setq ad (nth 1 (std11-extract-address-components
                                    reply-to))))) ;larsi@ifi
             (string-match "\\.\\." ad) ;larsi@ifi..uio
             (string-match "@\\." ad)   ;larsi@.ifi.uio
@@ -5136,6 +5200,16 @@ 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 " (")
@@ -5180,7 +5254,7 @@ give as trustworthy answer as possible."
 (defun message-sendmail-envelope-from ()
   "Return the envelope from."
   (cond ((eq message-sendmail-envelope-from 'header)
-        (nth 1 (mail-extract-address-components
+        (nth 1 (std11-extract-address-components
                 (message-fetch-field "from"))))
        ((stringp message-sendmail-envelope-from)
         message-sendmail-envelope-from)
@@ -5194,7 +5268,8 @@ give as trustworthy answer as possible."
         (user-domain
          (if (and user-mail
                   (string-match "@\\(.*\\)\\'" user-mail))
-             (match-string 1 user-mail))))
+             (match-string 1 user-mail)))
+        (case-fold-search t))
     (cond
      ((and message-user-fqdn
           (stringp message-user-fqdn)
@@ -5334,7 +5409,7 @@ I.e., calling it on a Subject: header is useless."
   (let (rhs ace start startpos endpos ovl)
     (goto-char (point-min))
     (while (re-search-forward (concat "^" header) nil t)
-      (while (re-search-forward "@\\([^ \t\r\n>]+\\)"
+      (while (re-search-forward "@\\([^ \t\r\n>,]+\\)"
                                (or (save-excursion
                                      (re-search-forward "^[^ \t]" nil t))
                                    (point-max))
@@ -5393,7 +5468,7 @@ Headers already prepared in the buffer are not modified."
           (Expires (message-make-expires))
           (case-fold-search t)
           (optionalp nil)
-          header value elem)
+          header value elem header-string)
       ;; First we remove any old generated headers.
       (let ((headers message-deletable-headers))
        (unless (buffer-modified-p)
@@ -5418,13 +5493,12 @@ Headers already prepared in the buffer are not modified."
                      optionalp t)
              (setq header (car elem)))
          (setq header elem))
+       (setq header-string  (if (stringp header)
+                                header
+                              (symbol-name header)))
        (when (or (not (re-search-forward
                        (concat "^"
-                               (regexp-quote
-                                (downcase
-                                 (if (stringp header)
-                                     header
-                                   (symbol-name header))))
+                               (regexp-quote (downcase header-string))
                                ":")
                        nil t))
                  (progn
@@ -5437,7 +5511,8 @@ Headers already prepared in the buffer are not modified."
          (setq value
                (cond
                 ((and (consp elem)
-                      (eq (car elem) 'optional))
+                      (eq (car elem) 'optional)
+                      (not (member header-string message-inserted-headers)))
                  ;; This is an optional header.  If the cdr of this
                  ;; is something that is nil, then we do not insert
                  ;; this header.
@@ -5477,9 +5552,7 @@ Headers already prepared in the buffer are not modified."
                           (cdr (assq header message-header-format-alist))))
                      (if formatter
                          (funcall formatter header value)
-                       (insert (if (stringp header)
-                                   header (symbol-name header))
-                               ": " value))
+                       (insert header-string ": " value))
                      ;; We check whether the value was ended by a
                      ;; newline.  If now, we insert one.
                      (unless (bolp)
@@ -5491,6 +5564,7 @@ Headers already prepared in the buffer are not modified."
                ;; If the header is optional, and the header was
                ;; empty, we can't insert it anyway.
                (unless optionalp
+                 (push header-string message-inserted-headers)
                  (insert value)
                  (when (bolp)
                    (delete-char -1))))
@@ -5694,12 +5768,24 @@ than 988 characters long, and if they are not, trim them until they are."
    (sit-for 0)))
 
 (defcustom message-beginning-of-line t
-  "Whether C-a goes to beginning of header values."
+  "Whether \\<message-mode-map>\\[message-beginning-of-line]\
+ goes to beginning of header values."
   :group 'message-buffers
   :type 'boolean)
 
 (defun message-beginning-of-line (&optional n)
-  "Move point to beginning of header value or to beginning of line."
+  "Move point to beginning of header value or to beginning of line.
+The prefix argument N is passed directly to `beginning-of-line'.
+
+This command is identical to `beginning-of-line' if point is
+outside the message header or if the option `message-beginning-of-line'
+is nil.
+
+If point is in the message header and on a (non-continued) header
+line, move point to the beginning of the header value.  If point
+is already there, move point to beginning of line.  Therefore,
+repeated calls will toggle point between beginning of field and
+beginning of line."
   (interactive "p")
   (let ((zrs 'zmacs-region-stays))
     (when (and (interactive-p) (boundp zrs))
@@ -5738,7 +5824,7 @@ than 988 characters long, and if they are not, trim them until they are."
      (concat "*unsent " type
             (if to
                 (concat " to "
-                        (or (car (mail-extract-address-components to))
+                        (or (car (std11-extract-address-components to))
                             to) "")
               "")
             (if (and group (not (string= group ""))) (concat " on " group) "")
@@ -5811,7 +5897,7 @@ than 988 characters long, and if they are not, trim them until they are."
          (setq name
                (cond
                 (to (concat "*sent mail to "
-                            (or (car (mail-extract-address-components to))
+                            (or (car (std11-extract-address-components to))
                                 to) "*"))
                 ((and group (not (string= group "")))
                  (concat "*sent posting on " group "*"))
@@ -6408,6 +6494,49 @@ that further discussion should take place only in "
        ,@(and distribution (list (cons 'Distribution distribution))))
      cur)))
 
+(defun message-is-yours-p ()
+  "Non-nil means current article is yours.
+If you have added 'cancel-messages to 'message-shoot-gnksa-feet', all articles
+are yours except those that have Cancel-Lock header not belonging to you.
+Instead of shooting GNKSA feet, you should modify 'message-alternative-emails'
+regexp to match all of yours addresses."
+  ;; 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...
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-head-1)
+      (if (message-fetch-field "Cancel-Lock")
+         (if (null (canlock-verify))
+             t
+           (error "Failed to verify Cancel-lock: This article is not yours"))
+       (let (sender from)
+         (or
+          (message-gnksa-enable-p 'cancel-messages)
+          (and (setq sender (message-fetch-field "sender"))
+               (string-equal (downcase sender)
+                             (downcase (message-make-sender))))
+          ;; Email address in From field equals to our address
+          (and (setq from (message-fetch-field "from"))
+               (string-equal
+                (downcase (cadr (std11-extract-address-components from)))
+                (downcase (cadr (std11-extract-address-components
+                                 (message-make-from))))))
+          ;; Email address in From field matches
+          ;; 'message-alternative-emails' regexp
+          (and from
+               message-alternative-emails
+               (string-match
+                message-alternative-emails
+                (cadr (std11-extract-address-components from))))))))))
+
 ;;;###autoload
 (defun message-cancel-news (&optional arg)
   "Cancel an article you posted.
@@ -6415,42 +6544,17 @@ If ARG, allow editing of the cancellation message."
   (interactive "P")
   (unless (message-news-p)
     (error "This is not a news article; canceling is impossible"))
-  (let (from newsgroups message-id distribution buf sender)
+  (let (from newsgroups message-id distribution buf)
     (save-excursion
       ;; Get header info from original article.
       (save-restriction
        (message-narrow-to-head-1)
        (setq from (message-fetch-field "from")
-             sender (message-fetch-field "sender")
              newsgroups (message-fetch-field "newsgroups")
              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
-              ;; 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))))))
+      (unless (message-is-yours-p)
        (error "This article is not yours"))
       (when (yes-or-no-p "Do you really want to cancel this article? ")
        ;; Make control message.
@@ -6488,35 +6592,9 @@ If ARG, allow editing of the cancellation message."
 This is done simply by taking the old article and adding a Supersedes
 header line with the old Message-ID."
   (interactive)
-  (let ((cur (current-buffer))
-       (sender (message-fetch-field "sender"))
-       (from (message-fetch-field "from")))
+  (let ((cur (current-buffer)))
     ;; Check whether the user owns the article that is to be superseded.
-    (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))))))
+    (unless (message-is-yours-p)
       (error "This article is not yours"))
     ;; Get a normal message buffer.
     (message-pop-to-buffer (message-buffer-name "supersede"))
@@ -6596,38 +6674,44 @@ Previous forwarders, replyers, etc. may add it."
 (defvar message-forward-decoded-p nil
   "Non-nil means the original message is decoded.")
 
-(defun message-forward-subject-author-subject (subject)
+(defun message-forward-subject-name-subject (subject)
   "Generate a SUBJECT for a forwarded message.
 The form is: [Source] Subject, where if the original message was mail,
-Source is the sender, and if the original message was news, Source is
-the list of newsgroups is was posted to."
+Source is the name of the sender, and if the original message was
+news, Source is the list of newsgroups is was posted to."
   (concat "["
-         (let ((prefix (message-fetch-field "newsgroups")))
-           (or prefix
-               (and (setq prefix (message-fetch-field "from"))
-                    (nnheader-decode-from prefix))
-               "(nowhere)"))
+         (let ((group (message-fetch-field "newsgroups"))
+               from)
+           (if group
+               (gnus-group-decoded-name group)
+             (or (and (setq from (message-fetch-field "from"))
+                      (car (std11-extract-address-components 
+                            (nnheader-decode-from from))))
+                 "(nowhere)")))
          "] " subject))
 
-(defun message-forward-subject-name-subject (subject)
+(defun message-forward-subject-author-subject (subject)
   "Generate a SUBJECT for a forwarded message.
 The form is: [Source] Subject, where if the original message was mail,
-Source is the name of the sender, and if the original message was
-news, Source is the list of newsgroups is was posted to."
+Source is the sender, and if the original message was news, Source is
+the list of newsgroups is was posted to."
   (concat "["
-         (let ((prefix (message-fetch-field "newsgroups")))
-           (or prefix
-               (and (setq prefix (message-fetch-field "from"))
-                    (car (std11-extract-address-components
-                          (nnheader-decode-from prefix))))
-               "(nowhere)"))
+         (let ((group (message-fetch-field "newsgroups"))
+               from)
+           (if group
+               (gnus-group-decoded-name group)
+             (if (setq from (message-fetch-field "from"))
+                 (nnheader-decode-from from)
+               "(nowhere)")))
          "] " subject))
 
 (defun message-forward-subject-fwd (subject)
   "Generate a SUBJECT for a forwarded message.
 The form is: Fwd: Subject, where Subject is the original subject of
 the message."
-  (concat "Fwd: " subject))
+  (if (string-match "^Fwd: " subject)
+      subject
+    (concat "Fwd: " subject)))
 
 (defun message-make-forward-subject ()
   "Return a Subject header suitable for the message in the current buffer."
@@ -6668,6 +6752,108 @@ Optional NEWS will use news to forward instead of mail."
       (message-mail nil subject))
     (message-forward-make-body cur)))
 
+(defun message-forward-make-body-plain (forward-buffer)
+  (insert
+   "\n-------------------- Start of forwarded message --------------------\n")
+  (let ((b (point)) e)
+    (insert
+     (with-temp-buffer
+       (mm-disable-multibyte)
+       (insert
+       (with-current-buffer forward-buffer
+         (mm-with-unibyte-current-buffer (buffer-string))))
+       (mm-enable-multibyte)
+       (mime-to-mml)
+       (goto-char (point-min))
+       (when (looking-at "From ")
+        (replace-match "X-From-Line: "))
+       (buffer-string)))
+    (setq e (point))
+    (insert
+     "\n-------------------- End of forwarded message --------------------\n")
+    (when (and (not current-prefix-arg)
+              message-forward-ignored-headers)
+      (save-restriction
+       (narrow-to-region b e)
+       (goto-char b)
+       (narrow-to-region (point)
+                         (or (search-forward "\n\n" nil t) (point)))
+       (message-remove-header message-forward-ignored-headers 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)
+    (save-restriction
+      (narrow-to-region (point) (point))
+      (mml-insert-buffer forward-buffer)
+      (goto-char (point-min))
+      (when (looking-at "From ")
+       (replace-match "X-From-Line: "))
+      (goto-char (point-max)))
+    (setq e (point))
+    (insert "<#/part>\n")))
+
+(defun message-forward-make-body-mml (forward-buffer)
+  (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n")
+  (let ((b (point)) e)
+    (if (not message-forward-decoded-p)
+       (insert
+        (with-temp-buffer
+          (mm-disable-multibyte)
+          (insert
+           (with-current-buffer forward-buffer
+             (mm-with-unibyte-current-buffer (buffer-string))))
+          (mm-enable-multibyte)
+          (mime-to-mml)
+          (goto-char (point-min))
+          (when (looking-at "From ")
+            (replace-match "X-From-Line: "))
+          (buffer-string)))
+      (save-restriction
+       (narrow-to-region (point) (point))
+       (mml-insert-buffer forward-buffer)
+       (goto-char (point-min))
+       (when (looking-at "From ")
+         (replace-match "X-From-Line: "))
+       (goto-char (point-max))))
+    (setq e (point))
+    (insert "<#/mml>\n")
+    (when (and (not current-prefix-arg)
+              message-forward-ignored-headers)
+      (save-restriction
+       (narrow-to-region b e)
+       (goto-char b)
+       (narrow-to-region (point)
+                         (or (search-forward "\n\n" nil t) (point)))
+       (message-remove-header message-forward-ignored-headers t)))))
+
+(defun message-forward-make-body-digest-plain (forward-buffer)
+  (insert
+   "\n-------------------- Start of forwarded message --------------------\n")
+  (let ((b (point)) e)
+    (mml-insert-buffer forward-buffer)
+    (setq e (point))
+    (insert
+     "\n-------------------- End of forwarded message --------------------\n")))
+
+(defun message-forward-make-body-digest-mime (forward-buffer)
+  (insert "\n<#multipart type=digest>\n")
+  (let ((b (point)) e)
+    (insert-buffer-substring forward-buffer)
+    (setq e (point))
+    (insert "<#/multipart>\n")
+    (save-restriction
+      (narrow-to-region b e)
+      (goto-char b)
+      (narrow-to-region (point)
+                       (or (search-forward "\n\n" nil t) (point)))
+      (delete-region (point-min) (point-max)))))
+
+(defun message-forward-make-body-digest (forward-buffer)
+  (if message-forward-as-mime
+      (message-forward-make-body-digest-mime forward-buffer)
+    (message-forward-make-body-digest-plain forward-buffer)))
+
 ;;;###autoload
 (defun message-forward-make-body (forward-buffer)
   ;; Put point where we want it before inserting the forwarded
@@ -6737,7 +6923,7 @@ Optional NEWS will use news to forward instead of mail."
            message-setup-hook)
        (message-setup `((To . ,address))))
       ;; Insert our usual headers.
-      (message-generate-headers '(From Date To))
+      (message-generate-headers '(From Date To Message-ID))
       (message-narrow-to-headers)
       ;; Remove X-Draft-From header etc.
       (message-remove-header message-ignored-mail-headers t)
@@ -6788,32 +6974,36 @@ contains some mail you have written which has been bounced back to
 you."
   (interactive)
   (let ((cur (current-buffer))
-       boundary)
+       mime-boundary boundary)
     (message-pop-to-buffer (message-buffer-name "bounce"))
     (insert-buffer-substring cur)
     (undo-boundary)
     (message-narrow-to-head)
     (if (and (message-fetch-field "MIME-Version")
-            (setq boundary (message-fetch-field "Content-Type")))
-       (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
-           (setq boundary (concat (match-string 1 boundary) " *\n"
-                                  "Content-Type: message/rfc822"))
-         (setq boundary nil)))
+            (setq mime-boundary (message-fetch-field "Content-Type")))
+       (if (string-match "boundary=\"\\([^\"]+\\)\"" mime-boundary)
+           (setq mime-boundary (concat (regexp-quote
+                                        (match-string 1 mime-boundary))
+                                       " *\nContent-Type: message/rfc822"))
+         (setq mime-boundary nil)))
     (widen)
     (goto-char (point-min))
-    (search-forward "\n\n" nil t)
-    (if (or (and boundary
-                (re-search-forward boundary nil t)
-                (forward-line 2))
-           (and (re-search-forward message-unsent-separator nil t)
+    (re-search-forward "\n\n+" nil t)
+    (setq boundary (point))
+    ;; We remove everything before the bounced mail.
+    (if (or (and mime-boundary
+                (re-search-forward mime-boundary nil t)
                 (forward-line 1))
-           (re-search-forward "^Return-Path:.*\n" nil t))
-       ;; We remove everything before the bounced mail.
-       (delete-region
-        (point-min)
-        (if (re-search-forward "^[^ \n\t]+:" nil t)
-            (match-beginning 0)
-          (point)))
+           (re-search-forward message-unsent-separator nil t)
+           (progn
+             (search-forward "\n\n" nil 'move)
+             (re-search-backward "^Return-Path:.*\n" boundary t)))
+       (progn
+         (forward-line 1)
+         (delete-region (point-min)
+                        (if (re-search-forward "^[^ \n\t]+:" nil t)
+                            (match-beginning 0)
+                          (point))))
       (when (re-search-backward "^.?From .*\n" nil t)
        (delete-region (match-beginning 0) (match-end 0))))
     (save-restriction
@@ -7107,10 +7297,10 @@ The following arguments may contain lists of values."
         (list list))))
 
 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
-  "Create and return a buffer with name based on NAME using `generate-new-buffer.'
+  "Create and return a buffer with name based on NAME using `generate-new-buffer'.
 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."
+regexp VARSTR."
   (let ((oldbuf (current-buffer)))
     (save-excursion
       (set-buffer (generate-new-buffer name))
@@ -7136,7 +7326,6 @@ regexp varstr."
                (cdr local)))))
      locals)))
 
-
 ;;; @ for MIME Edit mode
 ;;;