;;; gnus-agent.el --- unplugged support for Gnus
-;; Copyright (C) 1997, 1998, 1999, 2000, 2001
+;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
;; Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
(require 'nnvirtual)
(require 'gnus-sum)
(require 'gnus-score)
+(require 'gnus-srvr)
(eval-when-compile
(if (featurep 'xemacs)
(require 'itimer)
:group 'gnus-agent
:type 'hook)
+(defcustom gnus-agent-fetched-hook nil
+ "Hook run after finishing fetching articles."
+ :group 'gnus-agent
+ :type 'hook)
+
(defcustom gnus-agent-handle-level gnus-level-subscribed
"Groups on levels higher than this variable will be ignored by the Agent."
:group 'gnus-agent
:type 'integer)
(defcustom gnus-agent-expire-days 7
- "Read articles older than this will be expired."
+ "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."
:group 'gnus-agent
:type 'integer)
(const :tag "Ask" ask))
:group 'gnus-agent)
+(defcustom gnus-agent-go-online 'ask
+ "Indicate if offline servers go online when you plug in.
+If this is `ask' the hook will query the user."
+ :version "21.1"
+ :type '(choice (const :tag "Always" t)
+ (const :tag "Never" nil)
+ (const :tag "Ask" ask))
+ :group 'gnus-agent)
+
+(defcustom gnus-agent-mark-unread-after-downloaded t
+ "Indicate whether to mark articles unread after downloaded."
+ :version "21.1"
+ :type 'boolean
+ :group 'gnus-agent)
+
+(defcustom gnus-agent-download-marks '(download)
+ "Marks for downloading."
+ :version "21.1"
+ :type '(repeat (symbol :tag "Mark"))
+ :group 'gnus-agent)
+
;;; Internal variables
(defvar gnus-agent-history-buffers nil)
(defvar gnus-agent-buffer-alist nil)
(defvar gnus-agent-article-alist nil)
(defvar gnus-agent-group-alist nil)
-(defvar gnus-agent-covered-methods nil)
(defvar gnus-category-alist nil)
(defvar gnus-agent-current-history nil)
(defvar gnus-agent-overview-buffer nil)
(defvar gnus-agent-file-name nil)
(defvar gnus-agent-send-mail-function nil)
(defvar gnus-agent-file-coding-system 'raw-text)
+(defvar gnus-agent-file-loading-cache nil)
+
+(defvar 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'.")
;; Dynamic variables
(defvar gnus-headers)
"Jj" gnus-agent-toggle-plugged
"Js" gnus-agent-fetch-session
"JY" gnus-agent-synchronize-flags
- "JS" gnus-group-send-drafts
+ "JS" gnus-group-send-queue
"Ja" gnus-agent-add-group
- "Jr" gnus-agent-remove-group)
+ "Jr" gnus-agent-remove-group
+ "Jo" gnus-agent-toggle-group-plugged)
(defun gnus-agent-group-make-menu-bar ()
(unless (boundp 'gnus-agent-group-menu)
gnus-agent-group-menu gnus-agent-group-mode-map ""
'("Agent"
["Toggle plugged" gnus-agent-toggle-plugged t]
+ ["Toggle group plugged" gnus-agent-toggle-group-plugged t]
["List categories" gnus-enter-category-buffer t]
- ["Send drafts" gnus-group-send-drafts gnus-plugged]
+ ["Send queue" gnus-group-send-queue gnus-plugged]
("Fetch"
["All" gnus-agent-fetch-session gnus-plugged]
["Group" gnus-agent-fetch-group gnus-plugged])))))
(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
"J#" gnus-agent-mark-article
"J\M-#" gnus-agent-unmark-article
"@" gnus-agent-toggle-mark
["Mark as downloadable" gnus-agent-mark-article t]
["Unmark as downloadable" gnus-agent-unmark-article t]
["Toggle mark" gnus-agent-toggle-mark t]
+ ["Fetch downloadable" gnus-agent-summary-fetch-group t]
["Catchup undownloaded" gnus-agent-catchup t]))))
(defvar gnus-agent-server-mode-map (make-sparse-keymap))
["Add" gnus-agent-add-server t]
["Remove" gnus-agent-remove-server t]))))
+(defun gnus-agent-make-mode-line-string (string mouse-button mouse-func)
+ (if (and (fboundp 'propertize)
+ (fboundp 'make-mode-line-mouse-map))
+ (propertize string 'local-map
+ (make-mode-line-mouse-map mouse-button mouse-func))
+ string))
+
(defun gnus-agent-toggle-plugged (plugged)
"Toggle whether Gnus is unplugged or not."
(interactive (list (not gnus-plugged)))
(if plugged
(progn
(setq gnus-plugged plugged)
- (gnus-agent-possibly-synchronize-flags)
(gnus-run-hooks 'gnus-agent-plugged-hook)
- (setcar (cdr gnus-agent-mode-status) " Plugged"))
+ (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) " Unplugged"))
+ (setcar (cdr gnus-agent-mode-status)
+ (gnus-agent-make-mode-line-string " Unplugged"
+ 'mouse-2
+ 'gnus-agent-toggle-plugged)))
(set-buffer-modified-p t))
(defun gnus-agent-close-connections ()
(setq gnus-plugged t)
(gnus))
+;;;###autoload
+(defun gnus-slave-unplugged (&optional arg)
+ "Read news as a slave unplugged."
+ (interactive "P")
+ (setq gnus-plugged nil)
+ (gnus arg nil 'slave))
+
;;;###autoload
(defun gnus-agentize ()
"Allow Gnus to be an offline newsreader.
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))))
+ (mapcar
+ (lambda (server)
+ (if (memq (car (gnus-server-to-method server))
+ gnus-agent-auto-agentize-methods)
+ (setq gnus-agent-covered-methods
+ (cons (gnus-server-to-method server)
+ gnus-agent-covered-methods ))))
+ (append (list gnus-select-method) gnus-secondary-select-methods))))
(defun gnus-agent-queue-setup ()
"Make sure the queue group exists."
gcc " ,")))))
covered)
(while (and (not covered) methods)
- (setq covered
- (member (car methods) gnus-agent-covered-methods)
+ (setq covered (gnus-agent-method-p (car methods))
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))
(defun gnus-agent-fetch-group (group)
"Put all new articles in GROUP into the Agent."
(interactive (list (gnus-group-group-name)))
- (unless gnus-plugged
- (error "Groups can't be fetched when Gnus is unplugged"))
- (unless group
- (error "No group on the current line"))
- (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))))
+ (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)))))
(defun gnus-agent-add-group (category arg)
"Add the current group to an agent category."
(unless server
(error "No server on the current line"))
(let ((method (gnus-server-get-method nil (gnus-server-server-name))))
- (when (member method gnus-agent-covered-methods)
+ (when (gnus-agent-method-p method)
(error "Server already in the agent program"))
(push method gnus-agent-covered-methods)
(gnus-server-update-server server)
(unless server
(error "No server on the current line"))
(let ((method (gnus-server-get-method nil (gnus-server-server-name))))
- (unless (member method gnus-agent-covered-methods)
+ (unless (gnus-agent-method-p method)
(error "Server not in the agent program"))
(setq gnus-agent-covered-methods
(delete method gnus-agent-covered-methods))
(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"))))
+ (mapcar (lambda (m)
+ (let ((server (gnus-server-get-method
+ nil
+ (or m "native"))))
+ (if server
+ (push server gnus-agent-covered-methods)
+ (message "Ignoring disappeared server `%s'" m)
+ (sit-for 1))))
+ (gnus-agent-read-file
+ (nnheader-concat gnus-agent-directory "lib/servers"))))
(defun gnus-agent-write-servers ()
"Write the alist of covered servers."
(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")
- (prin1 gnus-agent-covered-methods (current-buffer)))))
+ (prin1 (mapcar 'gnus-method-simplify gnus-agent-covered-methods)
+ (current-buffer)))))
;;;
;;; Summary commands
(push article gnus-newsgroup-undownloaded))
(setq gnus-newsgroup-undownloaded
(delq article gnus-newsgroup-undownloaded))
- (push article gnus-newsgroup-downloadable))
+ (setq gnus-newsgroup-downloadable
+ (gnus-add-to-sorted-list gnus-newsgroup-downloadable article)))
(gnus-summary-update-mark
(if unmark gnus-undownloaded-mark gnus-downloadable-mark)
'unread)))
(defun gnus-agent-get-undownloaded-list ()
"Mark all unfetched articles as read."
(let ((gnus-command-method (gnus-find-method-for-group gnus-newsgroup-name)))
- (when (and (not gnus-plugged)
+ (when (and (not (gnus-online gnus-command-method))
(gnus-agent-method-p gnus-command-method))
(gnus-agent-load-alist gnus-newsgroup-name)
;; First mark all undownloaded articles as undownloaded.
- (dolist (article (mapcar (lambda (header) (mail-header-number header))
- gnus-newsgroup-headers))
- (unless (or (cdr (assq article gnus-agent-article-alist))
- (memq article gnus-newsgroup-downloadable)
- (memq article gnus-newsgroup-cached))
- (push article gnus-newsgroup-undownloaded)))
+ (let ((articles (mapcar (lambda (header) (mail-header-number header))
+ gnus-newsgroup-headers))
+ (agent-articles gnus-agent-article-alist)
+ candidates article)
+ (while (setq article (pop articles))
+ (while (and agent-articles
+ (< (caar agent-articles) article))
+ (setq agent-articles (cdr agent-articles)))
+ (when (or (not (cdar agent-articles))
+ (not (= (caar agent-articles) article)))
+ (push article candidates)))
+ (dolist (article candidates)
+ (unless (or (memq article gnus-newsgroup-downloadable)
+ (memq article gnus-newsgroup-cached))
+ (push article gnus-newsgroup-undownloaded))))
;; Then mark downloaded downloadable as not-downloadable,
;; if you get my drift.
(dolist (article gnus-newsgroup-downloadable)
(pop gnus-newsgroup-undownloaded) gnus-catchup-mark)))
(gnus-summary-position-point))
+(defun gnus-agent-summary-fetch-group (&optional all)
+ "Fetch the downloadable articles in the group.
+Optional arg ALL, if non-nil, means to fetch all articles."
+ (interactive "P")
+ (let ((articles
+ (if all gnus-newsgroup-articles
+ gnus-newsgroup-downloadable))
+ (gnus-command-method (gnus-find-method-for-group gnus-newsgroup-name))
+ (state gnus-plugged))
+ (unwind-protect
+ (progn
+ (unless state
+ (gnus-agent-toggle-plugged t))
+ (unless articles
+ (error "No articles to download"))
+ (gnus-agent-with-fetch
+ (gnus-agent-fetch-articles gnus-newsgroup-name articles))
+ (save-excursion
+ (dolist (article articles)
+ (setq gnus-newsgroup-downloadable
+ (delq article gnus-newsgroup-downloadable))
+ (if gnus-agent-mark-unread-after-downloaded
+ (gnus-summary-mark-article article gnus-unread-mark)))))
+ (when (and (not state)
+ gnus-plugged)
+ (gnus-agent-toggle-plugged nil)))))
+
+(defun gnus-agent-fetch-selected-article ()
+ "Fetch the current article as it is selected.
+This can be added to `gnus-select-article-hook' or
+`gnus-mark-article-hook'."
+ (let ((gnus-command-method gnus-current-select-method))
+ (gnus-agent-fetch-articles
+ gnus-newsgroup-name
+ (list gnus-current-article))))
+
;;;
;;; Internal functions
;;;
(when (and sym (boundp sym))
(if (and (boundp (setq osym (intern (symbol-name sym) orig)))
(setq elem (symbol-value osym)))
- (setcdr elem (cdr (symbol-value sym)))
+ (progn
+ (if (and (integerp (car (symbol-value sym)))
+ (> (car elem) (car (symbol-value sym))))
+ (setcar elem (car (symbol-value sym))))
+ (if (integerp (cdr (symbol-value sym)))
+ (setcdr elem (cdr (symbol-value sym)))))
(set (intern (symbol-name sym) orig) (symbol-value sym)))))
new))
(gnus-make-directory (file-name-directory file))
- (let ((coding-system-for-write gnus-agent-file-coding-system))
+ (let ((nnmail-active-file-coding-system gnus-agent-file-coding-system))
;; The hashtable contains real names of groups, no more prefix
;; removing, so set `full' to `t'.
(gnus-write-active-file file orig t))))
(coding-system-for-write nnheader-file-coding-system)
(file-name-coding-system nnmail-pathname-coding-system)
(file (gnus-agent-lib-file "active"))
- oactive)
+ oactive-min)
(gnus-make-directory (file-name-directory file))
&