* nnrss.el (nnrss-decode-entities-unibyte-string): Use `buffer-string'.
[gnus] / lisp / gnus-art.el
index ae96432..1af0325 100644 (file)
   :group 'gnus-article)
 
 (defcustom gnus-ignored-headers
-  '("^Path:" "^Expires:" "^Date-Received:" "^References:" "^Xref:" "^Lines:"
-    "^Relay-Version:" "^Message-ID:" "^Approved:" "^Sender:" "^Received:"
-    "^X-UIDL:" "^MIME-Version:" "^Return-Path:" "^In-Reply-To:"
-    "^Content-Type:" "^Content-Transfer-Encoding:" "^X-WebTV-Signature:"
-    "^X-MimeOLE:" "^X-MSMail-Priority:" "^X-Priority:" "^X-Loop:"
-    "^X-Authentication-Warning:" "^X-MIME-Autoconverted:" "^X-Face"
-    "^X-Attribution:" "^X-Originating-IP:" "^Delivered-To:"
-    "^NNTP-[-A-Za-z]+:" "^Distribution:" "^X-no-archive:" "^X-Trace:"
-    "^X-Complaints-To:" "^X-NNTP-Posting-Host:" "^X-Orig.*:"
-    "^Abuse-Reports-To:" "^Cache-Post-Path:" "^X-Article-Creation-Date:"
-    "^X-Poster:" "^X-Mail2News-Path:" "^X-Server-Date:" "^X-Cache:"
-    "^Originator:" "^X-Problems-To:" "^X-Auth-User:" "^X-Post-Time:"
-    "^X-Admin:" "^X-UID:" "^Resent-[-A-Za-z]+:" "^X-Mailing-List:"
-    "^Precedence:" "^Original-[-A-Za-z]+:" "^X-filename:" "^X-Orcpt:"
-    "^Old-Received:" "^X-Pgp" "^X-Auth:" "^X-From-Line:"
-    "^X-Gnus-Article-Number:" "^X-Majordomo:" "^X-Url:" "^X-Sender:"
-    "^MBOX-Line" "^Priority:" "^X400-[-A-Za-z]+:"
-    "^Status:" "^X-Gnus-Mail-Source:" "^Cancel-Lock:"
-    "^X-FTN" "^X-EXP32-SerialNo:" "^Encoding:" "^Importance:"
-    "^Autoforwarded:" "^Original-Encoded-Information-Types:" "^X-Ya-Pop3:"
-    "^X-Face-Version:" "^X-Vms-To:" "^X-ML-NAME:" "^X-ML-COUNT:"
-    "^Mailing-List:" "^X-finfo:" "^X-md5sum:" "^X-md5sum-Origin:"
-    "^X-Sun-Charset:" "^X-Accept-Language:" "^X-Envelope-Sender:"
-    "^List-[A-Za-z]+:" "^X-Listprocessor-Version:"
-    "^X-Received:" "^X-Distribute:" "^X-Sequence:" "^X-Juno-Line-Breaks:"
-    "^X-Notes-Item:" "^X-MS-TNEF-Correlator:" "^x-uunet-gateway:"
-    "^X-Received:" "^Content-length:" "X-precedence:"
-    "^X-Authenticated-User:" "^X-Comment" "^X-Report:" "^X-Abuse-Info:"
-    "^X-HTTP-Proxy:" "^X-Mydeja-Info:" "^X-Copyright" "^X-No-Markup:"
-    "^X-Abuse-Info:" "^X-From_:" "^X-Accept-Language:" "^Errors-To:"
-    "^X-BeenThere:" "^X-Mailman-Version:" "^List-Help:" "^List-Post:"
-    "^List-Subscribe:" "^List-Id:" "^List-Unsubscribe:" "^List-Archive:"
-     "^X-Content-length:" "^X-Posting-Agent:" "^Original-Received:"
-     "^X-Request-PGP:" "^X-Fingerprint:" "^X-WRIEnvto:" "^X-WRIEnvfrom:"
-     "^X-Virus-Scanned:" "^X-Delivery-Agent:" "^Posted-Date:" "^X-Gateway:"
-     "^X-Local-Origin:" "^X-Local-Destination:" "^X-UserInfo1:"
-     "^X-Received-Date:" "^X-Hashcash:")
+  (mapcar
+   (lambda (header)
+     (concat "^" header ":"))
+   '("Path" "Expires" "Date-Received" "References" "Xref" "Lines"
+     "Relay-Version" "Message-ID" "Approved" "Sender" "Received"
+     "X-UIDL" "MIME-Version" "Return-Path" "In-Reply-To"
+     "Content-Type" "Content-Transfer-Encoding" "X-WebTV-Signature"
+     "X-MimeOLE" "X-MSMail-Priority" "X-Priority" "X-Loop"
+     "X-Authentication-Warning" "X-MIME-Autoconverted" "X-Face"
+     "X-Attribution" "X-Originating-IP" "Delivered-To"
+     "NNTP-[-A-Za-z]+" "Distribution" "X-no-archive" "X-Trace"
+     "X-Complaints-To" "X-NNTP-Posting-Host" "X-Orig.*"
+     "Abuse-Reports-To" "Cache-Post-Path" "X-Article-Creation-Date"
+     "X-Poster" "X-Mail2News-Path" "X-Server-Date" "X-Cache"
+     "Originator" "X-Problems-To" "X-Auth-User" "X-Post-Time"
+     "X-Admin" "X-UID" "Resent-[-A-Za-z]+" "X-Mailing-List"
+     "Precedence" "Original-[-A-Za-z]+" "X-filename" "X-Orcpt"
+     "Old-Received" "X-Pgp" "X-Auth" "X-From-Line"
+     "X-Gnus-Article-Number" "X-Majordomo" "X-Url" "X-Sender"
+     "MBOX-Line" "Priority" "X400-[-A-Za-z]+"
+     "Status" "X-Gnus-Mail-Source" "Cancel-Lock"
+     "X-FTN" "X-EXP32-SerialNo" "Encoding" "Importance"
+     "Autoforwarded" "Original-Encoded-Information-Types" "X-Ya-Pop3"
+     "X-Face-Version" "X-Vms-To" "X-ML-NAME" "X-ML-COUNT"
+     "Mailing-List" "X-finfo" "X-md5sum" "X-md5sum-Origin"
+     "X-Sun-Charset" "X-Accept-Language" "X-Envelope-Sender"
+     "List-[A-Za-z]+" "X-Listprocessor-Version"
+     "X-Received" "X-Distribute" "X-Sequence" "X-Juno-Line-Breaks"
+     "X-Notes-Item" "X-MS-TNEF-Correlator" "x-uunet-gateway"
+     "X-Received" "Content-length" "X-precedence"
+     "X-Authenticated-User" "X-Comment" "X-Report" "X-Abuse-Info"
+     "X-HTTP-Proxy" "X-Mydeja-Info" "X-Copyright" "X-No-Markup"
+     "X-Abuse-Info" "X-From_" "X-Accept-Language" "Errors-To"
+     "X-BeenThere" "X-Mailman-Version" "List-Help" "List-Post"
+     "List-Subscribe" "List-Id" "List-Unsubscribe" "List-Archive"
+     "X-Content-length" "X-Posting-Agent" "Original-Received"
+     "X-Request-PGP" "X-Fingerprint" "X-WRIEnvto" "X-WRIEnvfrom"
+     "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
+     "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
+     "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
+     "X-Abuse-and-DMCA-Info" "X-Postfilter"))
   "*All headers that start with this regexp will be hidden.
 This variable can also be a list of regexps of headers to be ignored.
 If `gnus-visible-headers' is non-nil, this variable will be ignored."
