(message-exchange-point-and-mark): Use message-mark-active-p.
[gnus] / lisp / gnus-agent.el
index cfceee1..c5e8817 100644 (file)
@@ -1,5 +1,5 @@
 ;;; gnus-agent.el --- unplugged support for Gnus
-;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
+;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 
 (require 'gnus)
 (require 'gnus-cache)
+(require 'nnmail)
 (require 'nnvirtual)
 (require 'gnus-sum)
 (require 'gnus-score)
 (require 'gnus-srvr)
+(require 'gnus-util)
 (eval-when-compile
   (if (featurep 'xemacs)
       (require 'itimer)
@@ -37,7 +39,9 @@
   (require 'cl))
 
 (eval-and-compile
-  (autoload 'gnus-server-update-server "gnus-srvr"))
+  (autoload 'gnus-server-update-server "gnus-srvr")
+  (autoload 'gnus-agent-customize-category "gnus-cus")
+)
 
 (defcustom gnus-agent-directory (nnheader-concat gnus-directory "agent/")
   "Where the Gnus agent will store its files."
@@ -55,7 +59,7 @@
   :type 'hook)
 
 (defcustom gnus-agent-fetched-hook nil
-  "Hook run after finishing fetching articles."
+  "Hook run when finished fetching articles."
   :group 'gnus-agent
   :type 'hook)
 
 
 (defcustom gnus-agent-expire-days 7
   "Read articles older than this will be expired.
-This can also be a list of regexp/day pairs.  The regexps will
-be matched against group names."
+If you wish to disable Agent expiring, see `gnus-agent-enable-expiration'."
   :group 'gnus-agent
-  :type 'integer)
+  :type '(number :tag "days"))
 
 (defcustom gnus-agent-expire-all nil
   "If non-nil, also expire unread, ticked and dormant articles.
@@ -140,11 +143,81 @@ If this is `ask' the hook will query the user."
   :type '(repeat (symbol :tag "Mark"))
   :group 'gnus-agent)
 
+(defcustom gnus-agent-consider-all-articles nil
+  "When non-nil, the agent will let the agent predicate decide
+whether articles need to be downloaded or not, for all articles.  When
+nil, the default, the agent will only let the predicate decide
+whether unread articles are downloaded or not.  If you enable this,
+groups with large active ranges may open slower and you may also want
+to look into the agent expiry settings to block the expiration of
+read articles as they would just be downloaded again."
+  :version "21.4"
+  :type 'boolean
+  :group 'gnus-agent)
+
+(defcustom gnus-agent-max-fetch-size 10000000 ;; 10 Mb
+  "Chunk size for `gnus-agent-fetch-session'.
+The function will split its article fetches into chunks smaller than
+this limit."
+  :group 'gnus-agent
+  :type 'integer)
+
+(defcustom gnus-agent-enable-expiration 'ENABLE
+  "The default expiration state for each group.
+When set to ENABLE, the default, `gnus-agent-expire' will expire old
+contents from a group's local storage.  This value may be overridden
+to disable expiration in specific categories, topics, and groups.  Of
+course, you could change gnus-agent-enable-expiration to DISABLE then
+enable expiration per categories, topics, and groups."
+  :group 'gnus-agent
+  :type '(radio (const :format "Enable " ENABLE)
+                (const :format "Disable " DISABLE)))
+
+(defcustom gnus-agent-expire-unagentized-dirs t
+  "*Whether expiration should expire in unagentized directories.
+Have gnus-agent-expire scan the directories under
+\(gnus-agent-directory) for groups that are no longer agentized.
+When found, offer to remove them."
+  :type 'boolean
+  :group 'gnus-agent)
+
+(defcustom gnus-agent-auto-agentize-methods '(nntp nnimap)
+  "Initially, all servers from these methods are agentized.
+The user may remove or add servers using the Server buffer.
+See Info node `(gnus)Server Buffer'."
+  :type '(repeat symbol)
+  :group 'gnus-agent)
+
+(defcustom gnus-agent-queue-mail t
+  "Whether and when outgoing mail should be queued by the agent.
+When `always', always queue outgoing mail.  When nil, never
+queue.  Otherwise, queue if and only if unplugged."
+  :group 'gnus-agent
+  :type '(radio (const :format "Always" always)
+               (const :format "Never" nil)
+               (const :format "When plugged" t)))
+
+(defcustom gnus-agent-prompt-send-queue nil
+  "If non-nil, `gnus-group-send-queue' will prompt if called when
+unplugged."
+  :group 'gnus-agent
+  :type 'boolean)
+
 ;;; Internal variables
 
 (defvar gnus-agent-history-buffers nil)
 (defvar gnus-agent-buffer-alist nil)
-(defvar gnus-agent-article-alist nil)
+(defvar gnus-agent-article-alist nil
+  "An assoc list identifying the articles whose headers have been fetched.  
+If successfully fetched, these headers will be stored in the group's overview
+file.  The key of each assoc pair is the article ID, the value of each assoc
+pair is a flag indicating whether the identified article has been downloaded
+\(gnus-agent-fetch-articles sets the value to the day of the download).
+NOTES:
+1) The last element of this list can not be expired as some 
+   routines (for example, get-agent-fetch-headers) use the last
+   value to track which articles have had their headers retrieved.
+2) The function `gnus-agent-regenerate' may destructively modify the value.")
 (defvar gnus-agent-group-alist nil)
 (defvar gnus-category-alist nil)
 (defvar gnus-agent-current-history nil)
@@ -186,8 +259,7 @@ If this is `ask' the hook will query the user."
 (gnus-add-shutdown 'gnus-close-agent 'gnus)
 
 (defun gnus-close-agent ()
-  (setq gnus-agent-covered-methods nil
-       gnus-category-predicate-cache nil
+  (setq gnus-category-predicate-cache nil
        gnus-category-group-cache nil
        gnus-agent-spam-hashtb nil)
   (gnus-kill-buffer gnus-agent-overview-buffer))
@@ -211,28 +283,130 @@ If this is `ask' the hook will query the user."
            (cadr gnus-command-method))))
 
 (defsubst gnus-agent-directory ()
-  "Path of the Gnus agent directory."
+  "The name of the Gnus agent directory."
   (nnheader-concat gnus-agent-directory
                   (nnheader-translate-file-chars (gnus-agent-method)) "/"))
 
 (defun gnus-agent-lib-file (file)
-  "The full path of the Gnus agent library FILE."
+  "The full name of the Gnus agent library FILE."
   (expand-file-name file
                    (file-name-as-directory
                     (expand-file-name "agent.lib" (gnus-agent-directory)))))
 
+(defun gnus-agent-cat-set-property (category property value)
+  (if value
+      (setcdr (or (assq property category)
+              (let ((cell (cons property nil)))
+                    (setcdr category (cons cell (cdr category)))
+                    cell)) value)
+    (let ((category category))
+      (while (cond ((eq property (caadr category))
+                    (setcdr category (cddr category))
+                    nil)
+                   (t
+                    (setq category (cdr category)))))))
+  category)
+
+(eval-when-compile
+  (defmacro gnus-agent-cat-defaccessor (name prop-name)
+    "Define accessor and setter methods for manipulating a list of the form
+\(NAME (PROPERTY1 VALUE1) ... (PROPERTY_N VALUE_N)).
+Given the call (gnus-agent-cat-defaccessor func PROPERTY1), the list may be
+manipulated as follows:
+  (func LIST): Returns VALUE1
+  (setf (func LIST) NEW_VALUE1): Replaces VALUE1 with NEW_VALUE1."
+    `(progn (defmacro ,name (category)
+              (list (quote cdr) (list (quote assq)
+                                      (quote (quote ,prop-name)) category)))
+
+            (define-setf-method ,name (category)
+              (let* ((--category--temp-- (make-symbol "--category--"))
+                     (--value--temp-- (make-symbol "--value--")))
+                (list (list --category--temp--) ; temporary-variables
+                      (list category)   ; value-forms
+                      (list --value--temp--) ; store-variables
+                      (let* ((category --category--temp--) ; store-form
+                             (value --value--temp--))
+                        (list (quote gnus-agent-cat-set-property)
+                              category
+                              (quote (quote ,prop-name))
+                              value))
+                      (list (quote ,name) --category--temp--) ; access-form
+                      )))))
+  )
+
+(defmacro gnus-agent-cat-name (category)
+  `(car ,category))
+
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-days-until-old             agent-days-until-old)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-enable-expiration          agent-enable-expiration)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-groups                     agent-groups)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-high-score                 agent-high-score)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-length-when-long           agent-length-when-long)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-length-when-short          agent-length-when-short)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-low-score                  agent-low-score)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-predicate                  agent-predicate)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-score-file                 agent-score-file)
+(gnus-agent-cat-defaccessor
+ gnus-agent-cat-enable-undownloaded-faces agent-enable-undownloaded-faces)
+
+(eval-and-compile
+  (defsetf gnus-agent-cat-groups (category) (groups)
+    (list 'gnus-agent-set-cat-groups category groups)))
+
+(defun gnus-agent-set-cat-groups (category groups)
+  (unless (eq groups 'ignore)
+    (let ((new-g groups)
+          (old-g (gnus-agent-cat-groups category)))
+      (cond ((eq new-g old-g)
+             ;; gnus-agent-add-group is fiddling with the group
+             ;; list. Still, Im done.
+             nil
+             )
+            ((eq new-g (cdr old-g))
+             ;; gnus-agent-add-group is fiddling with the group list
+             (setcdr (or (assq 'agent-groups category)
+                         (let ((cell (cons 'agent-groups nil)))
+                           (setcdr category (cons cell (cdr category)))
+                           cell)) new-g))
+            (t
+             (let ((groups groups))
+               (while groups
+                 (let* ((group        (pop groups))
+                        (old-category (gnus-group-category group)))
+                   (if (eq category old-category)
+                       nil
+                     (setf (gnus-agent-cat-groups old-category)
+                           (delete group (gnus-agent-cat-groups
+                                          old-category))))))
+               ;; Purge cache as preceeding loop invalidated it.
+               (setq gnus-category-group-cache nil))
+
+             (setcdr (or (assq 'agent-groups category)
+                         (let ((cell (cons 'agent-groups nil)))
+                           (setcdr category (cons cell (cdr category)))
+                           cell)) groups))))))
+
+(defsubst gnus-agent-cat-make (name &optional default-agent-predicate)
+  (list name `(agent-predicate . ,(or default-agent-predicate 'false))))
+
 ;;; Fetching setup functions.
 
 (defun gnus-agent-start-fetch ()
   "Initialize data structures for efficient fetching."
-  (gnus-agent-open-history)
-  (setq gnus-agent-current-history (gnus-agent-history-buffer))
   (gnus-agent-create-buffer))
 
 (defun gnus-agent-stop-fetch ()
   "Save all data structures and clean up."
-  (gnus-agent-save-history)
-  (gnus-agent-close-history)
   (setq gnus-agent-spam-hashtb nil)
   (save-excursion
     (set-buffer nntp-server-buffer)
@@ -249,6 +423,13 @@ If this is `ask' the hook will query the user."
 (put 'gnus-agent-with-fetch 'lisp-indent-function 0)
 (put 'gnus-agent-with-fetch 'edebug-form-spec '(body))
 
+(defmacro gnus-agent-append-to-list (tail value)
+  `(setq ,tail (setcdr ,tail (cons ,value nil))))
+
+(defmacro gnus-agent-message (level &rest args)
+  `(if (<= ,level gnus-verbose)
+       (message ,@args)))
+
 ;;;
 ;;; Mode infestation
 ;;;
@@ -278,7 +459,13 @@ If this is `ask' the hook will query the user."
                                                     buffer))))
            minor-mode-map-alist))
     (when (eq major-mode 'gnus-group-mode)
-      (gnus-agent-toggle-plugged gnus-plugged))
+      (let ((init-plugged gnus-plugged)
+            (gnus-agent-go-online nil))
+        ;; g-a-t-p does nothing when gnus-plugged isn't changed.
+        ;; Therefore, make certain that the current value does not
+        ;; match the desired initial value.
+        (setq gnus-plugged :unknown)
+        (gnus-agent-toggle-plugged init-plugged)))
     (gnus-run-hooks 'gnus-agent-mode-hook
                    (intern (format "gnus-agent-%s-mode-hook" buffer)))))
 
@@ -302,15 +489,21 @@ If this is `ask' the hook will query the user."
        ["Toggle plugged" gnus-agent-toggle-plugged t]
        ["Toggle group plugged" gnus-agent-toggle-group-plugged t]
        ["List categories" gnus-enter-category-buffer t]
+       ["Add (current) group to category" gnus-agent-add-group t]
+       ["Remove (current) group from category" gnus-agent-remove-group t]
        ["Send queue" gnus-group-send-queue gnus-plugged]
        ("Fetch"
        ["All" gnus-agent-fetch-session gnus-plugged]
-       ["Group" gnus-agent-fetch-group gnus-plugged])))))
+       ["Group" gnus-agent-fetch-group gnus-plugged])
+       ["Synchronize flags" gnus-agent-synchronize-flags t]
+       ))))
 
 (defvar gnus-agent-summary-mode-map (make-sparse-keymap))
 (gnus-define-keys gnus-agent-summary-mode-map
   "Jj" gnus-agent-toggle-plugged
   "Ju" gnus-agent-summary-fetch-group
+  "JS" gnus-agent-fetch-group
+  "Js" gnus-agent-summary-fetch-series
   "J#" gnus-agent-mark-article
   "J\M-#" gnus-agent-unmark-article
   "@" gnus-agent-toggle-mark
@@ -350,31 +543,43 @@ If this is `ask' the hook will query the user."
                  (make-mode-line-mouse-map mouse-button mouse-func))
     string))
 
-(defun gnus-agent-toggle-plugged (plugged)
+(defun gnus-agent-toggle-plugged (set-to)
   "Toggle whether Gnus is unplugged or not."
   (interactive (list (not gnus-plugged)))
-  (if plugged
-      (progn
-       (setq gnus-plugged plugged)
-       (gnus-run-hooks 'gnus-agent-plugged-hook)
-       (setcar (cdr gnus-agent-mode-status)
-               (gnus-agent-make-mode-line-string " Plugged"
-                                                 'mouse-2
-                                                 'gnus-agent-toggle-plugged))
-       (gnus-agent-go-online gnus-agent-go-online)
-       (gnus-agent-possibly-synchronize-flags))
-    (gnus-agent-close-connections)
-    (setq gnus-plugged plugged)
-    (gnus-run-hooks 'gnus-agent-unplugged-hook)
-    (setcar (cdr gnus-agent-mode-status)
-           (gnus-agent-make-mode-line-string " Unplugged"
-                                             'mouse-2
-                                             'gnus-agent-toggle-plugged)))
+  (cond ((eq set-to gnus-plugged)
+         nil)
+        (set-to
+         (setq gnus-plugged set-to)
+         (gnus-run-hooks 'gnus-agent-plugged-hook)
+         (setcar (cdr gnus-agent-mode-status)
+                 (gnus-agent-make-mode-line-string " Plugged"
+                                                   'mouse-2
+                                                   'gnus-agent-toggle-plugged))
+         (gnus-agent-go-online gnus-agent-go-online)
+         (gnus-agent-possibly-synchronize-flags))
+        (t
+         (gnus-agent-close-connections)
+         (setq gnus-plugged set-to)
+         (gnus-run-hooks 'gnus-agent-unplugged-hook)
+         (setcar (cdr gnus-agent-mode-status)
+                 (gnus-agent-make-mode-line-string " Unplugged"
+                                                   'mouse-2
+                                                   'gnus-agent-toggle-plugged))))
   (set-buffer-modified-p t))
 
+(defmacro gnus-agent-while-plugged (&rest body)
+  `(let ((original-gnus-plugged gnus-plugged))
+    (unwind-protect
+        (progn (gnus-agent-toggle-plugged t)
+               ,@body)
+      (gnus-agent-toggle-plugged original-gnus-plugged))))
+
+(put 'gnus-agent-while-plugged 'lisp-indent-function 0)
+(put 'gnus-agent-while-plugged 'edebug-form-spec '(body))
+
 (defun gnus-agent-close-connections ()
   "Close all methods covered by the Gnus agent."
-  (let ((methods gnus-agent-covered-methods))
+  (let ((methods (gnus-agent-covered-methods)))
     (while methods
       (gnus-close-server (pop methods)))))
 
@@ -402,10 +607,10 @@ If this is `ask' the hook will query the user."
 ;;;###autoload
 (defun gnus-agentize ()
   "Allow Gnus to be an offline newsreader.
-The normal usage of this command is to put the following as the
-last form in your `.gnus.el' file:
 
-\(gnus-agentize)
+The gnus-agentize function is now called internally by gnus when
+gnus-agent is set.  If you wish to avoid calling gnus-agentize,
+customize gnus-agent to nil.
 
 This will modify the `gnus-setup-news-hook', and
 `message-send-mail-real-function' variables, and install the Gnus agent
@@ -414,24 +619,43 @@ minor mode in all Gnus buffers."
   (gnus-open-agent)
   (add-hook 'gnus-setup-news-hook 'gnus-agent-queue-setup)
   (unless gnus-agent-send-mail-function
-    (setq gnus-agent-send-mail-function (or
-                                        message-send-mail-real-function
-                                        message-send-mail-function)
+    (setq gnus-agent-send-mail-function
+         (or message-send-mail-real-function
+             message-send-mail-function)
          message-send-mail-real-function 'gnus-agent-send-mail))
-  (unless gnus-agent-covered-methods
-    (setq gnus-agent-covered-methods (list gnus-select-method))))
 
-(defun gnus-agent-queue-setup ()
-  "Make sure the queue group exists."
-  (unless (gnus-gethash "nndraft:queue" gnus-newsrc-hashtb)
-    (gnus-request-create-group "queue" '(nndraft ""))
+  ;; If the servers file doesn't exist, auto-agentize some servers and
+  ;; save the servers file so this auto-agentizing isn't invoked
+  ;; again.
+  (unless (file-exists-p (nnheader-concat gnus-agent-directory "lib/servers"))
+    (gnus-message 3 "First time agent user, agentizing remote groups...")
+    (mapc
+     (lambda (server-or-method)
+       (let ((method (gnus-server-to-method server-or-method)))
+        (when (memq (car method)
+                    gnus-agent-auto-agentize-methods)
+          (push (gnus-method-to-server method)
+                gnus-agent-covered-methods)
+          (setq gnus-agent-method-p-cache nil))))
+     (cons gnus-select-method gnus-secondary-select-methods))
+    (gnus-agent-write-servers)))
+
+(defun gnus-agent-queue-setup (&optional group-name)
+  "Make sure the queue group exists.
+Optional arg GROUP-NAME allows to specify another group."
+  (unless (gnus-gethash (format "nndraft:%s" (or group-name "queue"))
+                       gnus-newsrc-hashtb)
+    (gnus-request-create-group (or group-name "queue") '(nndraft ""))
     (let ((gnus-level-default-subscribed 1))
-      (gnus-subscribe-group "nndraft:queue" nil '(nndraft "")))
+      (gnus-subscribe-group (format "nndraft:%s" (or group-name "queue"))
+                           nil '(nndraft "")))
     (gnus-group-set-parameter
-     "nndraft:queue" 'gnus-dummy '((gnus-draft-mode)))))
+     (format "nndraft:%s" (or group-name "queue"))
+     'gnus-dummy '((gnus-draft-mode)))))
 
 (defun gnus-agent-send-mail ()
-  (if gnus-plugged
+  (if (or (not gnus-agent-queue-mail)
+         (and gnus-plugged (not (eq gnus-agent-queue-mail 'always))))
       (funcall gnus-agent-send-mail-function)
     (goto-char (point-min))
     (re-search-forward
@@ -476,6 +700,7 @@ be a select method."
              methods (cdr methods)))
       covered)))
 
+;;;###autoload
 (defun gnus-agent-possibly-save-gcc ()
   "Save GCC if Gnus is unplugged."
   (when (and (not gnus-plugged) (gnus-agent-any-covered-gcc))
@@ -501,23 +726,18 @@ be a select method."
     (error "Groups can't be fetched when Gnus is unplugged"))
   (gnus-group-iterate n 'gnus-agent-fetch-group))
 
-(defun gnus-agent-fetch-group (group)
+(defun gnus-agent-fetch-group (&optional group)
   "Put all new articles in GROUP into the Agent."
   (interactive (list (gnus-group-group-name)))
-  (let ((state gnus-plugged))
-    (unwind-protect
-       (progn
-         (unless group
-           (error "No group on the current line"))
-         (unless state
-           (gnus-agent-toggle-plugged gnus-plugged))
-         (let ((gnus-command-method (gnus-find-method-for-group group)))
-           (gnus-agent-with-fetch
-             (gnus-agent-fetch-group-1 group gnus-command-method)
-             (gnus-message 5 "Fetching %s...done" group))))
-      (when (and (not state)
-                gnus-plugged)
-       (gnus-agent-toggle-plugged gnus-plugged)))))
+  (setq group (or group gnus-newsgroup-name))
+  (unless group
+    (error "No group on the current line"))
+
+  (gnus-agent-while-plugged
+    (let ((gnus-command-method (gnus-find-method-for-group group)))
+      (gnus-agent-with-fetch
+        (gnus-agent-fetch-group-1 group gnus-command-method)
+        (gnus-message 5 "Fetching %s...done" group)))))
 
 (defun gnus-agent-add-group (category arg)
   "Add the current group to an agent category."
@@ -534,10 +754,12 @@ be a select method."
        c groups)
     (gnus-group-iterate arg
       (lambda (group)
-       (when (cadddr (setq c (gnus-group-category group)))
-         (setf (cadddr c) (delete group (cadddr c))))
+       (when (gnus-agent-cat-groups (setq c (gnus-group-category group)))
+         (setf (gnus-agent-cat-groups c)
+                (delete group (gnus-agent-cat-groups c))))
        (push group groups)))
-    (setf (cadddr cat) (nconc (cadddr cat) groups))
+    (setf (gnus-agent-cat-groups cat)
+          (nconc (gnus-agent-cat-groups cat) groups))
     (gnus-category-write)))
 
 (defun gnus-agent-remove-group (arg)
@@ -546,15 +768,16 @@ be a select method."
   (let (c)
     (gnus-group-iterate arg
       (lambda (group)
-       (when (cadddr (setq c (gnus-group-category group)))
-         (setf (cadddr c) (delete group (cadddr c))))))
+       (when (gnus-agent-cat-groups (setq c (gnus-group-category group)))
+         (setf (gnus-agent-cat-groups c)
+                (delete group (gnus-agent-cat-groups c))))))
     (gnus-category-write)))
 
 (defun gnus-agent-synchronize-flags ()
   "Synchronize unplugged flags with servers."
   (interactive)
   (save-excursion
-    (dolist (gnus-command-method gnus-agent-covered-methods)
+    (dolist (gnus-command-method (gnus-agent-covered-methods))
       (when (file-exists-p (gnus-agent-lib-file "flags"))
        (gnus-agent-synchronize-flags-server gnus-command-method)))))
 
@@ -562,7 +785,7 @@ be a select method."
   "Synchronize flags according to `gnus-agent-synchronize-flags'."
   (interactive)
   (save-excursion
-    (dolist (gnus-command-method gnus-agent-covered-methods)
+    (dolist (gnus-command-method (gnus-agent-covered-methods))
       (when (file-exists-p (gnus-agent-lib-file "flags"))
        (gnus-agent-possibly-synchronize-flags-server gnus-command-method)))))
 
@@ -574,11 +797,10 @@ be a select method."
       (erase-buffer)
       (nnheader-insert-file-contents (gnus-agent-lib-file "flags"))
       (if (null (gnus-check-server gnus-command-method))
-         (message "Couldn't open server %s" (nth 1 gnus-command-method))
+         (gnus-message 1 "Couldn't open server %s" (nth 1 gnus-command-method))
        (while (not (eobp))
          (if (null (eval (read (current-buffer))))
-             (progn (forward-line)
-                    (kill-line -1))
+             (gnus-delete-line)
            (write-file (gnus-agent-lib-file "flags"))
            (error "Couldn't set flags from file %s"
                   (gnus-agent-lib-file "flags"))))
@@ -598,38 +820,80 @@ be a select method."
 ;;; Server mode commands
 ;;;
 
-(defun gnus-agent-add-server (server)
+(defun gnus-agent-add-server ()
   "Enroll SERVER in the agent program."
-  (interactive (list (gnus-server-server-name)))
-  (unless server
-    (error "No server on the current line"))
-  (let ((method (gnus-server-get-method nil (gnus-server-server-name))))
+  (interactive)
+  (let* ((server       (gnus-server-server-name))
+         (named-server (gnus-server-named-server))
+         (method       (and server
+                            (gnus-server-get-method nil server))))
+    (unless server
+      (error "No server on the current line"))
+
     (when (gnus-agent-method-p method)
       (error "Server already in the agent program"))
-    (push method gnus-agent-covered-methods)
+
+    (push named-server gnus-agent-covered-methods)
+
+    (setq gnus-agent-method-p-cache nil)
     (gnus-server-update-server server)
     (gnus-agent-write-servers)
-    (message "Entered %s into the Agent" server)))
+    (gnus-message 1 "Entered %s into the Agent" server)))
 
-(defun gnus-agent-remove-server (server)
+(defun gnus-agent-remove-server ()
   "Remove SERVER from the agent program."
-  (interactive (list (gnus-server-server-name)))
-  (unless server
-    (error "No server on the current line"))
-  (let ((method (gnus-server-get-method nil (gnus-server-server-name))))
-    (unless (gnus-agent-method-p method)
+  (interactive)
+  (let* ((server       (gnus-server-server-name))
+         (named-server (gnus-server-named-server)))
+    (unless server
+      (error "No server on the current line"))
+
+    (unless (member named-server gnus-agent-covered-methods)
       (error "Server not in the agent program"))
-    (setq gnus-agent-covered-methods
-         (delete method gnus-agent-covered-methods))
+
+    (setq gnus-agent-covered-methods 
+          (delete named-server gnus-agent-covered-methods)
+          gnus-agent-method-p-cache nil)
+
     (gnus-server-update-server server)
     (gnus-agent-write-servers)
-    (message "Removed %s from the agent" server)))
+    (gnus-message 1 "Removed %s from the agent" server)))
 
 (defun gnus-agent-read-servers ()
   "Read the alist of covered servers."
+  (setq gnus-agent-covered-methods 
+        (gnus-agent-read-file
+         (nnheader-concat gnus-agent-directory "lib/servers"))
+        gnus-agent-method-p-cache nil)
+
+  ;; I am called so early in start-up that I can not validate server
+  ;; names.  When that is the case, I skip the validation.  That is
+  ;; alright as the gnus startup code calls the validate methods
+  ;; directly.
+  (if gnus-server-alist
+      (gnus-agent-read-servers-validate)))
+
+(defun gnus-agent-read-servers-validate ()
+  (mapcar (lambda (server-or-method)
+            (let* ((server (if (stringp server-or-method)
+                               server-or-method
+                             (gnus-method-to-server server-or-method)))
+                   (method (gnus-server-to-method server)))
+              (if method
+                  (unless (member server gnus-agent-covered-methods)
+                    (push server gnus-agent-covered-methods)
+                    (setq gnus-agent-method-p-cache nil))
+                (gnus-message 1 "Ignoring disappeared server `%s'" server))))
+          (prog1 gnus-agent-covered-methods
+            (setq gnus-agent-covered-methods nil))))
+
+(defun gnus-agent-read-servers-validate-native (native-method)
   (setq gnus-agent-covered-methods
-       (gnus-agent-read-file
-        (nnheader-concat gnus-agent-directory "lib/servers"))))
+        (mapcar (lambda (method)
+                  (if (or (not method)
+                          (equal method native-method))
+                      "native"
+                    method)) gnus-agent-covered-methods)))
 
 (defun gnus-agent-write-servers ()
   "Write the alist of covered servers."
@@ -637,7 +901,8 @@ be a select method."
   (let ((coding-system-for-write nnheader-file-coding-system)
        (file-name-coding-system nnmail-pathname-coding-system))
     (with-temp-file (nnheader-concat gnus-agent-directory "lib/servers")