T-gnus 6.14.4 r04
[elisp/gnus.git-] / lisp / gnus-sum.el
index 14186b6..76ec96c 100644 (file)
@@ -1,5 +1,6 @@
 ;;; gnus-sum.el --- summary mode commands for Semi-gnus
-;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000
+;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
@@ -43,7 +44,9 @@
   (require 'static))
 
 (eval-and-compile
-  (autoload 'gnus-cache-articles-in-group "gnus-cache"))
+  (autoload 'gnus-cache-articles-in-group "gnus-cache")
+  (autoload 'pgg-decrypt-region "pgg" nil t)
+  (autoload 'pgg-verify-region "pgg" nil t))
 
 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
 (autoload 'gnus-set-summary-default-charset "gnus-i18n" nil t)
@@ -183,9 +186,11 @@ This variable will only be used if the value of
 This applies to marking commands as well as other commands that
 \"naturally\" select the next article, like, for instance, `SPC' at
 the end of an article.
-If nil, only the marking commands will go to the next (un)read article.
-If `never', commands that usually go to the next unread article, will
-go to the next article, whether it is read or not."
+
+If nil, the marking commands do NOT go to the next unread article
+(they go to the next article instead).  If `never', commands that
+usually go to the next unread article, will go to the next article,
+whether it is read or not."
   :group 'gnus-summary-marks
   :link '(custom-manual "(gnus)Setting Marks")
   :type '(choice (const :tag "off" nil)
@@ -722,7 +727,8 @@ is not run if `gnus-visual' is nil."
   :type 'hook)
 
 (defcustom gnus-exit-group-hook nil
-  "*A hook called when exiting (not quitting) summary mode."
+  "*A hook called when exiting summary mode.
+This hook is not called from the non-updating exit commands like `Q'."
   :group 'gnus-various
   :type 'hook)
 
@@ -919,8 +925,15 @@ For example: ((1 . cn-gb-2312) (2 . big5))."
   :type 'boolean
   :group 'gnus-summary-marks)
 
+(defcustom gnus-alter-articles-to-read-function nil
+  "Function to be called to alter the list of articles to be selected."
+  :type 'function
+  :group 'gnus-summary)
+
 ;;; Internal variables
 
+(defvar gnus-article-mime-handles nil)
+(defvar gnus-article-decoded-p nil)
 (defvar gnus-scores-exclude-files nil)
 (defvar gnus-page-broken nil)
 (defvar gnus-inhibit-mime-unbuttonizing nil)
@@ -956,9 +969,9 @@ For example: ((1 . cn-gb-2312) (2 . big5))."
     (?s gnus-tmp-subject-or-nil ?s)
     (?n gnus-tmp-name ?s)
     (?A (std11-address-string
-        (car (mime-read-field 'From gnus-tmp-header))) ?s)
+        (car (mime-entity-read-field gnus-tmp-header 'From))) ?s)
     (?a (or (std11-full-name-string
-            (car (mime-read-field 'From gnus-tmp-header)))
+            (car (mime-entity-read-field gnus-tmp-header 'From)))
            gnus-tmp-from) ?s)
     (?F gnus-tmp-from ?s)
     (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
@@ -1339,6 +1352,8 @@ increase the score of each group you read."
     "\M-\C-h" gnus-summary-hide-thread
     "\M-\C-f" gnus-summary-next-thread
     "\M-\C-b" gnus-summary-prev-thread
+    [(meta down)] gnus-summary-next-thread
+    [(meta up)] gnus-summary-prev-thread
     "\M-\C-u" gnus-summary-up-thread
     "\M-\C-d" gnus-summary-down-thread
     "&" gnus-summary-execute-command
@@ -1526,7 +1541,9 @@ increase the score of each group you read."
     "g" gnus-summary-show-article
     "s" gnus-summary-isearch-article
     "P" gnus-summary-print-article
-    "t" gnus-article-babel)
+    "t" gnus-article-babel
+    "d" gnus-summary-decrypt-article
+    "v" gnus-summary-verify-article)
 
   (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
     "b" gnus-article-add-buttons
@@ -2995,7 +3012,8 @@ If SHOW-ALL is non-nil, already read articles are also listed."
   "Query where the respool algorithm would put this article."
   (interactive)
   (gnus-summary-select-article)
-  (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
+  (message "%s"
+          (gnus-general-simplify-subject (gnus-summary-article-subject))))
 
 (defun gnus-gather-threads-by-subject (threads)
   "Gather threads by looking at Subject headers."
@@ -3392,17 +3410,19 @@ Returns HEADER if it was entered in the DEPENDENCIES.  Returns nil otherwise."
        (memq article gnus-newsgroup-expirable)
        ;; Only insert the Subject string when it's different
        ;; from the previous Subject string.
-       (if (gnus-subject-equal
-           (condition-case ()
-               (mail-header-subject
-                (gnus-data-header
-                 (cadr
-                  (gnus-data-find-list
-                   article
-                   (gnus-data-list t)))))
-             ;; Error on the side of excessive subjects.
-             (error ""))
-           (mail-header-subject header))
+       (if (and
+           gnus-show-threads
+           (gnus-subject-equal
+            (condition-case ()
+                (mail-header-subject
+                 (gnus-data-header
+                  (cadr
+                   (gnus-data-find-list
+                    article
+                    (gnus-data-list t)))))
+              ;; Error on the side of excessive subjects.
+              (error ""))
+            (mail-header-subject header)))
           ""
         (mail-header-subject header))
        nil (cdr (assq article gnus-newsgroup-scored))
@@ -3616,7 +3636,6 @@ If LINE, insert the rebuilt thread starting on line LINE."
                  (while thread
                    (gnus-remove-thread-1 (car thread))
                    (setq thread (cdr thread))))
-             (gnus-summary-show-all-threads)
              (gnus-remove-thread-1 thread))))))))
 
 (defun gnus-remove-thread-1 (thread)
@@ -3628,6 +3647,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
       (gnus-remove-thread-1 (pop thread)))
     (when (setq d (gnus-data-find number))
       (goto-char (gnus-data-pos d))
+      (gnus-summary-show-thread)
       (gnus-data-remove
        number
        (- (gnus-point-at-bol)
@@ -3697,11 +3717,11 @@ If LINE, insert the rebuilt thread starting on line LINE."
 (defsubst gnus-article-sort-by-author (h1 h2)
   "Sort articles by root author."
   (string-lessp
-   (let ((addr (car (mime-read-field 'From h1))))
+   (let ((addr (car (mime-entity-read-field h1 'From))))
      (or (std11-full-name-string addr)
         (std11-address-string addr)
         ""))
-   (let ((addr (car (mime-read-field 'From h2))))
+   (let ((addr (car (mime-entity-read-field h2 'From))))
      (or (std11-full-name-string addr)
         (std11-address-string addr)
         ""))
@@ -4294,6 +4314,12 @@ If SELECT-ARTICLES, only select those articles from GROUP."
            (gnus-sorted-intersection
             gnus-newsgroup-unreads
             (gnus-sorted-complement gnus-newsgroup-unreads articles)))
+      (when gnus-alter-articles-to-read-function
+       (setq gnus-newsgroup-unreads
+             (sort 
+              (funcall gnus-alter-articles-to-read-function
+                       gnus-newsgroup-name gnus-newsgroup-unreads)
+              '<)))
       articles)))
 
 (defun gnus-killed-articles (killed articles)
@@ -5132,7 +5158,8 @@ articles with that subject.  If BACKWARD, search backward instead."
   "Center point in window and redisplay frame.
 Also do horizontal recentering."
   (interactive "P")
-  (when (and gnus-auto-center-summary
+  (when (and nil
+            gnus-auto-center-summary
             (not (eq gnus-auto-center-summary 'vertical)))
     (gnus-horizontal-recenter))
   (recenter n))
@@ -5143,6 +5170,7 @@ If `gnus-auto-center-summary' is nil, or the article buffer isn't
 displayed, no centering will be performed."
   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
   ;; Recenter only when requested.  Suggested by popovich@park.cs.columbia.edu.
+  (interactive)
   (let* ((top (cond ((< (window-height) 4) 0)
                    ((< (window-height) 7) 1)
                    (t (if (numberp gnus-auto-center-summary)
@@ -5290,33 +5318,18 @@ The prefix argument ALL means to select all articles."
   (when (gnus-ephemeral-group-p gnus-newsgroup-name)
     (error "Ephemeral groups can't be reselected"))
   (let ((current-subject (gnus-summary-article-number))
-       (group gnus-newsgroup-name)
-       masquerade)
-    (when rescan
-      (setq masquerade (get-buffer-create " *masquerade-summary*"))
-      (let ((pos (- (point) (window-start)))
-           (id mode-line-buffer-identification))
-       (copy-to-buffer masquerade (window-start) (point-max))
-       (with-current-buffer masquerade
-         (set-buffer-modified-p nil)
-         (goto-char (1+ (max 0 pos)))
-         (setq mode-line-buffer-identification id
-               mode-name "Masquerade"))))
+       (group gnus-newsgroup-name))
     (setq gnus-newsgroup-begin nil)
     (gnus-summary-exit)
-    (if rescan
-       (progn
-         (switch-to-buffer masquerade)
-         (set-window-start (selected-window) (point-min))
-         (set-buffer gnus-group-buffer)
-         (gnus-group-jump-to-group group)
-         (unwind-protect
-             (save-excursion
-               (gnus-group-get-new-news-this-group 1))
-           (kill-buffer masquerade)))
-      ;; We have to adjust the point of group mode buffer because
-      ;; point was moved to the next unread newsgroup by exiting.
-      (gnus-summary-jump-to-group group))
+    ;; We have to adjust the point of group mode buffer because
+    ;; point was moved to the next unread newsgroup by exiting.
+    (gnus-summary-jump-to-group group)
+    (when rescan
+      (save-excursion
+       (save-window-excursion
+         ;; Don't show group contents.
+         (set-window-start (selected-window) (point-max))
+         (gnus-group-get-new-news-this-group 1))))
     (gnus-group-read-group all t)
     (gnus-summary-goto-subject current-subject nil t)))
 
@@ -5455,7 +5468,14 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (if (not quit-config)
          (progn
            (goto-char group-point)
-           (gnus-configure-windows 'group 'force))
+           (gnus-configure-windows 'group 'force)
+           (unless (pos-visible-in-window-p)
+             (forward-line (/ (static-if (featurep 'xemacs)
+                                  (window-displayed-height)
+                                (1- (window-height)))
+                              -2))
+             (set-window-start (selected-window) (point))
+             (goto-char group-point)))
        (gnus-handle-ephemeral-exit quit-config))
       ;; Clear the current group name.
       (unless quit-config
@@ -5589,7 +5609,8 @@ The state which existed when entering the ephemeral is reset."
       (rename-buffer
        (concat (substring name 0 (match-beginning 0)) "Dead "
               (substring name (match-beginning 0)))
-       t))))
+       t)
+      (bury-buffer))))
 
 (defun gnus-kill-or-deaden-summary (buffer)
   "Kill or deaden the summary BUFFER."
@@ -5851,39 +5872,34 @@ be displayed."
     (set-buffer gnus-summary-buffer))
   (let ((article (or article (gnus-summary-article-number)))
        (all-headers (not (not all-headers))) ;Must be T or NIL.
-       gnus-summary-display-article-function
-       did)
+       gnus-summary-display-article-function)
     (and (not pseudo)
         (gnus-summary-article-pseudo-p article)
         (error "This is a pseudo-article"))
-    (prog1
-       (save-excursion
-         (set-buffer gnus-summary-buffer)
-         (if (or (and gnus-single-article-buffer
-                      (or (null gnus-current-article)
-                          (null gnus-article-current)
-                          (null (get-buffer gnus-article-buffer))
-                          (not (eq article (cdr gnus-article-current)))
-                          (not (equal (car gnus-article-current)
-                                      gnus-newsgroup-name))))
-                 (and (not gnus-single-article-buffer)
-                      (or (null gnus-current-article)
-                          (not (eq gnus-current-article article))))
-                 force)
-             ;; The requested article is different from the current article.
-             (prog1
-                 (gnus-summary-display-article article all-headers)
-               (setq did article)
-               (when (or all-headers gnus-show-all-headers)
-                 (if (eq 'gnus-summary-toggle-mime this-command)
-                     (gnus-article-show-all)
-                   (gnus-article-show-all-headers))))
+    (save-excursion
+      (set-buffer gnus-summary-buffer)
+      (if (or (and gnus-single-article-buffer
+                  (or (null gnus-current-article)
+                      (null gnus-article-current)
+                      (null (get-buffer gnus-article-buffer))
+                      (not (eq article (cdr gnus-article-current)))
+                      (not (equal (car gnus-article-current)
+                                  gnus-newsgroup-name))))
+             (and (not gnus-single-article-buffer)
+                  (or (null gnus-current-article)
+                      (not (eq gnus-current-article article))))
+             force)
+         ;; The requested article is different from the current article.
+         (progn
+           (gnus-summary-display-article article all-headers)
            (when (or all-headers gnus-show-all-headers)
              (gnus-article-show-all-headers))
-           'old))
-      (when did
-       (gnus-article-set-window-start
-        (cdr (assq article gnus-newsgroup-bookmarks)))))))
+           (gnus-article-set-window-start
+            (cdr (assq article gnus-newsgroup-bookmarks)))
+           article)
+       (when (or all-headers gnus-show-all-headers)
+         (gnus-article-show-all-headers))
+       'old))))
 
 (defun gnus-summary-set-current-mark (&optional current-mark)
   "Obsolete function."
@@ -7399,8 +7415,11 @@ If ARG is a negative number, hide the unwanted header lines."
          (if  hidden
              (let ((gnus-treat-hide-headers nil)
                    (gnus-treat-hide-boring-headers nil))
+               (setq gnus-article-wash-types
+                     (delq 'headers gnus-article-wash-types))
                (gnus-treat-article 'head))
-           (gnus-treat-article 'head)))))))
+           (gnus-treat-article 'head)))
+       (gnus-set-mode-line 'article)))))
 
 (defun gnus-summary-show-all-headers ()
   "Make all header lines visible."
@@ -7477,7 +7496,10 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
                    'request-replace-article gnus-newsgroup-name)))
         (error "The current group does not support article editing")))
   (let ((articles (gnus-summary-work-articles n))
-       (prefix (gnus-group-real-prefix gnus-newsgroup-name))
+       (prefix (if (gnus-check-backend-function
+                   'request-move-article gnus-newsgroup-name)
+                   (gnus-group-real-prefix gnus-newsgroup-name)
+                 ""))
        (names '((move "Move" "Moving")
                 (copy "Copy" "Copying")
                 (crosspost "Crosspost" "Crossposting")))
@@ -7499,7 +7521,8 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
             articles prefix))
       (set (intern (format "gnus-current-%s-group" action)) to-newsgroup))
     (setq to-method (or select-method
-                       (gnus-group-name-to-method to-newsgroup)))
+                       (gnus-server-to-method
+                        (gnus-group-method to-newsgroup))))
     ;; Check the method we are to move this article to...
     (unless (gnus-check-backend-function
             'request-accept-article (car to-method))
@@ -7569,10 +7592,10 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
        (gnus-message 1 "Couldn't %s article %s: %s"
                      (cadr (assq action names)) article
                      (nnheader-get-report (car to-method))))
-       ((and (eq art-group 'junk)
-            (eq action 'move))
-       (gnus-summary-mark-article article gnus-canceled-mark)
-       (gnus-message 4 "Deleted article %s" article))
+       ((eq art-group 'junk)
+       (when (eq action 'move)
+         (gnus-summary-mark-article article gnus-canceled-mark)
+         (gnus-message 4 "Deleted article %s" article)))
        (t
        (let* ((pto-group (gnus-group-prefixed-name
                           (car art-group) to-method))
@@ -9203,7 +9226,8 @@ save those articles instead."
                                  (mapcar (lambda (el) (list el))
                                          (nreverse split-name))
                                  nil nil nil
-                                 'gnus-group-history)))))
+                                 'gnus-group-history))))
+         (to-method (gnus-server-to-method (gnus-group-method to-newsgroup))))
     (when to-newsgroup
       (if (or (string= to-newsgroup "")
              (string= to-newsgroup prefix))
@@ -9211,25 +9235,23 @@ save those articles instead."
       (unless to-newsgroup
        (error "No group name entered"))
       (or (gnus-active to-newsgroup)
-         (gnus-activate-group to-newsgroup)
+         (gnus-activate-group to-newsgroup nil nil to-method)
          (if (gnus-y-or-n-p (format "No such group: %s.  Create it? "
                                     to-newsgroup))
-             (or (and (gnus-request-create-group
-                       to-newsgroup (gnus-group-name-to-method to-newsgroup))
+             (or (and (gnus-request-create-group to-newsgroup to-method)
                       (gnus-activate-group
-                       to-newsgroup nil nil
-                       (gnus-group-name-to-method to-newsgroup))
+                       to-newsgroup nil nil to-method)
                       (gnus-subscribe-group to-newsgroup))
                  (error "Couldn't create group %s" to-newsgroup)))
          (error "No such group: %s" to-newsgroup)))
     to-newsgroup))
 
-(defun gnus-summary-save-parts (type dir n reverse)
+(defun gnus-summary-save-parts (type dir n &optional reverse)
   "Save parts matching TYPE to DIR.
 If REVERSE, save parts that do not match TYPE."
   (interactive
    (list (read-string "Save parts of type: " "image/.*")
-        (read-file-name "Save to directory: " t nil t)
+        (read-file-name "Save to directory: " nil nil t)
         current-prefix-arg))
   (gnus-summary-iterate n
     (let ((gnus-display-mime-function nil)
@@ -9629,40 +9651,31 @@ If REVERSE, save parts that do not match TYPE."
 
 (defun gnus-mime-extract-message/rfc822 (entity situation)
   (let (group article num cwin swin cur)
-    (with-current-buffer (mime-entity-buffer entity)
-      (save-restriction
-       (narrow-to-region (mime-entity-body-start entity)
-                         (mime-entity-body-end entity))
-       (setq group (or (cdr (assq 'group situation))
-                       (completing-read "Group: "
-                                        gnus-active-hashtb
-                                        nil
-                                        (gnus-read-active-file-p)
-                                        gnus-newsgroup-name))
-             article (gnus-request-accept-article group)
-             )
-       ))
+    (with-temp-buffer
+      (mime-insert-entity-content entity)
+      (setq group (or (cdr (assq 'group situation))
+                     (completing-read "Group: "
+                                      gnus-active-hashtb
+                                      nil
+                                      (gnus-read-active-file-p)
+                                      gnus-newsgroup-name))
+           article (gnus-request-accept-article group)))
     (when (and (consp article)
               (numberp (setq article (cdr article))))
       (setq num (1+ (or (cdr (assq 'number situation)) 0))
-           cwin (get-buffer-window (current-buffer) t)
-           )
+           cwin (get-buffer-window (current-buffer) t))
       (save-window-excursion
        (if (setq swin (get-buffer-window gnus-summary-buffer t))
            (select-window swin)
-         (set-buffer gnus-summary-buffer)
-         )
+         (set-buffer gnus-summary-buffer))
        (setq cur gnus-current-article)
        (forward-line num)
        (let (gnus-show-threads)
-         (gnus-summary-goto-subject article t)
-         )
+         (gnus-summary-goto-subject article t))
        (gnus-summary-clear-mark-forward 1)
-       (gnus-summary-goto-subject cur)
-       )
+       (gnus-summary-goto-subject cur))
       (when (and cwin (window-frame cwin))
-       (select-frame (window-frame cwin))
-       )
+       (select-frame (window-frame cwin)))
       (when (boundp 'mime-acting-situation-to-override)
        (set-alist 'mime-acting-situation-to-override
                   'group
@@ -9672,15 +9685,11 @@ If REVERSE, save parts that do not match TYPE."
                   `(progn
                      (save-current-buffer
                        (set-buffer gnus-group-buffer)
-                       (gnus-activate-group ,group)
-                       )
+                       (gnus-activate-group ,group))
                      (gnus-summary-goto-article ,cur
-                                                gnus-show-all-headers)
-                     ))
+                                                gnus-show-all-headers)))
        (set-alist 'mime-acting-situation-to-override
-                  'number num)
-       )
-      )))
+                  'number num)))))
 
 (mime-add-condition
  'action '((type . message)(subtype . rfc822)
@@ -9844,10 +9853,8 @@ treated as multipart/mixed."
                                            'gnus-wheel-edge
                                            (* (1+ edge) direction))
                         nil))
-                  (eq last-command 'gnus-wheel-summary-scroll))
-             ))
-      (gnus-summary-next-article nil nil (minusp direction)))
-    ))
+                  (eq last-command 'gnus-wheel-summary-scroll))))
+      (gnus-summary-next-article nil nil (minusp direction)))))
 
 (defun gnus-wheel-install ()
   "Enable mouse wheel support on summary window."
@@ -9861,6 +9868,48 @@ treated as multipart/mixed."
 (add-hook 'gnus-summary-mode-hook 'gnus-wheel-install)
 
 ;;;
+;;; Traditional PGP commmands
+;;;
+
+(defun gnus-summary-decrypt-article (&optional force)
+  "Decrypt the current article in traditional PGP way.
+This will have permanent effect only in mail groups.
+If FORCE is non-nil, allow editing of articles even in read-only
+groups."
+  (interactive "P")
+  (gnus-summary-select-article t)
+  (gnus-eval-in-buffer-window gnus-article-buffer
+    (save-excursion
+      (save-restriction
+       (widen)
+       (goto-char (point-min))
+       (unless (re-search-forward (car pgg-armor-header-lines) nil t)
+         (error "Not a traditional PGP message!"))
+       (let ((armor-start (match-beginning 0)))
+         (if (and (pgg-decrypt-region armor-start (point-max))
+                  (or force (not (gnus-group-read-only-p))))
+             (let ((inhibit-read-only t) 
+                   buffer-read-only)
+               (delete-region armor-start
+                              (progn 
+                                (re-search-forward "^-+END PGP" nil t)
+                                (beginning-of-line 2)
+                                (point)))
+               (insert-buffer-substring pgg-output-buffer))))))))
+
+(defun gnus-summary-verify-article ()
+  "Verify the current article in traditional PGP way."
+  (interactive)
+  (save-excursion
+    (set-buffer gnus-original-article-buffer)
+    (goto-char (point-min))
+    (unless (re-search-forward "^-+BEGIN PGP SIGNED MESSAGE" nil t)
+      (error "Not a traditional PGP message!"))
+    (re-search-forward "^-+END PGP" nil t)
+    (beginning-of-line 2)
+    (call-interactively (function pgg-verify-region))))
+
+;;;
 ;;; with article
 ;;;