@@ -181,6 +185,8 @@ Possible values in this list are:
   'empty       Headers with no content.
   'newsgroups  Newsgroup identical to Gnus group.
   'to-address  To identical to To-address.
+  'to-list     To identical to To-list.
+  'cc-list     CC identical to To-list.
   'followup-to Followup-to identical to Newsgroups.
   'reply-to    Reply-to identical to From.
   'date        Date less than four days old.
@@ -189,6 +195,8 @@ Possible values in this list are:
   :type '(set (const :tag "Headers with no content." empty)
              (const :tag "Newsgroups identical to Gnus group." newsgroups)
              (const :tag "To identical to To-address." to-address)
+             (const :tag "To identical to To-list." to-list)
+             (const :tag "CC identical to To-list." cc-list)
              (const :tag "Followup-to identical to Newsgroups." followup-to)
              (const :tag "Reply-to identical to From." reply-to)
              (const :tag "Date less than four days old." date)
@@ -196,6 +204,15 @@ Possible values in this list are:
              (const :tag "Multiple To and/or Cc headers." many-to))
   :group 'gnus-article-hiding)
 
+(defcustom gnus-article-skip-boring nil
+  "Skip over text that is not worth reading.
+By default, if you set this t, then Gnus will display citations and
+signatures, but will never scroll down to show you a page consisting
+only of boring text.  Boring text is controlled by
+`gnus-article-boring-faces'."
+  :type 'boolean
+  :group 'gnus-article-hiding)
+
 (defcustom gnus-signature-separator '("^-- $" "^-- *$")
   "Regexp matching signature separator.
 This can also be a list of regexps.  In that case, it will be checked
@@ -540,10 +557,9 @@ The following additional specs are available:
   :type 'hook
   :group 'gnus-article-various)
 
-(defcustom gnus-article-hide-pgp-hook nil
-  "*A hook called after successfully hiding a PGP signature."
-  :type 'hook
-  :group 'gnus-article-various)
+(defvar gnus-article-hide-pgp-hook nil)
+(make-obsolete-variable 'gnus-article-hide-pgp-hook 
+                       "This variable is obsolete in Gnus 5.10.")
 
 (defcustom gnus-article-button-face 'bold
   "Face used for highlighting buttons in the article buffer.
@@ -788,7 +804,7 @@ This is meant for people who want to view first matched part.
 For `undisplayed-alternative' (default), the first undisplayed
 part or alternative part is used.  For `undisplayed', the first
 undisplayed part is used.  For a function, the first part which
-the function return `t' is used.  For `nil', the first part is
+the function return t is used.  For nil, the first part is
 used."
   :version "21.1"
   :group 'gnus-article-mime
@@ -962,13 +978,8 @@ See Info node `(gnus)Customizing Articles' for details."
   :link '(custom-manual "(gnus)Customizing Articles")
   :type gnus-article-treat-custom)
 
-(defcustom gnus-treat-strip-pgp t
-  "Strip PGP signatures.
-Valid values are nil, t, `head', `last', an integer or a predicate.
-See Info node `(gnus)Customizing Articles' for details."
-  :group 'gnus-article-treat
-  :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.")
 
 (defcustom gnus-treat-strip-pem nil
   "Strip PEM signatures.
@@ -1148,16 +1159,23 @@ See Info node `(gnus)Customizing Articles' and Info node
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-display-xface 'highlight t)
 
-(defcustom gnus-treat-display-grey-xface
+(defcustom gnus-treat-display-face
   (and (not noninteractive)
-       (string-match "^0x" (shell-command-to-string "uncompface"))
-       t)
-  "Display grey X-Face headers.
-Valid values are nil, t."
+       (or (and (fboundp 'image-type-available-p)
+               (image-type-available-p 'png))
+          (and (featurep 'xemacs)
+               (featurep 'png)))
+       'head)
+  "Display Face headers.
+Valid values are nil, t, `head', `last', an integer or a predicate.
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)X-Face' for details."
   :group 'gnus-article-treat
-  :version "21.3"
-  :type 'boolean)
-(put 'gnus-treat-display-grey-xface 'highlight t)
+  :version "21.1"
+  :link '(custom-manual "(gnus)Customizing Articles")
+  :link '(custom-manual "(gnus)X-Face")
+  :type gnus-article-treat-head-custom)
+(put 'gnus-treat-display-xface 'highlight t)
 
 (defcustom gnus-treat-display-smileys
   (if (or (and (featurep 'xemacs)
@@ -1331,12 +1349,12 @@ It is a string, such as \"PGP\". If nil, ask user."
     (gnus-treat-date-user-defined gnus-article-date-user)
     (gnus-treat-date-iso8601 gnus-article-date-iso8601)
     (gnus-treat-display-xface gnus-article-display-x-face)
+    (gnus-treat-display-face gnus-article-display-face)
     (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
     (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
     (gnus-treat-hide-signature gnus-article-hide-signature)
     (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers)
     (gnus-treat-leading-whitespace gnus-article-remove-leading-whitespace)
-    (gnus-treat-strip-pgp gnus-article-hide-pgp)
     (gnus-treat-strip-pem gnus-article-hide-pem)
     (gnus-treat-from-picon gnus-treat-from-picon)
     (gnus-treat-mail-picon gnus-treat-mail-picon)
@@ -1570,7 +1588,7 @@ always hide."
              (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
                (forward-line -1)
                (gnus-article-hide-text-type
-                (progn (beginning-of-line) (point))
+                (gnus-point-at-bol)
                 (progn
                   (end-of-line)
                   (if (re-search-forward "^[^ \t]" nil t)
@@ -1599,6 +1617,32 @@ always hide."
                              (nth 1 (mail-extract-address-components to))
                              to-address)))
                  (gnus-article-hide-header "to"))))
+            ((eq elem 'to-list)
+             (let ((to (message-fetch-field "to"))
+                   (to-list
+                    (gnus-parameter-to-list
+                     (if (boundp 'gnus-newsgroup-name)
+                         gnus-newsgroup-name ""))))
+               (when (and to to-list
+                          (ignore-errors
+                            (gnus-string-equal
+                             ;; only one address in To
+                             (nth 1 (mail-extract-address-components to))
+                             to-list)))
+                 (gnus-article-hide-header "to"))))
+            ((eq elem 'cc-list)
+             (let ((cc (message-fetch-field "cc"))
+                   (to-list
+                    (gnus-parameter-to-list
+                     (if (boundp 'gnus-newsgroup-name)
+                         gnus-newsgroup-name ""))))
+               (when (and cc to-list
+                          (ignore-errors
+                            (gnus-string-equal
+                             ;; only one address in CC
+                             (nth 1 (mail-extract-address-components cc))
+                             to-list)))
+                 (gnus-article-hide-header "cc"))))
             ((eq elem 'followup-to)
              (when (gnus-string-equal
                     (message-fetch-field "followup-to")
@@ -1660,7 +1704,7 @@ always hide."
     (goto-char (point-min))
     (when (re-search-forward (concat "^" header ":") nil t)
       (gnus-article-hide-text-type
-       (progn (beginning-of-line) (point))
+       (gnus-point-at-bol)
        (progn
         (end-of-line)
         (if (re-search-forward "^[^ \t]" nil t)
@@ -1776,7 +1820,7 @@ unfolded."
       (while (not (eobp))
        (save-restriction
          (mail-header-narrow-to-field)
-         (let ((header (buffer-substring (point-min) (point-max))))
+         (let ((header (buffer-string)))
            (with-temp-buffer
              (insert header)
              (goto-char (point-min))
@@ -1908,6 +1952,24 @@ unfolded."
         (forward-line 1)
         (point))))))
 
+(defun article-display-face ()
+  "Display any Face headers in the header."
+  (interactive)
+  (gnus-with-article-headers
+    (let ((face (message-fetch-field "face")))
+      (when face
+       (let ((png (gnus-convert-face-to-png face))
+             image)
+         (when png
+           (setq image (gnus-create-image png 'png t))
+           (gnus-article-goto-header "from")
+           (when (bobp)
+             (insert "From: [no `from' set]\n")
+             (forward-char -17))
+           (gnus-add-wash-type 'face)
+           (gnus-add-image 'face image)
+           (gnus-put-image image)))))))
+
 (defun article-display-x-face (&optional force)
   "Look for an X-Face header and display it if present."
   (interactive (list 'force))
@@ -1921,7 +1983,7 @@ unfolded."
          ;; instead.
          (gnus-delete-images 'xface)
        ;; Display X-Faces.
-       (let (x-faces from face grey)
+       (let (x-faces from face)
          (save-excursion
            (when (and wash-face-p
                       (progn
@@ -1935,67 +1997,39 @@ unfolded."
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
-             (if gnus-treat-display-grey-xface
-                 (progn
-                   (while (gnus-article-goto-header "X-Face\\(-[0-9]+\\)?")
-                     (if (match-beginning 2)
-                         (progn
-                           (setq grey t)
-                           (push (cons (- (string-to-number (match-string 2)))
-                                       (mail-header-field-value))
-                                 x-faces))
-                       (push (cons 0 (mail-header-field-value)) x-faces)))
-                   (dolist (x-face (prog1
-                                       (if grey
-                                           (sort x-faces 'car-less-than-car)
-                                         (nreverse x-faces))
-                                     (setq x-faces nil)))
-                     (push (cdr x-face) x-faces)))
-               (while (gnus-article-goto-header "X-Face")
-                 (push (mail-header-field-value) x-faces)))
+             (while (gnus-article-goto-header "X-Face")
+               (push (mail-header-field-value) x-faces))
              (setq from (message-fetch-field "from"))))
-         (if grey
-             (let ((xpm (gnus-convert-gray-x-face-to-xpm x-faces))
-                   image)
-               (when xpm
-                 (setq image (gnus-create-image xpm 'xpm t))
-                 (gnus-article-goto-header "from")
-                 (when (bobp)
-                   (insert "From: [no `from' set]\n")
-                   (forward-char -17))
-                 (gnus-add-wash-type 'xface)
-                 (gnus-add-image 'xface image)
-                 (gnus-put-image image)))
-           ;; 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))))
-           (while (and (setq face (pop x-faces))
-                       gnus-article-x-face-command
-                       (or force
-                           ;; Check whether this face is censored.
-                           (not gnus-article-x-face-too-ugly)
-                           (and gnus-article-x-face-too-ugly from
-                                (not (string-match gnus-article-x-face-too-ugly
-                                                   from)))))
-             ;; We display the face.
-             (if (symbolp gnus-article-x-face-command)
-                 ;; The command is a lisp function, so we call it.
-                 (if (gnus-functionp gnus-article-x-face-command)
-                     (funcall gnus-article-x-face-command face)
-                   (error "%s is not a function" gnus-article-x-face-command))
-               ;; The command is a string, so we interpret the command
-               ;; as a, well, command, and fork it off.
-               (let ((process-connection-type nil))
-                 (process-kill-without-query
-                  (start-process
-                   "article-x-face" nil shell-file-name shell-command-switch
-                   gnus-article-x-face-command))
-                 (with-temp-buffer
-                   (insert face)
-                   (process-send-region "article-x-face"
-                                        (point-min) (point-max)))
-                 (process-send-eof "article-x-face"))))))))))
+         ;; 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))))
+         (while (and (setq face (pop x-faces))
+                     gnus-article-x-face-command
+                     (or force
+                         ;; Check whether this face is censored.
+                         (not gnus-article-x-face-too-ugly)
+                         (and gnus-article-x-face-too-ugly from
+                              (not (string-match gnus-article-x-face-too-ugly
+                                                 from)))))
+           ;; We display the face.
+           (if (symbolp gnus-article-x-face-command)
+               ;; The command is a lisp function, so we call it.
+               (if (gnus-functionp gnus-article-x-face-command)
+                   (funcall gnus-article-x-face-command face)
+                 (error "%s is not a function" gnus-article-x-face-command))
+             ;; The command is a string, so we interpret the command
+             ;; as a, well, command, and fork it off.
+             (let ((process-connection-type nil))
+               (process-kill-without-query
+                (start-process
+                 "article-x-face" nil shell-file-name shell-command-switch
+                 gnus-article-x-face-command))
+               (with-temp-buffer
+                 (insert face)
+                 (process-send-region "article-x-face"
+                                      (point-min) (point-max)))
+               (process-send-eof "article-x-face")))))))))
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
@@ -2267,42 +2301,6 @@ The `gnus-list-identifiers' variable specifies what to do."
                 "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t)
            (delete-region (match-beginning 1) (match-end 1))))))))
 
-(defun article-hide-pgp ()
-  "Remove any PGP headers and signatures in the current article."
-  (interactive)
-  (save-excursion
-    (save-restriction
-      (let ((inhibit-point-motion-hooks t)
-           buffer-read-only beg end)
-       (article-goto-body)
-       ;; Hide the "header".
-       (when (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\n" nil t)
-         (gnus-add-wash-type 'pgp)
-         (delete-region (match-beginning 0) (match-end 0))
-         ;; Remove armor headers (rfc2440 6.2)
-         (delete-region (point) (or (re-search-forward "^[ \t]*\n" nil t)
-                                    (point)))
-         (setq beg (point))
-         ;; Hide the actual signature.
-         (and (search-forward "\n-----BEGIN PGP SIGNATURE-----\n" nil t)
-              (setq end (1+ (match-beginning 0)))
-              (delete-region
-               end
-               (if (search-forward "\n-----END PGP SIGNATURE-----\n" nil t)
-                   (match-end 0)
-                 ;; Perhaps we shouldn't hide to the end of the buffer
-                 ;; if there is no end to the signature?
-                 (point-max))))
-         ;; Hide "- " PGP quotation markers.
-         (when (and beg end)
-           (narrow-to-region beg end)
-           (goto-char (point-min))
-           (while (re-search-forward "^- " nil t)
-             (delete-region
-              (match-beginning 0) (match-end 0)))
-           (widen))
-         (gnus-run-hooks 'gnus-article-hide-pgp-hook))))))
-
 (defun article-hide-pem (&optional arg)
   "Toggle hiding of any PEM headers and signatures in the current article.
 If given a negative prefix, always show; if given a positive prefix,
@@ -2331,43 +2329,50 @@ always hide."
             (match-beginning 0) (match-end 0) 'pem)))))))
 
 (defun article-strip-banner ()
-  "Strip the banner specified by the `banner' group parameter."
+  "Strip the banners specified by the `banner' group parameter and by
+`gnus-article-address-banner-alist'."
   (interactive)
+  (save-excursion
+    (save-restriction
+      (let ((inhibit-point-motion-hooks t))
+       (when (gnus-parameter-banner gnus-newsgroup-name)
+         (article-really-strip-banner
+          (gnus-parameter-banner gnus-newsgroup-name)))
+       (when gnus-article-address-banner-alist
+         (article-really-strip-banner
+          (let ((from (save-restriction
+                        (widen)
+                        (article-narrow-to-head)
+                        (mail-fetch-field "from"))))
+            (when (and from
+                       (setq from
+                             (caar (mail-header-parse-addresses from))))
+              (catch 'found
+                (dolist (pair gnus-article-address-banner-alist)
+                  (when (string-match (car pair) from)
+                    (throw 'found (cdr pair)))))))))))))
+
+(defun article-really-strip-banner (banner)
+  "Strip the banner specified by the argument."
   (save-excursion
     (save-restriction
       (let ((inhibit-point-motion-hooks t)
-           (banner (gnus-parameter-banner gnus-newsgroup-name))
            (gnus-signature-limit nil)
-           buffer-read-only beg end)
-       (when (and gnus-article-address-banner-alist
-                  (not banner))
-         (setq banner
-               (let ((from (save-restriction
-                             (widen)
-                             (article-narrow-to-head)
-                             (mail-fetch-field "from"))))
-                 (when (and from
-                            (setq from
-                                  (caar (mail-header-parse-addresses from))))
-                   (catch 'found
-                     (dolist (pair gnus-article-address-banner-alist)
-                       (when (string-match (car pair) from)
-                         (throw 'found (cdr pair)))))))))
-       (when banner
-         (article-goto-body)
-         (cond
-          ((eq banner 'signature)
-           (when (gnus-article-narrow-to-signature)
-             (widen)
-             (forward-line -1)
-             (delete-region (point) (point-max))))
-          ((symbolp banner)
-           (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
-               (while (re-search-forward banner nil t)
-                 (delete-region (match-beginning 0) (match-end 0)))))
-          ((stringp banner)
-           (while (re-search-forward banner nil t)
-             (delete-region (match-beginning 0) (match-end 0))))))))))
+           buffer-read-only)
+       (article-goto-body)
+       (cond
+        ((eq banner 'signature)
+         (when (gnus-article-narrow-to-signature)
+           (widen)
+           (forward-line -1)
+           (delete-region (point) (point-max))))
+        ((symbolp banner)
+         (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
+             (while (re-search-forward banner nil t)
+               (delete-region (match-beginning 0) (match-end 0)))))
+        ((stringp banner)
+         (while (re-search-forward banner nil t)
+           (delete-region (match-beginning 0) (match-end 0)))))))))
 
 (defun article-babel ()
   "Translate article using an online translation service."
@@ -3088,6 +3093,7 @@ Directory to save to is default to `gnus-article-save-directory'."
       (save-restriction
        (widen)
        (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-mail filename)))))
@@ -3342,13 +3348,13 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-remove-cr
      article-remove-leading-whitespace
      article-display-x-face
+     article-display-face
      article-de-quoted-unreadable
      article-de-base64-unreadable
      article-decode-HZ
      article-wash-html
      article-unsplit-urls
      article-hide-list-identifiers
-     article-hide-pgp
      article-strip-banner
      article-babel
      article-hide-pem
@@ -3451,9 +3457,6 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 
     (gnus-run-hooks 'gnus-article-menu-hook)))
 
-;; Fixme: do something for the Emacs tool bar in Article mode a la
-;; Summary.
-
 (defun gnus-article-mode ()
   "Major mode for displaying an article.
 
@@ -3476,7 +3479,9 @@ commands:
   (make-local-variable 'minor-mode-alist)
   (use-local-map gnus-article-mode-map)
   (when (gnus-visual-p 'article-menu 'menu)
-    (gnus-article-make-menu-bar))
+    (gnus-article-make-menu-bar)
+    (when gnus-summary-tool-bar-map
+      (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
   (gnus-update-format-specifications nil 'article-mode)
   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
   (make-local-variable 'gnus-page-broken)
@@ -3537,6 +3542,8 @@ commands:
          (setq gnus-article-mime-handle-alist nil)
          (buffer-disable-undo)
          (setq buffer-read-only t)
+         ;; This list just keeps growing if we don't reset it.
+         (setq gnus-button-marker-list nil)
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
          (current-buffer))
@@ -3595,7 +3602,9 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                      (cons gnus-newsgroup-name article))
                (set-buffer gnus-summary-buffer)
                (setq gnus-current-article article)
-               (if (memq article gnus-newsgroup-undownloaded)
+               (if (and (memq article gnus-newsgroup-undownloaded)
+                        (not (gnus-online (gnus-find-method-for-group
+                                           gnus-newsgroup-name))))
                    (progn
                      (gnus-summary-set-agent-mark article)
                      (message "Message marked for downloading"))
@@ -3731,7 +3740,7 @@ General format specifiers can also be used.  See Info node
     (gnus-mime-view-part-externally "e" "View Externally")
     (gnus-mime-print-part "p" "Print")
     (gnus-mime-pipe-part "|" "Pipe To Command...")
-    (gnus-mime-action-on-part "." "Take action on the part")))
+    (gnus-mime-action-on-part "." "Take action on the part...")))
 
 (defun gnus-article-mime-part-status ()
   (if gnus-article-mime-handle-alist-1
@@ -3751,21 +3760,36 @@ General format specifiers can also be used.  See Info node
       (define-key map (cadr c) (car c)))
     map))
 
-(defun gnus-mime-button-menu (event)
-  "Construct a context-sensitive menu of MIME commands."
-  (interactive "e")
-  (save-window-excursion
-    (let ((pos (event-start event)))
-      (select-window (posn-window pos))
-      (goto-char (posn-point pos))
-      (gnus-article-check-buffer)
-      (let ((response (x-popup-menu
-                      t `("MIME Part"
-                          ("" ,@(mapcar (lambda (c)
-                                          (cons (caddr c) (car c)))
-                                        gnus-mime-button-commands))))))
-       (if response
-           (call-interactively response))))))
+(easy-menu-define gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
+  `("MIME Part"
+    ,@(mapcar (lambda (c)
+               (vector (caddr c) (car c) :enable t)) gnus-mime-button-commands)))
+
+(eval-when-compile
+  (define-compiler-macro popup-menu (&whole form
+                                           menu &optional position prefix)
+    (if (and (fboundp 'popup-menu)
+            (not (memq 'popup-menu (assoc "lmenu" load-history))))
+       form
+      ;; Gnus is probably running under Emacs 20.
+      `(let* ((menu (cdr ,menu))
+             (response (x-popup-menu
+                        t (list (car menu)
+                                (cons "" (mapcar (lambda (c)
+                                                   (cons (caddr c) (car c)))
+                                                 (cdr menu)))))))
+        (if response
+            (call-interactively (nth 3 (assq response menu))))))))
+
+(defun gnus-mime-button-menu (event prefix)
+ "Construct a context-sensitive menu of MIME commands."
+ (interactive "e\nP")
+ (save-window-excursion
+   (let ((pos (event-start event)))
+     (select-window (posn-window pos))
+     (goto-char (posn-point pos))
+     (gnus-article-check-buffer)
+     (popup-menu gnus-mime-button-menu nil prefix))))
 
 (defun gnus-mime-view-all-parts (&optional handles)
   "View all the MIME parts."
@@ -3940,7 +3964,9 @@ The uncompress method used is derived from `buffer-file-name'."
             (jka-compr-delete-temp-file err-file)))))))
 
 (defun gnus-mime-copy-part (&optional handle)
-  "Put the MIME part under point into a new buffer."
+  "Put the MIME part under point into a new buffer.
+If `auto-compression-mode' is enabled, compressed files like .gz and .bz2
+are decompressed."
   (interactive)
   (gnus-article-check-buffer)
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
@@ -3972,13 +3998,12 @@ The uncompress method used is derived from `buffer-file-name'."
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
         (contents (and handle (mm-get-part handle)))
         (file (mm-make-temp-file (expand-file-name "mm." mm-tmp-directory)))
-        (printer (mailcap-mime-info (mm-handle-type handle) "print")))
+        (printer (mailcap-mime-info (mm-handle-media-type handle) "print")))
     (when contents
        (if printer
            (unwind-protect
                (progn
-                 (with-temp-file file
-                   (insert contents))
+                 (mm-save-part-to-file handle file)
                  (call-process shell-file-name nil
                                (generate-new-buffer " *mm*")
                                nil
@@ -4071,7 +4096,8 @@ If no internal viewer is available, use an external viewer."
         (mail-parse-charset gnus-newsgroup-charset)
         (mail-parse-ignored-charsets
          (save-excursion (set-buffer gnus-summary-buffer)
-                         gnus-newsgroup-ignored-charsets)))
+                         gnus-newsgroup-ignored-charsets))
+        buffer-read-only)
     (when handle
       (if (mm-handle-undisplayer handle)
          (mm-remove-part handle)
@@ -4080,7 +4106,7 @@ If no internal viewer is available, use an external viewer."
 (defun gnus-mime-action-on-part (&optional action)
   "Do something with the MIME attachment at \(point\)."
   (interactive
-   (list (completing-read "Action: " gnus-mime-action-alist)))
+   (list (completing-read "Action: " gnus-mime-action-alist nil t)))
   (gnus-article-check-buffer)
   (let ((action-pair (assoc action gnus-mime-action-alist)))
     (if action-pair
@@ -4213,16 +4239,14 @@ If no internal viewer is available, use an external viewer."
              (if (window-live-p window)
                  (select-window window)))))
       (goto-char point)
-      (delete-region (gnus-point-at-bol) (progn (forward-line 1) (point)))
+      (gnus-delete-line)
       (gnus-insert-mime-button
        handle id (list (mm-handle-displayed-p handle)))
       (goto-char point))))
 
 (defun gnus-article-goto-part (n)
   "Go to MIME part N."
-  (let ((point (text-property-any (point-min) (point-max) 'gnus-part n)))
-    (when point
-      (goto-char point))))
+  (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
 
 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
   (let ((gnus-tmp-name
@@ -4301,9 +4325,10 @@ If no internal viewer is available, use an external viewer."
          ;; We have to do this since selecting the window
          ;; may change the point.  So we set the window point.
          (set-window-point window point)))
-      (let* ((handles (or ihandles (mm-dissect-buffer
-                                   nil gnus-article-loose-mime)
-                         (mm-uu-dissect)))
+      (let* ((handles (or ihandles
+                         (mm-dissect-buffer nil gnus-article-loose-mime)
+                         (and gnus-article-emulate-mime
+                              (mm-uu-dissect))))
             buffer-read-only handle name type b e display)
        (when (and (not ihandles)
                   (not gnus-displaying-mime))
@@ -4713,15 +4738,14 @@ If given a numerical ARG, move forward ARG pages."
 (defun gnus-article-goto-next-page ()
   "Show the next page of the article."
   (interactive)
-  (when (gnus-article-next-page)
-    (goto-char (point-min))
-    (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
+  (gnus-eval-in-buffer-window gnus-summary-buffer
+    (gnus-summary-next-page)))
 
 (defun gnus-article-goto-prev-page ()
   "Show the next page of the article."
   (interactive)
-  (if (bobp) (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
-    (gnus-article-prev-page nil)))
+  (gnus-eval-in-buffer-window gnus-summary-buffer
+    (gnus-summary-prev-page)))
 
 (defun gnus-article-next-page (&optional lines)
   "Show the next page of the current article.
@@ -4771,17 +4795,33 @@ Argument LINES specifies lines to be scrolled down."
             (goto-char (point-min))))
        (move-to-window-line 0)))))
 
+(defun gnus-article-only-boring-p ()
+  "Decide whether there is only boring text remaining in the article.
+Something \"interesting\" is a word of at least two letters that does
+not have a face in `gnus-article-boring-faces'."
+  (when (and gnus-article-skip-boring
+            (boundp 'gnus-article-boring-faces)
+            (symbol-value 'gnus-article-boring-faces))
+    (save-excursion
+      (catch 'only-boring
+       (while (re-search-forward "\\b\\w\\w" nil t)
+         (forward-char -1)
+         (when (not (gnus-intersection
+                     (gnus-faces-at (point))
+                     (symbol-value 'gnus-article-boring-faces)))
+           (throw 'only-boring nil)))
+       (throw 'only-boring t)))))
+
 (defun gnus-article-refer-article ()
   "Read article specified by message-id around point."
   (interactive)
-  (let ((point (point)))
-    (search-forward ">" nil t)         ;Move point to end of "<....>".
-    (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
-       (let ((message-id (match-string 1)))
-         (goto-char point)
+  (save-excursion
+    (re-search-backward "[ \t]\\|^" (gnus-point-at-bol) t)
+    (re-search-forward "<?news:<?\\|<" (gnus-point-at-eol) t)
+    (if (re-search-forward "[^@ ]+@[^ \t>]+" (gnus-point-at-eol) t)
+       (let ((msg-id (concat "<" (match-string 0) ">")))
          (set-buffer gnus-summary-buffer)
-         (gnus-summary-refer-article message-id))
-      (goto-char (point))
+         (gnus-summary-refer-article msg-id))
       (error "No references around point"))))
 
 (defun gnus-article-show-summary ()
@@ -4949,46 +4989,47 @@ Argument LINES specifies lines to be scrolled down."
 The text in the region will be yanked.  If the region isn't active,
 the entire article will be yanked."
   (interactive "P")
-  (let ((article (cdr gnus-article-current)) cont)
-    (if (not (mark t))
+  (let ((article (cdr gnus-article-current))
+       contents)
+    (if (not (gnus-mark-active-p))
        (with-current-buffer gnus-summary-buffer
          (gnus-summary-reply (list (list article)) wide))
-      (setq cont (buffer-substring (point) (mark t)))
+      (setq contents (buffer-substring (point) (mark t)))
       ;; Deactivate active regions.
       (when (and (boundp 'transient-mark-mode)
                 transient-mark-mode)
        (setq mark-active nil))
       (with-current-buffer gnus-summary-buffer
        (gnus-summary-reply
-        (list (list article cont)) wide)))))
+        (list (list article contents)) wide)))))
 
 (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,
 the entire article will be yanked."
   (interactive)
-  (let ((article (cdr gnus-article-current)) cont)
-      (if (not (mark t))
+  (let ((article (cdr gnus-article-current))
+       contents)
+      (if (not (gnus-mark-active-p))
          (with-current-buffer gnus-summary-buffer
            (gnus-summary-followup (list (list article))))
-       (setq cont (buffer-substring (point) (mark t)))
+       (setq contents (buffer-substring (point) (mark t)))
        ;; Deactivate active regions.
        (when (and (boundp 'transient-mark-mode)
                   transient-mark-mode)
          (setq mark-active nil))
        (with-current-buffer gnus-summary-buffer
          (gnus-summary-followup
-          (list (list article cont)))))))
+          (list (list article contents)))))))
 
 (defun gnus-article-hide (&optional arg force)
   "Hide all the gruft in the current article.
-This means that PGP stuff, signatures, cited text and (some)
-headers will be hidden.
+This means that signatures, cited text and (some) headers will be
+hidden.
 If given a prefix, show the hidden text instead."
   (interactive (append (gnus-article-hidden-arg) (list 'force)))
   (gnus-article-hide-headers arg)
   (gnus-article-hide-list-identifiers arg)
-  (gnus-article-hide-pgp arg)
   (gnus-article-hide-citation-maybe arg force)
   (gnus-article-hide-signature arg))
 
@@ -5099,9 +5140,7 @@ If given a prefix, show the hidden text instead."
                 (gnus-cache-request-article article group))
            'article)
           ;; Check the agent cache.
-          ((and gnus-agent gnus-agent-cache gnus-plugged
-                (numberp article)
-                (gnus-agent-request-article article group))
+          ((gnus-agent-request-article article group)
            'article)
           ;; Get the article and put into the article buffer.
           ((or (stringp article)
@@ -5382,8 +5421,8 @@ groups."
 
 (defcustom gnus-button-url-regexp
   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
-      "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~`%&*+|\\/:;.,[:word:]]+[-a-z0-9_=#$@~`%&*+|\\/[:word:]]\\)"
-    "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~`%&*+|\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~`%&*+|\\/]\\|\\w\\)\\)")
+      "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
+    "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
   "Regular expression that matches URLs."
   :group 'gnus-article-buttons
   :type 'regexp)
@@ -5442,57 +5481,173 @@ The function must take one argument, the string naming the URL."
   :group 'gnus-article-buttons
   :type 'regexp)
 
-(defcustom gnus-button-prefer-mid-or-mail 'guess
-  "What to do when the button on a string as \"foo123@bar.com\" is pushed.
-Strings like this can be either a message ID or a mail address.  If the
-variable is set to the symbol `ask', query the user what do do.  If it is the
-symbol `guess', Gnus will do a guess and query the user what do do if it is
-ambiguous.  See the variable `gnus-button-guessed-mid-regexp' for details
-concerning the guessing.  If it is one of the sybols `mid' or `mail', Gnus
-will always assume that the string is a message ID or a mail address,
-respectivly."
-  ;; FIXME: doc-string could/should be improved.
+(defcustom gnus-button-prefer-mid-or-mail 'gnus-button-mid-or-mail-heuristic
+  "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
+Strings like this can be either a message ID or a mail address.  If it is one
+of the symbols `mid' or `mail', Gnus will always assume that the string is a
+message ID or a mail address, respectivly.  If this variable is set to the
+symbol `ask', always query the user what do do.  If it is a function, this
+function will be called with the string as it's only argument.  The function
+must return `mid', `mail', `invalid' or `ask'."
   :group 'gnus-article-buttons
-  :type '(choice (const ask)
-                (const guess)
+  :type '(choice (function-item :tag "Heuristic function"
+                               gnus-button-mid-or-mail-heuristic)
+                (const ask)
                 (const mid)
                 (const mail)))
 
-(defcustom gnus-button-guessed-mid-regexp
-  (concat
-   "^<?\\(slrn\\|Pine\\.\\)"
-   "\\|\\.fsf@\\|\\.fsf_-_@\\|\\.ln@"
-   "\\|@4ax\\.com\\|@ID-[0-9]+\\.[a-zA-Z]+\\.dfncis\\.de"
-         "\\|^<?.*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*@")
-  "Regular expression that matches message IDs and not mail addresses."
-  ;; TODO: Incorporate more matches from
-  ;; <URL:http://piology.org/perl/id-or-mail.pl.html>. I.e. translate the
-  ;; Perl-REs to Elisp-REs.
+(defcustom gnus-button-mid-or-mail-heuristic-alist
+  '((-10.0 . ".+\\$.+@")
+    (-10.0 . "#")
+    (-10.0 . "\\*")
+    (-5.0  . "\\+[^+]*\\+.*@") ;; # two plus signs
+    (-5.0  . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
+    (-5.0  . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
+    (-1.0  . "^[^a-z]+@")
+   
+    (-5.0  . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
+    (-5.0  . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
+    (-3.0  . "[A-Z][A-Z][a-z][a-z].*@")
+    (-5.0  . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
+   
+    (-2.0  . "^[0-9]")
+    (-1.0  . "^[0-9][0-9]")
+    ;;
+    ;; -3.0 /^[0-9][0-9a-fA-F]{2,2}/;
+    (-3.0  . "^[0-9][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
+    ;; -5.0 /^[0-9][0-9a-fA-F]{3,3}/;
+    (-5.0  . "^[0-9][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
+    ;;
+    (-3.0  .  "[0-9][0-9][0-9][0-9][0-9][^0-9].*@") ;; "[0-9]{5,}.*\@"
+    (-3.0  .  "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][^0-9].*@")
+    ;;       "[0-9]{8,}.*\@"
+    (-3.0
+     . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*@")
+    ;; "[0-9]{12,}.*\@"
+    ;; compensation for TDMA dated mail addresses:
+    (25.0  . "-dated-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+.*@")
+    ;;
+    (-20.0 . "\\.fsf@")        ;; Gnus
+    (-20.0 . "^slrn")
+    (-20.0 . "^Pine")
+    (-20.0 . "_-_") ;; Subject change in thread
+    ;;
+    (-20.0 . "\\.ln@") ;; leafnode
+    (-30.0 . "@ID-[0-9]+\\.[a-zA-Z]+\\.dfncis\\.de")
+    (-30.0 . "@4[Aa][Xx]\\.com") ;; Forte Agent
+    ;;
+    ;; (5.0 . "") ;; $local_part_len <= 7
+    (10.0  . "^[^0-9]+@")
+    (3.0   . "^[^0-9]+[0-9][0-9]?[0-9]?@")
+    ;;      ^[^0-9]+[0-9]{1,3}\@ digits only at end of local part
+    (3.0   . "\@stud")
+    ;;
+    (2.0   . "[a-z][a-z][._-][A-Z][a-z].*@")
+    ;;
+    (0.5   . "^[A-Z][a-z]")
+    (0.5   . "^[A-Z][a-z][a-z]")
+    (1.5   . "^[A-Z][a-z][A-Z][a-z][^a-z]") ;; ^[A-Z][a-z]{3,3}
+    (2.0   . "^[A-Z][a-z][A-Z][a-z][a-z][^a-z]")) ;; ^[A-Z][a-z]{4,4}
+  "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
+
+A negative RATE indicates a message IDs, whereas a positive indicates a mail
+address.  The REGEXP is processed with `case-fold-search' set to `nil'."
   :group 'gnus-article-buttons
-  :type 'regexp)
+  :type '(repeat (cons (number :tag "Rate")
+                      (regexp :tag "Regexp"))))
+
+(defun gnus-button-mid-or-mail-heuristic (mid-or-mail)
+  "Guess whether MID-OR-MAIL is a message ID or a mail address.
+Returns `mid' if MID-OR-MAIL is a message IDs, `mail' if it's a mail
+address, `ask' if unsure and `invalid' if the string is invalid."
+  (let ((case-fold-search nil)
+       (list gnus-button-mid-or-mail-heuristic-alist)
+       (result 0) rate regexp lpartlen elem)
+    (setq lpartlen
+         (length (gnus-replace-in-string mid-or-mail "^\\(.*\\)@.*$" "\\1")))
+    (gnus-message 8 "`%s', length of local part=`%s'." mid-or-mail lpartlen)
+    ;; Certain special cases...
+    (when (string-match
+          (concat
+           "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$" "\\|"
+           "^[0-9]+\.[0-9]+\@compuserve")
+          mid-or-mail)
+      (gnus-message 8 "`%s' is a known mail address.")
+      (setq result 'mail))
+    (when (string-match "@.*@\\| " mid-or-mail)
+      (gnus-message 8 "`%s' is invalid.")
+      (setq result 'invalid))
+    ;; Nothing more to do, if result is not a number here...
+    (when (numberp result)
+      (while list
+       (setq elem (car list)
+             rate (car elem)
+             regexp (cdr elem)
+             list (cdr list))
+       (when (string-match regexp mid-or-mail)
+         (setq result (+ result rate))
+         (gnus-message
+          9 "`%s' matched `%s', rate `%s', result `%s'."
+          mid-or-mail regexp rate result)))
+      (when (<= lpartlen 7)
+       (setq result (+ result 5.0))
+       (gnus-message 9 "`%s' matched (<= lpartlen 7), result `%s'."
+                     mid-or-mail result))
+      (when (>= lpartlen 12)
+       (gnus-message 9 "`%s' matched (>= lpartlen 12)" mid-or-mail)
+       (cond
+        ((string-match "[0-9][^0-9]+[0-9].*@" mid-or-mail)
+         ;; Long local part should contain realname if e-mail address,
+         ;; too many digits: message-id.
+         ;; $score -= 5.0 + 0.1 * $local_part_len;
+         (setq rate (* -1.0 (+ 5.0 (* 0.1 lpartlen))))
+         (setq result (+ result rate))
+         (gnus-message
+          9 "Many digits in `%s', rate `%s', result `%s'."
+          mid-or-mail rate result))
+        ((string-match "[^aeiouy][^aeiouy][^aeiouy][^aeiouy]+.*\@"
+                       mid-or-mail)
+         ;; Too few vowels [^aeiouy]{4,}.*\@
+         (setq result (+ result -5.0))
+         (gnus-message
+          9 "Few vowels in `%s', rate `%s', result `%s'."
+          mid-or-mail -5.0 result))
+        (t
+         (setq result (+ result 5.0))
+         (gnus-message
+          9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
+    (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
+    (cond
+     ;; Maybe we should make this a customizable alist: (condition . 'result)
+     ((< result -10.0) 'mid)
+     ((> result  10.0) 'mail)
+     (t 'ask))))
 
 (defun gnus-button-handle-mid-or-mail (mid-or-mail)
-  (let* ((pref gnus-button-prefer-mid-or-mail)
+  (let* ((pref gnus-button-prefer-mid-or-mail) guessed
         (url-mid (concat "news" ":" mid-or-mail))
         (url-mailto (concat "mailto" ":" mid-or-mail)))
     (gnus-message 9 "mid-or-mail=%s" mid-or-mail)
-    ;; If it looks like a MID (well known readers or servers) use 'mid,
-    ;; otherwise 'ask the user.
-    (if (eq pref 'guess)
-       (if (string-match gnus-button-guessed-mid-regexp mid-or-mail)
-           (setq pref 'mid)
-         (setq pref 'ask)))
+    (when (fboundp pref)
+      (setq guessed
+           ;; get rid of surrounding angles...
+           (funcall pref
+                    (gnus-replace-in-string mid-or-mail "^<\\|>$" "")))
+      (if (or (eq 'mid guessed) (eq 'mail guessed))
+         (setq pref guessed)
+       (setq pref 'ask)))
     (if (eq pref 'ask)
        (save-window-excursion
          (if (y-or-n-p (concat "Is <" mid-or-mail "> a mail address? "))
              (setq pref 'mail)
            (setq pref 'mid))))
     (cond ((eq pref 'mid)
-          (gnus-message 9 "calling `gnus-button-handle-news' %s" url-mid)
+          (gnus-message 8 "calling `gnus-button-handle-news' %s" url-mid)
           (gnus-button-handle-news url-mid))
          ((eq pref 'mail)
-          (gnus-message 9 "calling `gnus-url-mailto'  %s" url-mailto)
-          (gnus-url-mailto url-mailto)))))
+          (gnus-message 8 "calling `gnus-url-mailto'  %s" url-mailto)
+          (gnus-url-mailto url-mailto))
+         (t (gnus-message 3 "Invalid string.")))))
 
 (defun gnus-button-handle-custom (url)
   "Follow a Custom URL."
@@ -5501,40 +5656,43 @@ respectivly."
 (defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
 
 (defun gnus-button-handle-describe-function (url)
-  "Call describe-function when pushing the corresponding URL button."
+  "Call `describe-function' when pushing the corresponding URL button."
   (describe-function
    (intern
     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
 
 (defun gnus-button-handle-describe-variable (url)
-  "Call describe-variable when pushing the corresponding URL button."
+  "Call `describe-variable' when pushing the corresponding URL button."
   (describe-variable
    (intern
     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
 
-;; FIXME: Is is possible to implement this?  Else it should be removed here
-;; and in `gnus-button-alist'.
 (defun gnus-button-handle-describe-key (url)
-  "Call describe-key when pushing the corresponding URL button."
-  (error "not implemented"))
+  "Call `describe-key' when pushing the corresponding URL button."
+  (let* ((key-string
+         (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))
+        (keys (ignore-errors (eval `(kbd ,key-string)))))
+    (if keys
+       (describe-key keys)
+      (gnus-message 3 "Invalid key sequence in button: %s" key-string))))
 
 (defun gnus-button-handle-apropos (url)
-  "Call apropos when pushing the corresponding URL button."
+  "Call `apropos' when pushing the corresponding URL button."
   (apropos (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
 
 (defun gnus-button-handle-apropos-command (url)
-  "Call apropos when pushing the corresponding URL button."
+  "Call `apropos' when pushing the corresponding URL button."
   (apropos-command
    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
 
 (defun gnus-button-handle-apropos-variable (url)
-  "Call apropos when pushing the corresponding URL button."
+  "Call `apropos' when pushing the corresponding URL button."
   (funcall
    (if (fboundp 'apropos-variable) 'apropos-variable 'apropos)
    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
 
 (defun gnus-button-handle-apropos-documentation (url)
-  "Call apropos when pushing the corresponding URL button."
+  "Call `apropos' when pushing the corresponding URL button."
   (funcall
    (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
@@ -5626,9 +5784,10 @@ positives are possible."
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
-    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+" 0
-     ;; this regexp needs to be fixed!
-     (>= gnus-button-emacs-level 9) gnus-button-handle-describe-key 2)
+    ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'" 1
+     ;; Unlike the other regexps we really have to require quoting
+     ;; here to determine where it ends.
+     (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
     ;; This is how URLs _should_ be embedded in text...
     ("<URL: *\\([^<>]*\\)>" 1 t gnus-button-embedded-url 1)
     ;; Raw URLs.
@@ -5733,8 +5892,8 @@ call it with the value of the `gnus-data' text property."
 If the text at point has a `gnus-callback' property,
 call it with the value of the `gnus-data' text property."
   (interactive)
-  (let* ((data (get-text-property (point) 'gnus-data))
-        (fun (get-text-property (point) 'gnus-callback)))
+  (let ((data (get-text-property (point) 'gnus-data))
+       (fun (get-text-property (point) 'gnus-callback)))
     (when fun
       (funcall fun data))))
 
@@ -6357,7 +6516,7 @@ For example:
                                   (search-forward field nil t))
                                 (prog2
                                     (message-narrow-to-field)
-                                    (buffer-substring (point-min) (point-max))
+                                    (buffer-string)
                                   (delete-region (point-min) (point-max))
                                   (widen))))
                          '("Content-Type:" "Content-Transfer-Encoding:"
@@ -6451,42 +6610,45 @@ For example:
 
 (defun gnus-mime-security-show-details (handle)
   (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
-    (if details
-       (if gnus-mime-security-show-details-inline
-           (let ((gnus-mime-security-button-pressed t)
-                 (gnus-mime-security-button-line-format
-                  (get-text-property (point) 'gnus-line-format))
+    (if (not details)
+       (gnus-message 5 "No details.")
+      (if gnus-mime-security-show-details-inline
+         (let ((gnus-mime-security-button-pressed
+                (not (get-text-property (point) 'gnus-mime-details)))
+               (gnus-mime-security-button-line-format
+                (get-text-property (point) 'gnus-line-format))
                buffer-read-only)
-             (forward-char -1)
-             (while (eq (get-text-property (point) 'gnus-line-format)
-                        gnus-mime-security-button-line-format)
-               (forward-char -1))
-             (forward-char)
-             (save-restriction
-               (narrow-to-region (point) (point))
-               (gnus-insert-mime-security-button handle))
-             (delete-region (point)
-                            (or (text-property-not-all
-                                 (point) (point-max)
-                                 'gnus-line-format
-                                 gnus-mime-security-button-line-format)
-                                (point-max))))
-         (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
-             (with-current-buffer gnus-mime-security-details-buffer
-               (erase-buffer)
-               t)
-           (setq gnus-mime-security-details-buffer
-                 (gnus-get-buffer-create "*MIME Security Details*")))
-         (with-current-buffer gnus-mime-security-details-buffer
-           (insert details)
-           (goto-char (point-min)))
-         (pop-to-buffer gnus-mime-security-details-buffer))
-      (gnus-message 5 "No details."))))
+           (forward-char -1)
+           (while (eq (get-text-property (point) 'gnus-line-format)
+                      gnus-mime-security-button-line-format)
+             (forward-char -1))
+           (forward-char)
+           (save-restriction
+             (narrow-to-region (point) (point))
+             (gnus-insert-mime-security-button handle))
+           (delete-region (point)
+                          (or (text-property-not-all
+                               (point) (point-max)
+                               'gnus-line-format
+                               gnus-mime-security-button-line-format)
+                              (point-max))))
+       ;; Not inlined.
+       (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
+           (with-current-buffer gnus-mime-security-details-buffer
+             (erase-buffer)
+             t)
+         (setq gnus-mime-security-details-buffer
+               (gnus-get-buffer-create "*MIME Security Details*")))
+       (with-current-buffer gnus-mime-security-details-buffer
+         (insert details)
+         (goto-char (point-min)))
+       (pop-to-buffer gnus-mime-security-details-buffer)))))
 
 (defun gnus-mime-security-press-button (handle)
-  (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
-      (gnus-mime-security-show-details handle)
-    (gnus-mime-security-verify-or-decrypt handle)))
+  (save-excursion
+    (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
+       (gnus-mime-security-show-details handle)
+      (gnus-mime-security-verify-or-decrypt handle))))
 
 (defun gnus-insert-mime-security-button (handle &optional displayed)
   (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
@@ -6507,7 +6669,8 @@ For example:
         b e)
     (setq gnus-tmp-details
          (if gnus-tmp-details
-             (concat "\n" gnus-tmp-details) ""))
+             (concat "\n" gnus-tmp-details)
+           ""))
     (setq gnus-tmp-pressed-details
          (if gnus-mime-security-button-pressed gnus-tmp-details ""))
     (unless (bolp)
@@ -6519,6 +6682,7 @@ For example:
      `(,@(gnus-local-map-property gnus-mime-security-button-map)
         gnus-callback gnus-mime-security-press-button
         gnus-line-format ,gnus-mime-security-button-line-format
+        gnus-mime-details ,gnus-mime-security-button-pressed
         article-type annotation
         gnus-data ,handle))
     (setq e (point))
@@ -6531,8 +6695,8 @@ For example:
      (lambda (widget/window &optional overlay pos)
        ;; Needed to properly clear the message due to a bug in
        ;; wid-edit (XEmacs only).
-       (if (boundp 'help-echo-owns-message)
-          (setq help-echo-owns-message t))
+       (when (boundp 'help-echo-owns-message)
+        (setq help-echo-owns-message t))
        (format
        "%S: show detail"
        (aref gnus-mouse-2 0))))))