* gnus-sieve.el (gnus-sieve-crosspost): Fix type.
[gnus] / lisp / message.el
index 1dc96a2..abd30b9 100644 (file)
@@ -32,6 +32,7 @@
 
 (eval-when-compile
   (require 'cl)
+  (defvar gnus-message-group-art)
   (defvar gnus-list-identifiers)) ; gnus-sum is required where necessary
 (require 'canlock)
 (require 'mailheader)
@@ -46,7 +47,9 @@
 (require 'mml)
 (require 'rfc822)
 (eval-and-compile
-  (autoload 'sha1 "sha1-el"))
+  (autoload 'sha1 "sha1-el")
+  (autoload 'gnus-find-method-for-group "gnus")
+  (autoload 'nnvirtual-find-group-art "nnvirtual"))
 
 (defgroup message '((user-mail-address custom-variable)
                    (user-full-name custom-variable))
@@ -142,7 +145,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\\):"
@@ -190,7 +193,8 @@ Checks include `subject-cmsg', `multiple-headers', `sendsys',
 `approved', `sender', `empty', `empty-headers', `message-id', `from',
 `subject', `shorten-followup-to', `existing-newsgroups',
 `buffer-file-name', `unchanged', `newsgroups', `reply-to',
-'continuation-headers', and `long-header-lines'."
+`continuation-headers', `long-header-lines', `invisible-text' and
+`illegible-text'."
   :group 'message-news
   :type '(repeat sexp))                        ; Fixme: improve this
 
@@ -326,7 +330,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)
 
@@ -335,7 +339,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
@@ -452,27 +457,31 @@ The provided functions are:
   :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
-  "*If non-nil, forward messages as an inline/rfc822 MIME section.  Otherwise, directly inline the old message in the forwarded message."
+  "*Non-nil means forward messages as an inline/rfc822 MIME section.
+Otherwise, directly inline the old message in the forwarded message."
   :version "21.1"
   :group 'message-forwarding
   :type 'boolean)
 
 (defcustom message-forward-show-mml t
-  "*If non-nil, forward messages are shown as mml.  Otherwise, forward messages are unchanged."
+  "*Non-nil means show forwarded messages as mml.
+Otherwise, forwarded messages are unchanged."
   :version "21.1"
   :group 'message-forwarding
   :type 'boolean)
 
 (defcustom message-forward-before-signature t
-  "*If non-nil, put forwarded message before signature, else after."
+  "*Non-nil means put forwarded message before signature, else after."
   :group 'message-forwarding
   :type 'boolean)
 
 (defcustom message-wash-forwarded-subjects nil
-  "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
+  "*Non-nil means try to remove as much cruft as possible from the subject.
+Done before generating the new subject of a forward."
   :group 'message-forwarding
   :type 'boolean)
 
@@ -595,7 +604,7 @@ always use the value."
 
 (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
@@ -608,12 +617,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))
@@ -621,7 +631,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))
@@ -1304,11 +1314,13 @@ 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))
                                 (mm-coding-system-p 'utf-8)
+                                (executable-find idna-program)
                                 'ask)
   "Whether to encode non-ASCII in domain names into ASCII according to IDNA."
   :group 'message-headers
@@ -1532,7 +1544,9 @@ is used by default."
       (looking-at message-unix-mail-delimiter))))
 
 (defun message-fetch-field (header &optional not-all)
-  "The same as `mail-fetch-field', only remove all newlines."
+  "The same as `mail-fetch-field', only remove all newlines.
+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))))
@@ -1590,12 +1604,6 @@ is used by default."
       (mail-narrow-to-head)
       (message-fetch-field header))))
 
-(defun message-functionp (form)
-  "Return non-nil if FORM is funcallable."
-  (or (and (symbolp form) (fboundp form))
-      (and (listp form) (eq (car form) 'lambda))
-      (byte-code-function-p form)))
-
 (defun message-strip-list-identifiers (subject)
   "Remove list identifiers in `gnus-list-identifiers' from string SUBJECT."
   (require 'gnus-sum)                  ; for gnus-list-identifiers
@@ -1663,7 +1671,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
@@ -1849,19 +1860,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'.
 
@@ -2116,11 +2134,11 @@ Point is left at the beginning of the narrowed-to region."
     ["Insert Signature" message-insert-signature t]
     ["Caesar (rot13) Message" message-caesar-buffer-body t]
     ["Caesar (rot13) Region" message-caesar-region (message-mark-active-p)]
-    ["Elide Region" message-elide-region 
+    ["Elide Region" message-elide-region
      :active (message-mark-active-p)
      ,@(if (featurep 'xemacs) nil
         '(:help "Replace text in region with an ellipsis"))]
-    ["Delete Outside Region" message-delete-not-region 
+    ["Delete Outside Region" message-delete-not-region
      :active (message-mark-active-p)
      ,@(if (featurep 'xemacs) nil
         '(:help "Delete all quoted text outside region"))]
@@ -2347,7 +2365,8 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
   (when (eq message-mail-alias-type 'abbrev)
     (if (fboundp 'mail-abbrevs-setup)
        (mail-abbrevs-setup)
-      (mail-aliases-setup)))
+      (if (fboundp 'mail-aliases-setup)        ; warning avoidance
+         (mail-aliases-setup))))
   (unless buffer-file-name
     (message-set-auto-save-file-name))
   (unless (buffer-base-buffer)
@@ -2511,11 +2530,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))))
 
@@ -2748,7 +2770,7 @@ Prefix arg means justify as well."
           ((and (null message-signature)
                 force)
            t)
-          ((message-functionp message-signature)
+          ((functionp message-signature)
            (funcall message-signature))
           ((listp message-signature)
            (eval message-signature))
@@ -2778,7 +2800,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")))
 
@@ -2786,7 +2810,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")))
 
@@ -2799,14 +2825,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)))))
 
@@ -2815,10 +2843,16 @@ 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 (save-excursion
+                         (save-restriction
+                           (message-narrow-to-headers)
+                           (message-fetch-field "From")))
+                       (message-make-from))))))
 
 (defun message-elide-region (b e)
   "Elide the text in the region.
@@ -3292,16 +3326,18 @@ It should typically alter the sending method in some way or other."
        (add-text-properties point (1+ point)
                             '(invisible nil intangible nil)))))
   ;; Make invisible text visible.
+  ;; 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
     (let ((points (message-text-with-property 'invisible)))
       (when points
        (goto-char (car points))
        (dolist (point points)
-         (add-text-properties point (1+ point)
-                              '(invisible nil face highlight
-                                          font-lock-face highlight)))
+         (put-text-property point (1+ point) 'invisible nil)
+         (message-overlay-put (message-make-overlay point (1+ point))
+                              'face 'highlight))
        (unless (yes-or-no-p
-                "Invisible text found and made visible; continue posting? ")
+                "Invisible text found and made visible; continue sending? ")
          (error "Invisible text found and made visible")))))
   (message-check 'illegible-text
     (let (found choice)
@@ -3311,36 +3347,38 @@ It should typically alter the sending method in some way or other."
        (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))
-                              '(font-lock-face highlight face highlight))
+                        (> (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
-              "Illegible text found.  Continue posting?"
-              '((?d "Remove and continue posting")
-                (?r "Replace with dots and continue posting")
-                (?i "Ignore and continue posting")
+              "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 "Illegible text found"))
+         (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)
-               (remove-text-properties (point) (1+ (point))
-                                       '(font-lock-face highlight face highlight))
+               (message-kill-all-overlays)
              (delete-char 1)
              (when (eq choice ?r)
                (insert "."))))
@@ -3367,7 +3405,7 @@ It should typically alter the sending method in some way or other."
     (ignore-errors
       (cond
        ;; A simple function.
-       ((message-functionp (car actions))
+       ((functionp (car actions))
        (funcall (car actions)))
        ;; Something to be evaled.
        (t
@@ -3499,6 +3537,9 @@ It should typically alter the sending method in some way or other."
          (or (= (preceding-char) ?\n)
              (insert ?\n))
          (message-cleanup-headers)
+         ;; FIXME: we're inserting the courtesy copy after encoding.
+         ;; This is wrong if the courtesy copy string contains
+         ;; non-ASCII characters. -- jh
          (when
              (save-restriction
                (message-narrow-to-headers)
@@ -3506,13 +3547,19 @@ It should typically alter the sending method in some way or other."
                     (or (message-fetch-field "cc")
                         (message-fetch-field "bcc")
                         (message-fetch-field "to"))
-                    (let ((content-type (message-fetch-field "content-type")))
-                      (or
-                       (not content-type)
-                       (string= "text/plain"
-                                (car
-                                 (mail-header-parse-content-type
-                                  content-type)))))))
+                    (let ((content-type (message-fetch-field
+                                         "content-type")))
+                      (and
+                       (or
+                        (not content-type)
+                        (string= "text/plain"
+                                 (car
+                                  (mail-header-parse-content-type
+                                   content-type))))
+                       (not
+                        (string= "base64"
+                                 (message-fetch-field
+                                  "content-transfer-encoding")))))))
            (message-insert-courtesy-copy))
          (if (or (not message-send-mail-partially-limit)
                  (< (point-max) message-send-mail-partially-limit)
@@ -3604,7 +3651,7 @@ If you always want Gnus to send messages in one piece, set
                        (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
@@ -3651,7 +3698,7 @@ to find out how to use this."
         ;; free for -inject-arguments -- a big win for the user and for us
         ;; since we don't have to play that double-guessing game and the user
         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
-         (if (message-functionp message-qmail-inject-args)
+         (if (functionp message-qmail-inject-args)
              (funcall message-qmail-inject-args)
            message-qmail-inject-args)))
     ;; qmail-inject doesn't say anything on it's stdout/stderr,
@@ -3690,7 +3737,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)
@@ -3715,7 +3762,7 @@ Otherwise, generate and save a value for `canlock-password' first."
 (defun message-send-news (&optional arg)
   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
         (case-fold-search nil)
-        (method (if (message-functionp message-post-method)
+        (method (if (functionp message-post-method)
                     (funcall message-post-method arg)
                   message-post-method))
         (newsgroups-field (save-restriction
@@ -3962,7 +4009,7 @@ Otherwise, generate and save a value for `canlock-password' first."
                     (if followup-to
                         (concat newsgroups "," followup-to)
                       newsgroups)))
-           (post-method (if (message-functionp message-post-method)
+           (post-method (if (functionp message-post-method)
                             (funcall message-post-method)
                           message-post-method))
            ;; KLUDGE to handle nnvirtual groups.  Doing this right
@@ -4401,7 +4448,7 @@ If NOW, use that time instead."
   "Make an Organization header."
   (let* ((organization
          (when message-user-organization
-           (if (message-functionp message-user-organization)
+           (if (functionp message-user-organization)
                (funcall message-user-organization)
              message-user-organization))))
     (with-temp-buffer
@@ -4456,7 +4503,7 @@ If NOW, use that time instead."
 (defun message-make-distribution ()
   "Make a Distribution header."
   (let ((orig-distribution (message-fetch-reply-field "distribution")))
-    (cond ((message-functionp message-distribution-function)
+    (cond ((functionp message-distribution-function)
           (funcall message-distribution-function))
          (t orig-distribution))))
 
@@ -4506,6 +4553,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 " (")
@@ -4590,7 +4647,8 @@ give as trustworthy answer as possible."
       user-domain)
      ;; Default to this bogus thing.
      (t
-      (concat system-name ".i-did-not-set--mail-host-address--so-tickle-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."
@@ -4794,7 +4852,7 @@ Headers already prepared in the buffer are not modified."
                  ;; is something that is nil, then we do not insert
                  ;; this header.
                  (setq header (cdr elem))
-                 (or (and (message-functionp (cdr elem))
+                 (or (and (functionp (cdr elem))
                           (funcall (cdr elem)))
                      (and (boundp (cdr elem))
                           (symbol-value (cdr elem)))))
@@ -4805,7 +4863,7 @@ Headers already prepared in the buffer are not modified."
                  ;; this function.
                  (or (and (stringp (cdr elem))
                           (cdr elem))
-                     (and (message-functionp (cdr elem))
+                     (and (functionp (cdr elem))
                           (funcall (cdr elem)))))
                 ((and (boundp header)
                       (symbol-value header))
@@ -5038,12 +5096,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))
@@ -5075,7 +5145,7 @@ than 988 characters long, and if they are not, trim them until they are."
             "*")))
    ;; Check whether `message-generate-new-buffers' is a function,
    ;; and if so, call it.
-   ((message-functionp message-generate-new-buffers)
+   ((functionp message-generate-new-buffers)
     (funcall message-generate-new-buffers type to group))
    ((eq message-generate-new-buffers 'unsent)
     (generate-new-buffer-name
@@ -5492,11 +5562,11 @@ responses here are directed to other addresses.")))
       ;; Allow customizations to have their say.
       (if (not wide)
          ;; This is a regular reply.
-         (when (message-functionp message-reply-to-function)
+         (when (functionp message-reply-to-function)
            (save-excursion
              (setq follow-to (funcall message-reply-to-function))))
        ;; This is a followup.
-       (when (message-functionp message-wide-reply-to-function)
+       (when (functionp message-wide-reply-to-function)
          (save-excursion
            (setq follow-to
                  (funcall message-wide-reply-to-function)))))
@@ -5556,7 +5626,7 @@ If TO-NEWSGROUPS, use that as the new Newsgroups line."
        (if (search-forward "\n\n" nil t)
           (1- (point))
         (point-max)))
-      (when (message-functionp message-followup-to-function)
+      (when (functionp message-followup-to-function)
        (setq follow-to
              (funcall message-followup-to-function)))
       (setq from (message-fetch-field "from")
@@ -5649,6 +5719,48 @@ responses here are directed to other newsgroups."))
 
      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 (mail-extract-address-components from)))
+                (downcase (cadr (mail-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 (mail-extract-address-components from))))))))))
 
 ;;;###autoload
 (defun message-cancel-news (&optional arg)
@@ -5657,42 +5769,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 (mail-extract-address-components from)))
-               (downcase (cadr (mail-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.
@@ -5724,35 +5811,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 (mail-extract-address-components from)))
-                (downcase (cadr (mail-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"))
@@ -5889,7 +5950,7 @@ the message."
        ;; Apply funcs in order, passing subject generated by previous
        ;; func to the next one.
        (while funcs
-         (when (message-functionp (car funcs))
+         (when (functionp (car funcs))
            (setq subject (funcall (car funcs) subject)))
          (setq funcs (cdr funcs)))
        subject))))
@@ -5992,9 +6053,12 @@ Optional DIGEST will use digest to forward."
        (rmail-msg-restore-non-pruned-header)))
   (message-forward-make-body forward-buffer))
 
+(eval-when-compile (defvar rmail-enable-mime-composing))
+
+;; Fixme: Should have defcustom.
 ;;;###autoload
 (defun message-insinuate-rmail ()
-  "Let RMAIL uses message to forward."
+  "Let RMAIL use message to forward."
   (interactive)
   (setq rmail-enable-mime-composing t)
   (setq rmail-insert-mime-forwarded-message-function
@@ -6194,6 +6258,10 @@ which specify the range to operate on."
 (defalias 'message-make-overlay 'make-overlay)
 (defalias 'message-delete-overlay 'delete-overlay)
 (defalias 'message-overlay-put 'overlay-put)
+(defun message-kill-all-overlays ()
+  (if (featurep 'xemacs)
+      (map-extents (lambda (extent ignore) (delete-extent extent)))
+    (mapcar #'delete-overlay (overlays-in (point-min) (point-max)))))
 
 ;; Support for toolbar
 (eval-when-compile
@@ -6362,10 +6430,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))