1 ;;; gnus-cache.el --- cache interface for Gnus
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
3 ;; Free Software Foundation, Inc.
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
29 (eval-when-compile (require 'cl))
36 (if (not (fboundp 'gnus-agent-load-alist))
37 (defun gnus-agent-load-alist (group)))
40 (defcustom gnus-cache-active-file
41 (expand-file-name "active" gnus-cache-directory)
42 "*The cache active file."
46 (defcustom gnus-cache-enter-articles '(ticked dormant)
47 "Classes of articles to enter into the cache."
49 :type '(set (const ticked) (const dormant) (const unread) (const read)))
51 (defcustom gnus-cache-remove-articles '(read)
52 "Classes of articles to remove from the cache."
54 :type '(set (const ticked) (const dormant) (const unread) (const read)))
56 (defcustom gnus-cacheable-groups nil
57 "*Groups that match this regexp will be cached.
59 If you only want to cache your nntp groups, you could set this
60 variable to \"^nntp\".
62 If a group matches both gnus-cacheable-groups and gnus-uncacheable-groups
65 :type '(choice (const :tag "off" nil)
68 (defcustom gnus-uncacheable-groups nil
69 "*Groups that match this regexp will not be cached.
71 If you want to avoid caching your nnml groups, you could set this
72 variable to \"^nnml\".
74 If a group matches both gnus-cacheable-groups and gnus-uncacheable-groups
77 :type '(choice (const :tag "off" nil)
80 (defvar gnus-cache-overview-coding-system 'raw-text
81 "Coding system used on Gnus cache files.")
83 (defvar gnus-cache-coding-system 'raw-text
84 "Coding system used on Gnus cache files.")
88 ;;; Internal variables.
90 (defvar gnus-cache-removable-articles nil)
91 (defvar gnus-cache-buffer nil)
92 (defvar gnus-cache-active-hashtb nil)
93 (defvar gnus-cache-active-altered nil)
96 (autoload 'nnml-generate-nov-databases-1 "nnml")
97 (autoload 'nnvirtual-find-group-art "nnvirtual"))
101 ;;; Functions called from Gnus.
103 (defun gnus-cache-open ()
104 "Initialize the cache."
105 (when (or (file-exists-p gnus-cache-directory)
107 (not (eq gnus-use-cache 'passive))))
108 (gnus-cache-read-active)))
110 ;; Complexities of byte-compiling make this kludge necessary. Eeek.
112 (gnus-add-shutdown 'gnus-cache-close 'gnus))
114 (defun gnus-cache-close ()
115 "Shut down the cache."
116 (gnus-cache-write-active)
117 (gnus-cache-save-buffers)
118 (setq gnus-cache-active-hashtb nil))
120 (defun gnus-cache-save-buffers ()
121 ;; save the overview buffer if it exists and has been modified
122 ;; delete empty cache subdirectories
123 (when gnus-cache-buffer
124 (let ((buffer (cdr gnus-cache-buffer))
125 (overview-file (gnus-cache-file-name
126 (car gnus-cache-buffer) ".overview")))
127 ;; write the overview only if it was modified
128 (when (buffer-modified-p buffer)
131 (if (> (buffer-size) 0)
132 ;; Non-empty overview, write it to a file.
133 (let ((coding-system-for-write
134 gnus-cache-overview-coding-system))
135 (gnus-write-buffer overview-file))
136 ;; Empty overview file, remove it
137 (when (file-exists-p overview-file)
138 (delete-file overview-file))
139 ;; If possible, remove group's cache subdirectory.
141 ;; FIXME: we can detect the error type and warn the user
142 ;; of any inconsistencies (articles w/o nov entries?).
143 ;; for now, just be conservative...delete only if safe -- sj
144 (delete-directory (file-name-directory overview-file))
146 ;; Kill the buffer -- it's either unmodified or saved.
147 (gnus-kill-buffer buffer)
148 (setq gnus-cache-buffer nil))))
150 (defun gnus-cache-possibly-enter-article
151 (group article ticked dormant unread &optional force)
152 (when (and (or force (not (eq gnus-use-cache 'passive)))
154 (> article 0)) ; This might be a dummy article.
155 (let ((number article) file headers)
156 ;; If this is a virtual group, we find the real group.
157 (when (gnus-virtual-group-p group)
158 (let ((result (nnvirtual-find-group-art
159 (gnus-group-real-name group) article)))
160 (setq group (car result)
161 number (cdr result))))
163 (> number 0) ; Reffed article.
165 (and (gnus-cache-fully-p group)
166 (gnus-cache-member-of-class
167 gnus-cache-enter-articles ticked dormant unread)))
168 (not (file-exists-p (setq file (gnus-cache-file-name
170 ;; Possibly create the cache directory.
171 (gnus-make-directory (file-name-directory file))
172 ;; Save the article in the cache.
173 (if (file-exists-p file)
174 t ; The article already is saved.
176 (set-buffer nntp-server-buffer)
178 (let ((gnus-use-cache nil)
179 (gnus-article-decode-hook nil))
180 (gnus-request-article-this-buffer number group))
181 (when (> (buffer-size) 0)
182 (let ((coding-system-for-write gnus-cache-coding-system))
183 (gnus-write-buffer file))
184 (nnheader-remove-body)
185 (setq headers (nnheader-parse-naked-head))
186 (mail-header-set-number headers number)
187 (gnus-cache-change-buffer group)
188 (set-buffer (cdr gnus-cache-buffer))
189 (goto-char (point-max))
191 (while (condition-case ()
193 (> (read (current-buffer)) number))
195 ;; The line was malformed, so we just remove it!!
203 (when (< (read (current-buffer)) number)
208 (nnheader-insert-nov headers)
209 ;; Update the active info.
210 (set-buffer gnus-summary-buffer)
211 (gnus-cache-possibly-update-active group (cons number number))
212 (setq gnus-newsgroup-cached
213 (gnus-add-to-sorted-list gnus-newsgroup-cached article))
214 (gnus-summary-update-secondary-mark article))
217 (defun gnus-cache-enter-remove-article (article)
218 "Mark ARTICLE for later possible removal."
220 (push article gnus-cache-removable-articles)))
222 (defun gnus-cache-possibly-remove-articles ()
223 "Possibly remove some of the removable articles."
224 (if (not (gnus-virtual-group-p gnus-newsgroup-name))
225 (gnus-cache-possibly-remove-articles-1)
226 (let ((arts gnus-cache-removable-articles)