(gnus-point-at-bol, gnus-point-at-eol): Remove.
[gnus] / lisp / gnus-art.el
index 59c40e9..b134501 100644 (file)
@@ -26,7 +26,9 @@
 
 ;;; Code:
 
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
+(eval-when-compile
+  (require 'cl)
+  (defvar tool-bar-map))
 
 (require 'gnus)
 (require 'gnus-sum)
 
 (require 'gnus)
 (require 'gnus-sum)
@@ -39,6 +41,7 @@
 (require 'mm-view)
 (require 'wid-edit)
 (require 'mm-uu)
 (require 'mm-view)
 (require 'wid-edit)
 (require 'mm-uu)
+(require 'message)
 
 (autoload 'gnus-msg-mail "gnus-msg" nil t)
 (autoload 'gnus-button-mailto "gnus-msg")
 
 (autoload 'gnus-msg-mail "gnus-msg" nil t)
 (autoload 'gnus-button-mailto "gnus-msg")
      "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
      "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
      "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
      "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
      "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
      "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
-     "X-Abuse-and-DMCA-Info" "X-Postfilter"))
+     "X-Abuse-and-DMCA-Info" "X-Postfilter" "X-Gpg-.*" "X-Disclaimer"))
   "*All headers that start with this regexp will be hidden.
 This variable can also be a list of regexps of headers to be ignored.
 If `gnus-visible-headers' is non-nil, this variable will be ignored."
   "*All headers that start with this regexp will be hidden.
 This variable can also be a list of regexps of headers to be ignored.
 If `gnus-visible-headers' is non-nil, this variable will be ignored."
@@ -240,8 +243,8 @@ regexp.  If it matches, the text in question is not a signature."
   :type 'sexp
   :group 'gnus-article-hiding)
 
   :type 'sexp
   :group 'gnus-article-hiding)
 
-;; Fixme: This isn't the right thing for mixed graphical and and
-;; non-graphical frames in a session.
+;; Fixme: This isn't the right thing for mixed graphical and non-graphical
+;; frames in a session.
 (defcustom gnus-article-x-face-command
   (if (featurep 'xemacs)
       (if (or (gnus-image-type-available-p 'xface)
 (defcustom gnus-article-x-face-command
   (if (featurep 'xemacs)
       (if (or (gnus-image-type-available-p 'xface)
@@ -393,7 +396,9 @@ and the latter avoids underlining any whitespace at all."
 Example: (_/*word*/_)."
   :group 'gnus-article-emphasis)
 
 Example: (_/*word*/_)."
   :group 'gnus-article-emphasis)
 
-(defface gnus-emphasis-strikethru '((t (:strikethru t)))
+(defface gnus-emphasis-strikethru (if (featurep 'xemacs)
+                                     '((t (:strikethru t)))
+                                   '((t (:strike-through t))))
   "Face used for displaying strike-through text (-word-)."
   :group 'gnus-article-emphasis)
 
   "Face used for displaying strike-through text (-word-)."
   :group 'gnus-article-emphasis)
 
@@ -557,8 +562,7 @@ The following additional specs are available:
   :type 'hook
   :group 'gnus-article-various)
 
   :type 'hook
   :group 'gnus-article-various)
 
-(defvar gnus-article-hide-pgp-hook nil)
-(make-obsolete-variable 'gnus-article-hide-pgp-hook 
+(make-obsolete-variable 'gnus-article-hide-pgp-hook
                        "This variable is obsolete in Gnus 5.10.")
 
 (defcustom gnus-article-button-face 'bold
                        "This variable is obsolete in Gnus 5.10.")
 
 (defcustom gnus-article-button-face 'bold
@@ -626,7 +630,9 @@ Obsolete; use the face `gnus-signature-face' for customizations instead."
      (:foreground "MidnightBlue" :italic t))
     (t
      (:italic t)))
      (:foreground "MidnightBlue" :italic t))
     (t
      (:italic t)))
-  "Face used for displaying newsgroups headers."
+  "Face used for displaying newsgroups headers.
+In the default setup this face is only used for crossposted
+articles."
   :group 'gnus-article-headers
   :group 'gnus-article-highlight)
 
   :group 'gnus-article-headers
   :group 'gnus-article-highlight)
 
@@ -660,17 +666,17 @@ Obsolete; use the face `gnus-signature-face' for customizations instead."
     ("Subject" nil gnus-header-subject-face)
     ("Newsgroups:.*," nil gnus-header-newsgroups-face)
     ("" gnus-header-name-face gnus-header-content-face))
     ("Subject" nil gnus-header-subject-face)
     ("Newsgroups:.*," nil gnus-header-newsgroups-face)
     ("" gnus-header-name-face gnus-header-content-face))
-  "*Controls highlighting of article header.
+  "*Controls highlighting of article headers.
 
 An alist of the form (HEADER NAME CONTENT).
 
 
 An alist of the form (HEADER NAME CONTENT).
 
-HEADER is a regular expression which should match the name of an
-header header and NAME and CONTENT are either face names or nil.
+HEADER is a regular expression which should match the name of a
+header and NAME and CONTENT are either face names or nil.
 
 The name of each header field will be displayed using the face
 
 The name of each header field will be displayed using the face
-specified by the first element in the list where HEADER match the
-header name and NAME is non-nil.  Similarly, the content will be
-displayed by the first non-nil matching CONTENT face."
+specified by the first element in the list where HEADER matches
+the header name and NAME is non-nil.  Similarly, the content will
+be displayed by the first non-nil matching CONTENT face."
   :group 'gnus-article-headers
   :group 'gnus-article-highlight
   :type '(repeat (list (regexp :tag "Header")
   :group 'gnus-article-headers
   :group 'gnus-article-highlight
   :type '(repeat (list (regexp :tag "Header")
@@ -683,7 +689,7 @@ displayed by the first non-nil matching CONTENT face."
 
 (defcustom gnus-article-decode-hook
   '(article-decode-charset article-decode-encoded-words
 
 (defcustom gnus-article-decode-hook
   '(article-decode-charset article-decode-encoded-words
-                          article-decode-group-name)
+                          article-decode-group-name article-decode-idna-rhs)
   "*Hook run to decode charsets in articles."
   :group 'gnus-article-headers
   :type 'hook)
   "*Hook run to decode charsets in articles."
   :group 'gnus-article-headers
   :type 'hook)
@@ -818,6 +824,7 @@ used."
 (defcustom gnus-mime-action-alist
   '(("save to file" . gnus-mime-save-part)
     ("save and strip" . gnus-mime-save-part-and-strip)
 (defcustom gnus-mime-action-alist
   '(("save to file" . gnus-mime-save-part)
     ("save and strip" . gnus-mime-save-part-and-strip)
+    ("delete part" . gnus-mime-delete-part)
     ("display as text" . gnus-mime-inline-part)
     ("view the part" . gnus-mime-view-part)
     ("pipe to command" . gnus-mime-pipe-part)
     ("display as text" . gnus-mime-inline-part)
     ("view the part" . gnus-mime-view-part)
     ("pipe to command" . gnus-mime-pipe-part)
@@ -857,7 +864,7 @@ used."
 (defvar gnus-inhibit-treatment nil
   "Whether to inhibit treatment.")
 
 (defvar gnus-inhibit-treatment nil
   "Whether to inhibit treatment.")
 
-(defcustom gnus-treat-highlight-signature '(or last (typep "text/x-vcard"))
+(defcustom gnus-treat-highlight-signature '(or t (typep "text/x-vcard"))
   "Highlight the signature.
 Valid values are nil, t, `head', `last', an integer or a predicate.
 See Info node `(gnus)Customizing Articles'."
   "Highlight the signature.
 Valid values are nil, t, `head', `last', an integer or a predicate.
 See Info node `(gnus)Customizing Articles'."
@@ -978,7 +985,7 @@ See Info node `(gnus)Customizing Articles' for details."
   :link '(custom-manual "(gnus)Customizing Articles")
   :type gnus-article-treat-custom)
 
   :link '(custom-manual "(gnus)Customizing Articles")
   :type gnus-article-treat-custom)
 
-(make-obsolete-variable 'gnus-treat-strip-pgp 
+(make-obsolete-variable 'gnus-treat-strip-pgp
                        "This option is obsolete in Gnus 5.10.")
 
 (defcustom gnus-treat-strip-pem nil
                        "This option is obsolete in Gnus 5.10.")
 
 (defcustom gnus-treat-strip-pem nil
@@ -1140,11 +1147,15 @@ See Info node `(gnus)Customizing Articles' for details."
   :type gnus-article-treat-custom)
 (put 'gnus-treat-overstrike 'highlight t)
 
   :type gnus-article-treat-custom)
 (put 'gnus-treat-overstrike 'highlight t)
 
-(defcustom gnus-treat-display-xface
+(make-obsolete-variable 'gnus-treat-display-xface
+                       'gnus-treat-display-x-face)
+
+(defcustom gnus-treat-display-x-face
   (and (not noninteractive)
        (or (and (fboundp 'image-type-available-p)
                (image-type-available-p 'xbm)
   (and (not noninteractive)
        (or (and (fboundp 'image-type-available-p)
                (image-type-available-p 'xbm)
-               (string-match "^0x" (shell-command-to-string "uncompface")))
+               (string-match "^0x" (shell-command-to-string "uncompface"))
+               (executable-find "icontopbm"))
           (and (featurep 'xemacs)
                (featurep 'xface)))
        'head)
           (and (featurep 'xemacs)
                (featurep 'xface)))
        'head)
@@ -1156,8 +1167,25 @@ See Info node `(gnus)Customizing Articles' and Info node
   :version "21.1"
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)X-Face")
   :version "21.1"
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)X-Face")
-  :type gnus-article-treat-head-custom)
-(put 'gnus-treat-display-xface 'highlight t)
+  :type gnus-article-treat-head-custom
+  :set (lambda (symbol value)
+        (set-default
+         symbol
+         (cond ((or (boundp symbol) (get symbol 'saved-value))
+                value)
+               ((boundp 'gnus-treat-display-xface)
+                (message "\
+** gnus-treat-display-xface is an obsolete variable;\
+ use gnus-treat-display-x-face instead")
+                (default-value 'gnus-treat-display-xface))
+               ((get 'gnus-treat-display-xface 'saved-value)
+                (message "\
+** gnus-treat-display-xface is an obsolete variable;\
+ use gnus-treat-display-x-face instead")
+                (eval (car (get 'gnus-treat-display-xface 'saved-value))))
+               (t
+                value)))))
+(put 'gnus-treat-display-x-face 'highlight t)
 
 (defcustom gnus-treat-display-face
   (and (not noninteractive)
 
 (defcustom gnus-treat-display-face
   (and (not noninteractive)
@@ -1175,7 +1203,7 @@ See Info node `(gnus)Customizing Articles' and Info node
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)X-Face")
   :type gnus-article-treat-head-custom)
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)X-Face")
   :type gnus-article-treat-head-custom)
-(put 'gnus-treat-display-xface 'highlight t)
+(put 'gnus-treat-display-face 'highlight t)
 
 (defcustom gnus-treat-display-smileys
   (if (or (and (featurep 'xemacs)
 
 (defcustom gnus-treat-display-smileys
   (if (or (and (featurep 'xemacs)
@@ -1245,12 +1273,12 @@ See Info node `(gnus)Customizing Articles' and Info node
          gnus-treat-from-picon)
       'head nil)
   "Draw a boundary at the end of the headers.
          gnus-treat-from-picon)
       'head nil)
   "Draw a boundary at the end of the headers.
-Valid values are nil, t, `head', `last', an integer or a predicate.
+Valid values are nil and `head'.
 See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :link '(custom-manual "(gnus)Customizing Articles")
 See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :link '(custom-manual "(gnus)Customizing Articles")
-  :type gnus-article-treat-custom)
+  :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-capitalize-sentences nil
   "Capitalize sentence-starting words.
 
 (defcustom gnus-treat-capitalize-sentences nil
   "Capitalize sentence-starting words.
@@ -1319,6 +1347,19 @@ It is a string, such as \"PGP\". If nil, ask user."
 (defvar gnus-article-wash-function nil
   "Function used for converting HTML into text.")
 
 (defvar gnus-article-wash-function nil
   "Function used for converting HTML into text.")
 
+(defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
+                             (mm-coding-system-p 'utf-8)
+                             (executable-find idna-program))
+  "Whether IDNA decoding of headers is used when viewing messages.
+This requires GNU Libidn, and by default only enabled if it is found."
+  :group 'gnus-article-headers
+  :type 'boolean)
+
+(defcustom gnus-article-over-scroll nil
+  "If non-nil, allow scrolling the article buffer even when there no more text."
+  :group 'gnus-article
+  :type 'boolean)
+
 ;;; Internal variables
 
 (defvar gnus-english-month-names
 ;;; Internal variables
 
 (defvar gnus-english-month-names
@@ -1348,7 +1389,7 @@ It is a string, such as \"PGP\". If nil, ask user."
     (gnus-treat-date-original gnus-article-date-original)
     (gnus-treat-date-user-defined gnus-article-date-user)
     (gnus-treat-date-iso8601 gnus-article-date-iso8601)
     (gnus-treat-date-original gnus-article-date-original)
     (gnus-treat-date-user-defined gnus-article-date-user)
     (gnus-treat-date-iso8601 gnus-article-date-iso8601)
-    (gnus-treat-display-xface gnus-article-display-x-face)
+    (gnus-treat-display-x-face gnus-article-display-x-face)
     (gnus-treat-display-face gnus-article-display-face)
     (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
     (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
     (gnus-treat-display-face gnus-article-display-face)
     (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
     (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
@@ -1588,7 +1629,7 @@ always hide."
              (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
                (forward-line -1)
                (gnus-article-hide-text-type
              (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
                (forward-line -1)
                (gnus-article-hide-text-type
-                (progn (beginning-of-line) (point))
+                (point-at-bol)
                 (progn
                   (end-of-line)
                   (if (re-search-forward "^[^ \t]" nil t)
                 (progn
                   (end-of-line)
                   (if (re-search-forward "^[^ \t]" nil t)
@@ -1649,15 +1690,25 @@ always hide."
                     (message-fetch-field "newsgroups"))
                (gnus-article-hide-header "followup-to")))
             ((eq elem 'reply-to)
                     (message-fetch-field "newsgroups"))
                (gnus-article-hide-header "followup-to")))
             ((eq elem 'reply-to)
-             (let ((from (message-fetch-field "from"))
-                   (reply-to (message-fetch-field "reply-to")))
-               (when (and
+             (if (gnus-group-find-parameter
+                  gnus-newsgroup-name 'broken-reply-to)
+                 (gnus-article-hide-header "reply-to")
+               (let ((from (message-fetch-field "from"))
+                     (reply-to (message-fetch-field "reply-to")))
+                 (when
+                     (and
                       from reply-to
                       (ignore-errors
                       from reply-to
                       (ignore-errors
-                        (gnus-string-equal
-                         (nth 1 (mail-extract-address-components from))
-                         (nth 1 (mail-extract-address-components reply-to)))))
-                 (gnus-article-hide-header "reply-to"))))
+                        (equal
+                         (sort (mapcar
+                                (lambda (x) (downcase (cadr x)))
+                                (mail-extract-address-components from t))
+                               'string<)
+                         (sort (mapcar
+                                (lambda (x) (downcase (cadr x)))
+                                (mail-extract-address-components reply-to t))
+                               'string<))))
+                   (gnus-article-hide-header "reply-to")))))
             ((eq elem 'date)
              (let ((date (message-fetch-field "date")))
                (when (and date
             ((eq elem 'date)
              (let ((date (message-fetch-field "date")))
                (when (and date
@@ -1704,7 +1755,7 @@ always hide."
     (goto-char (point-min))
     (when (re-search-forward (concat "^" header ":") nil t)
       (gnus-article-hide-text-type
     (goto-char (point-min))
     (when (re-search-forward (concat "^" header ":") nil t)
       (gnus-article-hide-text-type
-       (progn (beginning-of-line) (point))
+       (point-at-bol)
        (progn
         (end-of-line)
         (if (re-search-forward "^[^ \t]" nil t)
        (progn
         (end-of-line)
         (if (re-search-forward "^[^ \t]" nil t)
@@ -1725,7 +1776,7 @@ always hide."
        (article-narrow-to-head)
        (while (not (eobp))
          (cond
        (article-narrow-to-head)
        (while (not (eobp))
          (cond
-          ((< (setq column (- (gnus-point-at-eol) (point)))
+          ((< (setq column (- (point-at-eol) (point)))
               gnus-article-normalized-header-length)
            (end-of-line)
            (insert (make-string
               gnus-article-normalized-header-length)
            (end-of-line)
            (insert (make-string
@@ -1736,7 +1787,7 @@ always hide."
             (progn
               (forward-char gnus-article-normalized-header-length)
               (point))
             (progn
               (forward-char gnus-article-normalized-header-length)
               (point))
-            (gnus-point-at-eol)
+            (point-at-eol)
             'invisible t))
           (t
            ;; Do nothing.
             'invisible t))
           (t
            ;; Do nothing.
@@ -1744,14 +1795,15 @@ always hide."
          (forward-line 1))))))
 
 (defun article-treat-dumbquotes ()
          (forward-line 1))))))
 
 (defun article-treat-dumbquotes ()
-  "Translate M****s*** sm*rtq**t*s into proper text.
+  "Translate M****s*** sm*rtq**t*s and other symbols into proper text.
 Note that this function guesses whether a character is a sm*rtq**t* or
 not, so it should only be used interactively.
 
 Note that this function guesses whether a character is a sm*rtq**t* or
 not, so it should only be used interactively.
 
-Sm*rtq**t*s are M****s***'s unilateral extension to the character map
-in an attempt to provide more quoting characters.  If you see
-something like \\222 or \\264 where you're expecting some kind of
-apostrophe or quotation mark, then try this wash."
+Sm*rtq**t*s are M****s***'s unilateral extension to the
+iso-8859-1 character map in an attempt to provide more quoting
+characters.  If you see something like \\222 or \\264 where
+you're expecting some kind of apostrophe or quotation mark, then
+try this wash."
   (interactive)
   (article-translate-strings gnus-article-dumbquotes-map))
 
   (interactive)
   (article-translate-strings gnus-article-dumbquotes-map))
 
@@ -1820,7 +1872,7 @@ unfolded."
       (while (not (eobp))
        (save-restriction
          (mail-header-narrow-to-field)
       (while (not (eobp))
        (save-restriction
          (mail-header-narrow-to-field)
-         (let ((header (buffer-substring (point-min) (point-max))))
+         (let ((header (buffer-string)))
            (with-temp-buffer
              (insert header)
              (goto-char (point-min))
            (with-temp-buffer
              (insert header)
              (goto-char (point-min))
@@ -1905,7 +1957,8 @@ unfolded."
          (while (not (eobp))
            (end-of-line)
            (when (>= (current-column) (min fill-column width))
          (while (not (eobp))
            (end-of-line)
            (when (>= (current-column) (min fill-column width))
-             (narrow-to-region (min (1+ (point)) (point-max)) (gnus-point-at-bol))
+             (narrow-to-region (min (1+ (point)) (point-max))
+                               (point-at-bol))
               (let ((goback (point-marker)))
                 (fill-paragraph nil)
                 (goto-char (marker-position goback)))
               (let ((goback (point-marker)))
                 (fill-paragraph nil)
                 (goto-char (marker-position goback)))
@@ -1947,7 +2000,7 @@ unfolded."
         (while (and (not (bobp))
                     (looking-at "^[ \t]*$")
                     (not (gnus-annotation-in-region-p
         (while (and (not (bobp))
                     (looking-at "^[ \t]*$")
                     (not (gnus-annotation-in-region-p
-                          (point) (gnus-point-at-eol))))
+                          (point) (point-at-eol))))
           (forward-line -1))
         (forward-line 1)
         (point))))))
           (forward-line -1))
         (forward-line 1)
         (point))))))
@@ -1955,20 +2008,40 @@ unfolded."
 (defun article-display-face ()
   "Display any Face headers in the header."
   (interactive)
 (defun article-display-face ()
   "Display any Face headers in the header."
   (interactive)
-  (gnus-with-article-headers
-    (let ((face (message-fetch-field "face")))
-      (when face
-       (let ((png (gnus-convert-face-to-png face))
-             image)
-         (when png
-           (setq image (gnus-create-image png 'png t))
-           (gnus-article-goto-header "from")
-           (when (bobp)
-             (insert "From: [no `from' set]\n")
-             (forward-char -17))
-           (gnus-add-wash-type 'face)
-           (gnus-add-image 'face image)
-           (gnus-put-image image)))))))
+  (let ((wash-face-p buffer-read-only))
+    (gnus-with-article-headers
+      ;; When displaying parts, this function can be called several times on
+      ;; the same article, without any intended toggle semantic (as typing `W
+      ;; D d' would have). So face deletion must occur only when we come from
+      ;; an interactive command, that is when the *Article* buffer is
+      ;; read-only.
+      (if (and wash-face-p (memq 'face gnus-article-wash-types))
+         (gnus-delete-images 'face)
+       (let (face faces)
+         (save-excursion
+           (when (and wash-face-p
+                      (progn
+                        (goto-char (point-min))
+                        (not (re-search-forward "^Face:[\t ]*" nil t)))
+                      (gnus-buffer-live-p gnus-original-article-buffer))
+             (set-buffer gnus-original-article-buffer))
+           (save-restriction
+             (mail-narrow-to-head)
+             (while (gnus-article-goto-header "Face")
+               (push (mail-header-field-value) faces))))
+         (while (setq face (pop faces))
+           (let ((png (gnus-convert-face-to-png face))
+                 image)
+             (when png
+               (setq image (gnus-create-image png 'png t))
+               (gnus-article-goto-header "from")
+               (when (bobp)
+                 (insert "From: [no `from' set]\n")
+                 (forward-char -17))
+               (gnus-add-wash-type 'face)
+               (gnus-add-image 'face image)
+               (gnus-put-image image nil 'face))))))
+      )))
 
 (defun article-display-x-face (&optional force)
   "Look for an X-Face header and display it if present."
 
 (defun article-display-x-face (&optional force)
   "Look for an X-Face header and display it if present."
@@ -1978,7 +2051,8 @@ unfolded."
       ;; Delete the old process, if any.
       (when (process-status "article-x-face")
        (delete-process "article-x-face"))
       ;; Delete the old process, if any.
       (when (process-status "article-x-face")
        (delete-process "article-x-face"))
-      (if (memq 'xface gnus-article-wash-types)
+      ;; See the comment in `article-display-face'.
+      (if (and wash-face-p (memq 'xface gnus-article-wash-types))
          ;; We have already displayed X-Faces, so we remove them
          ;; instead.
          (gnus-delete-images 'xface)
          ;; We have already displayed X-Faces, so we remove them
          ;; instead.
          (gnus-delete-images 'xface)
@@ -2013,23 +2087,25 @@ unfolded."
                               (not (string-match gnus-article-x-face-too-ugly
                                                  from)))))
            ;; We display the face.
                               (not (string-match gnus-article-x-face-too-ugly
                                                  from)))))
            ;; We display the face.
-           (if (symbolp gnus-article-x-face-command)
-               ;; The command is a lisp function, so we call it.
-               (if (gnus-functionp gnus-article-x-face-command)
-                   (funcall gnus-article-x-face-command face)
-                 (error "%s is not a function" gnus-article-x-face-command))
-             ;; The command is a string, so we interpret the command
-             ;; as a, well, command, and fork it off.
-             (let ((process-connection-type nil))
-               (process-kill-without-query
-                (start-process
-                 "article-x-face" nil shell-file-name shell-command-switch
-                 gnus-article-x-face-command))
-               (with-temp-buffer
-                 (insert face)
-                 (process-send-region "article-x-face"
-                                      (point-min) (point-max)))
-               (process-send-eof "article-x-face")))))))))
+           (cond ((stringp gnus-article-x-face-command)
+                  ;; The command is a string, so we interpret the command
+                  ;; as a, well, command, and fork it off.
+                  (let ((process-connection-type nil))
+                    (process-kill-without-query
+                     (start-process
+                      "article-x-face" nil shell-file-name
+                      shell-command-switch gnus-article-x-face-command))
+                    (with-temp-buffer
+                      (insert face)
+                      (process-send-region "article-x-face"
+                                           (point-min) (point-max)))
+                    (process-send-eof "article-x-face")))
+                 ((functionp gnus-article-x-face-command)
+                  ;; The command is a lisp function, so we call it.
+                  (funcall gnus-article-x-face-command face))
+                 (t
+                  (error "%s is not a function"
+                         gnus-article-x-face-command)))))))))
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
@@ -2112,20 +2188,60 @@ If PROMPT (the prefix), prompt for a coding system to use."
     (when (and (or gnus-group-name-charset-method-alist
                   gnus-group-name-charset-group-alist)
               (gnus-buffer-live-p gnus-original-article-buffer))
     (when (and (or gnus-group-name-charset-method-alist
                   gnus-group-name-charset-group-alist)
               (gnus-buffer-live-p gnus-original-article-buffer))
-      (when (nnmail-fetch-field "Newsgroups")
-       (nnheader-replace-header "Newsgroups"
-                                (gnus-decode-newsgroups
-                                 (with-current-buffer
-                                     gnus-original-article-buffer
-                                   (nnmail-fetch-field "Newsgroups"))
-                                 gnus-newsgroup-name method)))
-      (when (nnmail-fetch-field "Followup-To")
-       (nnheader-replace-header "Followup-To"
-                                (gnus-decode-newsgroups
-                                 (with-current-buffer
-                                     gnus-original-article-buffer
-                                   (nnmail-fetch-field "Followup-To"))
-                                 gnus-newsgroup-name method))))))
+      (save-restriction
+       (article-narrow-to-head)
+       (with-current-buffer gnus-original-article-buffer
+         (goto-char (point-min)))
+       (while (re-search-forward
+               "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
+         (replace-match (save-match-data
+                          (gnus-decode-newsgroups
+                           ;; XXX how to use data in article buffer?
+                           (with-current-buffer gnus-original-article-buffer
+                             (re-search-forward
+                              "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
+                              nil t)
+                             (match-string 1))
+                           gnus-newsgroup-name method))
+                        t t nil 1))
+       (goto-char (point-min))
+       (with-current-buffer gnus-original-article-buffer
+         (goto-char (point-min)))
+       (while (re-search-forward
+               "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
+         (replace-match (save-match-data
+                          (gnus-decode-newsgroups
+                           ;; XXX how to use data in article buffer?
+                           (with-current-buffer gnus-original-article-buffer
+                             (re-search-forward
+                              "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
+                              nil t)
+                             (match-string 1))
+                           gnus-newsgroup-name method))
+                        t t nil 1))))))
+
+(autoload 'idna-to-unicode "idna")
+
+(defun article-decode-idna-rhs ()
+  "Decode IDNA strings in RHS in From:, To: and Cc: headers in current buffer."
+  (when gnus-use-idna
+    (save-restriction
+      (let ((inhibit-point-motion-hooks t)
+           buffer-read-only)
+       (article-narrow-to-head)
+       (goto-char (point-min))
+       (while (re-search-forward "\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
+         (let (ace unicode)
+           (when (save-match-data
+                   (and (setq ace (match-string 1))
+                        (save-excursion
+                          (and (re-search-backward "^[^ \t]" nil t)
+                               (looking-at "From\\|To\\|Cc")))
+                        (save-excursion (backward-char)
+                                        (message-idna-inside-rhs-p))
+                        (setq unicode (idna-to-unicode ace))))
+             (unless (string= ace unicode)
+               (replace-match unicode nil nil nil 1)))))))))
 
 (defun article-de-quoted-unreadable (&optional force read-charset)
   "Translate a quoted-printable-encoded article.
 
 (defun article-de-quoted-unreadable (&optional force read-charset)
   "Translate a quoted-printable-encoded article.
@@ -2211,8 +2327,8 @@ If READ-CHARSET, ask for a coding system."
       (while (re-search-forward
              "^\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
        (replace-match "\\1\\3" t)))
       (while (re-search-forward
              "^\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
        (replace-match "\\1\\3" t)))
-    (when (and gnus-display-mime-function (interactive-p))
-      (funcall gnus-display-mime-function))))
+    (when (interactive-p)
+      (gnus-treat-article nil))))
 
 
 (defun article-wash-html (&optional read-charset)
 
 
 (defun article-wash-html (&optional read-charset)
@@ -2245,7 +2361,7 @@ If READ-CHARSET, ask for a coding system."
            (when entry
              (setq func (cdr entry)))
            (cond
            (when entry
              (setq func (cdr entry)))
            (cond
-            ((gnus-functionp func)
+            ((functionp func)
              (funcall func))
             (t
              (apply (car func) (cdr func))))))))))
              (funcall func))
             (t
              (apply (car func) (cdr func))))))))))
@@ -2269,7 +2385,6 @@ If READ-CHARSET, ask for a coding system."
     (let ((w3m-safe-url-regexp (if mm-inline-text-html-with-images
                                   nil
                                 "\\`cid:"))
     (let ((w3m-safe-url-regexp (if mm-inline-text-html-with-images
                                   nil
                                 "\\`cid:"))
-         (w3m-display-inline-images mm-inline-text-html-with-images)
          w3m-force-redisplay)
       (w3m-region (point-min) (point-max)))
     (when mm-inline-text-html-with-w3m-keymap
          w3m-force-redisplay)
       (w3m-region (point-min) (point-max)))
     (when mm-inline-text-html-with-w3m-keymap
@@ -2520,7 +2635,7 @@ Point is left at the beginning of the narrowed-to region."
                       (< (- (point-max) (point)) limit))
                  (and (floatp limit)
                       (< (count-lines (point) (point-max)) limit))
                       (< (- (point-max) (point)) limit))
                  (and (floatp limit)
                       (< (count-lines (point) (point-max)) limit))
-                 (and (gnus-functionp limit)
+                 (and (functionp limit)
                       (funcall limit))
                  (and (stringp limit)
                       (not (re-search-forward limit nil t))))
                       (funcall limit))
                  (and (stringp limit)
                       (not (re-search-forward limit nil t))))
@@ -2646,11 +2761,11 @@ should replace the \"Date:\" one, or should be added below it."
       (save-restriction
        (article-narrow-to-head)
        (when (re-search-forward tdate-regexp nil t)
       (save-restriction
        (article-narrow-to-head)
        (when (re-search-forward tdate-regexp nil t)
-         (setq bface (get-text-property (gnus-point-at-bol) 'face)
-               date (or (get-text-property (gnus-point-at-bol)
+         (setq bface (get-text-property (point-at-bol) 'face)
+               date (or (get-text-property (point-at-bol)
                                            'original-date)
                         date)
                                            'original-date)
                         date)
-               eface (get-text-property (1- (gnus-point-at-eol)) 'face))
+               eface (get-text-property (1- (point-at-eol)) 'face))
          (forward-line 1))
        (when (and date (not (string= date "")))
          (goto-char (point-min))
          (forward-line 1))
        (when (and date (not (string= date "")))
          (goto-char (point-min))
@@ -2722,7 +2837,7 @@ should replace the \"Date:\" one, or should be added below it."
                                  gnus-article-time-format)
                              (error nil))
                            gnus-article-time-format)))
                                  gnus-article-time-format)
                              (error nil))
                            gnus-article-time-format)))
-           (if (gnus-functionp format)
+           (if (functionp format)
                (funcall format time)
              (concat "Date: " (format-time-string format time)))))
         ;; ISO 8601.
                (funcall format time)
              (concat "Date: " (format-time-string format time)))))
         ;; ISO 8601.
@@ -2832,9 +2947,12 @@ function and want to see what the date was before converting."
         (lambda (w)
           (set-buffer (window-buffer w))
           (when (eq major-mode 'gnus-article-mode)
         (lambda (w)
           (set-buffer (window-buffer w))
           (when (eq major-mode 'gnus-article-mode)
-            (goto-char (point-min))
-            (when (re-search-forward "^X-Sent:" nil t)
-              (article-date-lapsed t))))
+            (let ((mark (point-marker)))
+              (goto-char (point-min))
+              (when (re-search-forward "^X-Sent:" nil t)
+                (article-date-lapsed t))
+              (goto-char (marker-position mark))
+              (move-marker mark nil))))
         nil 'visible)))))
 
 (defun gnus-start-date-timer (&optional n)
         nil 'visible)))))
 
 (defun gnus-start-date-timer (&optional n)
@@ -3167,9 +3285,17 @@ The directory to save in defaults to `gnus-article-save-directory'."
       (shell-command-on-region (point-min) (point-max) command nil)))
   (setq gnus-last-shell-command command))
 
       (shell-command-on-region (point-min) (point-max) command nil)))
   (setq gnus-last-shell-command command))
 
+(defmacro gnus-read-string (prompt &optional initial-contents history
+                           default-value)
+  "Like `read-string' but allow for older XEmacsen that don't have the 5th arg."
+  (if (and (featurep 'xemacs)
+          (< emacs-minor-version 2))
+      `(read-string ,prompt ,initial-contents ,history)
+    `(read-string ,prompt ,initial-contents ,history ,default-value)))
+
 (defun gnus-summary-pipe-to-muttprint (&optional command)
   "Pipe this article to muttprint."
 (defun gnus-summary-pipe-to-muttprint (&optional command)
   "Pipe this article to muttprint."
-  (setq command (read-string
+  (setq command (gnus-read-string
                 "Print using command: " gnus-summary-muttprint-program
                 nil gnus-summary-muttprint-program))
   (gnus-summary-save-in-pipe command))
                 "Print using command: " gnus-summary-muttprint-program
                 nil gnus-summary-muttprint-program))
   (gnus-summary-save-in-pipe command))
@@ -3251,7 +3377,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                   mml2015-use
                   (mml2015-clear-verify-function))
          (with-temp-buffer
                   mml2015-use
                   (mml2015-clear-verify-function))
          (with-temp-buffer
-           (insert-buffer gnus-original-article-buffer)
+           (insert-buffer-substring gnus-original-article-buffer)
            (setq items (split-string sig))
            (message-narrow-to-head)
            (let ((inhibit-point-motion-hooks t)
            (setq items (split-string sig))
            (message-narrow-to-head)
            (let ((inhibit-point-motion-hooks t)
@@ -3259,7 +3385,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
              ;; Don't verify multiple headers.
              (setq headers (mapconcat (lambda (header)
                                         (concat header ": "
              ;; Don't verify multiple headers.
              (setq headers (mapconcat (lambda (header)
                                         (concat header ": "
-                                                (mail-fetch-field header) "\n"))
+                                                (mail-fetch-field header)
+                                                "\n"))
                                       (split-string (nth 1 items) ",") "")))
            (delete-region (point-min) (point-max))
            (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
                                       (split-string (nth 1 items) ",") "")))
            (delete-region (point-min) (point-max))
            (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
@@ -3291,8 +3418,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                (message-narrow-to-head)
                (goto-char (point-max))
                (forward-line -1)
                (message-narrow-to-head)
                (goto-char (point-max))
                (forward-line -1)
-               (setq bface (get-text-property (gnus-point-at-bol) 'face)
-                     eface (get-text-property (1- (gnus-point-at-eol)) 'face))
+               (setq bface (get-text-property (point-at-bol) 'face)
+                     eface (get-text-property (1- (point-at-eol)) 'face))
                (message-remove-header "X-Gnus-PGP-Verify")
                (if (re-search-forward "^X-PGP-Sig:" nil t)
                    (forward-line)
                (message-remove-header "X-Gnus-PGP-Verify")
                (if (re-search-forward "^X-PGP-Sig:" nil t)
                    (forward-line)
@@ -3484,7 +3611,7 @@ commands:
       (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
   (gnus-update-format-specifications nil 'article-mode)
   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
       (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
   (gnus-update-format-specifications nil 'article-mode)
   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
-  (make-local-variable 'gnus-page-broken)
+  (set (make-local-variable 'gnus-page-broken) nil)
   (make-local-variable 'gnus-button-marker-list)
   (make-local-variable 'gnus-article-current-summary)
   (make-local-variable 'gnus-article-mime-handles)
   (make-local-variable 'gnus-button-marker-list)
   (make-local-variable 'gnus-article-current-summary)
   (make-local-variable 'gnus-article-mime-handles)
@@ -3668,10 +3795,8 @@ If ALL-HEADERS is non-nil, no headers are hidden."
              (gnus-article-prepare-display)
              ;; Do page break.
              (goto-char (point-min))
              (gnus-article-prepare-display)
              ;; Do page break.
              (goto-char (point-min))
-             (setq gnus-page-broken
-                   (when gnus-break-pages
-                     (gnus-narrow-to-page)
-                     t)))
+             (when gnus-break-pages
+               (gnus-narrow-to-page)))
            (let ((gnus-article-mime-handle-alist-1
                   gnus-article-mime-handle-alist))
              (gnus-set-mode-line 'article))
            (let ((gnus-article-mime-handle-alist-1
                   gnus-article-mime-handle-alist))
              (gnus-set-mode-line 'article))
@@ -3734,6 +3859,7 @@ General format specifiers can also be used.  See Info node
     (gnus-mime-view-part-as-charset "C" "View As charset...")
     (gnus-mime-save-part "o" "Save...")
     (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
     (gnus-mime-view-part-as-charset "C" "View As charset...")
     (gnus-mime-save-part "o" "Save...")
     (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
+    (gnus-mime-delete-part "d" "Delete part")
     (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
     (gnus-mime-view-part-internally "E" "View Internally")
     (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
     (gnus-mime-view-part-internally "E" "View Internally")
@@ -3760,26 +3886,12 @@ General format specifiers can also be used.  See Info node
       (define-key map (cadr c) (car c)))
     map))
 
       (define-key map (cadr c) (car c)))
     map))
 
-(easy-menu-define gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
+(easy-menu-define
+  gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
   `("MIME Part"
     ,@(mapcar (lambda (c)
   `("MIME Part"
     ,@(mapcar (lambda (c)
-               (vector (caddr c) (car c) :enable t)) gnus-mime-button-commands)))
-
-(eval-when-compile
-  (define-compiler-macro popup-menu (&whole form
-                                           menu &optional position prefix)
-    (if (and (fboundp 'popup-menu)
-            (not (memq 'popup-menu (assoc "lmenu" load-history))))
-       form
-      ;; Gnus is probably running under Emacs 20.
-      `(let* ((menu (cdr ,menu))
-             (response (x-popup-menu
-                        t (list (car menu)
-                                (cons "" (mapcar (lambda (c)
-                                                   (cons (caddr c) (car c)))
-                                                 (cdr menu)))))))
-        (if response
-            (call-interactively (nth 3 (assq response menu))))))))
+               (vector (caddr c) (car c) :enable t))
+             gnus-mime-button-commands)))
 
 (defun gnus-mime-button-menu (event prefix)
  "Construct a context-sensitive menu of MIME commands."
 
 (defun gnus-mime-button-menu (event prefix)
  "Construct a context-sensitive menu of MIME commands."
@@ -3851,9 +3963,7 @@ General format specifiers can also be used.  See Info node
             (let ((mbl1 mml-buffer-list))
               (setq mml-buffer-list mbl)
               (set (make-local-variable 'mml-buffer-list) mbl1))
             (let ((mbl1 mml-buffer-list))
               (setq mml-buffer-list mbl)
               (set (make-local-variable 'mml-buffer-list) mbl1))
-            ;; LOCAL argument of add-hook differs between GNU Emacs
-            ;; and XEmacs. make-local-hook makes sure they are local.
-            (make-local-hook 'kill-buffer-hook)
+            (gnus-make-local-hook 'kill-buffer-hook)
             (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
        `(lambda (no-highlight)
          (let ((mail-parse-charset (or gnus-article-charset
             (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
        `(lambda (no-highlight)
          (let ((mail-parse-charset (or gnus-article-charset
@@ -3873,6 +3983,88 @@ General format specifiers can also be used.  See Info node
           ,(gnus-group-read-only-p)
           ,gnus-summary-buffer no-highlight))))))
 
           ,(gnus-group-read-only-p)
           ,gnus-summary-buffer no-highlight))))))
 
+(defun gnus-mime-delete-part ()
+  "Delete the MIME part under point.
+Replace it with some information about the removed part."
+  (interactive)
+  (gnus-article-check-buffer)
+  (unless (and gnus-novice-user
+              (not (gnus-yes-or-no-p
+                    "Really delete attachment forever? ")))
+    (let* ((data (get-text-property (point) 'gnus-data))
+          (handles gnus-article-mime-handles)
+          (none "(none)")
+          (description
+           (or
+            (mail-decode-encoded-word-string (or (mm-handle-description data)
+                                                 none))))
+          (filename
+           (or (mail-content-type-get (mm-handle-disposition data) 'filename)
+               none))
+          (type (mm-handle-media-type data)))
+      (if (mm-multiple-handles gnus-article-mime-handles)
+         (error "This function is not implemented"))
+      (with-current-buffer (mm-handle-buffer data)
+       (let ((bsize (format "%s" (buffer-size))))
+         (erase-buffer)
+         (insert
+          (concat
+           ",----\n"
+           "| The following attachment has been deleted:\n"
+           "|\n"
+           "| Type:           " type "\n"
+           "| Filename:       " filename "\n"
+           "| Size (encoded): " bsize " Byte\n"
+           "| Description:    " description "\n"
+           "`----\n"))
+         (setcdr data
+                 (cdr (mm-make-handle
+                       nil `("text/plain") nil nil
+                       (list "attachment")
+                       (format "Deleted attachment (%s bytes)" bsize))))))
+      (set-buffer gnus-summary-buffer)
+      ;; FIXME: maybe some of the following code (borrowed from
+      ;; `gnus-mime-save-part-and-strip') isn't necessary?
+      (gnus-article-edit-article
+       `(lambda ()
+         (erase-buffer)
+         (let ((mail-parse-charset (or gnus-article-charset
+                                       ',gnus-newsgroup-charset))
+               (mail-parse-ignored-charsets
+                (or gnus-article-ignored-charsets
+                    ',gnus-newsgroup-ignored-charsets))
+               (mbl mml-buffer-list))
+           (setq mml-buffer-list nil)
+           (insert-buffer gnus-original-article-buffer)
+           (mime-to-mml ',handles)
+           (setq gnus-article-mime-handles nil)
+           (let ((mbl1 mml-buffer-list))
+             (setq mml-buffer-list mbl)
+             (set (make-local-variable 'mml-buffer-list) mbl1))
+           (gnus-make-local-hook 'kill-buffer-hook)
+           (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
+       `(lambda (no-highlight)
+         (let ((mail-parse-charset (or gnus-article-charset
+                                       ',gnus-newsgroup-charset))
+               (message-options message-options)
+               (message-options-set-recipient)
+               (mail-parse-ignored-charsets
+                (or gnus-article-ignored-charsets
+                    ',gnus-newsgroup-ignored-charsets)))
+           (mml-to-mime)
+           (mml-destroy-buffers)
+           (remove-hook 'kill-buffer-hook
+                        'mml-destroy-buffers t)
+           (kill-local-variable 'mml-buffer-list))
+         (gnus-summary-edit-article-done
+          ,(or (mail-header-references gnus-current-headers) "")
+          ,(gnus-group-read-only-p)
+          ,gnus-summary-buffer no-highlight)))))
+  ;; Not in `gnus-mime-save-part-and-strip':
+  (gnus-article-edit-done)
+  (gnus-summary-expand-window)
+  (gnus-summary-show-article))
+
 (defun gnus-mime-save-part ()
   "Save the MIME part under point."
   (interactive)
 (defun gnus-mime-save-part ()
   "Save the MIME part under point."
   (interactive)
@@ -3955,7 +4147,7 @@ The uncompress method used is derived from `buffer-file-name'."
               (message "%s %s..." message basename))
           (unwind-protect
               (unless (memq (apply 'call-process-region
               (message "%s %s..." message basename))
           (unwind-protect
               (unless (memq (apply 'call-process-region
-                                   (point-min) (point-max) 
+                                   (point-min) (point-max)
                                    prog
                                    t (list t err-file) nil
                                    args)
                                    prog
                                    t (list t err-file) nil
                                    args)
@@ -4142,7 +4334,8 @@ If no internal viewer is available, use an external viewer."
   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
 
 (defun gnus-article-view-part-as-charset (n)
   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
 
 (defun gnus-article-view-part-as-charset (n)
-  "Copy MIME part N, which is the numerical prefix."
+  "View MIME part N using a specified charset.
+N is the numerical prefix."
   (interactive "p")
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
 
   (interactive "p")
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
 
@@ -4239,16 +4432,14 @@ If no internal viewer is available, use an external viewer."
              (if (window-live-p window)
                  (select-window window)))))
       (goto-char point)
              (if (window-live-p window)
                  (select-window window)))))
       (goto-char point)
-      (delete-region (gnus-point-at-bol) (progn (forward-line 1) (point)))
+      (gnus-delete-line)
       (gnus-insert-mime-button
        handle id (list (mm-handle-displayed-p handle)))
       (goto-char point))))
 
 (defun gnus-article-goto-part (n)
   "Go to MIME part N."
       (gnus-insert-mime-button
        handle id (list (mm-handle-displayed-p handle)))
       (goto-char point))))
 
 (defun gnus-article-goto-part (n)
   "Go to MIME part N."
-  (let ((point (text-property-any (point-min) (point-max) 'gnus-part n)))
-    (when point
-      (goto-char point))))
+  (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
 
 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
   (let ((gnus-tmp-name
 
 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
   (let ((gnus-tmp-name
@@ -4284,7 +4475,10 @@ If no internal viewer is available, use an external viewer."
         gnus-part ,gnus-tmp-id
         article-type annotation
         gnus-data ,handle))
         gnus-part ,gnus-tmp-id
         article-type annotation
         gnus-data ,handle))
-    (setq e (point))
+    (setq e (if (bolp)
+               ;; Exclude a newline.
+               (1- (point))
+             (point)))
     (widget-convert-button
      'link b e
      :mime-handle handle
     (widget-convert-button
      'link b e
      :mime-handle handle
@@ -4366,9 +4560,28 @@ If no internal viewer is available, use an external viewer."
              (narrow-to-region (point-min) (point))
              (gnus-treat-article 'head))))))))
 
              (narrow-to-region (point-min) (point))
              (gnus-treat-article 'head))))))))
 
-(defvar gnus-mime-display-multipart-as-mixed nil)
-(defvar gnus-mime-display-multipart-alternative-as-mixed nil)
-(defvar gnus-mime-display-multipart-related-as-mixed nil)
+(defcustom gnus-mime-display-multipart-as-mixed nil
+  "Display \"multipart\" parts as  \"multipart/mixed\".
+
+If t, it overrides nil values of
+`gnus-mime-display-multipart-alternative-as-mixed' and
+`gnus-mime-display-multipart-related-as-mixed'."
+  :group 'gnus-article-mime
+  :type 'boolean)
+
+(defcustom gnus-mime-display-multipart-alternative-as-mixed nil
+  "Display \"multipart/alternative\" parts as  \"multipart/mixed\"."
+  :group 'gnus-article-mime
+  :type 'boolean)
+
+(defcustom gnus-mime-display-multipart-related-as-mixed nil
+  "Display \"multipart/related\" parts as  \"multipart/mixed\".
+
+If displaying \"text/html\" is discouraged \(see
+`mm-discouraged-alternatives'\) images or other material inside a
+\"multipart/related\" part might be overlooked when this variable is nil."
+  :group 'gnus-article-mime
+  :type 'boolean)
 
 (defun gnus-mime-display-part (handle)
   (cond
 
 (defun gnus-mime-display-part (handle)
   (cond
@@ -4669,7 +4882,7 @@ is the string to use when it is inactive.")
   "Delete all images in CATEGORY."
   (let ((entry (assq category gnus-article-image-alist)))
     (dolist (image (cdr entry))
   "Delete all images in CATEGORY."
   (let ((entry (assq category gnus-article-image-alist)))
     (dolist (image (cdr entry))
-      (gnus-remove-image image))
+      (gnus-remove-image image category))
     (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
     (gnus-delete-wash-type category)))
 
     (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
     (gnus-delete-wash-type category)))
 
@@ -4713,41 +4926,63 @@ If given a numerical ARG, move forward ARG pages."
       (let ((buffer-read-only nil))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
       (let ((buffer-read-only nil))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
-    (when
+    (if
        (cond ((< arg 0)
               (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
              ((> arg 0)
               (re-search-forward page-delimiter nil 'move arg)))
        (cond ((< arg 0)
               (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
              ((> arg 0)
               (re-search-forward page-delimiter nil 'move arg)))
-      (goto-char (match-end 0)))
-    (narrow-to-region
-     (point)
-     (if (re-search-forward page-delimiter nil 'move)
-        (match-beginning 0)
-       (point)))
-    (when (and (gnus-visual-p 'page-marker)
-              (not (= (point-min) 1)))
+       (goto-char (match-end 0))
       (save-excursion
        (goto-char (point-min))
       (save-excursion
        (goto-char (point-min))
-       (gnus-insert-prev-page-button)))
-    (when (and (gnus-visual-p 'page-marker)
-              (< (+ (point-max) 2) (buffer-size)))
-      (save-excursion
-       (goto-char (point-max))
-       (gnus-insert-next-page-button)))))
+       (setq gnus-page-broken
+             (and (re-search-forward page-delimiter nil t) t))))
+    (when gnus-page-broken
+      (narrow-to-region
+       (point)
+       (if (re-search-forward page-delimiter nil 'move)
+          (match-beginning 0)
+        (point)))
+      (when (and (gnus-visual-p 'page-marker)
+                (not (= (point-min) 1)))
+       (save-excursion
+         (goto-char (point-min))
+         (gnus-insert-prev-page-button)))
+      (when (and (gnus-visual-p 'page-marker)
+                (< (+ (point-max) 2) (buffer-size)))
+       (save-excursion
+         (goto-char (point-max))
+         (gnus-insert-next-page-button))))))
 
 ;; Article mode commands
 
 (defun gnus-article-goto-next-page ()
   "Show the next page of the article."
   (interactive)
 
 ;; Article mode commands
 
 (defun gnus-article-goto-next-page ()
   "Show the next page of the article."
   (interactive)
-  (gnus-eval-in-buffer-window gnus-summary-buffer
-    (gnus-summary-next-page)))
+  (when (gnus-article-next-page)
+    (goto-char (point-min))
+    (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
+
 
 (defun gnus-article-goto-prev-page ()
 
 (defun gnus-article-goto-prev-page ()
-  "Show the next page of the article."
+  "Show the previous page of the article."
   (interactive)
   (interactive)
-  (gnus-eval-in-buffer-window gnus-summary-buffer
-    (gnus-summary-prev-page)))
+  (if (bobp)
+      (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
+    (gnus-article-prev-page nil)))
+
+;; This is cleaner but currently breaks `gnus-pick-mode':
+;;
+;; (defun gnus-article-goto-next-page ()
+;;   "Show the next page of the article."
+;;   (interactive)
+;;   (gnus-eval-in-buffer-window gnus-summary-buffer
+;;     (gnus-summary-next-page)))
+;;
+;; (defun gnus-article-goto-prev-page ()
+;;   "Show the next page of the article."
+;;   (interactive)
+;;   (gnus-eval-in-buffer-window gnus-summary-buffer
+;;     (gnus-summary-prev-page)))
 
 (defun gnus-article-next-page (&optional lines)
   "Show the next page of the current article.
 
 (defun gnus-article-next-page (&optional lines)
   "Show the next page of the current article.
@@ -4763,20 +4998,27 @@ Argument LINES specifies lines to be scrolled up."
       (if (or (not gnus-page-broken)
              (save-excursion
                (save-restriction
       (if (or (not gnus-page-broken)
              (save-excursion
                (save-restriction
-                 (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
-         t                             ;Nothing more.
+                 (widen)
+                 (eobp)))) ;Real end-of-buffer?
+         (progn
+           (when gnus-article-over-scroll
+             (gnus-article-next-page-1 lines))
+           t)                  ;Nothing more.
        (gnus-narrow-to-page 1)         ;Go to next page.
        nil)
     ;; More in this page.
        (gnus-narrow-to-page 1)         ;Go to next page.
        nil)
     ;; More in this page.
-    (let ((scroll-in-place nil))
-      (condition-case ()
-         (scroll-up lines)
-       (end-of-buffer
-        ;; Long lines may cause an end-of-buffer error.
-        (goto-char (point-max)))))
-    (move-to-window-line 0)
+    (gnus-article-next-page-1 lines)
     nil))
 
     nil))
 
+(defun gnus-article-next-page-1 (lines)
+  (let ((scroll-in-place nil))
+    (condition-case ()
+       (scroll-up lines)
+      (end-of-buffer
+       ;; Long lines may cause an end-of-buffer error.
+       (goto-char (point-max)))))
+  (move-to-window-line 0))
+
 (defun gnus-article-prev-page (&optional lines)
   "Show previous page of current article.
 Argument LINES specifies lines to be scrolled down."
 (defun gnus-article-prev-page (&optional lines)
   "Show previous page of current article.
 Argument LINES specifies lines to be scrolled down."
@@ -4818,9 +5060,9 @@ not have a face in `gnus-article-boring-faces'."
   "Read article specified by message-id around point."
   (interactive)
   (save-excursion
   "Read article specified by message-id around point."
   (interactive)
   (save-excursion
-    (re-search-backward "[ \t]\\|^" (gnus-point-at-bol) t)
-    (re-search-forward "<?news:<?\\|<" (gnus-point-at-eol) t)
-    (if (re-search-forward "[^@ ]+@[^ \t>]+" (gnus-point-at-eol) t)
+    (re-search-backward "[ \t]\\|^" (point-at-bol) t)
+    (re-search-forward "<?news:<?\\|<" (point-at-eol) t)
+    (if (re-search-forward "[^@ ]+@[^ \t>]+" (point-at-eol) t)
        (let ((msg-id (concat "<" (match-string 0) ">")))
          (set-buffer gnus-summary-buffer)
          (gnus-summary-refer-article msg-id))
        (let ((msg-id (concat "<" (match-string 0) ">")))
          (set-buffer gnus-summary-buffer)
          (gnus-summary-refer-article msg-id))
@@ -5381,7 +5623,8 @@ groups."
     (set-window-configuration winconf)
     (set-buffer buf)
     (set-window-start (get-buffer-window buf) start)
     (set-window-configuration winconf)
     (set-buffer buf)
     (set-window-start (get-buffer-window buf) start)
-    (set-window-point (get-buffer-window buf) (point))))
+    (set-window-point (get-buffer-window buf) (point)))
+  (gnus-summary-show-article))
 
 (defun gnus-article-edit-exit ()
   "Exit the article editing without updating."
 
 (defun gnus-article-edit-exit ()
   "Exit the article editing without updating."
@@ -5402,7 +5645,8 @@ groups."
        (save-current-buffer
          (set-buffer curbuf)
          (set-window-start (get-buffer-window (current-buffer)) window-start)
        (save-current-buffer
          (set-buffer curbuf)
          (set-window-start (get-buffer-window (current-buffer)) window-start)
-         (goto-char p))))))
+         (goto-char p))))
+    (gnus-summary-show-article)))
 
 (defun gnus-article-edit-full-stops ()
   "Interactively repair spacing at end of sentences."
 
 (defun gnus-article-edit-full-stops ()
   "Interactively repair spacing at end of sentences."
@@ -5423,20 +5667,14 @@ groups."
 
 (defcustom gnus-button-url-regexp
   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
 
 (defcustom gnus-button-url-regexp
   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
-      "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
-    "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
+      "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
+    "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
   "Regular expression that matches URLs."
   :group 'gnus-article-buttons
   :type 'regexp)
 
 (defcustom gnus-button-valid-fqdn-regexp
   "Regular expression that matches URLs."
   :group 'gnus-article-buttons
   :type 'regexp)
 
 (defcustom gnus-button-valid-fqdn-regexp
-  (concat "[a-z0-9][-.a-z0-9]+\\." ;; [hostname.subdomain.]domain.
-         ;; valid TLDs:
-         "\\([a-z][a-z]" ;; two letter country TDLs
-         "\\|biz\\|com\\|edu\\|gov\\|int\\|mil\\|net\\|org"
-         "\\|aero\\|coop\\|info\\|name\\|museum"
-         "\\|arpa\\|pro\\|uucp\\|bitnet\\|bofh" ;; old style?
-         "\\)")
+  message-valid-fqdn-regexp
   "Regular expression that matches a valid FQDN."
   :group 'gnus-article-buttons
   :type 'regexp)
   "Regular expression that matches a valid FQDN."
   :group 'gnus-article-buttons
   :type 'regexp)
@@ -5475,8 +5713,21 @@ The function must take one argument, the string naming the URL."
   :type '(choice (const "^/?tex-archive/\\|/")
                 (regexp :tag "Other")))
 
   :type '(choice (const "^/?tex-archive/\\|/")
                 (regexp :tag "Other")))
 
+(defcustom gnus-button-ctan-directory-regexp
+  (concat
+   "\\("; Cannot use `\(?: ... \)' (compatibility with Emacs 20).
+   "biblio\\|digests\\|dviware\\|fonts\\|graphics\\|help\\|"
+   "indexing\\|info\\|language\\|macros\\|support\\|systems\\|"
+   "tds\\|tools\\|usergrps\\|web\\|nonfree\\|obsolete"
+   "\\)")
+  "Regular expression for ctan directories.
+It should match all directories in the top level of `gnus-ctan-url'."
+  :group 'gnus-article-buttons
+  :type 'regexp)
+
 (defcustom gnus-button-mid-or-mail-regexp
 (defcustom gnus-button-mid-or-mail-regexp
-  (concat "\\b\\(<?[a-z0-9][^<>\")!;:,{}\n\t ]*@"
+  (concat "\\b\\(<?[a-z0-9$%(*-=?[_][^<>\")!;:,{}\n\t ]*@"
+         ;; Felix Wiemann in <87oeuomcz9.fsf@news2.ososo.de>
          gnus-button-valid-fqdn-regexp
          ">?\\)\\b")
   "Regular expression that matches a message ID or a mail address."
          gnus-button-valid-fqdn-regexp
          ">?\\)\\b")
   "Regular expression that matches a message ID or a mail address."
@@ -5487,7 +5738,7 @@ The function must take one argument, the string naming the URL."
   "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
 Strings like this can be either a message ID or a mail address.  If it is one
 of the symbols `mid' or `mail', Gnus will always assume that the string is a
   "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
 Strings like this can be either a message ID or a mail address.  If it is one
 of the symbols `mid' or `mail', Gnus will always assume that the string is a
-message ID or a mail address, respectivly.  If this variable is set to the
+message ID or a mail address, respectively.  If this variable is set to the
 symbol `ask', always query the user what do do.  If it is a function, this
 function will be called with the string as it's only argument.  The function
 must return `mid', `mail', `invalid' or `ask'."
 symbol `ask', always query the user what do do.  If it is a function, this
 function will be called with the string as it's only argument.  The function
 must return `mid', `mail', `invalid' or `ask'."
@@ -5506,12 +5757,12 @@ must return `mid', `mail', `invalid' or `ask'."
     (-5.0  . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
     (-5.0  . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
     (-1.0  . "^[^a-z]+@")
     (-5.0  . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
     (-5.0  . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
     (-1.0  . "^[^a-z]+@")
-   
+    ;;
     (-5.0  . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
     (-5.0  . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
     (-3.0  . "[A-Z][A-Z][a-z][a-z].*@")
     (-5.0  . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
     (-5.0  . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
     (-5.0  . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
     (-3.0  . "[A-Z][A-Z][a-z][a-z].*@")
     (-5.0  . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
-   
+    ;;
     (-2.0  . "^[0-9]")
     (-1.0  . "^[0-9][0-9]")
     ;;
     (-2.0  . "^[0-9]")
     (-1.0  . "^[0-9][0-9]")
     ;;
@@ -5553,7 +5804,7 @@ must return `mid', `mail', `invalid' or `ask'."
   "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
 
 A negative RATE indicates a message IDs, whereas a positive indicates a mail
   "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
 
 A negative RATE indicates a message IDs, whereas a positive indicates a mail
-address.  The REGEXP is processed with `case-fold-search' set to `nil'."
+address.  The REGEXP is processed with `case-fold-search' set to nil."
   :group 'gnus-article-buttons
   :type '(repeat (cons (number :tag "Rate")
                       (regexp :tag "Regexp"))))
   :group 'gnus-article-buttons
   :type '(repeat (cons (number :tag "Rate")
                       (regexp :tag "Regexp"))))
@@ -5571,13 +5822,14 @@ address, `ask' if unsure and `invalid' if the string is invalid."
     ;; Certain special cases...
     (when (string-match
           (concat
     ;; Certain special cases...
     (when (string-match
           (concat
-           "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$" "\\|"
-           "^[0-9]+\.[0-9]+\@compuserve")
+           "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$\\|"
+           "^[0-9]+\\.[0-9]+@compuserve\\|"
+           "@public\\.gmane\\.org")
           mid-or-mail)
           mid-or-mail)
-      (gnus-message 8 "`%s' is a known mail address.")
+      (gnus-message 8 "`%s' is a known mail address." mid-or-mail)
       (setq result 'mail))
     (when (string-match "@.*@\\| " mid-or-mail)
       (setq result 'mail))
     (when (string-match "@.*@\\| " mid-or-mail)
-      (gnus-message 8 "`%s' is invalid.")
+      (gnus-message 8 "`%s' is invalid." mid-or-mail)
       (setq result 'invalid))
     ;; Nothing more to do, if result is not a number here...
     (when (numberp result)
       (setq result 'invalid))
     ;; Nothing more to do, if result is not a number here...
     (when (numberp result)
@@ -5619,8 +5871,10 @@ address, `ask' if unsure and `invalid' if the string is invalid."
          (gnus-message
           9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
     (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
          (gnus-message
           9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
     (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
+    ;; Maybe we should make this a customizable alist: (condition . 'result)
     (cond
     (cond
-     ;; Maybe we should make this a customizable alist: (condition . 'result)
+     ((symbolp result) result)
+     ;; Now convert number into proper results:
      ((< result -10.0) 'mid)
      ((> result  10.0) 'mail)
      (t 'ask))))
      ((< result -10.0) 'mid)
      ((> result  10.0) 'mail)
      (t 'ask))))
@@ -5657,6 +5911,9 @@ address, `ask' if unsure and `invalid' if the string is invalid."
 
 (defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
 
 
 (defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
 
+;; FIXME: Maybe we should merge some of the functions that do quite similar
+;; stuff?
+
 (defun gnus-button-handle-describe-function (url)
   "Call `describe-function' when pushing the corresponding URL button."
   (describe-function
 (defun gnus-button-handle-describe-function (url)
   "Call `describe-function' when pushing the corresponding URL button."
   (describe-function
@@ -5669,6 +5926,15 @@ address, `ask' if unsure and `invalid' if the string is invalid."
    (intern
     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
 
    (intern
     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
 
+(defun gnus-button-handle-symbol (url)
+"Display help on variable or function.
+Calls `describe-variable' or `describe-function'."
+  (let ((sym (intern url)))
+    (cond
+     ((fboundp sym) (describe-function sym))
+     ((boundp sym) (describe-variable sym))
+     (t (gnus-message 3 "`%s' is not a known function of variable." url)))))
+
 (defun gnus-button-handle-describe-key (url)
   "Call `describe-key' when pushing the corresponding URL button."
   (let* ((key-string
 (defun gnus-button-handle-describe-key (url)
   "Call `describe-key' when pushing the corresponding URL button."
   (let* ((key-string
@@ -5699,6 +5965,15 @@ address, `ask' if unsure and `invalid' if the string is invalid."
    (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
 
    (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
 
+(defun gnus-button-handle-library (url)
+  "Call `locate-library' when pushing the corresponding URL button."
+  (gnus-message 9 "url=`%s'" url)
+  (let* ((lib (locate-library url))
+        (file (gnus-replace-in-string (or lib "") "\.elc" ".el")))
+    (if (not lib)
+       (gnus-message 1 "Cannot locale library `%s'." url)
+      (find-file-read-only file))))
+
 (defun gnus-button-handle-ctan (url)
   "Call `browse-url' when pushing a CTAN URL button."
   (funcall
 (defun gnus-button-handle-ctan (url)
   "Call `browse-url' when pushing a CTAN URL button."
   (funcall
@@ -5711,7 +5986,7 @@ address, `ask' if unsure and `invalid' if the string is invalid."
   "*Integer that says how many TeX-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
   "*Integer that says how many TeX-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
-specifific groups.  Setting it higher in TeX groups is probably a good idea.
+specific groups.  Setting it higher in TeX groups is probably a good idea.
 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
 how to set variables in specific groups."
   :group 'gnus-article-buttons
 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
 how to set variables in specific groups."
   :group 'gnus-article-buttons
@@ -5722,7 +5997,7 @@ how to set variables in specific groups."
   "*Integer that says how many man-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
   "*Integer that says how many man-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
-specifific groups.  Setting it higher in Unix groups is probably a good idea.
+specific groups.  Setting it higher in Unix groups is probably a good idea.
 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
 how to set variables in specific groups."
   :group 'gnus-article-buttons
 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
 how to set variables in specific groups."
   :group 'gnus-article-buttons
@@ -5733,43 +6008,81 @@ how to set variables in specific groups."
   "*Integer that says how many emacs-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
   "*Integer that says how many emacs-related buttons Gnus will show.
 The higher the number, the more buttons will appear and the more false
 positives are possible.  Note that you can set this variable local to
-specifific groups.  Setting it higher in Emacs or Gnus related groups is
+specific groups.  Setting it higher in Emacs or Gnus related groups is
 probably a good idea.  See Info node `(gnus)Group Parameters' and the variable
 `gnus-parameters' on how to set variables in specific groups."
   :group 'gnus-article-buttons
   :link '(custom-manual "(gnus)Group Parameters")
   :type 'integer)
 
 probably a good idea.  See Info node `(gnus)Group Parameters' and the variable
 `gnus-parameters' on how to set variables in specific groups."
   :group 'gnus-article-buttons
   :link '(custom-manual "(gnus)Group Parameters")
   :type 'integer)
 
-(defcustom gnus-button-mail-level 5
-  "*Integer that says how many buttons for message IDs or mail addresses will appear.
+(defcustom gnus-button-message-level 5
+  "*Integer that says how many buttons for news or mail messages will appear.
+The higher the number, the more buttons will appear and the more false
+positives are possible."
+  ;; mail addresses, MIDs, URLs for news, ...
+  :group 'gnus-article-buttons
+  :type 'integer)
+
+(defcustom gnus-button-browse-level 5
+  "*Integer that says how many buttons for browsing will appear.
 The higher the number, the more buttons will appear and the more false
 positives are possible."
 The higher the number, the more buttons will appear and the more false
 positives are possible."
+  ;; stuff handled by `browse-url' or `gnus-button-embedded-url'
   :group 'gnus-article-buttons
   :type 'integer)
 
 (defcustom gnus-button-alist
   '(("<\\(url:[>\n\t ]*?\\)?\\(nntp\\|news\\):[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
   :group 'gnus-article-buttons
   :type 'integer)
 
 (defcustom gnus-button-alist
   '(("<\\(url:[>\n\t ]*?\\)?\\(nntp\\|news\\):[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
-     0 t gnus-button-handle-news 3)
+     0 (>= gnus-button-message-level 0) gnus-button-handle-news 3)
     ("\\b\\(nntp\\|news\\):\\([^>\n\t ]*@[^>)!;:,\n\t ]*\\)" 0 t
      gnus-button-handle-news 2)
     ("\\b\\(nntp\\|news\\):\\([^>\n\t ]*@[^>)!;:,\n\t ]*\\)" 0 t
      gnus-button-handle-news 2)
-    ("\\(\\b<\\(url:[>\n\t ]*\\)?news:[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
-     1 t
-     gnus-button-fetch-group 4)
-    ("\\bnews:\\(//\\)?\\([^'\">\n\t ]+\\)" 0 t gnus-button-fetch-group 2)
-    ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)" 2
-     t gnus-button-message-id 3)
-    ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>" 0 t gnus-url-mailto 2)
-    ("mailto:\\([-a-z.@_+0-9%=?]+\\)" 0 t gnus-url-mailto 1)
-    ("\\bmailto:\\([^ \n\t]+\\)" 0 t gnus-url-mailto 1)
+    ("\\(\\b<\\(url:[>\n\t ]*\\)?\\(nntp\\|news\\):[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
+     1 (>= gnus-button-message-level 0) gnus-button-fetch-group 5)
+    ("\\b\\(nntp\\|news\\):\\(//\\)?\\([^'\">\n\t ]+\\)"
+     0 (>= gnus-button-message-level 0) gnus-button-fetch-group 3)
+    ;; RFC 2392 (Don't allow `/' in domain part --> CID)
+    ("\\bmid:\\(//\\)?\\([^'\">\n\t ]+@[^'\">\n\t /]+\\)"
+     0 (>= gnus-button-message-level 0) gnus-button-message-id 2)
+    ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)"
+     2 (>= gnus-button-message-level 0) gnus-button-message-id 3)
+    ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>"
+     0 (>= gnus-button-message-level 0) gnus-url-mailto 2)
+    ;; RFC 2368 (The mailto URL scheme)
+    ("mailto:\\([-a-z.@_+0-9%=?&]+\\)"
+     0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
+    ("\\bmailto:\\([^ \n\t]+\\)"
+     0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
     ;; CTAN
     ;; CTAN
-    ("\\bCTAN:[ \t\n]*\\([^>)!;:,\n\t ]*\\)" 0 (>= gnus-button-tex-level 1)
-     gnus-button-handle-ctan 1)
-    ;; This is info
-    ("\\binfo:\\(//\\)?\\([^'\">\n\t ]+\\)" 0
-     (>= gnus-button-emacs-level 1) gnus-button-handle-info 2)
+    ((concat "\\bCTAN:[ \t\n]?[^>)!;:,'\n\t ]*\\("
+            gnus-button-ctan-directory-regexp
+            "[^][>)!;:,'\n\t ]+\\)")
+     0 (>= gnus-button-tex-level 1) gnus-button-handle-ctan 1)
+    ((concat "\\btex-archive/\\("
+            gnus-button-ctan-directory-regexp
+            "/[-_.a-z0-9/]+[-_./a-z0-9]+[/a-z0-9]\\)")
+     1 (>= gnus-button-tex-level 6) gnus-button-handle-ctan 1)
+    ((concat
+      "\\b\\("
+      gnus-button-ctan-directory-regexp
+      "/[-_.a-z0-9]+/[-_./a-z0-9]+[/a-z0-9]\\)")
+     1 (>= gnus-button-tex-level 8) gnus-button-handle-ctan 1)
+    ;; This is info (home-grown style) <info://foo/bar+baz>
+    ("\\binfo://\\([^'\">\n\t ]+\\)"
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
+    ;; Info GNOME style <info:foo#bar_baz>
+    ("\\binfo:\\([^('\n\t\r \"><][^'\n\t\r \"><]*\\)"
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-gnome 1)
+    ;; Info KDE style <info:(foo)bar baz>
+    ("<\\(info:\\(([^)]+)[^>\n\r]*\\)\\)>"
+     1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
+    ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
+     (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
+    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+     ;; Info links like `C-h i d m CC Mode RET'
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 2)
     ;; This is custom
     ;; This is custom
-    ("\\bcustom:\\(//\\)?\\([^'\">\n\t ]+\\)" 0
-     (>= gnus-button-emacs-level 5) gnus-button-handle-custom 2)
+    ("\\bcustom:\\(//\\)?\\([^'\">\n\t ]+\\)"
+     (>= gnus-button-emacs-level 5) gnus-button-handle-custom 2)
     ("M-x[ \t\n]customize-[^ ]+[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1)
     ;; Emacs help commands
     ("M-x[ \t\n]customize-[^ ]+[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1)
     ;; Emacs help commands
@@ -5782,44 +6095,66 @@ positives are possible."
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
     ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
     ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
+    ;; The following entries may lead to many false positives so don't enable
+    ;; them by default (use a high button level):
+    ("/\\([a-z][-a-z0-9]+\\.el\\)\\>"
+     1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
+    ("`\\([a-z][-a-z0-9]+\\.el\\)'"
+     1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
+    ("`\\([a-z][a-z0-9]+-[a-z]+-[-a-z]+\\|\\(gnus\\|message\\)-[-a-z]+\\)'"
+     0 (>= gnus-button-emacs-level 8) gnus-button-handle-symbol 1)
+    ("`\\([a-z][a-z0-9]+-[a-z]+\\)'"
+     0 (>= gnus-button-emacs-level 9) gnus-button-handle-symbol 1)
+    ("(setq[ \t\n]+\\([a-z][a-z0-9]+-[-a-z0-9]+\\)[ \t\n]+.+)"
+     1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-variable 1)
+    ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+     1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-function 1)
     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
-    ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'" 1
+    ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'"
      ;; Unlike the other regexps we really have to require quoting
      ;; here to determine where it ends.
      ;; Unlike the other regexps we really have to require quoting
      ;; here to determine where it ends.
-     (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
-    ;; This is how URLs _should_ be embedded in text...
-    ("<URL: *\\([^<>]*\\)>" 1 t gnus-button-embedded-url 1)
+     1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
+    ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
+    ("<URL: *\\([^<>]*\\)>"
+     1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
+    ;; RFC 2396 (2.4.3., delims) ...
+    ("\"URL: *\\([^\"]*\\)\""
+     1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
+    ;; RFC 2396 (2.4.3., delims) ...
+    ("\"URL: *\\([^\"]*\\)\""
+     1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; Raw URLs.
     ;; Raw URLs.
-    (gnus-button-url-regexp 0 t browse-url 0)
+    (gnus-button-url-regexp
+     0 (>= gnus-button-browse-level 0) browse-url 0)
     ;; man pages
     ;; man pages
-    ("\\b\\([a-z][a-z]+\\)([1-9])\\W" 0
-     (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
+    ("\\b\\([a-z][a-z]+\\)([1-9])\\W"
+     (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
      gnus-button-handle-man 1)
     ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
      gnus-button-handle-man 1)
     ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
-    ("\\b\\([a-z][-_.a-z0-9]+\\)([1-9])\\W" 0
-     (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
+    ("\\b\\([a-z][-_.a-z0-9]+\\)([1-9])\\W"
+     (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
      gnus-button-handle-man 1)
     ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
      gnus-button-handle-man 1)
     ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
-    ;; SoWWWAnchor(3iv), XSelectInput(3X11)
-    ("\\b\\([a-z][-_.:a-z0-9]+\\)([1-9][X1a-z]*)\\W" 0
-     (>= gnus-button-man-level 5) gnus-button-handle-man 1)
+    ;; SoWWWAnchor(3iv), XSelectInput(3X11), X(1), X(7)
+    ("\\b\\([a-z][-+_.:a-z0-9]+\\)([1-9][X1a-z]*)\\W\\|\\b\\(X\\)([1-9])\\W"
+     (>= gnus-button-man-level 5) gnus-button-handle-man 1)
     ;; MID or mail: To avoid too many false positives we don't try to catch
     ;; all kind of allowed MIDs or mail addresses.  Domain part must contain
     ;; at least one dot.  TLD must contain two or three chars or be a know TLD
     ;; (info|name|...).  Put this entry near the _end_ of `gnus-button-alist'
     ;; so that non-ambiguous entries (see above) match first.
     (gnus-button-mid-or-mail-regexp
     ;; MID or mail: To avoid too many false positives we don't try to catch
     ;; all kind of allowed MIDs or mail addresses.  Domain part must contain
     ;; at least one dot.  TLD must contain two or three chars or be a know TLD
     ;; (info|name|...).  Put this entry near the _end_ of `gnus-button-alist'
     ;; so that non-ambiguous entries (see above) match first.
     (gnus-button-mid-or-mail-regexp
-     0 (>= gnus-button-mail-level 5) gnus-button-handle-mid-or-mail 1))
+     0 (>= gnus-button-message-level 5) gnus-button-handle-mid-or-mail 1))
   "*Alist of regexps matching buttons in article bodies.
 
 Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
 REGEXP: is the string (case insensitive) matching text around the button (can
   "*Alist of regexps matching buttons in article bodies.
 
 Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
 REGEXP: is the string (case insensitive) matching text around the button (can
-also be lisp expression evaluating to a string),
+also be Lisp expression evaluating to a string),
 BUTTON: is the number of the regexp grouping actually matching the button,
 BUTTON: is the number of the regexp grouping actually matching the button,
-FORM: is a lisp expression which must eval to true for the button to
+FORM: is a Lisp expression which must eval to true for the button to
 be added,
 CALLBACK: is the function to call when the user push this button, and each
 PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
 be added,
 CALLBACK: is the function to call when the user push this button, and each
 PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
@@ -5827,7 +6162,7 @@ PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
 CALLBACK can also be a variable, in that case the value of that
 variable it the real callback function."
   :group 'gnus-article-buttons
 CALLBACK can also be a variable, in that case the value of that
 variable it the real callback function."
   :group 'gnus-article-buttons
-  :type '(repeat (list (choice regexp variable)
+  :type '(repeat (list (choice regexp variable sexp)
                       (integer :tag "Button")
                       (sexp :tag "Form")
                       (function :tag "Callback")
                       (integer :tag "Button")
                       (sexp :tag "Form")
                       (function :tag "Callback")
@@ -5836,17 +6171,22 @@ variable it the real callback function."
                               (integer :tag "Regexp group")))))
 
 (defcustom gnus-header-button-alist
                               (integer :tag "Regexp group")))))
 
 (defcustom gnus-header-button-alist
-  '(("^\\(References\\|Message-I[Dd]\\):" "<[^<>]+>"
-     0 t gnus-button-message-id 0)
-    ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$" 1 t gnus-button-reply 1)
+  '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
+     0 (>= gnus-button-message-level 0) gnus-button-message-id 0)
+    ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$"
+     1 (>= gnus-button-message-level 0) gnus-button-reply 1)
     ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
     ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
-     0 t gnus-button-mailto 0)
-    ("^X-[Uu][Rr][Ll]:" gnus-button-url-regexp 0 t browse-url 0)
-    ("^Subject:" gnus-button-url-regexp 0 t browse-url 0)
-    ("^[^:]+:" gnus-button-url-regexp 0 t browse-url 0)
-    ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?]+\\)" 0 t gnus-url-mailto 1)
-    ("^[^:]+:" "\\(<\\(url: \\)?news:\\([^>\n ]*\\)>\\)" 1 t
-     gnus-button-message-id 3))
+     0 (>= gnus-button-message-level 0) gnus-button-mailto 0)
+    ("^X-[Uu][Rr][Ll]:" gnus-button-url-regexp
+     0 (>= gnus-button-browse-level 0) browse-url 0)
+    ("^Subject:" gnus-button-url-regexp
+     0 (>= gnus-button-browse-level 0) browse-url 0)
+    ("^[^:]+:" gnus-button-url-regexp
+     0 (>= gnus-button-browse-level 0) browse-url 0)
+    ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&]+\\)"
+     0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
+    ("^[^:]+:" "\\(<\\(url: \\)?\\(nntp\\|news\\):\\([^>\n ]*\\)>\\)"
+     1 (>= gnus-button-message-level 0) gnus-button-message-id 4))
   "*Alist of headers and regexps to match buttons in article heads.
 
 This alist is very similar to `gnus-button-alist', except that each
   "*Alist of headers and regexps to match buttons in article heads.
 
 This alist is very similar to `gnus-button-alist', except that each
@@ -5859,7 +6199,7 @@ HEADER is a regexp to match a header.  For a fuller explanation, see
   :group 'gnus-article-buttons
   :group 'gnus-article-headers
   :type '(repeat (list (regexp :tag "Header")
   :group 'gnus-article-buttons
   :group 'gnus-article-headers
   :type '(repeat (list (regexp :tag "Header")
-                      regexp
+                      (choice regexp variable)
                       (integer :tag "Button")
                       (sexp :tag "Form")
                       (function :tag "Callback")
                       (integer :tag "Button")
                       (sexp :tag "Form")
                       (function :tag "Callback")
@@ -6190,18 +6530,47 @@ specified by `gnus-button-alist'."
   "Fetch a man page."
   (funcall gnus-button-man-handler url))
 
   "Fetch a man page."
   (funcall gnus-button-man-handler url))
 
-(defun gnus-button-handle-info (url)
+(defun gnus-button-handle-info-url (url)
   "Fetch an info URL."
   "Fetch an info URL."
-  (if (string-match
-       "^\\([^:/]+\\)?/\\(.*\\)"
-       url)
+  (setq url (mm-subst-char-in-string ?+ ?\  url))
+  (cond
+   ((string-match "^\\([^:/]+\\)?/\\(.*\\)" url)
+    (gnus-info-find-node
+     (concat "(" (or (gnus-url-unhex-string (match-string 1 url))
+                    "Gnus")
+            ")" (gnus-url-unhex-string (match-string 2 url)))))
+   ((string-match "([^)\"]+)[^\"]+" url)
+    (setq url
+         (gnus-replace-in-string
+          (gnus-replace-in-string url "[\n\t ]+" " ") "\"" ""))
+    (gnus-info-find-node url))
+   (t (error "Can't parse %s" url))))
+
+(defun gnus-button-handle-info-url-gnome (url)
+  "Fetch GNOME style info URL."
+  (setq url (mm-subst-char-in-string ?_ ?\  url))
+  (if (string-match "\\([^#]+\\)#?\\(.*\\)" url)
       (gnus-info-find-node
       (gnus-info-find-node
-       (concat "(" (or (gnus-url-unhex-string (match-string 1 url))
-                      "Gnus")
+       (concat "("
+              (gnus-url-unhex-string 
+                (match-string 1 url))
               ")"
               ")"
-              (gnus-url-unhex-string (match-string 2 url))))
+              (or (gnus-url-unhex-string 
+                   (match-string 2 url))
+                  "Top")))
     (error "Can't parse %s" url)))
 
     (error "Can't parse %s" url)))
 
+(defun gnus-button-handle-info-url-kde (url)
+  "Fetch KDE style info URL."
+  (gnus-info-find-node (gnus-url-unhex-string url)))
+
+(defun gnus-button-handle-info-keystrokes (url)
+  "Call `info' when pushing the corresponding URL button."
+  ;; For links like `C-h i d m gnus RET', `C-h i d m CC Mode RET'.
+  (info)
+  (Info-directory)
+  (Info-menu url))
+
 (defun gnus-button-message-id (message-id)
   "Fetch MESSAGE-ID."
   (save-excursion
 (defun gnus-button-message-id (message-id)
   "Fetch MESSAGE-ID."
   (save-excursion
@@ -6251,12 +6620,14 @@ specified by `gnus-button-alist'."
   (when (string-match "mailto:/*\\(.*\\)" url)
     (setq url (substring url (match-beginning 1) nil)))
   (let (to args subject func)
   (when (string-match "mailto:/*\\(.*\\)" url)
     (setq url (substring url (match-beginning 1) nil)))
   (let (to args subject func)
-    (if (string-match (regexp-quote "?") url)
-       (setq to (gnus-url-unhex-string (substring url 0 (match-beginning 0)))
-             args (gnus-url-parse-query-string
-                   (substring url (match-end 0) nil) t))
-      (setq to (gnus-url-unhex-string url)))
-    (setq args (cons (list "to" to) args)
+    (setq args (gnus-url-parse-query-string
+               (if (string-match "^\\?" url)
+                   (substring url 1)
+                 (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
+                     (concat "to=" (match-string 1 url) "&"
+                             (match-string 2 url))
+                   (concat "to=" url)))
+               t)
          subject (cdr-safe (assoc "subject" args)))
     (gnus-msg-mail)
     (while args
          subject (cdr-safe (assoc "subject" args)))
     (gnus-msg-mail)
     (while args
@@ -6264,7 +6635,9 @@ specified by `gnus-button-alist'."
       (if (fboundp func)
          (funcall func)
        (message-position-on-field (caar args)))
       (if (fboundp func)
          (funcall func)
        (message-position-on-field (caar args)))
-      (insert (mapconcat 'identity (cdar args) ", "))
+      (insert (gnus-replace-in-string
+              (mapconcat 'identity (reverse (cdar args)) ", ")
+              "\r\n" "\n" t))
       (setq args (cdr args)))
     (if subject
        (message-goto-body)
       (setq args (cdr args)))
     (if subject
        (message-goto-body)
@@ -6298,7 +6671,10 @@ specified by `gnus-button-alist'."
         gnus-callback gnus-article-button-prev-page
         article-type annotation))
     (widget-convert-button
         gnus-callback gnus-article-button-prev-page
         article-type annotation))
     (widget-convert-button
-     'link b (point)
+     'link b (if (bolp)
+                ;; Exclude a newline.
+                (1- (point))
+              (point))
      :action 'gnus-button-prev-page
      :button-keymap gnus-prev-page-map)))
 
      :action 'gnus-button-prev-page
      :button-keymap gnus-prev-page-map)))
 
@@ -6345,7 +6721,10 @@ specified by `gnus-button-alist'."
                          gnus-callback gnus-article-button-next-page
                          article-type annotation))
     (widget-convert-button
                          gnus-callback gnus-article-button-next-page
                          article-type annotation))
     (widget-convert-button
-     'link b (point)
+     'link b (if (bolp)
+                ;; Exclude a newline.
+                (1- (point))
+              (point))
      :action 'gnus-button-next-page
      :button-keymap gnus-next-page-map)))
 
      :action 'gnus-button-next-page
      :button-keymap gnus-next-page-map)))
 
@@ -6371,7 +6750,7 @@ specified by `gnus-button-alist'."
 
 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
 is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
 
 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
 is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
-\(REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
+\(REGEXP . FUNCTION), FUNCTION will be only apply to the newsgroups
 whose names match REGEXP.
 
 For example:
 whose names match REGEXP.
 
 For example:
@@ -6518,7 +6897,7 @@ For example:
                                   (search-forward field nil t))
                                 (prog2
                                     (message-narrow-to-field)
                                   (search-forward field nil t))
                                 (prog2
                                     (message-narrow-to-field)
-                                    (buffer-substring (point-min) (point-max))
+                                    (buffer-string)
                                   (delete-region (point-min) (point-max))
                                   (widen))))
                          '("Content-Type:" "Content-Transfer-Encoding:"
                                   (delete-region (point-min) (point-max))
                                   (widen))))
                          '("Content-Type:" "Content-Transfer-Encoding:"
@@ -6687,7 +7066,10 @@ For example:
         gnus-mime-details ,gnus-mime-security-button-pressed
         article-type annotation
         gnus-data ,handle))
         gnus-mime-details ,gnus-mime-security-button-pressed
         article-type annotation
         gnus-data ,handle))
-    (setq e (point))
+    (setq e (if (bolp)
+               ;; Exclude a newline.
+               (1- (point))
+             (point)))
     (widget-convert-button
      'link b e
      :mime-handle handle
     (widget-convert-button
      'link b e
      :mime-handle handle