Return the group/article number, so that Gnus `^' works as expected.
[gnus] / lisp / gnus-art.el
index 80e2cd2..7e51abb 100644 (file)
@@ -1,17 +1,17 @@
 ;;; gnus-art.el --- article mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 ;;; gnus-art.el --- article mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;; Code:
 
 
 ;;; Commentary:
 
 ;;; Code:
 
+;; For Emacs < 22.2.
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
 (eval-when-compile
-  (require 'cl)
-  (defvar tool-bar-map)
-  (defvar w3m-minor-mode-map))
+  (require 'cl))
+(defvar tool-bar-map)
+(defvar w3m-minor-mode-map)
 
 (require 'gnus)
 ;; Avoid the "Recursive load suspected" error in Emacs 21.1.
 
 (require 'gnus)
 ;; Avoid the "Recursive load suspected" error in Emacs 21.1.
@@ -175,12 +176,15 @@ If `gnus-visible-headers' is non-nil, this variable will be ignored."
   "*All headers that do not match this regexp will be hidden.
 This variable can also be a list of regexp of headers to remain visible.
 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
   "*All headers that do not match this regexp will be hidden.
 This variable can also be a list of regexp of headers to remain visible.
 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
-  :type '(repeat :value-to-internal (lambda (widget value)
-                                     (custom-split-regexp-maybe value))
-                :match (lambda (widget value)
-                         (or (stringp value)
-                             (widget-editable-list-match widget value)))
-                regexp)
+  :type '(choice
+         (repeat :value-to-internal (lambda (widget value)
+                                      (custom-split-regexp-maybe value))
+                 :match (lambda (widget value)
+                          (or (stringp value)
+                              (widget-editable-list-match widget value)))
+                 regexp)
+         (const :tag "Use gnus-ignored-headers" nil)
+         regexp)
   :group 'gnus-article-hiding)
 
 (defcustom gnus-sorted-header-list
   :group 'gnus-article-hiding)
 
 (defcustom gnus-sorted-header-list
@@ -533,6 +537,7 @@ that the symbol of the saver function, which is specified by
   :group 'gnus-article-saving
   :type 'regexp)
 
   :group 'gnus-article-saving
   :type 'regexp)
 
+;; Note that "Rmail format" is mbox since Emacs 23, but Babyl before.
 (defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
   "A function to save articles in your favourite format.
 The function will be called by way of the `gnus-summary-save-article'
 (defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
   "A function to save articles in your favourite format.
 The function will be called by way of the `gnus-summary-save-article'
@@ -548,13 +553,15 @@ Gnus provides the following functions:
 * gnus-summary-save-in-vm (use VM's folder format)
 * gnus-summary-write-to-file (article format -- overwrite)
 * gnus-summary-write-body-to-file (article body -- overwrite)
 * gnus-summary-save-in-vm (use VM's folder format)
 * gnus-summary-write-to-file (article format -- overwrite)
 * gnus-summary-write-body-to-file (article body -- overwrite)
+* gnus-summary-save-in-pipe (article format)
 
 The symbol of each function may have the following properties:
 
 * :decode
 The value non-nil means save decoded articles.  This is meaningful
 only with `gnus-summary-save-in-file', `gnus-summary-save-body-in-file',
 
 The symbol of each function may have the following properties:
 
 * :decode
 The value non-nil means save decoded articles.  This is meaningful
 only with `gnus-summary-save-in-file', `gnus-summary-save-body-in-file',
-`gnus-summary-write-to-file', and `gnus-summary-write-body-to-file'.
+`gnus-summary-write-to-file', `gnus-summary-write-body-to-file', and
+`gnus-summary-save-in-pipe'.
 
 * :function
 The value specifies an alternative function which appends, not
 
 * :function
 The value specifies an alternative function which appends, not
@@ -577,6 +584,7 @@ headers should be saved."
                (function-item gnus-summary-save-in-vm)
                (function-item gnus-summary-write-to-file)
                (function-item gnus-summary-write-body-to-file)
                (function-item gnus-summary-save-in-vm)
                (function-item gnus-summary-write-to-file)
                (function-item gnus-summary-write-body-to-file)
+               (function-item gnus-summary-save-in-pipe)
                (function)))
 
 (defcustom gnus-article-save-coding-system
                (function)))
 
 (defcustom gnus-article-save-coding-system
@@ -715,12 +723,12 @@ The following additional specs are available:
 (defcustom gnus-copy-article-ignored-headers nil
   "List of headers to be removed when copying an article.
 Each element is a regular expression."
 (defcustom gnus-copy-article-ignored-headers nil
   "List of headers to be removed when copying an article.
 Each element is a regular expression."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type '(repeat regexp)
   :group 'gnus-article-various)
 
   :type '(repeat regexp)
   :group 'gnus-article-various)
 
-(make-obsolete-variable 'gnus-article-hide-pgp-hook
-                       "This variable is obsolete in Gnus 5.10.")
+(make-obsolete-variable 'gnus-article-hide-pgp-hook nil
+                       "Gnus 5.10 (Emacs-22.1)")
 
 (defface gnus-button
   '((t (:weight bold)))
 
 (defface gnus-button
   '((t (:weight bold)))
@@ -758,6 +766,7 @@ Obsolete; use the face `gnus-signature' for customizations instead."
   :group 'gnus-article-signature)
 ;; backward-compatibility alias
 (put 'gnus-signature-face 'face-alias 'gnus-signature)
   :group 'gnus-article-signature)
 ;; backward-compatibility alias
 (put 'gnus-signature-face 'face-alias 'gnus-signature)
+(put 'gnus-signature-face 'obsolete-face "22.1")
 
 (defface gnus-header-from
   '((((class color)
 
 (defface gnus-header-from
   '((((class color)
@@ -773,6 +782,7 @@ Obsolete; use the face `gnus-signature' for customizations instead."
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-from-face 'face-alias 'gnus-header-from)
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-from-face 'face-alias 'gnus-header-from)
+(put 'gnus-header-from-face 'obsolete-face "22.1")
 
 (defface gnus-header-subject
   '((((class color)
 
 (defface gnus-header-subject
   '((((class color)
@@ -788,6 +798,7 @@ Obsolete; use the face `gnus-signature' for customizations instead."
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-subject-face 'face-alias 'gnus-header-subject)
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-subject-face 'face-alias 'gnus-header-subject)
+(put 'gnus-header-subject-face 'obsolete-face "22.1")
 
 (defface gnus-header-newsgroups
   '((((class color)
 
 (defface gnus-header-newsgroups
   '((((class color)
@@ -805,6 +816,7 @@ articles."
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-newsgroups-face 'face-alias 'gnus-header-newsgroups)
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-newsgroups-face 'face-alias 'gnus-header-newsgroups)
+(put 'gnus-header-newsgroups-face 'obsolete-face "22.1")
 
 (defface gnus-header-name
   '((((class color)
 
 (defface gnus-header-name
   '((((class color)
@@ -820,6 +832,7 @@ articles."
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-name-face 'face-alias 'gnus-header-name)
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-name-face 'face-alias 'gnus-header-name)
+(put 'gnus-header-name-face 'obsolete-face "22.1")
 
 (defface gnus-header-content
   '((((class color)
 
 (defface gnus-header-content
   '((((class color)
@@ -834,6 +847,7 @@ articles."
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-content-face 'face-alias 'gnus-header-content)
   :group 'gnus-article-highlight)
 ;; backward-compatibility alias
 (put 'gnus-header-content-face 'face-alias 'gnus-header-content)
+(put 'gnus-header-content-face 'obsolete-face "22.1")
 
 (defcustom gnus-header-face-alist
   '(("From" nil gnus-header-from)
 
 (defcustom gnus-header-face-alist
   '(("From" nil gnus-header-from)
@@ -882,7 +896,7 @@ See the manual for the valid properties for various image types.
 Currently, `pbm' is used for X-Face images and `png' is used for Face
 images in Emacs.  Only the `:face' property is effective on the `xface'
 image type in XEmacs if it is built with the libcompface library."
 Currently, `pbm' is used for X-Face images and `png' is used for Face
 images in Emacs.  Only the `:face' property is effective on the `xface'
 image type in XEmacs if it is built with the libcompface library."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-headers
   :type '(repeat (cons :format "%v" (symbol :tag "Image type") plist)))
 
   :group 'gnus-article-headers
   :type '(repeat (cons :format "%v" (symbol :tag "Image type") plist)))
 
@@ -1054,7 +1068,7 @@ used."
 When 0, point will be placed on the same part as before.  When
 positive (negative), move point forward (backwards) this many
 parts.  When nil, redisplay article."
 When 0, point will be placed on the same part as before.  When
 positive (negative), move point forward (backwards) this many
 parts.  When nil, redisplay article."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-mime
   :type '(choice (const nil :tag "Redisplay article.")
                 (const 1 :tag "Next part.")
   :group 'gnus-article-mime
   :type '(choice (const nil :tag "Redisplay article.")
                 (const 1 :tag "Next part.")
@@ -1116,10 +1130,7 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-buttonize-head 'highlight t)
 
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-buttonize-head 'highlight t)
 
-(defcustom gnus-treat-emphasize
-  (and (or window-system
-          (featurep 'xemacs))
-       50000)
+(defcustom gnus-treat-emphasize 50000
   "Emphasize text.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
   "Emphasize text.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
@@ -1212,8 +1223,8 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   :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
-                       "This option is obsolete in Gnus 5.10.")
+(make-obsolete-variable 'gnus-treat-strip-pgp nil
+                       "Gnus 5.10 (Emacs 22.1)")
 
 (defcustom gnus-treat-strip-pem nil
   "Strip PEM signatures.
 
 (defcustom gnus-treat-strip-pem nil
   "Strip PEM signatures.
@@ -1362,7 +1373,7 @@ If it is a regexp, only long headers matching this regexp are unfolded.
 If it is t, all long headers are unfolded.
 
 This variable has no effect if `gnus-treat-unfold-headers' is nil."
 If it is t, all long headers are unfolded.
 
 This variable has no effect if `gnus-treat-unfold-headers' is nil."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-treat
   :type '(choice (const nil)
                 (const :tag "all" t)
   :group 'gnus-article-treat
   :type '(choice (const nil)
                 (const :tag "all" t)
@@ -1404,15 +1415,19 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   :type gnus-article-treat-custom)
 
 (make-obsolete-variable 'gnus-treat-display-xface
   :type gnus-article-treat-custom)
 
 (make-obsolete-variable 'gnus-treat-display-xface
-                       'gnus-treat-display-x-face)
+                       'gnus-treat-display-x-face "22.1")
 
 (defcustom gnus-treat-display-x-face
   (and (not noninteractive)
        (gnus-image-type-available-p 'xbm)
        (if (featurep 'xemacs)
           (featurep 'xface)
 
 (defcustom gnus-treat-display-x-face
   (and (not noninteractive)
        (gnus-image-type-available-p 'xbm)
        (if (featurep 'xemacs)
           (featurep 'xface)
-        (and (string-match "^0x" (shell-command-to-string "uncompface"))
-             (executable-find "icontopbm")))
+        (condition-case nil
+             (and (string-match "^0x" (shell-command-to-string "uncompface"))
+                  (executable-find "icontopbm"))
+           ;; shell-command-to-string may signal an error, e.g. if
+           ;; shell-file-name is not found.
+           (error nil)))
        'head)
   "Display X-Face headers.
 Valid values are nil and `head'.
        'head)
   "Display X-Face headers.
 Valid values are nil and `head'.
@@ -1449,7 +1464,7 @@ See Info node `(gnus)Customizing Articles' and Info node
   "Display Face headers.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles' and Info
   "Display Face headers.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles' and Info
-node `(gnus)X-Face' for details."
+node `(gnus)Face' for details."
   :group 'gnus-article-treat
   :version "22.1"
   :link '(custom-manual "(gnus)Customizing Articles")
   :group 'gnus-article-treat
   :version "22.1"
   :link '(custom-manual "(gnus)Customizing Articles")
@@ -1518,11 +1533,12 @@ node `(gnus)Picons' for details."
 (put 'gnus-treat-newsgroups-picon 'highlight t)
 
 (defcustom gnus-treat-body-boundary
 (put 'gnus-treat-newsgroups-picon 'highlight t)
 
 (defcustom gnus-treat-body-boundary
-  (if (and (eq window-system 'x)
-          (or gnus-treat-newsgroups-picon
-              gnus-treat-mail-picon
-              gnus-treat-from-picon))
-      'head nil)
+  (if (or gnus-treat-newsgroups-picon
+         gnus-treat-mail-picon
+         gnus-treat-from-picon)
+      ;; If there's much decoration, the user might prefer a boundery.
+      'head
+    nil)
   "Draw a boundary at the end of the headers.
 Valid values are nil and `head'.
 See Info node `(gnus)Customizing Articles' for details."
   "Draw a boundary at the end of the headers.
 Valid values are nil and `head'.
 See Info node `(gnus)Customizing Articles' for details."
@@ -1699,11 +1715,6 @@ Initialized from `text-mode-syntax-table.")
 
 (defvar gnus-save-article-buffer nil)
 
 
 (defvar gnus-save-article-buffer nil)
 
-(defvar gnus-article-mode-line-format-alist
-  (nconc '((?w (gnus-article-wash-status) ?s)
-          (?m (gnus-article-mime-part-status) ?s))
-        gnus-summary-mode-line-format-alist))
-
 (defvar gnus-number-of-articles-to-be-saved nil)
 
 (defvar gnus-inhibit-hiding nil)
 (defvar gnus-number-of-articles-to-be-saved nil)
 
 (defvar gnus-inhibit-hiding nil)
@@ -1713,8 +1724,7 @@ Initialized from `text-mode-syntax-table.")
 ;;; Macros for dealing with the article buffer.
 
 (defmacro gnus-with-article-headers (&rest forms)
 ;;; Macros for dealing with the article buffer.
 
 (defmacro gnus-with-article-headers (&rest forms)
-  `(save-excursion
-     (set-buffer gnus-article-buffer)
+  `(with-current-buffer gnus-article-buffer
      (save-restriction
        (let ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
      (save-restriction
        (let ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
@@ -1726,8 +1736,7 @@ Initialized from `text-mode-syntax-table.")
 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
 
 (defmacro gnus-with-article-buffer (&rest forms)
 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
 
 (defmacro gnus-with-article-buffer (&rest forms)
-  `(save-excursion
-     (set-buffer gnus-article-buffer)
+  `(with-current-buffer gnus-article-buffer
      (let ((inhibit-read-only t))
        ,@forms)))
 
      (let ((inhibit-read-only t))
        ,@forms)))
 
@@ -2224,11 +2233,11 @@ unfolded."
        (mail-header-fold-field)
        (goto-char (point-max))))))
 
        (mail-header-fold-field)
        (goto-char (point-max))))))
 
-(defcustom gnus-article-truncate-lines default-truncate-lines
+(defcustom gnus-article-truncate-lines (default-value 'truncate-lines)
   "Value of `truncate-lines' in Gnus Article buffer.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
   "Value of `truncate-lines' in Gnus Article buffer.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article
   ;; :link '(custom-manual "(gnus)Customizing Articles")
   :type 'boolean)
   :group 'gnus-article
   ;; :link '(custom-manual "(gnus)Customizing Articles")
   :type 'boolean)
@@ -2237,7 +2246,7 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   "Toggle whether to fold or truncate long lines in article the buffer.
 If ARG is non-nil and not a number, toggle
 `gnus-article-truncate-lines' too.  If ARG is a number, truncate
   "Toggle whether to fold or truncate long lines in article the buffer.
 If ARG is non-nil and not a number, toggle
 `gnus-article-truncate-lines' too.  If ARG is a number, truncate
-long lines iff arg is positive."
+long lines if and only if arg is positive."
   (interactive "P")
   (cond
    ((and (numberp arg) (> arg 0))
   (interactive "P")
   (cond
    ((and (numberp arg) (> arg 0))
@@ -2334,12 +2343,11 @@ long lines iff arg is positive."
         (forward-line 1)
         (point))))))
 
         (forward-line 1)
         (point))))))
 
-(eval-when-compile
-  (defvar gnus-face-properties-alist))
+(defvar gnus-face-properties-alist)
 
 
-(defun article-display-face ()
+(defun article-display-face (&optional force)
   "Display any Face headers in the header."
   "Display any Face headers in the header."
-  (interactive)
+  (interactive (list 'force))
   (let ((wash-face-p buffer-read-only))
     (gnus-with-article-headers
       ;; When displaying parts, this function can be called several times on
   (let ((wash-face-p buffer-read-only))
     (gnus-with-article-headers
       ;; When displaying parts, this function can be called several times on
@@ -2349,7 +2357,8 @@ long lines iff arg is positive."
       ;; read-only.
       (if (and wash-face-p (memq 'face gnus-article-wash-types))
          (gnus-delete-images 'face)
       ;; read-only.
       (if (and wash-face-p (memq 'face gnus-article-wash-types))
          (gnus-delete-images 'face)
-       (let (face faces from)
+       (let ((from (message-fetch-field "from"))
+             face faces)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
@@ -2357,16 +2366,22 @@ long lines iff arg is positive."
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
-             (while (gnus-article-goto-header "Face")
-               (push (mail-header-field-value) faces))))
+             (when (or force
+                       ;; Check whether this face is censored.
+                       (not (and gnus-article-x-face-too-ugly
+                                 (or from
+                                     (setq from (message-fetch-field "from")))
+                                 (string-match gnus-article-x-face-too-ugly
+                                               from))))
+               (while (gnus-article-goto-header "Face")
+                 (push (mail-header-field-value) faces)))))
          (when faces
            (goto-char (point-min))
          (when faces
            (goto-char (point-min))
-           (let ((from (gnus-article-goto-header "from"))
-                 png image)
-             (unless from
+           (let (png image)
+             (unless (setq from (gnus-article-goto-header "from"))
                (insert "From:")
                (setq from (point))
                (insert "From:")
                (setq from (point))
-               (insert "[no `from' set]\n"))
+               (insert " [no `from' set]\n"))
              (while faces
                (when (setq png (gnus-convert-face-to-png (pop faces)))
                  (setq image
              (while faces
                (when (setq png (gnus-convert-face-to-png (pop faces)))
                  (setq image
@@ -2391,7 +2406,8 @@ long lines iff arg is positive."
          ;; instead.
          (gnus-delete-images 'xface)
        ;; Display X-Faces.
          ;; instead.
          (gnus-delete-images 'xface)
        ;; Display X-Faces.
-       (let (x-faces from face)
+       (let ((from (message-fetch-field "from"))
+             x-faces face)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
@@ -2402,43 +2418,41 @@ long lines iff arg is positive."
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
-             (while (gnus-article-goto-header "X-Face")
-               (push (mail-header-field-value) x-faces))
-             (setq from (message-fetch-field "from"))))
-         ;; Sending multiple EOFs to xv doesn't work, so we only do a
-         ;; single external face.
-         (when (stringp gnus-article-x-face-command)
-           (setq x-faces (list (car x-faces))))
-         (when (and x-faces
-                    gnus-article-x-face-command
-                    (or force
-                        ;; Check whether this face is censored.
-                        (not gnus-article-x-face-too-ugly)
-                        (and from
-                             (not (string-match gnus-article-x-face-too-ugly
-                                                from)))))
-           (while (setq face (pop x-faces))
-             ;; We display the 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))
-                      (gnus-set-process-query-on-exit-flag
-                       (start-process
-                        "article-x-face" nil shell-file-name
-                        shell-command-switch gnus-article-x-face-command)
-                       nil)
-                      (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))))))))))
+             (and gnus-article-x-face-command
+                  (or force
+                      ;; Check whether this face is censored.
+                      (not (and gnus-article-x-face-too-ugly
+                                (or from
+                                    (setq from (message-fetch-field "from")))
+                                (string-match gnus-article-x-face-too-ugly
+                                              from))))
+                  (while (gnus-article-goto-header "X-Face")
+                    (push (mail-header-field-value) x-faces)))))
+         (when x-faces
+           ;; We display the face.
+           (cond ((functionp gnus-article-x-face-command)
+                  ;; The command is a lisp function, so we call it.
+                  (mapc gnus-article-x-face-command x-faces))
+                 ((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))
+                    (gnus-set-process-query-on-exit-flag
+                     (start-process
+                      "article-x-face" nil shell-file-name
+                      shell-command-switch gnus-article-x-face-command)
+                     nil)
+                    ;; Sending multiple EOFs to xv doesn't work,
+                    ;; so we only do a single external face.
+                    (with-temp-buffer
+                      (insert (car x-faces))
+                      (process-send-region "article-x-face"
+                                           (point-min) (point-max)))
+                    (process-send-eof "article-x-face")))
+                 (t
+                  (error "`%s' set to `%s' is not a function"
+                         gnus-article-x-face-command
+                         '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."
@@ -2702,6 +2716,9 @@ charset defined in `gnus-summary-show-article-charset-alist' is used."
             (t
              (apply (car func) (cdr func))))))))))
 
             (t
              (apply (car func) (cdr func))))))))))
 
+;; External.
+(declare-function w3-region "ext:w3-display" (st nd))
+
 (defun gnus-article-wash-html-with-w3 ()
   "Wash the current buffer with w3."
   (mm-setup-w3)
 (defun gnus-article-wash-html-with-w3 ()
   "Wash the current buffer with w3."
   (mm-setup-w3)
@@ -2713,22 +2730,44 @@ charset defined in `gnus-summary-show-article-charset-alist' is used."
        (w3-region (point-min) (point-max))
       (error))))
 
        (w3-region (point-min) (point-max))
       (error))))
 
+;; External.
+(declare-function w3m-region "ext:w3m" (start end &optional url charset))
+
 (defun gnus-article-wash-html-with-w3m ()
   "Wash the current buffer with emacs-w3m."
   (mm-setup-w3m)
   (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
        w3m-force-redisplay)
     (w3m-region (point-min) (point-max)))
 (defun gnus-article-wash-html-with-w3m ()
   "Wash the current buffer with emacs-w3m."
   (mm-setup-w3m)
   (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
        w3m-force-redisplay)
     (w3m-region (point-min) (point-max)))
+  ;; Put the mark meaning this part was rendered by emacs-w3m.
+  (put-text-property (point-min) (point-max) 'mm-inline-text-html-with-w3m t)
   (when (and mm-inline-text-html-with-w3m-keymap
             (boundp 'w3m-minor-mode-map)
             w3m-minor-mode-map)
   (when (and mm-inline-text-html-with-w3m-keymap
             (boundp 'w3m-minor-mode-map)
             w3m-minor-mode-map)
-    (add-text-properties
-     (point-min) (point-max)
-     (list 'keymap w3m-minor-mode-map
-          ;; Put the mark meaning this part was rendered by emacs-w3m.
-          'mm-inline-text-html-with-w3m t))))
-
-(eval-when-compile (defvar charset)) ;; Bound by `article-wash-html'.
+    (if (and (boundp 'w3m-link-map)
+            w3m-link-map)
+       (let* ((start (point-min))
+              (end (point-max))
+              (on (get-text-property start 'w3m-href-anchor))
+              (map (copy-keymap w3m-link-map))
+              next)
+         (set-keymap-parent map w3m-minor-mode-map)
+         (while (< start end)
+           (if on
+               (progn
+                 (setq next (or (text-property-any start end
+                                                   'w3m-href-anchor nil)
+                                end))
+                 (put-text-property start next 'keymap map))
+             (setq next (or (text-property-not-all start end
+                                                   'w3m-href-anchor nil)
+                            end))
+             (put-text-property start next 'keymap w3m-minor-mode-map))
+           (setq start next
+                 on (not on))))
+      (put-text-property (point-min) (point-max) 'keymap w3m-minor-mode-map))))
+
+(defvar charset) ;; Bound by `article-wash-html'.
 
 (defun gnus-article-wash-html-with-w3m-standalone ()
   "Wash the current buffer with w3m."
 
 (defun gnus-article-wash-html-with-w3m-standalone ()
   "Wash the current buffer with w3m."
@@ -2756,7 +2795,7 @@ exit from the summary buffer.  If it is the symbol `file', query
 on each file, if it is `ask' ask once when exiting from the
 summary buffer."
   :group 'gnus-article
 on each file, if it is `ask' ask once when exiting from the
 summary buffer."
   :group 'gnus-article
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type '(choice (const :tag "Don't delete" nil)
                 (const :tag "Don't ask" t)
                 (const :tag "Ask" ask)
   :type '(choice (const :tag "Don't delete" nil)
                 (const :tag "Don't ask" t)
                 (const :tag "Ask" ask)
@@ -2767,81 +2806,309 @@ summary buffer."
 (defun gnus-article-browse-delete-temp-files (&optional how)
   "Delete temp-files created by `gnus-article-browse-html-parts'."
   (when (and gnus-article-browse-html-temp-list
 (defun gnus-article-browse-delete-temp-files (&optional how)
   "Delete temp-files created by `gnus-article-browse-html-parts'."
   (when (and gnus-article-browse-html-temp-list
-            (or how
-                (setq how gnus-article-browse-delete-temp)))
-    (when (and (eq how 'ask)
-              (y-or-n-p (format
-                         "Delete all %s temporary HTML file(s)? "
-                         (length gnus-article-browse-html-temp-list)))
-              (setq how t)))
+            (progn
+              (or how (setq how gnus-article-browse-delete-temp))
+              (if (eq how 'ask)
+                  (let ((files (length gnus-article-browse-html-temp-list)))
+                    (gnus-y-or-n-p (format
+                                    "Delete all %s temporary HTML file%s? "
+                                    files
+                                    (if (> files 1) "s" ""))))
+                how)))
     (dolist (file gnus-article-browse-html-temp-list)
     (dolist (file gnus-article-browse-html-temp-list)
-      (when (and (file-exists-p file)
-                (or (eq how t)
-                    ;; `how' is neither `nil', `ask' nor `t' (i.e. `file'):
-                    (gnus-y-or-n-p
-                     (format "Delete temporary HTML file `%s'? " file))))
-       (delete-file file)))
+      (cond ((file-directory-p file)
+            (when (or (not (eq how 'file))
+                      (gnus-y-or-n-p
+                       (format
+                        "Delete temporary HTML file(s) in directory `%s'? "
+                        (file-name-as-directory file))))
+              (gnus-delete-directory file)))
+           ((file-exists-p file)
+            (when (or (not (eq how 'file))
+                      (gnus-y-or-n-p
+                       (format "Delete temporary HTML file `%s'? " file)))
+              (delete-file file)))))
     ;; Also remove file from the list when not deleted or if file doesn't
     ;; exist anymore.
     (setq gnus-article-browse-html-temp-list nil))
   gnus-article-browse-html-temp-list)
 
     ;; Also remove file from the list when not deleted or if file doesn't
     ;; exist anymore.
     (setq gnus-article-browse-html-temp-list nil))
   gnus-article-browse-html-temp-list)
 
-(defun gnus-article-browse-html-parts (list)
+(defun gnus-article-browse-html-save-cid-content (cid handles directory)
+  "Find CID content in HANDLES and save it in a file in DIRECTORY.
+Return file name."
+  (save-match-data
+    (let (file type)
+      (catch 'found
+       (dolist (handle handles)
+         (cond
+          ((not (listp handle)))
+          ((equal (mm-handle-media-supertype handle) "multipart")
+           (when (setq file (gnus-article-browse-html-save-cid-content
+                             cid handle directory))
+             (throw 'found file)))
+          ((equal (concat "<" cid ">") (mm-handle-id handle))
+           (setq file
+                 (expand-file-name
+                  (or (mail-content-type-get
+                       (mm-handle-disposition handle) 'filename)
+                      (mail-content-type-get
+                       (setq type (mm-handle-type handle)) 'name)
+                      (concat
+                       (make-temp-name "cid")
+                       (car (rassoc (car type) mailcap-mime-extensions))))
+                  directory))
+           (mm-save-part-to-file handle file)
+           (throw 'found file))))))))
+
+(defun gnus-article-browse-html-parts (list &optional header)
   "View all \"text/html\" parts from LIST.
   "View all \"text/html\" parts from LIST.
-Recurse into multiparts."
+Recurse into multiparts.  The optional HEADER that should be a decoded
+message header will be added to the bodies of the \"text/html\" parts."
   ;; Internal function used by `gnus-article-browse-html-article'.
   ;; Internal function used by `gnus-article-browse-html-article'.
-  (let ((showed))
+  (let (type file charset content cid-dir tmp-file showed)
     ;; Find and show the html-parts.
     (dolist (handle list)
       ;; If HTML, show it:
     ;; Find and show the html-parts.
     (dolist (handle list)
       ;; If HTML, show it:
-      (when (listp handle)
-       (cond ((and (bufferp (car handle))
-                   (string-match "text/html" (car (mm-handle-type handle))))
-              (let ((tmp-file (mm-make-temp-file
-                               ;; Do we need to care for 8.3 filenames?
-                               "mm-" nil ".html")))
-                (mm-save-part-to-file handle tmp-file)
-                (add-to-list 'gnus-article-browse-html-temp-list tmp-file)
-                (add-hook 'gnus-summary-prepare-exit-hook
-                          'gnus-article-browse-delete-temp-files)
-                (add-hook 'gnus-exit-gnus-hook
-                          (lambda  ()
-                            (gnus-article-browse-delete-temp-files t)))
-                ;; FIXME: Warn if there's an <img> tag?
-                (browse-url-of-file tmp-file)
-                (setq showed t)))
-             ;; If multipart, recurse
-             ((and (stringp (car handle))
-                   (string-match "^multipart/" (car handle))
-                   (setq showed
-                         (or showed
-                             (gnus-article-browse-html-parts handle))))))))
+      (cond ((not (listp handle)))
+           ((or (equal (car (setq type (mm-handle-type handle))) "text/html")
+                (and (equal (car type) "message/external-body")
+                     (or header
+                         (setq file (or (mail-content-type-get type 'name)
+                                        (mail-content-type-get
+                                         (mm-handle-disposition handle)
+                                         'filename))))
+                     (or (mm-handle-cache handle)
+                         (condition-case code
+                             (progn (mm-extern-cache-contents handle) t)
+                           (error
+                            (gnus-message 3 "%s" (error-message-string code))
+                            (when (>= gnus-verbose 3) (sit-for 2))
+                            nil)))
+                     (progn
+                       (setq handle (mm-handle-cache handle)
+                             type (mm-handle-type handle))
+                       (equal (car type) "text/html"))))
+            (setq charset (mail-content-type-get type 'charset)
+                  content (mm-get-part handle))
+            (with-temp-buffer
+              (if (eq charset 'gnus-decoded)
+                  (mm-enable-multibyte)
+                (mm-disable-multibyte))
+              (insert content)
+              ;; resolve cid contents
+              (let ((case-fold-search t)
+                    cid-file)
+                (goto-char (point-min))
+                (while (re-search-forward "\
+<img[\t\n ]+\\(?:[^\t\n >]+[\t\n ]+\\)*src=\"\\(cid:\\([^\"]+\\)\\)\""
+                                          nil t)
+                  (unless cid-dir
+                    (setq cid-dir (mm-make-temp-file "cid" t))
+                    (add-to-list 'gnus-article-browse-html-temp-list cid-dir))
+                  (setq file nil
+                        content nil)
+                  (when (setq cid-file
+                              (gnus-article-browse-html-save-cid-content
+                               (match-string 2)
+                               (with-current-buffer gnus-article-buffer
+                                 gnus-article-mime-handles)
+                               cid-dir))
+                    (replace-match (concat "file://" cid-file)
+                                   nil nil nil 1))))
+              (unless content (setq content (buffer-string))))
+            (when (or charset header (not file))
+              (setq tmp-file (mm-make-temp-file
+                              ;; Do we need to care for 8.3 filenames?
+                              "mm-" nil ".html")))
+            ;; Add a meta html tag to specify charset and a header.
+            (cond
+             (header
+              (let (title eheader body hcharset coding force-charset)
+                (with-temp-buffer
+                  (mm-enable-multibyte)
+                  (setq case-fold-search t)
+                  (insert header "\n")
+                  (setq title (message-fetch-field "subject"))
+                  (goto-char (point-min))
+                  (while (re-search-forward "\\(<\\)\\|\\(>\\)\\|&" nil t)
+                    (replace-match (cond ((match-beginning 1) "&lt;")
+                                         ((match-beginning 2) "&gt;")
+                                         (t "&amp;"))))
+                  (goto-char (point-min))
+                  (insert "<pre>\n")
+                  (goto-char (point-max))
+                  (insert "</pre>\n<hr>\n")
+                  ;; We have to examine charset one by one since
+                  ;; charset specified in parts might be different.
+                  (if (eq charset 'gnus-decoded)
+                      (setq charset 'utf-8
+                            eheader (mm-encode-coding-string (buffer-string)
+                                                             charset)
+                            title (when title
+                                    (mm-encode-coding-string title charset))
+                            body (mm-encode-coding-string content charset)
+                            force-charset t)
+                    (setq hcharset (mm-find-mime-charset-region (point-min)
+                                                                (point-max)))
+                    (cond ((= (length hcharset) 1)
+                           (setq hcharset (car hcharset)
+                                 coding (mm-charset-to-coding-system
+                                         hcharset)))
+                          ((> (length hcharset) 1)
+                           (setq hcharset 'utf-8
+                                 coding hcharset)))
+                    (if coding
+                        (if charset
+                            (progn
+                              (setq body
+                                    (mm-charset-to-coding-system charset))
+                              (if (eq coding body)
+                                  (setq eheader (mm-encode-coding-string
+                                                 (buffer-string) coding)
+                                        title (when title
+                                                (mm-encode-coding-string
+                                                 title coding))
+                                        body content)
+                                (setq charset 'utf-8
+                                      eheader (mm-encode-coding-string
+                                               (buffer-string) charset)
+                                      title (when title
+                                              (mm-encode-coding-string
+                                               title charset))
+                                      body (mm-encode-coding-string
+                                            (mm-decode-coding-string
+                                             content body)
+                                            charset)
+                                      force-charset t)))
+                          (setq charset hcharset
+                                eheader (mm-encode-coding-string
+                                         (buffer-string) coding)
+                                title (when title
+                                        (mm-encode-coding-string
+                                         title coding))
+                                body content))
+                      (setq eheader (mm-string-as-unibyte (buffer-string))
+                            body content)))
+                  (erase-buffer)
+                  (mm-disable-multibyte)
+                  (insert body)
+                  (when charset
+                    (mm-add-meta-html-tag handle charset force-charset))
+                  (when title
+                    (goto-char (point-min))
+                    (unless (search-forward "<title>" nil t)
+                      (re-search-forward "<head>\\s-*" nil t)
+                      (insert "<title>" title "</title>\n")))
+                  (goto-char (point-min))
+                  (or (re-search-forward
+                       "<body\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t)
+                      (re-search-forward
+                       "</head\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t))
+                  (insert eheader)
+                  (mm-write-region (point-min) (point-max)
+                                   tmp-file nil nil nil 'binary t))))
+             (charset
+              (mm-with-unibyte-buffer
+                (insert (if (eq charset 'gnus-decoded)
+                            (mm-encode-coding-string content
+                                                     (setq charset 'utf-8))
+                          content))
+                (if (or (mm-add-meta-html-tag handle charset)
+                        (not file))
+                    (mm-write-region (point-min) (point-max)
+                                     tmp-file nil nil nil 'binary t)
+                  (setq tmp-file nil))))
+             (tmp-file
+              (mm-save-part-to-file handle tmp-file)))
+            (when tmp-file
+              (add-to-list 'gnus-article-browse-html-temp-list tmp-file))
+            (add-hook 'gnus-summary-prepare-exit-hook
+                      'gnus-article-browse-delete-temp-files)
+            (add-hook 'gnus-exit-gnus-hook
+                      (lambda  ()
+                        (gnus-article-browse-delete-temp-files t)))
+            ;; FIXME: Warn if there's an <img> tag?
+            (browse-url-of-file (or tmp-file (expand-file-name file)))
+            (setq showed t))
+           ;; If multipart, recurse
+           ((equal (mm-handle-media-supertype handle) "multipart")
+            (when (gnus-article-browse-html-parts handle header)
+              (setq showed t)))
+           ((equal (mm-handle-media-type handle) "message/rfc822")
+            (mm-with-multibyte-buffer
+              (mm-insert-part handle)
+              (setq handle (mm-dissect-buffer t t))
+              (when (and (bufferp (car handle))
+                         (stringp (car (mm-handle-type handle))))
+                (setq handle (list handle)))
+              (when header
+                (article-decode-encoded-words)
+                (let ((gnus-visible-headers
+                       (or (get 'gnus-visible-headers 'standard-value)
+                           gnus-visible-headers)))
+                  (article-hide-headers))
+                (goto-char (point-min))
+                (search-forward "\n\n" nil 'move)
+                (skip-chars-backward "\t\n ")
+                (setq header (buffer-substring (point-min) (point)))))
+            (when (prog1
+                      (gnus-article-browse-html-parts handle header)
+                    (mm-destroy-parts handle))
+              (setq showed t)))))
     showed))
 
     showed))
 
-;; FIXME: Documentation in texi/gnus.texi missing.
-(defun gnus-article-browse-html-article ()
+(defun gnus-article-browse-html-article (&optional arg)
   "View \"text/html\" parts of the current article with a WWW browser.
   "View \"text/html\" parts of the current article with a WWW browser.
-
-Warning: Spammers use links to images in HTML articles to verify
-whether you have read the message.  As
-`gnus-article-browse-html-article' passes the unmodified HTML
-content to the browser without eliminating these \"web bugs\" you
-should only use it for mails from trusted senders."
+Inline images embedded in a message using the cid scheme, as they are
+generally considered to be safe, will be processed properly.
+The message header is added to the beginning of every html part unless
+the prefix argument ARG is given.
+
+Warning: Spammers use links to images (using the http scheme) in HTML
+articles to verify whether you have read the message.  As
+`gnus-article-browse-html-article' passes the HTML content to the
+browser without eliminating these \"web bugs\" you should only
+use it for mails from trusted senders.
+
+If you always want to display HTML parts in the browser, set
+`mm-text-html-renderer' to nil.
+
+This command creates temporary files to pass HTML contents including
+images if any to the browser, and deletes them when exiting the group
+\(if you want)."
   ;; Cf. `mm-w3m-safe-url-regexp'
   ;; Cf. `mm-w3m-safe-url-regexp'
-  (interactive)
-  (save-window-excursion
-    ;; Open raw article and select the buffer
-    (gnus-summary-show-article t)
-    (gnus-summary-select-article-buffer)
-    (let ((parts (mm-dissect-buffer t t)))
+  (interactive "P")
+  (if arg
+      (gnus-summary-show-article)
+    (let ((gnus-visible-headers (or (get 'gnus-visible-headers 'standard-value)
+                                   gnus-visible-headers))
+         ;; As we insert a <hr>, there's no need for the body boundary.
+         (gnus-treat-body-boundary nil))
+      (gnus-summary-show-article)))
+  (with-current-buffer gnus-article-buffer
+    (let ((header (unless arg
+                   (save-restriction
+                     (widen)
+                     (buffer-substring-no-properties
+                      (goto-char (point-min))
+                      (if (search-forward "\n\n" nil t)
+                          (match-beginning 0)
+                        (goto-char (point-max))
+                        (skip-chars-backward "\t\n ")
+                        (point))))))
+         parts)
+      (set-buffer gnus-original-article-buffer)
+      (setq parts (mm-dissect-buffer t t))
       ;; If singlepart, enforce a list.
       (when (and (bufferp (car parts))
                 (stringp (car (mm-handle-type parts))))
        (setq parts (list parts)))
       ;; Process the list
       ;; If singlepart, enforce a list.
       (when (and (bufferp (car parts))
                 (stringp (car (mm-handle-type parts))))
        (setq parts (list parts)))
       ;; Process the list
-      (unless (gnus-article-browse-html-parts parts)
+      (unless (gnus-article-browse-html-parts parts header)
        (gnus-error 3 "Mail doesn't contain a \"text/html\" part!"))
        (gnus-error 3 "Mail doesn't contain a \"text/html\" part!"))
-      (gnus-summary-show-article))))
+      (mm-destroy-parts parts)
+      (unless arg
+       (gnus-summary-show-article)))))
 
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
 
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
@@ -3215,9 +3482,15 @@ should replace the \"Date:\" one, or should be added below it."
                                    (point) 'original-date))
                     (setq date (get-text-property pos 'original-date))
                     t))
                                    (point) 'original-date))
                     (setq date (get-text-property pos 'original-date))
                     t))
-         (narrow-to-region pos (or (text-property-any pos (point-max)
-                                                      'original-date nil)
-                                   (point-max)))
+         (narrow-to-region
+          pos (if (setq pos (text-property-any pos (point-max)
+                                               'original-date nil))
+                  (progn
+                    (goto-char pos)
+                    (if (or (bolp) (eobp))
+                        (point)
+                      (1+ (point))))
+                (point-max)))
          (goto-char (point-min))
          (when (re-search-forward tdate-regexp nil t)
            (setq bface (get-text-property (point-at-bol) 'face)
          (goto-char (point-min))
          (when (re-search-forward tdate-regexp nil t)
            (setq bface (get-text-property (point-at-bol) 'face)
@@ -3531,9 +3804,8 @@ This format is defined by the `gnus-article-time-format' variable."
                                 gnus-newsgroup-name 'highlight-words t)))
             gnus-emphasis-alist)))))
 
                                 gnus-newsgroup-name 'highlight-words t)))
             gnus-emphasis-alist)))))
 
-(eval-when-compile
-  (defvar gnus-summary-article-menu)
-  (defvar gnus-summary-post-menu))
+(defvar gnus-summary-article-menu)
+(defvar gnus-summary-post-menu)
 
 ;;; Saving functions.
 
 
 ;;; Saving functions.
 
@@ -3546,9 +3818,9 @@ This format is defined by the `gnus-article-time-format' variable."
     (let ((gnus-visible-headers
           (or (symbol-value (get gnus-default-article-saver :headers))
               gnus-saved-headers gnus-visible-headers))
     (let ((gnus-visible-headers
           (or (symbol-value (get gnus-default-article-saver :headers))
               gnus-saved-headers gnus-visible-headers))
-         (gnus-article-buffer save-buffer))
-      (save-excursion
-       (set-buffer save-buffer)
+         ;; Ignore group parameter.  See `article-hide-headers'.
+         (gnus-summary-buffer nil))
+      (with-current-buffer save-buffer
        (article-hide-headers 1 t))))
   (save-window-excursion
     (if (not gnus-default-article-saver)
        (article-hide-headers 1 t))))
   (save-window-excursion
     (if (not gnus-default-article-saver)
@@ -3682,6 +3954,9 @@ Directory to save to is default to `gnus-article-save-directory'."
     (save-excursion
       (save-restriction
        (widen)
     (save-excursion
       (save-restriction
        (widen)
+       ;; Note that unlike gnus-summary-save-in-mail, there is no
+       ;; check to see if filename is Babyl.  Rmail in Emacs 23 does
+       ;; not use Babyl.
        (gnus-output-to-rmail filename))))
   filename)
 
        (gnus-output-to-rmail filename))))
   filename)
 
@@ -3700,7 +3975,7 @@ Directory to save to is default to `gnus-article-save-directory'."
        (if (and (file-readable-p filename)
                 (file-regular-p filename)
                 (mail-file-babyl-p filename))
        (if (and (file-readable-p filename)
                 (file-regular-p filename)
                 (mail-file-babyl-p filename))
-           (rmail-output-to-rmail-file filename t)
+           (gnus-output-to-rmail filename)
          (gnus-output-to-mail filename)))))
   filename)
 
          (gnus-output-to-mail filename)))))
   filename)
 
@@ -3771,39 +4046,77 @@ The directory to save in defaults to `gnus-article-save-directory'."
                  gnus-current-headers nil 'gnus-newsgroup-last-directory))
   (gnus-summary-save-body-in-file filename t))
 
                  gnus-current-headers nil 'gnus-newsgroup-last-directory))
   (gnus-summary-save-body-in-file filename t))
 
-(defun gnus-summary-save-in-pipe (&optional command)
-  "Pipe this article to subprocess."
-  (setq command
-       (cond ((and (eq command 'default)
-                   gnus-last-shell-command)
-              gnus-last-shell-command)
-             ((stringp command)
-              command)
-             (t (read-string
-                 (format
-                  "Shell command on %s: "
-                  (if (and gnus-number-of-articles-to-be-saved
-                           (> gnus-number-of-articles-to-be-saved 1))
-                      (format "these %d articles"
-                              gnus-number-of-articles-to-be-saved)
-                    "this article"))
-                 gnus-last-shell-command))))
-  (when (string-equal command "")
-    (if gnus-last-shell-command
-       (setq command gnus-last-shell-command)
-      (error "A command is required")))
-  (gnus-eval-in-buffer-window gnus-article-buffer
-    (save-restriction
-      (widen)
-      (shell-command-on-region (point-min) (point-max) command nil)))
-  (setq gnus-last-shell-command command))
+(put 'gnus-summary-save-in-pipe :decode t)
+(put 'gnus-summary-save-in-pipe :headers 'gnus-saved-headers)
+(defun gnus-summary-save-in-pipe (&optional command raw)
+  "Pipe this article to subprocess COMMAND.
+Valid values for COMMAND include:
+  a string
+    The executable command name and possibly arguments.
+  nil
+    You will be prompted for the command in the minibuffer.
+  the symbol `default'
+    It will be replaced with the command which the variable
+    `gnus-summary-pipe-output-default-command' holds or the command
+    last used for saving.
+Non-nil value for RAW overrides `:decode' and `:headers' properties
+and the raw article including all headers will be piped."
+  (let ((article (gnus-summary-article-number))
+       (decode (unless raw
+                 (get 'gnus-summary-save-in-pipe :decode)))
+       save-buffer default)
+    (if article
+       (if (vectorp (gnus-summary-article-header article))
+           (save-current-buffer
+             (gnus-summary-select-article decode decode nil article)
+             (insert-buffer-substring
+              (prog1
+                  (if decode
+                      gnus-article-buffer
+                    gnus-original-article-buffer)
+                (setq save-buffer
+                      (nnheader-set-temp-buffer " *Gnus Save*"))))
+             ;; Remove unwanted headers.
+             (when (and (not raw)
+                        (or (get 'gnus-summary-save-in-pipe :headers)
+                            (not gnus-save-all-headers)))
+               (let ((gnus-visible-headers
+                      (or (symbol-value (get 'gnus-summary-save-in-pipe
+                                             :headers))
+                          gnus-saved-headers gnus-visible-headers))
+                     (gnus-summary-buffer nil))
+                 (article-hide-headers 1 t))))
+         (error "%d is not a real article" article))
+      (error "No article to pipe"))
+    (setq default (or gnus-summary-pipe-output-default-command
+                     gnus-last-shell-command))
+    (unless (stringp command)
+      (setq command
+           (if (and (eq command 'default) default)
+               default
+             (gnus-read-shell-command "Shell command on this article: "
+                                      default))))
+    (when (string-equal command "")
+      (if default
+         (setq command default)
+       (error "A command is required")))
+    (gnus-eval-in-buffer-window save-buffer
+      (save-restriction
+       (widen)
+       (shell-command-on-region (point-min) (point-max) command nil)))
+    (gnus-kill-buffer save-buffer))
+  (setq gnus-summary-pipe-output-default-command command))
 
 (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
-                "Print using command: " gnus-summary-muttprint-program
-                nil gnus-summary-muttprint-program))
-  (gnus-summary-save-in-pipe command))
+  (unless (stringp command)
+    (setq command (read-string
+                  "Print using command: " gnus-summary-muttprint-program
+                  nil gnus-summary-muttprint-program)))
+  (let ((gnus-summary-pipe-output-default-command
+        gnus-summary-pipe-output-default-command))
+    (gnus-summary-save-in-pipe command))
+  (setq gnus-summary-muttprint-program command))
 
 ;;; Article file names when saving.
 
 
 ;;; Article file names when saving.
 
@@ -3873,6 +4186,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 
 (defun article-verify-x-pgp-sig ()
   "Verify X-PGP-Sig."
 
 (defun article-verify-x-pgp-sig ()
   "Verify X-PGP-Sig."
+  ;; <ftp://ftp.isc.org/pub/pgpcontrol/FORMAT>
   (interactive)
   (if (gnus-buffer-live-p gnus-original-article-buffer)
       (let ((sig (with-current-buffer gnus-original-article-buffer
   (interactive)
   (if (gnus-buffer-live-p gnus-original-article-buffer)
       (let ((sig (with-current-buffer gnus-original-article-buffer
@@ -3945,6 +4259,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                  (put-text-property (match-end 0) (point-max)
                                     'face eface)))))))))
 
                  (put-text-property (match-end 0) (point-max)
                                     'face eface)))))))))
 
+(autoload 'canlock-verify "canlock" nil t) ;; for Emacs 21.
+
 (defun article-verify-cancel-lock ()
   "Verify Cancel-Lock header."
   (interactive)
 (defun article-verify-cancel-lock ()
   "Verify Cancel-Lock header."
   (interactive)
@@ -3952,7 +4268,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
       (canlock-verify gnus-original-article-buffer)))
 
 (eval-and-compile
       (canlock-verify gnus-original-article-buffer)))
 
 (eval-and-compile
-  (mapcar
+  (mapc
    (lambda (func)
      (let (afunc gfunc)
        (if (consp func)
    (lambda (func)
      (let (afunc gfunc)
        (if (consp func)
@@ -3965,8 +4281,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
           `(lambda (&optional interactive &rest args)
              ,(documentation afunc t)
              (interactive (list t))
           `(lambda (&optional interactive &rest args)
              ,(documentation afunc t)
              (interactive (list t))
-             (save-excursion
-               (set-buffer gnus-article-buffer)
+             (with-current-buffer gnus-article-buffer
                (if interactive
                    (call-interactively ',afunc)
                  (apply ',afunc args))))))))
                (if interactive
                    (call-interactively ',afunc)
                  (apply ',afunc args))))))))
@@ -4013,7 +4328,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-emphasize
      article-treat-dumbquotes
      article-normalize-headers
      article-emphasize
      article-treat-dumbquotes
      article-normalize-headers
-;;     (article-show-all . gnus-article-show-all-headers)
+     ;;(article-show-all . gnus-article-show-all-headers)
      )))
 \f
 ;;;
      )))
 \f
 ;;;
@@ -4034,7 +4349,6 @@ If variable `gnus-use-long-file-name' is non-nil, it is
   "s" gnus-article-show-summary
   "\C-c\C-m" gnus-article-mail
   "?" gnus-article-describe-briefly
   "s" gnus-article-show-summary
   "\C-c\C-m" gnus-article-mail
   "?" gnus-article-describe-briefly
-  "e" gnus-summary-edit-article
   "<" beginning-of-buffer
   ">" end-of-buffer
   "\C-c\C-i" gnus-info-find-node
   "<" beginning-of-buffer
   ">" end-of-buffer
   "\C-c\C-i" gnus-info-find-node
@@ -4043,7 +4357,9 @@ If variable `gnus-use-long-file-name' is non-nil, it is
   "F" gnus-article-followup-with-original
   "\C-hk" gnus-article-describe-key
   "\C-hc" gnus-article-describe-key-briefly
   "F" gnus-article-followup-with-original
   "\C-hk" gnus-article-describe-key
   "\C-hc" gnus-article-describe-key-briefly
+  "\C-hb" gnus-article-describe-bindings
 
 
+  "e" gnus-article-read-summary-keys
   "\C-d" gnus-article-read-summary-keys
   "\M-*" gnus-article-read-summary-keys
   "\M-#" gnus-article-read-summary-keys
   "\C-d" gnus-article-read-summary-keys
   "\M-*" gnus-article-read-summary-keys
   "\M-#" gnus-article-read-summary-keys
@@ -4053,6 +4369,13 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 (substitute-key-definition
  'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
 
 (substitute-key-definition
  'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
 
+(gnus-define-keys (gnus-article-send-map "S" gnus-article-mode-map)
+  "W" gnus-article-wide-reply-with-original)
+(if (featurep 'xemacs)
+    (set-keymap-default-binding gnus-article-send-map
+                               'gnus-article-read-summary-send-keys)
+  (define-key gnus-article-send-map [t] 'gnus-article-read-summary-send-keys))
+
 (defun gnus-article-make-menu-bar ()
   (unless (boundp 'gnus-article-commands-menu)
     (gnus-summary-make-menu-bar))
 (defun gnus-article-make-menu-bar ()
   (unless (boundp 'gnus-article-commands-menu)
     (gnus-summary-make-menu-bar))
@@ -4091,6 +4414,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 
     (gnus-run-hooks 'gnus-article-menu-hook)))
 
 
     (gnus-run-hooks 'gnus-article-menu-hook)))
 
+(defvar bookmark-make-record-function)
+
 (defun gnus-article-mode ()
   "Major mode for displaying an article.
 
 (defun gnus-article-mode ()
   "Major mode for displaying an article.
 
@@ -4129,6 +4454,8 @@ commands:
   (make-local-variable 'gnus-article-image-alist)
   (make-local-variable 'gnus-article-charset)
   (make-local-variable 'gnus-article-ignored-charsets)
   (make-local-variable 'gnus-article-image-alist)
   (make-local-variable 'gnus-article-charset)
   (make-local-variable 'gnus-article-ignored-charsets)
+  (set (make-local-variable 'bookmark-make-record-function)
+       'gnus-summary-bookmark-make-record)
   ;; Prevent Emacs 22 from displaying non-break space with `nobreak-space'
   ;; face.
   (set (make-local-variable 'nobreak-char-display) nil)
   ;; Prevent Emacs 22 from displaying non-break space with `nobreak-space'
   ;; face.
   (set (make-local-variable 'nobreak-char-display) nil)
@@ -4165,8 +4492,7 @@ Internal variable.")
        (gnus-set-global-variables)))
     (gnus-article-setup-highlight-words)
     ;; Init original article buffer.
        (gnus-set-global-variables)))
     (gnus-article-setup-highlight-words)
     ;; Init original article buffer.
-    (save-excursion
-      (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
+    (with-current-buffer (gnus-get-buffer-create gnus-original-article-buffer)
       (mm-enable-multibyte)
       (setq major-mode 'gnus-original-article-mode)
       (make-local-variable 'gnus-original-article))
       (mm-enable-multibyte)
       (setq major-mode 'gnus-original-article-mode)
       (make-local-variable 'gnus-original-article))
@@ -4181,8 +4507,7 @@ Internal variable.")
                         nil)
                     (error "Action aborted"))
                 t)))
                         nil)
                     (error "Action aborted"))
                 t)))
-       (save-excursion
-         (set-buffer name)
+       (with-current-buffer name
          (set (make-local-variable 'gnus-article-edit-mode) nil)
          (when gnus-article-mime-handles
            (mm-destroy-parts gnus-article-mime-handles)
          (set (make-local-variable 'gnus-article-edit-mode) nil)
          (when gnus-article-mime-handles
            (mm-destroy-parts gnus-article-mime-handles)
@@ -4196,8 +4521,7 @@ Internal variable.")
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
          (current-buffer))
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
          (current-buffer))
-      (save-excursion
-       (set-buffer (gnus-get-buffer-create name))
+      (with-current-buffer (gnus-get-buffer-create name)
        (gnus-article-mode)
        (make-local-variable 'gnus-summary-buffer)
        (setq gnus-summary-buffer
        (gnus-article-mode)
        (make-local-variable 'gnus-summary-buffer)
        (setq gnus-summary-buffer
@@ -4212,8 +4536,7 @@ Internal variable.")
     (when article-window
       (set-window-start
        article-window
     (when article-window
       (set-window-start
        article-window
-       (save-excursion
-        (set-buffer gnus-article-buffer)
+       (with-current-buffer gnus-article-buffer
         (goto-char (point-min))
         (if (not line)
             (point-min)
         (goto-char (point-min))
         (if (not line)
             (point-min)
@@ -4267,8 +4590,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
          (if (or (eq result 'pseudo)
                  (eq result 'nneething))
              (progn
          (if (or (eq result 'pseudo)
                  (eq result 'nneething))
              (progn
-               (save-excursion
-                 (set-buffer summary-buffer)
+               (with-current-buffer summary-buffer
                  (push article gnus-newsgroup-history)
                  (setq gnus-last-article gnus-current-article
                        gnus-current-article 0
                  (push article gnus-newsgroup-history)
                  (setq gnus-last-article gnus-current-article
                        gnus-current-article 0
@@ -4288,8 +4610,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                       (not (eq article gnus-current-article)))
              ;; Seems like a new article has been selected.
              ;; `gnus-current-article' must be an article number.
                       (not (eq article gnus-current-article)))
              ;; Seems like a new article has been selected.
              ;; `gnus-current-article' must be an article number.
-             (save-excursion
-               (set-buffer summary-buffer)
+             (with-current-buffer summary-buffer
                (push article gnus-newsgroup-history)
                (setq gnus-last-article gnus-current-article
                      gnus-current-article article
                (push article gnus-newsgroup-history)
                (setq gnus-last-article gnus-current-article
                      gnus-current-article article
@@ -4502,6 +4823,43 @@ General format specifiers can also be used.  See Info node
                (vector (caddr c) (car c) :active t))
              gnus-mime-button-commands)))
 
                (vector (caddr c) (car c) :active t))
              gnus-mime-button-commands)))
 
+(defvar gnus-url-button-commands
+  '((gnus-article-copy-string "u" "Copy URL to kill ring")))
+
+(defvar gnus-url-button-map
+  (let ((map (make-sparse-keymap)))
+    (dolist (c gnus-url-button-commands)
+      (define-key map (cadr c) (car c)))
+    map))
+
+(easy-menu-define
+  gnus-url-button-menu gnus-url-button-map "URL button menu."
+  `("Url Button"
+    ,@(mapcar (lambda (c)
+               (vector (caddr c) (car c) :active t))
+             gnus-url-button-commands)))
+
+(defmacro gnus-bind-safe-url-regexp (&rest body)
+  "Bind `mm-w3m-safe-url-regexp' according to `gnus-safe-html-newsgroups'."
+  `(let ((mm-w3m-safe-url-regexp
+         (let ((group (if (and (eq major-mode 'gnus-article-mode)
+                               (gnus-buffer-live-p
+                                gnus-article-current-summary))
+                          (with-current-buffer gnus-article-current-summary
+                            gnus-newsgroup-name)
+                        gnus-newsgroup-name)))
+           (if (cond ((not group)
+                      ;; Maybe we're in a mml-preview buffer
+                      ;; and no group is selected.
+                      t)
+                     ((stringp gnus-safe-html-newsgroups)
+                      (string-match gnus-safe-html-newsgroups group))
+                     ((consp gnus-safe-html-newsgroups)
+                      (member group gnus-safe-html-newsgroups)))
+               nil
+             mm-w3m-safe-url-regexp))))
+     ,@body))
+
 (defun gnus-mime-button-menu (event prefix)
  "Construct a context-sensitive menu of MIME commands."
  (interactive "e\nP")
 (defun gnus-mime-button-menu (event prefix)
  "Construct a context-sensitive menu of MIME commands."
  (interactive "e\nP")
@@ -4527,7 +4885,7 @@ General format specifiers can also be used.  See Info node
        (or (search-forward "\n\n") (goto-char (point-max)))
        (let ((inhibit-read-only t))
          (delete-region (point) (point-max))
        (or (search-forward "\n\n") (goto-char (point-max)))
        (let ((inhibit-read-only t))
          (delete-region (point) (point-max))
-         (mm-display-parts handles))))))
+         (gnus-bind-safe-url-regexp (mm-display-parts handles)))))))
 
 (defun gnus-article-jump-to-part (n)
   "Jump to MIME part N."
 
 (defun gnus-article-jump-to-part (n)
   "Jump to MIME part N."
@@ -4558,6 +4916,10 @@ General format specifiers can also be used.  See Info node
          (t
           (gnus-article-goto-part n)))))
 
          (t
           (gnus-article-goto-part n)))))
 
+(defvar gnus-mime-buttonized-part-id nil
+  "ID of a mime part that should be buttonized.
+`gnus-mime-save-part-and-strip' and `gnus-mime-delete-part' bind it.")
+
 (eval-when-compile
   (defsubst gnus-article-edit-part (handles &optional current-id)
     "Edit an article in order to delete a mime part.
 (eval-when-compile
   (defsubst gnus-article-edit-part (handles &optional current-id)
     "Edit an article in order to delete a mime part.
@@ -4600,22 +4962,24 @@ and `gnus-mime-delete-part', and not provided at run-time normally."
         ,(gnus-group-read-only-p)
         ,gnus-summary-buffer no-highlight))
      t)
         ,(gnus-group-read-only-p)
         ,gnus-summary-buffer no-highlight))
      t)
-    (gnus-article-edit-done)
-    (gnus-summary-expand-window)
-    (gnus-summary-show-article)
+    ;; Force buttonizing this part.
+    (let ((gnus-mime-buttonized-part-id current-id))
+      (gnus-article-edit-done))
+    (gnus-configure-windows 'article)
     (when (and current-id (integerp gnus-auto-select-part))
       (gnus-article-jump-to-part
     (when (and current-id (integerp gnus-auto-select-part))
       (gnus-article-jump-to-part
-       (+ current-id gnus-auto-select-part)))))
+       (min (max (+ current-id gnus-auto-select-part) 1)
+           (with-current-buffer gnus-article-buffer
+             (length gnus-article-mime-handle-alist)))))))
 
 (defun gnus-mime-replace-part (file)
   "Replace MIME part under point with an external body."
   ;; Useful if file has already been saved to disk
   (interactive
    (list
 
 (defun gnus-mime-replace-part (file)
   "Replace MIME part under point with an external body."
   ;; Useful if file has already been saved to disk
   (interactive
    (list
-    (mm-with-multibyte
-      (read-file-name "Replace MIME part with file: "
-                     (or mm-default-directory default-directory)
-                     nil nil))))
+    (read-file-name "Replace MIME part with file: "
+                    (or mm-default-directory default-directory)
+                    nil nil)))
   (gnus-mime-save-part-and-strip file))
 
 (defun gnus-mime-save-part-and-strip (&optional file)
   (gnus-mime-save-part-and-strip file))
 
 (defun gnus-mime-save-part-and-strip (&optional file)
@@ -4677,8 +5041,9 @@ Deleting parts may malfunction or destroy the article; continue? "))
           (handles gnus-article-mime-handles)
           (none "(none)")
           (description
           (handles gnus-article-mime-handles)
           (none "(none)")
           (description
-           (mail-decode-encoded-word-string (or (mm-handle-description data)
-                                                none)))
+           (let ((desc (mm-handle-description data)))
+             (when desc
+               (mail-decode-encoded-word-string desc))))
           (filename
            (or (mail-content-type-get (mm-handle-disposition data) 'filename)
                none))
           (filename
            (or (mail-content-type-get (mm-handle-disposition data) 'filename)
                none))
@@ -4696,11 +5061,12 @@ Deleting parts may malfunction or destroy the article; continue? "))
            "| Type:           " type "\n"
            "| Filename:       " filename "\n"
            "| Size (encoded): " bsize " Byte\n"
            "| Type:           " type "\n"
            "| Filename:       " filename "\n"
            "| Size (encoded): " bsize " Byte\n"
-           "| Description:    " description "\n"
+           (when description
+             (concat    "| Description:    " description "\n"))
            "`----\n"))
          (setcdr data
                  (cdr (mm-make-handle
            "`----\n"))
          (setcdr data
                  (cdr (mm-make-handle
-                       nil `("text/plain") nil nil
+                       nil `("text/plain" (charset . gnus-decoded)) nil nil
                        (list "attachment")
                        (format "Deleted attachment (%s bytes)" bsize))))))
       ;; (set-buffer gnus-summary-buffer)
                        (list "attachment")
                        (format "Deleted attachment (%s bytes)" bsize))))))
       ;; (set-buffer gnus-summary-buffer)
@@ -4714,13 +5080,14 @@ Deleting parts may malfunction or destroy the article; continue? "))
     (when data
       (mm-save-part data))))
 
     (when data
       (mm-save-part data))))
 
-(defun gnus-mime-pipe-part ()
-  "Pipe the MIME part under point to a process."
+(defun gnus-mime-pipe-part (&optional cmd)
+  "Pipe the MIME part under point to a process.
+Use CMD as the process."
   (interactive)
   (gnus-article-check-buffer)
   (let ((data (get-text-property (point) 'gnus-data)))
     (when data
   (interactive)
   (gnus-article-check-buffer)
   (let ((data (get-text-property (point) 'gnus-data)))
     (when data
-      (mm-pipe-part data))))
+      (mm-pipe-part data cmd))))
 
 (defun gnus-mime-view-part ()
   "Interactively choose a viewing method for the MIME part under point."
 
 (defun gnus-mime-view-part ()
   "Interactively choose a viewing method for the MIME part under point."
@@ -4742,7 +5109,11 @@ Deleting parts may malfunction or destroy the article; continue? "))
                ;; Content-Disposition: attachment; filename=...
                (cdr (assq 'filename (cdr (mm-handle-disposition handle))))))
         (def-type (and name (mm-default-file-encoding name))))
                ;; Content-Disposition: attachment; filename=...
                (cdr (assq 'filename (cdr (mm-handle-disposition handle))))))
         (def-type (and name (mm-default-file-encoding name))))
-    (and def-type (cons def-type 0))))
+    (or (and def-type (cons def-type 0))
+       (and handle
+            (equal (mm-handle-media-supertype handle) "text")
+            '("text/plain" . 0))
+       '("application/octet-stream" . 0))))
 
 (defun gnus-mime-view-part-as-type (&optional mime-type pred)
   "Choose a MIME media type, and view the part as such.
 
 (defun gnus-mime-view-part-as-type (&optional mime-type pred)
   "Choose a MIME media type, and view the part as such.
@@ -4776,6 +5147,8 @@ available media-types."
                            (mm-handle-id handle)))
       (setq gnus-article-mime-handles
            (mm-merge-handles gnus-article-mime-handles handle))
                            (mm-handle-id handle)))
       (setq gnus-article-mime-handles
            (mm-merge-handles gnus-article-mime-handles handle))
+      (when (mm-handle-displayed-p handle)
+       (mm-remove-part handle))
       (gnus-mm-display-part handle))))
 
 (defun gnus-mime-copy-part (&optional handle arg)
       (gnus-mm-display-part handle))))
 
 (defun gnus-mime-copy-part (&optional handle arg)
@@ -4918,10 +5291,14 @@ Compressed files like .gz and .bz2 are decompressed."
           (mm-string-to-multibyte contents)))
        (goto-char b)))))
 
           (mm-string-to-multibyte contents)))
        (goto-char b)))))
 
-(defun gnus-mime-strip-charset-parameters (handle)
-  "Strip charset parameters from HANDLE."
+(defun gnus-mime-set-charset-parameters (handle charset)
+  "Set CHARSET to parameters in HANDLE.
+CHARSET may either be a string or a symbol."
+  (unless (stringp charset)
+    (setq charset (symbol-name charset)))
   (if (stringp (car handle))
   (if (stringp (car handle))
-      (mapc #'gnus-mime-strip-charset-parameters (cdr handle))
+      (dolist (h (cdr handle))
+       (gnus-mime-set-charset-parameters h charset))
     (let* ((type (mm-handle-type (if (equal (mm-handle-media-type handle)
                                            "message/external-body")
                                     (progn
     (let* ((type (mm-handle-type (if (equal (mm-handle-media-type handle)
                                            "message/external-body")
                                     (progn
@@ -4929,9 +5306,10 @@ Compressed files like .gz and .bz2 are decompressed."
                                         (mm-extern-cache-contents handle))
                                       (mm-handle-cache handle))
                                   handle)))
                                         (mm-extern-cache-contents handle))
                                       (mm-handle-cache handle))
                                   handle)))
-          (charset (assq 'charset (cdr type))))
-      (when charset
-       (delq charset type)))))
+          (param (assq 'charset (cdr type))))
+      (if param
+         (setcdr param charset)
+       (setcdr type (cons (cons 'charset charset) (cdr type)))))))
 
 (defun gnus-mime-view-part-as-charset (&optional handle arg)
   "Insert the MIME part under point into the current buffer using the
 
 (defun gnus-mime-view-part-as-charset (&optional handle arg)
   "Insert the MIME part under point into the current buffer using the
@@ -4941,15 +5319,18 @@ specified charset."
   (let ((handle (or handle (get-text-property (point) 'gnus-data)))
        (fun (get-text-property (point) 'gnus-callback))
        (gnus-newsgroup-ignored-charsets 'gnus-all)
   (let ((handle (or handle (get-text-property (point) 'gnus-data)))
        (fun (get-text-property (point) 'gnus-callback))
        (gnus-newsgroup-ignored-charsets 'gnus-all)
-       gnus-newsgroup-charset form preferred parts)
+       charset form preferred parts)
     (when handle
     (when handle
-      (if (mm-handle-undisplayer handle)
-         (mm-remove-part handle))
-      (when fun
-       (setq gnus-newsgroup-charset
-             (or (cdr (assq arg gnus-summary-show-article-charset-alist))
-                 (mm-read-coding-system "Charset: ")))
-       (gnus-mime-strip-charset-parameters handle)
+      (when (prog1
+               (and fun
+                    (setq charset
+                          (or (cdr (assq
+                                    arg
+                                    gnus-summary-show-article-charset-alist))
+                              (mm-read-coding-system "Charset: "))))
+             (if (mm-handle-undisplayer handle)
+                 (mm-remove-part handle)))
+       (gnus-mime-set-charset-parameters handle charset)
        (when (and (consp (setq form (cdr-safe fun)))
                   (setq form (ignore-errors
                                (assq 'gnus-mime-display-alternative form)))
        (when (and (consp (setq form (cdr-safe fun)))
                   (setq form (ignore-errors
                                (assq 'gnus-mime-display-alternative form)))
@@ -5010,7 +5391,7 @@ If no internal viewer is available, use an external viewer."
       (when handle
        (if (mm-handle-undisplayer handle)
            (mm-remove-part handle)
       (when handle
        (if (mm-handle-undisplayer handle)
            (mm-remove-part handle)
-         (mm-display-part handle))))))
+         (gnus-bind-safe-url-regexp (mm-display-part handle)))))))
 
 (defun gnus-mime-action-on-part (&optional action)
   "Do something with the MIME attachment at \(point\)."
 
 (defun gnus-mime-action-on-part (&optional action)
   "Do something with the MIME attachment at \(point\)."
@@ -5184,7 +5565,9 @@ N is the numerical prefix."
     1))
 
 (defun gnus-article-view-part (&optional n)
     1))
 
 (defun gnus-article-view-part (&optional n)
-  "View MIME part N, which is the numerical prefix."
+  "View MIME part N, which is the numerical prefix.
+If the part is already shown, hide the part.  If N is nil, view
+all parts."
   (interactive "P")
   (with-current-buffer gnus-article-buffer
     (or (numberp n) (setq n (gnus-article-mime-match-handle-first
   (interactive "P")
   (with-current-buffer gnus-article-buffer
     (or (numberp n) (setq n (gnus-article-mime-match-handle-first
@@ -5231,7 +5614,7 @@ N is the numerical prefix."
                    (save-restriction
                      (narrow-to-region (point)
                                        (if (eobp) (point) (1+ (point))))
                    (save-restriction
                      (narrow-to-region (point)
                                        (if (eobp) (point) (1+ (point))))
-                     (mm-display-part handle)
+                     (gnus-bind-safe-url-regexp (mm-display-part handle))
                      ;; We narrow to the part itself and
                      ;; then call the treatment functions.
                      (goto-char (point-min))
                      ;; We narrow to the part itself and
                      ;; then call the treatment functions.
                      (goto-char (point-min))
@@ -5260,9 +5643,7 @@ N is the numerical prefix."
             (mail-content-type-get (mm-handle-type handle) 'url)
             ""))
        (gnus-tmp-type (mm-handle-media-type handle))
             (mail-content-type-get (mm-handle-type handle) 'url)
             ""))
        (gnus-tmp-type (mm-handle-media-type handle))
-       (gnus-tmp-description
-        (mail-decode-encoded-word-string (or (mm-handle-description handle)
-                                             "")))
+       (gnus-tmp-description (or (mm-handle-description handle) ""))
        (gnus-tmp-dots
         (if (if displayed (car displayed)
               (mm-handle-displayed-p handle))
        (gnus-tmp-dots
         (if (if displayed (car displayed)
               (mm-handle-displayed-p handle))
@@ -5494,7 +5875,8 @@ If displaying \"text/html\" is discouraged \(see
                   ((or (bobp) (eq (char-before (1- (point))) ?\n)) 0)
                   (t 1))))
          (when (or (not display)
                   ((or (bobp) (eq (char-before (1- (point))) ?\n)) 0)
                   (t 1))))
          (when (or (not display)
-                   (not (gnus-unbuttonized-mime-type-p type)))
+                   (not (gnus-unbuttonized-mime-type-p type))
+                   (eq id gnus-mime-buttonized-part-id))
            (gnus-insert-mime-button
             handle id (list (or display (and not-attachment text))))
            (gnus-article-insert-newline)
            (gnus-insert-mime-button
             handle id (list (or display (and not-attachment text))))
            (gnus-article-insert-newline)
@@ -5512,7 +5894,7 @@ If displaying \"text/html\" is discouraged \(see
                                       (set-buffer gnus-summary-buffer)
                                     (error))
                                   gnus-newsgroup-ignored-charsets)))
                                       (set-buffer gnus-summary-buffer)
                                     (error))
                                   gnus-newsgroup-ignored-charsets)))
-             (mm-display-part handle t))
+             (gnus-bind-safe-url-regexp (mm-display-part handle t)))
            (goto-char (point-max)))
           ((and text not-attachment)
            (when move
            (goto-char (point-max)))
           ((and text not-attachment)
            (when move
@@ -5521,8 +5903,9 @@ If displaying \"text/html\" is discouraged \(see
            (gnus-article-insert-newline)
            (mm-insert-inline
             handle
            (gnus-article-insert-newline)
            (mm-insert-inline
             handle
-            (let ((charset (mail-content-type-get (mm-handle-type handle)
-                                                  'charset)))
+            (let ((charset (or (mail-content-type-get (mm-handle-type handle)
+                                                      'charset)
+                               (and (equal type "text/calendar") 'utf-8))))
               (cond ((not charset)
                      (mm-string-as-multibyte (mm-get-part handle)))
                     ((eq charset 'gnus-decoded)
               (cond ((not charset)
                      (mm-string-as-multibyte (mm-get-part handle)))
                     ((eq charset 'gnus-decoded)
@@ -5647,7 +6030,7 @@ If displaying \"text/html\" is discouraged \(see
                  (mail-parse-ignored-charsets
                   (with-current-buffer gnus-summary-buffer
                     gnus-newsgroup-ignored-charsets)))
                  (mail-parse-ignored-charsets
                   (with-current-buffer gnus-summary-buffer
                     gnus-newsgroup-ignored-charsets)))
-             (mm-display-part preferred)
+             (gnus-bind-safe-url-regexp (mm-display-part preferred))
              ;; Do highlighting.
              (save-excursion
                (save-restriction
              ;; Do highlighting.
              (save-excursion
                (save-restriction
@@ -5808,41 +6191,52 @@ the coding cookie."
 If given a numerical ARG, move forward ARG pages."
   (interactive "P")
   (setq arg (if arg (prefix-numeric-value arg) 0))
 If given a numerical ARG, move forward ARG pages."
   (interactive "P")
   (setq arg (if arg (prefix-numeric-value arg) 0))
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (goto-char (point-min))
+  (with-current-buffer gnus-article-buffer
     (widen)
     ;; Remove any old next/prev buttons.
     (when (gnus-visual-p 'page-marker)
       (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
     (widen)
     ;; Remove any old next/prev buttons.
     (when (gnus-visual-p 'page-marker)
       (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
-    (if
-       (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))
-      (save-excursion
-       (goto-char (point-min))
-       (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)
-                (> (point-min) (save-restriction (widen) (point-min))))
-       (save-excursion
-         (goto-char (point-min))
-         (gnus-insert-prev-page-button)))
-      (when (and (gnus-visual-p 'page-marker)
-                (< (point-max) (save-restriction (widen) (point-max))))
-       (save-excursion
-         (goto-char (point-max))
-         (gnus-insert-next-page-button))))))
+    (let (st nd pt)
+      (when (save-excursion
+             (cond ((< arg 0)
+                    (if (re-search-backward page-delimiter nil 'move (abs arg))
+                        (prog1
+                            (setq nd (match-beginning 0)
+                                  pt nd)
+                          (when (re-search-backward page-delimiter nil t)
+                            (setq st (match-end 0))))
+                      (when (re-search-forward page-delimiter nil t)
+                        (setq nd (match-beginning 0)
+                              pt (point-min)))))
+                   ((> arg 0)
+                    (if (re-search-forward page-delimiter nil 'move arg)
+                        (prog1
+                            (setq st (match-end 0)
+                                  pt st)
+                          (when (re-search-forward page-delimiter nil t)
+                            (setq nd (match-beginning 0))))
+                      (when (re-search-backward page-delimiter nil t)
+                        (setq st (match-end 0)
+                              pt (point-max)))))
+                   (t
+                    (when (re-search-backward page-delimiter nil t)
+                      (goto-char (setq st (match-end 0))))
+                    (when (re-search-forward page-delimiter nil t)
+                      (setq nd (match-beginning 0)))
+                    (or st nd))))
+       (setq gnus-page-broken t)
+       (when pt (goto-char pt))
+       (narrow-to-region (or st (point-min)) (or nd (point-max)))
+       (when (gnus-visual-p 'page-marker)
+         (save-excursion
+           (when nd
+             (goto-char nd)
+             (gnus-insert-next-page-button))
+           (when st
+             (goto-char st)
+             (gnus-insert-prev-page-button))))))))
 
 ;; Article mode commands
 
 
 ;; Article mode commands
 
@@ -5857,7 +6251,7 @@ If given a numerical ARG, move forward ARG pages."
 (defun gnus-article-goto-prev-page ()
   "Show the previous page of the article."
   (interactive)
 (defun gnus-article-goto-prev-page ()
   "Show the previous page of the article."
   (interactive)
-  (if (bobp)
+  (if (save-restriction (widen) (bobp)) ;; Real beginning-of-buffer?
       (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
     (gnus-article-prev-page nil)))
 
       (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
     (gnus-article-prev-page nil)))
 
@@ -5880,11 +6274,16 @@ If given a numerical ARG, move forward ARG pages."
 If end of article, return non-nil.  Otherwise return nil.
 Argument LINES specifies lines to be scrolled up."
   (interactive "p")
 If end of article, return non-nil.  Otherwise return nil.
 Argument LINES specifies lines to be scrolled up."
   (interactive "p")
-  (move-to-window-line -1)
-  (if (save-excursion
-       (end-of-line)
-       (and (pos-visible-in-window-p)  ;Not continuation line.
-            (>= (1+ (point)) (point-max)))) ;Allow for trailing newline.
+  (move-to-window-line (if (featurep 'xemacs) -1 (- -1 scroll-margin)))
+  (if (and (not (and gnus-article-over-scroll
+                    (> (count-lines (window-start) (point-max))
+                       (if (featurep 'xemacs)
+                           (or lines (1- (window-height)))
+                         (+ (or lines (1- (window-height))) scroll-margin)))))
+          (save-excursion
+            (end-of-line)
+            (and (pos-visible-in-window-p)     ;Not continuation line.
+                 (>= (1+ (point)) (point-max))))) ;Allow for trailing newline.
       ;; Nothing in this page.
       (if (or (not gnus-page-broken)
              (save-excursion
       ;; Nothing in this page.
       (if (or (not gnus-page-broken)
              (save-excursion
@@ -5902,29 +6301,33 @@ Argument LINES specifies lines to be scrolled up."
     (gnus-article-next-page-1 lines)
     nil))
 
     (gnus-article-next-page-1 lines)
     nil))
 
-(defmacro gnus-article-beginning-of-window ()
+(defun gnus-article-beginning-of-window ()
   "Move point to the beginning of the window.
 In Emacs, the point is placed at the line number which `scroll-margin'
 specifies."
   (if (featurep 'xemacs)
   "Move point to the beginning of the window.
 In Emacs, the point is placed at the line number which `scroll-margin'
 specifies."
   (if (featurep 'xemacs)
-      '(move-to-window-line 0)
-    '(move-to-window-line
-      (min (max 0 scroll-margin)
-          (max 1 (- (window-height)
-                    (if mode-line-format 1 0)
-                    (if header-line-format 1 0)))))))
+      (move-to-window-line 0)
+    ;; There is an obscure bug in Emacs that makes it impossible to
+    ;; scroll past big pictures in the article buffer.  Try to fix
+    ;; this by adding a sanity check by counting the lines visible.
+    (when (> (count-lines (window-start) (window-end)) 30)
+      (move-to-window-line
+       (min (max 0 scroll-margin)
+           (max 1 (- (window-height)
+                     (if mode-line-format 1 0)
+                     (if header-line-format 1 0)
+                     2)))))))
 
 (defun gnus-article-next-page-1 (lines)
 
 (defun gnus-article-next-page-1 (lines)
-  (when (and (not (featurep 'xemacs))
-            (numberp lines)
-            (> lines 0)
-            (numberp (symbol-value 'scroll-margin))
-            (> (symbol-value 'scroll-margin) 0))
+  (unless (featurep 'xemacs)
     ;; Protect against the bug that Emacs 21.x hangs up when scrolling up for
     ;; too many number of lines if `scroll-margin' is set as two or greater.
     ;; Protect against the bug that Emacs 21.x hangs up when scrolling up for
     ;; too many number of lines if `scroll-margin' is set as two or greater.
-    (setq lines (min lines
-                    (max 0 (- (count-lines (window-start) (point-max))
-                              (symbol-value 'scroll-margin))))))
+    (when (and (numberp lines)
+              (> lines 0)
+              (> scroll-margin 0))
+      (setq lines (min lines
+                      (max 0 (- (count-lines (window-start) (point-max))
+                                scroll-margin))))))
   (condition-case ()
       (let ((scroll-in-place nil))
        (scroll-up lines))
   (condition-case ()
       (let ((scroll-in-place nil))
        (scroll-up lines))
@@ -5944,7 +6347,14 @@ Argument LINES specifies lines to be scrolled down."
       (progn
        (gnus-narrow-to-page -1)        ;Go to previous page.
        (goto-char (point-max))
       (progn
        (gnus-narrow-to-page -1)        ;Go to previous page.
        (goto-char (point-max))
-       (recenter -1))
+       (recenter (if gnus-article-over-scroll
+                     (if lines
+                         (max (if (featurep 'xemacs)
+                                  lines
+                                (+ lines scroll-margin))
+                              3)
+                       (- (window-height) 2))
+                   -1)))
     (prog1
        (condition-case ()
            (let ((scroll-in-place nil))
     (prog1
        (condition-case ()
            (let ((scroll-in-place nil))
@@ -5996,28 +6406,7 @@ not have a face in `gnus-article-boring-faces'."
 (defun gnus-article-describe-briefly ()
   "Describe article mode commands briefly."
   (interactive)
 (defun gnus-article-describe-briefly ()
   "Describe article mode commands briefly."
   (interactive)
-  (gnus-message 6 (substitute-command-keys "\\<gnus-article-mode-map>\\[gnus-article-goto-next-page]:Next page  \\[gnus-article-goto-prev-page]:Prev page  \\[gnus-article-show-summary]:Show summary  \\[gnus-info-find-node]:Run Info  \\[gnus-article-describe-briefly]:This help")))
-
-(defun gnus-article-summary-command ()
-  "Execute the last keystroke in the summary buffer."
-  (interactive)
-  (let ((obuf (current-buffer))
-       (owin (current-window-configuration))
-       func)
-    (switch-to-buffer gnus-article-current-summary 'norecord)
-    (setq func (lookup-key (current-local-map) (this-command-keys)))
-    (call-interactively func)
-    (set-buffer obuf)
-    (set-window-configuration owin)
-    (set-window-point (get-buffer-window (current-buffer)) (point))))
-
-(defun gnus-article-summary-command-nosave ()
-  "Execute the last keystroke in the summary buffer."
-  (interactive)
-  (let (func)
-    (pop-to-buffer gnus-article-current-summary)
-    (setq func (lookup-key (current-local-map) (this-command-keys)))
-    (call-interactively func)))
+  (gnus-message 6 "%s" (substitute-command-keys "\\<gnus-article-mode-map>\\[gnus-article-goto-next-page]:Next page     \\[gnus-article-goto-prev-page]:Prev page  \\[gnus-article-show-summary]:Show summary  \\[gnus-info-find-node]:Run Info  \\[gnus-article-describe-briefly]:This help")))
 
 (defun gnus-article-check-buffer ()
   "Beep if not in an article buffer."
 
 (defun gnus-article-check-buffer ()
   "Beep if not in an article buffer."
@@ -6033,26 +6422,26 @@ not have a face in `gnus-article-boring-faces'."
           "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
           "=" "^" "\M-^" "|"))
        (nosave-but-article
           "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
           "=" "^" "\M-^" "|"))
        (nosave-but-article
-        '("A\r"))
+        '("A " "A<" "A>" "AM" "AP" "AR" "AT" "A\C-?" "A\M-\r" "A\r" "Ab" "Ae"
+          "An" "Ap" [?A (meta return)] [?A delete]))
        (nosave-in-article
        (nosave-in-article
-        '("\C-d"))
+        '("AS" "\C-d"))
        (up-to-top
         '("n" "Gn" "p" "Gp"))
        keys new-sum-point)
        (up-to-top
         '("n" "Gn" "p" "Gp"))
        keys new-sum-point)
-    (save-excursion
-      (set-buffer gnus-article-current-summary)
+    (with-current-buffer gnus-article-current-summary
       (let (gnus-pick-mode)
       (let (gnus-pick-mode)
-       (push (or key last-command-event) unread-command-events)
-       (setq keys (if (featurep 'xemacs)
-                      (events-to-keys (read-key-sequence nil))
-                    (read-key-sequence nil)))))
+       (setq unread-command-events (nconc unread-command-events
+                                          (list (or key last-command-event)))
+             keys (if (featurep 'xemacs)
+                      (events-to-keys (read-key-sequence nil t))
+                    (read-key-sequence nil t)))))
 
     (message "")
 
     (cond
      ((eq (aref keys (1- (length keys))) ?\C-h)
 
     (message "")
 
     (cond
      ((eq (aref keys (1- (length keys))) ?\C-h)
-      (with-current-buffer gnus-article-current-summary
-       (describe-bindings (substring keys 0 -1))))
+      (gnus-article-describe-bindings (substring keys 0 -1)))
      ((or (member keys nosaves)
          (member keys nosave-but-article)
          (member keys nosave-in-article))
      ((or (member keys nosaves)
          (member keys nosave-but-article)
          (member keys nosave-in-article))
@@ -6061,7 +6450,7 @@ not have a face in `gnus-article-boring-faces'."
          (pop-to-buffer gnus-article-current-summary)
          ;; We disable the pick minor mode commands.
          (let (gnus-pick-mode)
          (pop-to-buffer gnus-article-current-summary)
          ;; We disable the pick minor mode commands.
          (let (gnus-pick-mode)
-           (setq func (lookup-key (current-local-map) keys))))
+           (setq func (key-binding keys t))))
        (if (or (not func)
                (numberp func))
            (ding)
        (if (or (not func)
                (numberp func))
            (ding)
@@ -6086,9 +6475,9 @@ not have a face in `gnus-article-boring-faces'."
                 (gnus-configure-windows 'article)
                 (unless (setq win (get-buffer-window summary-buffer 'visible))
                   (let ((gnus-buffer-configuration
                 (gnus-configure-windows 'article)
                 (unless (setq win (get-buffer-window summary-buffer 'visible))
                   (let ((gnus-buffer-configuration
-                         '(article ((vertical 1.0
-                                              (summary 0.25 point)
-                                              (article 1.0))))))
+                         '((article ((vertical 1.0
+                                               (summary 0.25 point)
+                                               (article 1.0)))))))
                     (gnus-configure-windows 'article))
                   (setq win (get-buffer-window summary-buffer 'visible)))
                 (gnus-select-frame-set-input-focus (window-frame win))
                     (gnus-configure-windows 'article))
                   (setq win (get-buffer-window summary-buffer 'visible)))
                 (gnus-select-frame-set-input-focus (window-frame win))
@@ -6096,7 +6485,7 @@ not have a face in `gnus-article-boring-faces'."
        (setq in-buffer (current-buffer))
        ;; We disable the pick minor mode commands.
        (if (and (setq func (let (gnus-pick-mode)
        (setq in-buffer (current-buffer))
        ;; We disable the pick minor mode commands.
        (if (and (setq func (let (gnus-pick-mode)
-                             (lookup-key (current-local-map) keys)))
+                             (key-binding keys t)))
                 (functionp func)
                 (condition-case code
                     (progn
                 (functionp func)
                 (condition-case code
                     (progn
@@ -6128,6 +6517,7 @@ not have a face in `gnus-article-boring-faces'."
                                      (point))))
                (when (and (not not-restore-window)
                           new-sum-point
                                      (point))))
                (when (and (not not-restore-window)
                           new-sum-point
+                          (window-live-p win)
                           (with-current-buffer (window-buffer win)
                             (eq major-mode 'gnus-summary-mode)))
                  (set-window-point win new-sum-point)
                           (with-current-buffer (window-buffer win)
                             (eq major-mode 'gnus-summary-mode)))
                  (set-window-point win new-sum-point)
@@ -6138,53 +6528,119 @@ not have a face in `gnus-article-boring-faces'."
              (signal (car err) (cdr err))
            (ding))))))))
 
              (signal (car err) (cdr err))
            (ding))))))))
 
+(defun gnus-article-read-summary-send-keys ()
+  (interactive)
+  (let ((unread-command-events (list (gnus-character-to-event ?S))))
+    (gnus-article-read-summary-keys)))
+
 (defun gnus-article-describe-key (key)
 (defun gnus-article-describe-key (key)
-  "Display documentation of the function invoked by KEY.  KEY is a string."
-  (interactive "kDescribe key: ")
+  "Display documentation of the function invoked by KEY.
+KEY is a string or a vector."
+  (interactive (list (let ((cursor-in-echo-area t)) ;; better for XEmacs.
+                      (read-key-sequence "Describe key: "))))
   (gnus-article-check-buffer)
   (gnus-article-check-buffer)
-  (if (eq (key-binding key) 'gnus-article-read-summary-keys)
-      (save-excursion
-       (set-buffer gnus-article-current-summary)
-       (let (gnus-pick-mode)
-         (if (featurep 'xemacs)
-             (progn
-               (push (elt key 0) unread-command-events)
-               (setq key (events-to-keys
-                          (read-key-sequence "Describe key: "))))
-           (setq unread-command-events
-                 (mapcar
-                  (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
-                  (string-to-list key)))
-           (setq key (read-key-sequence "Describe key: "))))
-       (describe-key key))
+  (if (memq (key-binding key t) '(gnus-article-read-summary-keys
+                                 gnus-article-read-summary-send-keys))
+      (with-current-buffer gnus-article-current-summary
+       (setq unread-command-events
+             (if (featurep 'xemacs)
+                 (append key nil)
+               (mapcar (lambda (x) (if (and (integerp x) (>= x 128))
+                                       (list 'meta (- x 128))
+                                     x))
+                       key)))
+       (let ((cursor-in-echo-area t)
+             gnus-pick-mode)
+         (describe-key (read-key-sequence nil t))))
     (describe-key key)))
 
 (defun gnus-article-describe-key-briefly (key &optional insert)
     (describe-key key)))
 
 (defun gnus-article-describe-key-briefly (key &optional insert)
-  "Display documentation of the function invoked by KEY.  KEY is a string."
-  (interactive "kDescribe key: \nP")
+  "Display documentation of the function invoked by KEY.
+KEY is a string or a vector."
+  (interactive (list (let ((cursor-in-echo-area t)) ;; better for XEmacs.
+                      (read-key-sequence "Describe key: "))
+                    current-prefix-arg))
   (gnus-article-check-buffer)
   (gnus-article-check-buffer)
-  (if (eq (key-binding key) 'gnus-article-read-summary-keys)
-      (save-excursion
-       (set-buffer gnus-article-current-summary)
-       (let (gnus-pick-mode)
-         (if (featurep 'xemacs)
-             (progn
-               (push (elt key 0) unread-command-events)
-               (setq key (events-to-keys
-                          (read-key-sequence "Describe key: "))))
-           (setq unread-command-events
-                 (mapcar
-                  (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
-                  (string-to-list key)))
-           (setq key (read-key-sequence "Describe key: "))))
-       (describe-key-briefly key insert))
+  (if (memq (key-binding key t) '(gnus-article-read-summary-keys
+                                 gnus-article-read-summary-send-keys))
+      (with-current-buffer gnus-article-current-summary
+       (setq unread-command-events
+             (if (featurep 'xemacs)
+                 (append key nil)
+               (mapcar (lambda (x) (if (and (integerp x) (>= x 128))
+                                       (list 'meta (- x 128))
+                                     x))
+                       key)))
+       (let ((cursor-in-echo-area t)
+             gnus-pick-mode)
+         (describe-key-briefly (read-key-sequence nil t) insert)))
     (describe-key-briefly key insert)))
 
     (describe-key-briefly key insert)))
 
+;;`gnus-agent-mode' in gnus-agent.el will define it.
+(defvar gnus-agent-summary-mode)
+(defvar gnus-draft-mode)
+;; Calling help-buffer will autoload help-mode.
+(defvar help-xref-stack-item)
+
+(defun gnus-article-describe-bindings (&optional prefix)
+  "Show a list of all defined keys, and their definitions.
+The optional argument PREFIX, if non-nil, should be a key sequence;
+then we display only bindings that start with that prefix."
+  (interactive)
+  (gnus-article-check-buffer)
+  (let ((keymap (copy-keymap gnus-article-mode-map))
+       (map (copy-keymap gnus-article-send-map))
+       (sumkeys (where-is-internal 'gnus-article-read-summary-keys))
+       parent agent draft)
+    (define-key keymap "S" map)
+    (define-key map [t] nil)
+    (with-current-buffer gnus-article-current-summary
+      (set-keymap-parent
+       keymap
+       (if (setq parent (keymap-parent gnus-article-mode-map))
+          (prog1
+              (setq parent (copy-keymap parent))
+            (set-keymap-parent parent (current-local-map)))
+        (current-local-map)))
+      (set-keymap-parent map (key-binding "S"))
+      (let (key def gnus-pick-mode)
+       (while sumkeys
+         (setq key (pop sumkeys))
+         (cond ((and (vectorp key) (= (length key) 1)
+                     (consp (setq def (aref key 0)))
+                     (numberp (car def)) (numberp (cdr def)))
+                (when (< (max (car def) (cdr def)) 128)
+                  (setq sumkeys
+                        (append (mapcar
+                                 #'vector
+                                 (nreverse (gnus-uncompress-range def)))
+                                sumkeys))))
+               ((setq def (key-binding key))
+                (unless (eq def 'undefined)
+                  (define-key keymap key def))))))
+      (when (boundp 'gnus-agent-summary-mode)
+       (setq agent gnus-agent-summary-mode))
+      (when (boundp 'gnus-draft-mode)
+       (setq draft gnus-draft-mode)))
+    (with-temp-buffer
+      (use-local-map keymap)
+      (set (make-local-variable 'gnus-agent-summary-mode) agent)
+      (set (make-local-variable 'gnus-draft-mode) draft)
+      (describe-bindings prefix))
+    (let ((item `((lambda (prefix)
+                   (with-current-buffer ,(current-buffer)
+                     (gnus-article-describe-bindings prefix)))
+                 ,prefix)))
+      (with-current-buffer (if (fboundp 'help-buffer)
+                              (let (help-xref-following) (help-buffer))
+                            "*Help*") ;; Emacs 21
+       (setq help-xref-stack-item item)))))
+
 (defun gnus-article-reply-with-original (&optional wide)
   "Start composing a reply mail to the current message.
 The text in the region will be yanked.  If the region isn't active,
 the entire article will be yanked."
 (defun gnus-article-reply-with-original (&optional wide)
   "Start composing a reply mail to the current message.
 The text in the region will be yanked.  If the region isn't active,
 the entire article will be yanked."
-  (interactive "P")
+  (interactive)
   (let ((article (cdr gnus-article-current))
        contents)
     (if (not (gnus-region-active-p))
   (let ((article (cdr gnus-article-current))
        contents)
     (if (not (gnus-region-active-p))
@@ -6199,6 +6655,13 @@ the entire article will be yanked."
        (gnus-summary-reply
         (list (list article contents)) wide)))))
 
        (gnus-summary-reply
         (list (list article contents)) wide)))))
 
+(defun gnus-article-wide-reply-with-original ()
+  "Start composing a wide reply mail to the current message.
+The text in the region will be yanked.  If the region isn't active,
+the entire article will be yanked."
+  (interactive)
+  (gnus-article-reply-with-original t))
+
 (defun gnus-article-followup-with-original ()
   "Compose a followup to the current article.
 The text in the region will be yanked.  If the region isn't active,
 (defun gnus-article-followup-with-original ()
   "Compose a followup to the current article.
 The text in the region will be yanked.  If the region isn't active,
@@ -6269,8 +6732,7 @@ If given a prefix, show the hidden text instead."
                     gnus-summary-buffer
                     (get-buffer gnus-summary-buffer)
                     (gnus-buffer-exists-p gnus-summary-buffer))
                     gnus-summary-buffer
                     (get-buffer gnus-summary-buffer)
                     (gnus-buffer-exists-p gnus-summary-buffer))
-           (save-excursion
-             (set-buffer gnus-summary-buffer)
+           (with-current-buffer gnus-summary-buffer
              (let ((header (gnus-summary-article-header article)))
                (when (< article 0)
                  (cond
              (let ((header (gnus-summary-article-header article)))
                (when (< article 0)
                  (cond
@@ -6316,7 +6778,13 @@ If given a prefix, show the hidden text instead."
                 (with-current-buffer gnus-original-article-buffer
                   (and (equal (car gnus-original-article) group)
                        (eq (cdr gnus-original-article) article))))
                 (with-current-buffer gnus-original-article-buffer
                   (and (equal (car gnus-original-article) group)
                        (eq (cdr gnus-original-article) article))))
-           (insert-buffer-substring gnus-original-article-buffer)
+            ;; `insert-buffer-substring' would incorrectly use the
+            ;; equivalent of string-make-multibyte which amount to decoding
+            ;; with locale-coding-system, causing failure of
+            ;; subsequent decoding.
+            (insert (mm-string-to-multibyte
+                     (with-current-buffer gnus-original-article-buffer
+                       (buffer-substring (point-min) (point-max)))))
            'article)
           ;; Check the backlog.
           ((and gnus-keep-backlog
            'article)
           ;; Check the backlog.
           ((and gnus-keep-backlog
@@ -6562,9 +7030,8 @@ groups."
       (gnus-backlog-remove-article
        (car gnus-article-current) (cdr gnus-article-current)))
     ;; Flush original article as well.
       (gnus-backlog-remove-article
        (car gnus-article-current) (cdr gnus-article-current)))
     ;; Flush original article as well.
-    (save-excursion
-      (when (get-buffer gnus-original-article-buffer)
-       (set-buffer gnus-original-article-buffer)
+    (when (get-buffer gnus-original-article-buffer)
+      (with-current-buffer gnus-original-article-buffer
        (setq gnus-original-article nil)))
     (when gnus-use-cache
       (gnus-cache-update-article
        (setq gnus-original-article nil)))
     (when gnus-use-cache
       (gnus-cache-update-article
@@ -6629,7 +7096,8 @@ groups."
         (concat
          "\\(?:"
          ;; Match paired parentheses, e.g. in Wikipedia URLs:
         (concat
          "\\(?:"
          ;; Match paired parentheses, e.g. in Wikipedia URLs:
-         "[" chars punct "]+" "(" "[" chars punct "]+" "[" chars "]*)" "[" chars "]"
+         ;; http://thread.gmane.org/47B4E3B2.3050402@gmail.com
+         "[" chars punct "]+" "(" "[" chars punct "]+" "[" chars "]*)" "[" chars "]*"
          "\\|"
          "[" chars punct     "]+" "[" chars "]"
          "\\)"))
          "\\|"
          "[" chars punct     "]+" "[" chars "]"
          "\\)"))
@@ -6765,6 +7233,7 @@ must return `mid', `mail', `invalid' or `ask'."
     (-20.0 . "\\.fsf@")        ;; Gnus
     (-20.0 . "^slrn")
     (-20.0 . "^Pine")
     (-20.0 . "\\.fsf@")        ;; Gnus
     (-20.0 . "^slrn")
     (-20.0 . "^Pine")
+    (-20.0 . "^alpine\\.")
     (-20.0 . "_-_") ;; Subject change in thread
     ;;
     (-20.0 . "\\.ln@") ;; leafnode
     (-20.0 . "_-_") ;; Subject change in thread
     ;;
     (-20.0 . "\\.ln@") ;; leafnode
@@ -7062,7 +7531,11 @@ positives are possible."
       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)
       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>
+    ;; Info Konqueror style <info:/foo/bar baz>.
+    ;; Must come before " Gnus home-grown style".
+    ("\\binfo://?\\([^'\">\n\t]+\\)"
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
+   ;; Info, Gnus home-grown style (deprecated) <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 ]+\\)"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
     ;; Info GNOME style <info:foo#bar_baz>
@@ -7073,9 +7546,9 @@ positives are possible."
      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)
      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)
+    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET\\([ \t\n,]*\\)\\)?"
+     ;; Info links like `C-h i d m Gnus RET' or `C-h i d m Gnus RET i partial RET'
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 0)
     ;; This is custom
     ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1 2)
     ;; This is custom
     ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1 2)
@@ -7113,13 +7586,10 @@ positives are possible."
      ;; here to determine where it ends.
      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)...
      ;; here to determine where it ends.
      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: *\\([^<>]*\\)>"
+    ("<URL: *\\([^\n<>]*\\)>"
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; RFC 2396 (2.4.3., delims) ...
      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: *\\([^\"]*\\)\""
+    ("\"URL: *\\([^\n\"]*\\)\""
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; Raw URLs.
     (gnus-button-url-regexp
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; Raw URLs.
     (gnus-button-url-regexp
@@ -7171,6 +7641,7 @@ variable it the real callback function."
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
+(put 'gnus-button-alist 'risky-local-variable t)
 
 (defcustom gnus-header-button-alist
   '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
 
 (defcustom gnus-header-button-alist
   '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
@@ -7210,6 +7681,7 @@ HEADER is a regexp to match a header.  For a fuller explanation, see
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
+(put 'gnus-header-button-alist 'risky-local-variable t)
 
 ;;; Commands:
 
 
 ;;; Commands:
 
@@ -7301,7 +7773,7 @@ It does this by highlighting everything after
       (save-restriction
        (when (and gnus-signature-face
                   (gnus-article-narrow-to-signature))
       (save-restriction
        (when (and gnus-signature-face
                   (gnus-article-narrow-to-signature))
-         (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
+         (gnus-overlay-put (gnus-make-overlay (point-min) (point-max) nil t)
                            'face gnus-signature-face)
          (widen)
          (gnus-article-search-signature)
                            'face gnus-signature-face)
          (widen)
          (gnus-article-search-signature)
@@ -7343,19 +7815,83 @@ specified by `gnus-button-alist'."
        (setq regexp (eval (car entry)))
        (goto-char beg)
        (while (re-search-forward regexp nil t)
        (setq regexp (eval (car entry)))
        (goto-char beg)
        (while (re-search-forward regexp nil t)
-         (let* ((start (and entry (match-beginning (nth 1 entry))))
-                (end (and entry (match-end (nth 1 entry))))
-                (from (match-beginning 0)))
+         (let ((start (match-beginning (nth 1 entry)))
+               (end (match-end (nth 1 entry)))
+               (from (match-beginning 0)))
            (when (and (or (eq t (nth 2 entry))
                           (eval (nth 2 entry)))
                       (not (gnus-button-in-region-p
                             start end 'gnus-callback)))
              ;; That optional form returned non-nil, so we add the
              ;; button.
            (when (and (or (eq t (nth 2 entry))
                           (eval (nth 2 entry)))
                       (not (gnus-button-in-region-p
                             start end 'gnus-callback)))
              ;; That optional form returned non-nil, so we add the
              ;; button.
-             (gnus-article-add-button
-              start end 'gnus-button-push
-              (car (push (set-marker (make-marker) from)
-                         gnus-button-marker-list))))))))))
+             (setq from (set-marker (make-marker) from))
+             (push from gnus-button-marker-list)
+             (unless (and (eq (car entry) 'gnus-button-url-regexp)
+                          (gnus-article-extend-url-button from start end))
+               (gnus-article-add-button start end
+                                        'gnus-button-push from)
+               (gnus-put-text-property
+                start end
+                'gnus-string (buffer-substring-no-properties
+                              start end))))))))))
+
+(defun gnus-article-extend-url-button (beg start end)
+  "Extend url button if url is folded into two or more lines.
+Return non-nil if button is extended.  BEG is a marker that points to
+the beginning position of a text containing url.  START and END are
+the endpoints of a url button before it is extended.  The concatenated
+url is put as the `gnus-button-url' overlay property on the button."
+  (let ((opoint (point))
+       (points (list start end))
+       url delim regexp)
+    (prog1
+       (when (and (progn
+                    (goto-char end)
+                    (not (looking-at "[\t ]*[\">]")))
+                  (progn
+                    (goto-char start)
+                    (string-match
+                     "\\(?:\"\\|\\(<\\)\\)[\t ]*\\(?:url[\t ]*:[\t ]*\\)?\\'"
+                     (buffer-substring (point-at-bol) start)))
+                  (progn
+                    (setq url (list (buffer-substring start end))
+                          delim (if (match-beginning 1) ">" "\""))
+                    (beginning-of-line)
+                    (setq regexp (concat
+                                  (when (and (looking-at
+                                              message-cite-prefix-regexp)
+                                             (< (match-end 0) start))
+                                    (regexp-quote (match-string 0)))
+                                  "\
+\[\t ]*\\(?:\\([^\t\n \">]+\\)[\t ]*$\\|\\([^\t\n \">]*\\)[\t ]*"
+                                  delim "\\)"))
+                    (while (progn
+                             (forward-line 1)
+                             (and (looking-at regexp)
+                                  (prog1
+                                      (match-beginning 1)
+                                    (push (or (match-string 2)
+                                              (match-string 1))
+                                          url)
+                                    (push (setq end (or (match-end 2)
+                                                        (match-end 1)))
+                                          points)
+                                    (push (or (match-beginning 2)
+                                              (match-beginning 1))
+                                          points)))))
+                    (match-beginning 2)))
+         (let (gnus-article-mouse-face widget-mouse-face)
+           (while points
+             (gnus-article-add-button (pop points) (pop points)
+                                      'gnus-button-push beg)))
+         (let ((overlay (gnus-make-overlay start end)))
+           (gnus-overlay-put overlay 'evaporate t)
+           (gnus-overlay-put overlay 'gnus-button-url
+                             (list (mapconcat 'identity (nreverse url) "")))
+           (when gnus-article-mouse-face
+             (gnus-overlay-put overlay 'mouse-face gnus-article-mouse-face)))
+         t)
+      (goto-char opoint))))
 
 ;; Add buttons to the head of an article.
 (defun gnus-article-add-buttons-to-head ()
 
 ;; Add buttons to the head of an article.
 (defun gnus-article-add-buttons-to-head ()
@@ -7389,10 +7925,10 @@ specified by `gnus-button-alist'."
 
 ;;; External functions:
 
 
 ;;; External functions:
 
-(defun gnus-article-add-button (from to fun &optional data)
+(defun gnus-article-add-button (from to fun &optional data text)
   "Create a button between FROM and TO with callback FUN and data DATA."
   (when gnus-article-button-face
   "Create a button between FROM and TO with callback FUN and data DATA."
   (when gnus-article-button-face
-    (gnus-overlay-put (gnus-make-overlay from to)
+    (gnus-overlay-put (gnus-make-overlay from to nil t)
                      'face gnus-article-button-face))
   (gnus-add-text-properties
    from to
                      'face gnus-article-button-face))
   (gnus-add-text-properties
    from to
@@ -7401,8 +7937,21 @@ specified by `gnus-button-alist'."
          (list 'gnus-callback fun)
          (and data (list 'gnus-data data))))
   (widget-convert-button 'link from to :action 'gnus-widget-press-button
          (list 'gnus-callback fun)
          (and data (list 'gnus-data data))))
   (widget-convert-button 'link from to :action 'gnus-widget-press-button
+                        :help-echo (or text "Follow the link")
+                        :keymap gnus-url-button-map
                         :button-keymap gnus-widget-button-keymap))
 
                         :button-keymap gnus-widget-button-keymap))
 
+(defun gnus-article-copy-string ()
+  "Copy the string in the button to the kill ring."
+  (interactive)
+  (gnus-article-check-buffer)
+  (let ((data (get-text-property (point) 'gnus-string)))
+    (when data
+      (with-temp-buffer
+       (insert data)
+       (copy-region-as-kill (point-min) (point-max))
+       (message "Copied %s" data)))))
+
 ;;; Internal functions:
 
 (defun gnus-article-set-globals ()
 ;;; Internal functions:
 
 (defun gnus-article-set-globals ()
@@ -7445,12 +7994,14 @@ specified by `gnus-button-alist'."
     (let* ((entry (gnus-button-entry))
           (inhibit-point-motion-hooks t)
           (fun (nth 3 entry))
     (let* ((entry (gnus-button-entry))
           (inhibit-point-motion-hooks t)
           (fun (nth 3 entry))
-          (args (mapcar (lambda (group)
-                          (let ((string (match-string group)))
-                            (set-text-properties
-                             0 (length string) nil string)
-                            string))
-                        (nthcdr 4 entry))))
+          (args (or (and (eq (car entry) 'gnus-button-url-regexp)
+                         (get-char-property marker 'gnus-button-url))
+                    (mapcar (lambda (group)
+                              (let ((string (match-string group)))
+                                (set-text-properties
+                                 0 (length string) nil string)
+                                string))
+                            (nthcdr 4 entry)))))
       (cond
        ((fboundp fun)
        (apply fun args))
       (cond
        ((fboundp fun)
        (apply fun args))
@@ -7494,8 +8045,7 @@ specified by `gnus-button-alist'."
       (gnus-parse-news-url url)
     (cond
      (message-id
       (gnus-parse-news-url url)
     (cond
      (message-id
-      (save-excursion
-       (set-buffer gnus-summary-buffer)
+      (with-current-buffer gnus-summary-buffer
        (if server
            (let ((gnus-refer-article-method
                   (nconc (list (list 'nntp server))
        (if server
            (let ((gnus-refer-article-method
                   (nconc (list (list 'nntp server))
@@ -7515,7 +8065,8 @@ specified by `gnus-button-alist'."
     (unless file
       (error "Couldn't find library %s" library))
     (find-file file)
     (unless file
       (error "Couldn't find library %s" library))
     (find-file file)
-    (goto-line (string-to-number line))))
+    (goto-char (point-min))
+    (forward-line (1- (string-to-number line)))))
 
 (defun gnus-button-handle-man (url)
   "Fetch a man page."
 
 (defun gnus-button-handle-man (url)
   "Fetch a man page."
@@ -7559,12 +8110,45 @@ specified by `gnus-button-alist'."
   "Fetch KDE style info URL."
   (gnus-info-find-node (gnus-url-unhex-string url)))
 
   "Fetch KDE style info URL."
   (gnus-info-find-node (gnus-url-unhex-string url)))
 
+;; (info) will autoload info.el
+(declare-function Info-menu "info" (menu-item &optional fork))
+(declare-function Info-index-next "info" (num))
+
 (defun gnus-button-handle-info-keystrokes (url)
   "Call `info' when pushing the corresponding URL button."
 (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))
+  ;; For links like `C-h i d m gnus RET part RET , ,', `C-h i d m CC Mode RET'.
+  (let (node indx comma)
+    (if (string-match
+        (concat "\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+"
+                "\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+                "\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET"
+                "\\(?:[ \t\n,]*\\)\\)?")
+        url)
+       (setq node (match-string 2 url)
+             indx (match-string 3 url))
+      (error "Can't parse %s" url))
+    (info)
+    (Info-directory)
+    (Info-menu node)
+    (when (> (length indx) 0)
+      (string-match (concat "[ \t\n]+i[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+                           "\\([ \t\n,]*\\)")
+                   indx)
+      (setq comma (match-string 2 indx))
+      (setq indx  (match-string 1 indx))
+      (Info-index indx)
+      (when comma
+       (dotimes (i (with-temp-buffer
+                     (insert comma)
+                     ;; Note: the XEmacs version of `how-many' takes
+                     ;; no optional argument.
+                     (goto-char (point-min))
+                     (how-many ",")))
+         (Info-index-next 1)))
+      nil)))
+
+;; Called after pgg-snarf-keys-region, which autoloads pgg.el.
+(declare-function pgg-display-output-buffer "pgg" (start end status))
 
 (defun gnus-button-openpgp (url)
   "Retrieve and add an OpenPGP key given URL from an OpenPGP header."
 
 (defun gnus-button-openpgp (url)
   "Retrieve and add an OpenPGP key given URL from an OpenPGP header."
@@ -7687,7 +8271,7 @@ specified by `gnus-button-alist'."
                (1- (point))
              (point)))
     (when gnus-article-button-face
                (1- (point))
              (point)))
     (when gnus-article-button-face
-      (gnus-overlay-put (gnus-make-overlay b e)
+      (gnus-overlay-put (gnus-make-overlay b e nil t)
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
@@ -7723,7 +8307,7 @@ specified by `gnus-button-alist'."
                (1- (point))
              (point)))
     (when gnus-article-button-face
                (1- (point))
              (point)))
     (when gnus-article-button-face
-      (gnus-overlay-put (gnus-make-overlay b e)
+      (gnus-overlay-put (gnus-make-overlay b e nil t)
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
@@ -7769,14 +8353,13 @@ For example:
               (eq gnus-newsgroup-name
                   (car gnus-decode-header-methods-cache)))
     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
               (eq gnus-newsgroup-name
                   (car gnus-decode-header-methods-cache)))
     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
-    (mapcar (lambda (x)
-             (if (symbolp x)
-                 (nconc gnus-decode-header-methods-cache (list x))
-               (if (and gnus-newsgroup-name
-                        (string-match (car x) gnus-newsgroup-name))
-                   (nconc gnus-decode-header-methods-cache
-                          (list (cdr x))))))
-         gnus-decode-header-methods))
+    (dolist (x gnus-decode-header-methods)
+      (if (symbolp x)
+         (nconc gnus-decode-header-methods-cache (list x))
+       (if (and gnus-newsgroup-name
+                (string-match (car x) gnus-newsgroup-name))
+           (nconc gnus-decode-header-methods-cache
+                  (list (cdr x)))))))
   (let ((xlist gnus-decode-header-methods-cache))
     (pop xlist)
     (save-restriction
   (let ((xlist gnus-decode-header-methods-cache))
     (pop xlist)
     (save-restriction
@@ -7817,12 +8400,11 @@ For example:
          (funcall (cadr elem)))))))
 
 ;; Dynamic variables.
          (funcall (cadr elem)))))))
 
 ;; Dynamic variables.
-(eval-when-compile
-  (defvar part-number)
-  (defvar total-parts)
-  (defvar type)
-  (defvar condition)
-  (defvar length))
+(defvar part-number)
+(defvar total-parts)
+(defvar type)
+(defvar condition)
+(defvar length)
 
 (defun gnus-treat-predicate (val)
   (cond
 
 (defun gnus-treat-predicate (val)
   (cond
@@ -7870,6 +8452,11 @@ For example:
                         gnus-article-encrypt-protocol-alist
                         nil t))
     current-prefix-arg))
                         gnus-article-encrypt-protocol-alist
                         nil t))
     current-prefix-arg))
+  ;; User might hit `K E' instead of `K e', so prompt once.
+  (when (and gnus-article-encrypt-protocol
+            gnus-novice-user)
+    (unless (gnus-y-or-n-p "Really encrypt article(s)? ")
+      (error "Encrypt aborted")))
   (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
     (unless func
       (error "Can't find the encrypt protocol %s" protocol))
   (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
     (unless func
       (error "Can't find the encrypt protocol %s" protocol))
@@ -7879,8 +8466,7 @@ For example:
        (error "Can't encrypt the article in group %s"
               gnus-newsgroup-name))
     (gnus-summary-iterate n
        (error "Can't encrypt the article in group %s"
               gnus-newsgroup-name))
     (gnus-summary-iterate n
-      (save-excursion
-       (set-buffer gnus-summary-buffer)
+      (with-current-buffer gnus-summary-buffer
        (let ((mail-parse-charset gnus-newsgroup-charset)
              (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
              (summary-buffer gnus-summary-buffer)
        (let ((mail-parse-charset gnus-newsgroup-charset)
              (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
              (summary-buffer gnus-summary-buffer)
@@ -7926,9 +8512,8 @@ For example:
          (when gnus-keep-backlog
            (gnus-backlog-remove-article
             (car gnus-article-current) (cdr gnus-article-current)))
          (when gnus-keep-backlog
            (gnus-backlog-remove-article
             (car gnus-article-current) (cdr gnus-article-current)))
-         (save-excursion
-           (when (get-buffer gnus-original-article-buffer)
-             (set-buffer gnus-original-article-buffer)
+          (when (get-buffer gnus-original-article-buffer)
+            (with-current-buffer gnus-original-article-buffer
              (setq gnus-original-article nil)))
          (when gnus-use-cache
            (gnus-cache-update-article
              (setq gnus-original-article nil)))
          (when gnus-use-cache
            (gnus-cache-update-article
@@ -8179,5 +8764,4 @@ For example:
 
 (run-hooks 'gnus-art-load-hook)
 
 
 (run-hooks 'gnus-art-load-hook)
 
-;;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
 ;;; gnus-art.el ends here
 ;;; gnus-art.el ends here