Regenerate ldefs-boot.el
[bpt/emacs.git] / lisp / gnus / nnfolder.el
CommitLineData
eec82323 1;;; nnfolder.el --- mail folder access for Gnus
e84b4b86 2
ba318903 3;; Copyright (C) 1995-2014 Free Software Foundation, Inc.
eec82323 4
89b163db 5;; Author: Simon Josefsson <simon@josefsson.org>
23f87bed
MB
6;; ShengHuo Zhu <zsh@cs.rochester.edu> (adding NOV)
7;; Scott Byer <byer@mv.us.adobe.com>
6748645f 8;; Lars Magne Ingebrigtsen <larsi@gnus.org>
23f87bed 9;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
eec82323
LMI
10;; Keywords: mail
11
12;; This file is part of GNU Emacs.
13
5e809f55 14;; GNU Emacs is free software: you can redistribute it and/or modify
eec82323 15;; it under the terms of the GNU General Public License as published by
5e809f55
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
eec82323
LMI
18
19;; GNU Emacs is distributed in the hope that it will be useful,
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
23
24;; You should have received a copy of the GNU General Public License
5e809f55 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
eec82323
LMI
26
27;;; Commentary:
28
29;;; Code:
30
f0b7f5a8 31;; For Emacs <22.2 and XEmacs.
75327e94
GM
32(eval-and-compile
33 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
34
eec82323
LMI
35(require 'nnheader)
36(require 'message)
37(require 'nnmail)
38(require 'nnoo)
6748645f 39(eval-when-compile (require 'cl))
23f87bed 40(require 'gnus)
eec82323 41(require 'gnus-util)
0d972486 42(require 'gnus-range)
eec82323 43
547e921e 44;; FIXME not explicitly used in this file.
c5ce620e 45(autoload 'gnus-article-unpropagatable-p "gnus-sum")
23f87bed 46
eec82323
LMI
47(nnoo-declare nnfolder)
48
49(defvoo nnfolder-directory (expand-file-name message-directory)
50 "The name of the nnfolder directory.")
51
23f87bed
MB
52(defvoo nnfolder-nov-directory nil
53 "The name of the nnfolder NOV directory.
54If nil, `nnfolder-directory' is used.")
55
eec82323 56(defvoo nnfolder-active-file
16409b0b 57 (nnheader-concat nnfolder-directory "active")
eec82323
LMI
58 "The name of the active file.")
59
60;; I renamed this variable to something more in keeping with the general GNU
61;; style. -SLB
62
63(defvoo nnfolder-ignore-active-file nil
dcf4ae73 64 "If non-nil, the active file is ignored.
16409b0b
GM
65This causes nnfolder to do some extra work in order to determine the
66true active ranges of an mbox file. Note that the active file is
dcf4ae73 67still saved, but its values are not used. This costs some extra time
16409b0b 68when scanning an mbox when opening it.")
eec82323
LMI
69
70(defvoo nnfolder-distrust-mbox nil
16409b0b
GM
71 "If non-nil, the folder will be distrusted.
72This means that nnfolder will not trust the user with respect to
73inserting unaccounted for mail in the middle of an mbox file. This
74can greatly slow down scans, which now must scan the entire file for
75unmarked messages. When nil, scans occur forward from the last marked
76message, a huge time saver for large mailboxes.")
eec82323
LMI
77
78(defvoo nnfolder-newsgroups-file
16409b0b 79 (concat (file-name-as-directory nnfolder-directory) "newsgroups")
eec82323
LMI
80 "Mail newsgroups description file.")
81
82(defvoo nnfolder-get-new-mail t
83 "If non-nil, nnfolder will check the incoming mail file and split the mail.")
84
85(defvoo nnfolder-prepare-save-mail-hook nil
86 "Hook run narrowed to an article before saving.")
87
88(defvoo nnfolder-save-buffer-hook nil
89 "Hook run before saving the nnfolder mbox buffer.")
90
23f87bed 91
eec82323
LMI
92(defvoo nnfolder-inhibit-expiry nil
93 "If non-nil, inhibit expiry.")
94
95\f
96
23f87bed 97(defconst nnfolder-version "nnfolder 2.0"
eec82323
LMI
98 "nnfolder version.")
99
100(defconst nnfolder-article-marker "X-Gnus-Article-Number: "
101 "String used to demarcate what the article number for a message is.")
102
103(defvoo nnfolder-current-group nil)
104(defvoo nnfolder-current-buffer nil)
105(defvoo nnfolder-status-string "")
106(defvoo nnfolder-group-alist nil)
107(defvoo nnfolder-buffer-alist nil)
108(defvoo nnfolder-scantime-alist nil)
109(defvoo nnfolder-active-timestamp nil)
16409b0b 110(defvoo nnfolder-active-file-coding-system mm-text-coding-system)
a1506d29 111(defvoo nnfolder-active-file-coding-system-for-write
16409b0b
GM
112 nnmail-active-file-coding-system)
113(defvoo nnfolder-file-coding-system mm-text-coding-system)
114(defvoo nnfolder-file-coding-system-for-write nnheader-file-coding-system
115 "Coding system for save nnfolder file.
23f87bed
MB
116if nil, `nnfolder-file-coding-system' is used.") ; FIXME: fill-in the doc-string of this variable
117
118(defvoo nnfolder-nov-is-evil nil
119 "If non-nil, Gnus will never generate and use nov databases for mail groups.
120Using nov databases will speed up header fetching considerably.
121This variable shouldn't be flipped much. If you have, for some reason,
122set this to t, and want to set it to nil again, you should always run
123the `nnfolder-generate-active-file' command. The function will go
124through all nnfolder directories and generate nov databases for them
125all. This may very well take some time.")
126
127(defvoo nnfolder-nov-file-suffix ".nov")
128
129(defvoo nnfolder-nov-buffer-alist nil)
130
131(defvar nnfolder-nov-buffer-file-name nil)
132
eec82323
LMI
133\f
134
135;;; Interface functions
136
137(nnoo-define-basics nnfolder)
138
139(deffoo nnfolder-retrieve-headers (articles &optional group server fetch-old)
20a673b2 140 (with-current-buffer nntp-server-buffer
eec82323 141 (erase-buffer)
23f87bed 142 (let (article start stop num)
eec82323
LMI
143 (nnfolder-possibly-change-group group server)
144 (when nnfolder-current-buffer
145 (set-buffer nnfolder-current-buffer)
146 (goto-char (point-min))
147 (if (stringp (car articles))
148 'headers
23f87bed
MB
149 (if (nnfolder-retrieve-headers-with-nov articles fetch-old)
150 'nov
151 (setq articles (gnus-sorted-intersection
152 ;; Is ARTICLES sorted?
153 (sort articles '<)
154 (nnfolder-existing-articles)))
155 (while (setq article (pop articles))
156 (set-buffer nnfolder-current-buffer)
157 (cond ((nnfolder-goto-article article)
158 (setq start (point))
159 (setq stop (if (search-forward "\n\n" nil t)
160 (1- (point))
161 (point-max)))
162 (set-buffer nntp-server-buffer)
163 (insert (format "221 %d Article retrieved.\n" article))
164 (insert-buffer-substring nnfolder-current-buffer
165 start stop)
166 (goto-char (point-max))
167 (insert ".\n"))
168
169 ;; If we couldn't find this article, skip over ranges
170 ;; of missing articles so we don't search the whole file
171 ;; for each of them.
172 ((numberp article)
173 (setq start (point))
174 (and
175 ;; Check that we are either at BOF or after an
176 ;; article with a lower number. We do this so we
177 ;; won't be confused by out-of-order article numbers,
178 ;; as caused by active file bogosity.
179 (cond
180 ((bobp))
181 ((search-backward (concat "\n" nnfolder-article-marker)
182 nil t)
183 (goto-char (match-end 0))
e9bd5782 184 (setq num (string-to-number
23f87bed 185 (buffer-substring
01c52d31 186 (point) (point-at-eol))))
23f87bed
MB
187 (goto-char start)
188 (< num article)))
189 ;; Check that we are before an article with a
190 ;; higher number.
191 (search-forward (concat "\n" nnfolder-article-marker)
192 nil t)
193 (progn
e9bd5782 194 (setq num (string-to-number
23f87bed 195 (buffer-substring
01c52d31 196 (point) (point-at-eol))))
23f87bed
MB
197 (> num article))
198 ;; Discard any article numbers before the one we're
199 ;; now looking at.
200 (while (and articles
201 (< (car articles) num))
202 (setq articles (cdr articles))))
203 (goto-char start))))
204 (set-buffer nntp-server-buffer)
205 (nnheader-fold-continuation-lines)
206 'headers))))))
eec82323
LMI
207
208(deffoo nnfolder-open-server (server &optional defs)
209 (nnoo-change-server 'nnfolder server defs)
210 (nnmail-activate 'nnfolder t)
211 (gnus-make-directory nnfolder-directory)
23f87bed
MB
212 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
213 (and nnfolder-nov-directory
214 (gnus-make-directory nnfolder-nov-directory)))
eec82323
LMI
215 (cond
216 ((not (file-exists-p nnfolder-directory))
217 (nnfolder-close-server)
218 (nnheader-report 'nnfolder "Couldn't create directory: %s"
219 nnfolder-directory))
220 ((not (file-directory-p (file-truename nnfolder-directory)))
221 (nnfolder-close-server)
222 (nnheader-report 'nnfolder "Not a directory: %s" nnfolder-directory))
223 (t
224 (nnmail-activate 'nnfolder)
225 (nnheader-report 'nnfolder "Opened server %s using directory %s"
226 server nnfolder-directory)
227 t)))
228
229(deffoo nnfolder-request-close ()
230 (let ((alist nnfolder-buffer-alist))
231 (while alist
232 (nnfolder-close-group (caar alist) nil t)
233 (setq alist (cdr alist))))
234 (nnoo-close-server 'nnfolder)
235 (setq nnfolder-buffer-alist nil
236 nnfolder-group-alist nil))
237
238(deffoo nnfolder-request-article (article &optional group server buffer)
239 (nnfolder-possibly-change-group group server)
20a673b2 240 (with-current-buffer nnfolder-current-buffer
eec82323 241 (goto-char (point-min))
6748645f 242 (when (nnfolder-goto-article article)
eec82323 243 (let (start stop)
eec82323
LMI
244 (setq start (point))
245 (forward-line 1)
246 (unless (and (nnmail-search-unix-mail-delim)
247 (forward-line -1))
248 (goto-char (point-max)))
249 (setq stop (point))
250 (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
251 (set-buffer nntp-server-buffer)
252 (erase-buffer)
253 (insert-buffer-substring nnfolder-current-buffer start stop)
254 (goto-char (point-min))
255 (while (looking-at "From ")
256 (delete-char 5)
257 (insert "X-From-Line: ")
258 (forward-line 1))
259 (if (numberp article)
260 (cons nnfolder-current-group article)
261 (goto-char (point-min))
eec82323 262 (cons nnfolder-current-group
a1506d29 263 (if (search-forward (concat "\n" nnfolder-article-marker)
16409b0b 264 nil t)
e9bd5782 265 (string-to-number (buffer-substring
01c52d31 266 (point) (point-at-eol)))
16409b0b 267 -1))))))))
eec82323 268
286c4fc2 269(deffoo nnfolder-request-group (group &optional server dont-check info)
eec82323
LMI
270 (nnfolder-possibly-change-group group server t)
271 (save-excursion
01c52d31
MB
272 (cond ((not (assoc group nnfolder-group-alist))
273 (nnheader-report 'nnfolder "No such group: %s" group))
274 ((file-directory-p (nnfolder-group-pathname group))
275 (nnheader-report 'nnfolder "%s is a directory"
276 (file-name-as-directory
277 (let ((nnmail-pathname-coding-system nil))
278 (nnfolder-group-pathname group)))))
279 (dont-check
280 (nnheader-report 'nnfolder "Selected group %s" group)
281 t)
282 (t
283 (let* ((active (assoc group nnfolder-group-alist))
284 (group (car active))
285 (range (cadr active)))
286 (cond
287 ((null active)
288 (nnheader-report 'nnfolder "No such group: %s" group))
289 ((null nnfolder-current-group)
290 (nnheader-report 'nnfolder "Empty group: %s" group))
291 (t
292 (nnheader-report 'nnfolder "Selected group %s" group)
293 (nnheader-insert "211 %d %d %d %s\n"
294 (1+ (- (cdr range) (car range)))
295 (car range) (cdr range) group))))))))
eec82323
LMI
296
297(deffoo nnfolder-request-scan (&optional group server)
298 (nnfolder-possibly-change-group nil server)
299 (when nnfolder-get-new-mail
300 (nnfolder-possibly-change-group group server)
301 (nnmail-get-new-mail
549c9aed
G
302 'nnfolder 'nnfolder-save-all-buffers
303 nnfolder-directory group)))
304
305(defun nnfolder-save-all-buffers ()
306 (let ((bufs nnfolder-buffer-alist))
307 (save-excursion
308 (while bufs
309 (if (not (gnus-buffer-live-p (nth 1 (car bufs))))
310 (setq nnfolder-buffer-alist
311 (delq (car bufs) nnfolder-buffer-alist))
312 (set-buffer (nth 1 (car bufs)))
313 (nnfolder-save-buffer)
314 (kill-buffer (current-buffer)))
315 (setq bufs (cdr bufs))))))
eec82323
LMI
316
317;; Don't close the buffer if we're not shutting down the server. This way,
318;; we can keep the buffer in the group buffer cache, and not have to grovel
319;; over the buffer again unless we add new mail to it or modify it in some
320;; way.
321
322(deffoo nnfolder-close-group (group &optional server force)
323 ;; Make sure we _had_ the group open.
324 (when (or (assoc group nnfolder-buffer-alist)
325 (equal group nnfolder-current-group))
326 (let ((inf (assoc group nnfolder-buffer-alist)))
327 (when inf
328 (when (and nnfolder-current-group
329 nnfolder-current-buffer)
330 (push (list nnfolder-current-group nnfolder-current-buffer)
331 nnfolder-buffer-alist))
332 (setq nnfolder-buffer-alist
333 (delq inf nnfolder-buffer-alist))
334 (setq nnfolder-current-buffer (cadr inf)
335 nnfolder-current-group (car inf))))
336 (when (and nnfolder-current-buffer
337 (buffer-name nnfolder-current-buffer))
20a673b2 338 (with-current-buffer nnfolder-current-buffer
eec82323
LMI
339 ;; If the buffer was modified, write the file out now.
340 (nnfolder-save-buffer)
341 ;; If we're shutting the server down, we need to kill the
342 ;; buffer and remove it from the open buffer list. Or, of
343 ;; course, if we're trying to minimize our space impact.
344 (kill-buffer (current-buffer))
345 (setq nnfolder-buffer-alist (delq (assoc group nnfolder-buffer-alist)
346 nnfolder-buffer-alist)))))
347 (setq nnfolder-current-group nil
348 nnfolder-current-buffer nil)
349 t)
350
351(deffoo nnfolder-request-create-group (group &optional server args)
352 (nnfolder-possibly-change-group nil server)
353 (nnmail-activate 'nnfolder)
01c52d31
MB
354 (cond ((zerop (length group))
355 (nnheader-report 'nnfolder "Invalid (empty) group name"))
356 ((file-directory-p (nnfolder-group-pathname group))
357 (nnheader-report 'nnfolder "%s is a directory"
358 (file-name-as-directory
359 (let ((nnmail-pathname-coding-system nil))
360 (nnfolder-group-pathname group)))))
361 ((assoc group nnfolder-group-alist)
362 t)
363 (t
364 (push (list group (cons 1 0)) nnfolder-group-alist)
365 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
366 (save-current-buffer
367 (nnfolder-read-folder group))
368 t)))
eec82323
LMI
369
370(deffoo nnfolder-request-list (&optional server)
371 (nnfolder-possibly-change-group nil server)
372 (save-excursion
16409b0b 373 (let ((nnmail-file-coding-system nnfolder-active-file-coding-system))
58724016
KH
374 (nnmail-find-file nnfolder-active-file)
375 (setq nnfolder-group-alist (nnmail-get-active)))
eec82323
LMI
376 t))
377
378(deffoo nnfolder-request-newgroups (date &optional server)
379 (nnfolder-possibly-change-group nil server)
380 (nnfolder-request-list server))
381
382(deffoo nnfolder-request-list-newsgroups (&optional server)
383 (nnfolder-possibly-change-group nil server)
384 (save-excursion
16409b0b
GM
385 (let ((nnmail-file-coding-system nnfolder-file-coding-system))
386 (nnmail-find-file nnfolder-newsgroups-file))))
387
388;; Return a list consisting of all article numbers existing in the
389;; current folder.
390
391(defun nnfolder-existing-articles ()
392 (save-excursion
393 (when nnfolder-current-buffer
394 (set-buffer nnfolder-current-buffer)
395 (goto-char (point-min))
396 (let ((marker (concat "\n" nnfolder-article-marker))
397 (number "[0-9]+")
398 numbers)
16409b0b
GM
399 (while (and (search-forward marker nil t)
400 (re-search-forward number nil t))
401 (let ((newnum (string-to-number (match-string 0))))
402 (if (nnmail-within-headers-p)
403 (push newnum numbers))))
23f87bed
MB
404 ;; The article numbers are increasing, so this result is sorted.
405 (nreverse numbers)))))
eec82323 406
75327e94
GM
407(autoload 'gnus-request-group "gnus-int")
408(declare-function gnus-request-create-group "gnus-int"
409 (group &optional gnus-command-method args))
410
01c52d31
MB
411(deffoo nnfolder-request-expire-articles (articles newsgroup
412 &optional server force)
eec82323 413 (nnfolder-possibly-change-group newsgroup server)
01c52d31
MB
414 (let ((is-old t)
415 ;; The articles we have deleted so far.
416 (deleted-articles nil)
417 ;; The articles that really exist and will
418 ;; be expired if they are old enough.
419 (maybe-expirable
420 (gnus-sorted-intersection articles (nnfolder-existing-articles)))
421 target)
eec82323
LMI
422 (nnmail-activate 'nnfolder)
423
20a673b2 424 (with-current-buffer nnfolder-current-buffer
16409b0b
GM
425 ;; Since messages are sorted in arrival order and expired in the
426 ;; same order, we can stop as soon as we find a message that is
427 ;; too old.
428 (while (and maybe-expirable is-old)
eec82323 429 (goto-char (point-min))
16409b0b
GM
430 (when (and (nnfolder-goto-article (car maybe-expirable))
431 (search-forward (concat "\n" nnfolder-article-marker)
432 nil t))
433 (forward-sexp)
434 (when (setq is-old
435 (nnmail-expired-article-p
436 newsgroup
437 (buffer-substring
438 (point) (progn (end-of-line) (point)))
439 force nnfolder-inhibit-expiry))
01c52d31
MB
440 (setq target nnmail-expiry-target)
441 (unless (eq target 'delete)
0d972486 442 (with-temp-buffer
a1506d29 443 (nnfolder-request-article (car maybe-expirable)
0d972486 444 newsgroup server (current-buffer))
23f87bed 445 (let ((nnfolder-current-directory nil))
01c52d31
MB
446 (when (functionp target)
447 (setq target (funcall target newsgroup)))
9091f2d3
MB
448 (when (and target (not (eq target 'delete)))
449 (if (or (gnus-request-group target)
450 (gnus-request-create-group target))
451 (nnmail-expiry-target-group target newsgroup)
452 (setq target nil)))))
23f87bed 453 (nnfolder-possibly-change-group newsgroup server))
01c52d31
MB
454 (when target
455 (nnheader-message 5 "Deleting article %d in %s..."
456 (car maybe-expirable) newsgroup)
457 (nnfolder-delete-mail)
458 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
459 (nnfolder-nov-delete-article newsgroup (car maybe-expirable)))
460 ;; Must remember which articles were actually deleted
461 (push (car maybe-expirable) deleted-articles))))
16409b0b 462 (setq maybe-expirable (cdr maybe-expirable)))
eec82323
LMI
463 (unless nnfolder-inhibit-expiry
464 (nnheader-message 5 "Deleting articles...done"))
465 (nnfolder-save-buffer)
466 (nnfolder-adjust-min-active newsgroup)
16409b0b 467 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
acf151a8
LI
468 (nnfolder-save-all-buffers)
469 (gnus-sorted-difference articles (nreverse deleted-articles)))))
eec82323 470
c9fc72fa 471(deffoo nnfolder-request-move-article (article group server accept-form
01c52d31 472 &optional last move-is-internal)
6748645f
LMI
473 (save-excursion
474 (let ((buf (get-buffer-create " *nnfolder move*"))
475 result)
476 (and
477 (nnfolder-request-article article group server)
20a673b2 478 (with-current-buffer buf
6748645f
LMI
479 (erase-buffer)
480 (insert-buffer-substring nntp-server-buffer)
481 (goto-char (point-min))
482 (while (re-search-forward
483 (concat "^" nnfolder-article-marker)
a1506d29 484 (save-excursion (and (search-forward "\n\n" nil t) (point)))
16409b0b 485 t)
23f87bed 486 (gnus-delete-line))
6748645f
LMI
487 (setq result (eval accept-form))
488 (kill-buffer buf)
489 result)
490 (save-excursion
491 (nnfolder-possibly-change-group group server)
492 (set-buffer nnfolder-current-buffer)
493 (goto-char (point-min))
494 (when (nnfolder-goto-article article)
495 (nnfolder-delete-mail))
23f87bed
MB
496 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
497 (nnfolder-nov-delete-article group article))
6748645f
LMI
498 (when last
499 (nnfolder-save-buffer)
500 (nnfolder-adjust-min-active group)
16409b0b 501 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file))))
6748645f 502 result)))
eec82323
LMI
503
504(deffoo nnfolder-request-accept-article (group &optional server last)
6748645f
LMI
505 (save-excursion
506 (nnfolder-possibly-change-group group server)
507 (nnmail-check-syntax)
508 (let ((buf (current-buffer))
509 result art-group)
510 (goto-char (point-min))
511 (when (looking-at "X-From-Line: ")
f9936da6
SZ
512 (replace-match "From ")
513 (while (progn (forward-line) (looking-at "[ \t]"))
514 (delete-char -1)))
23f87bed
MB
515 (with-temp-buffer
516 (let ((nnmail-file-coding-system nnfolder-active-file-coding-system)
517 (nntp-server-buffer (current-buffer)))
518 (nnmail-find-file nnfolder-active-file)
519 (setq nnfolder-group-alist (nnmail-parse-active))))
520 (save-excursion
521 (goto-char (point-min))
522 (if (search-forward "\n\n" nil t)
523 (forward-line -1)
524 (goto-char (point-max)))
525 (while (re-search-backward (concat "^" nnfolder-article-marker) nil t)
526 (delete-region (point) (progn (forward-line 1) (point))))
527 (when nnmail-cache-accepted-message-ids
c9fc72fa 528 (nnmail-cache-insert (nnmail-fetch-field "message-id")
23f87bed
MB
529 group
530 (nnmail-fetch-field "subject")
531 (nnmail-fetch-field "from")))
532 (setq result (if (stringp group)
533 (list (cons group (nnfolder-active-number group)))
534 (setq art-group
535 (nnmail-article-group 'nnfolder-active-number))))
536 (if (and (null result)
537 (yes-or-no-p "Moved to `junk' group; delete article? "))
538 (setq result 'junk)
539 (setq result
540 (car (nnfolder-save-mail result)))))
541 (when last
542 (save-excursion
543 (nnfolder-possibly-change-folder (or (caar art-group) group))
544 (nnfolder-save-buffer)
545 (when nnmail-cache-accepted-message-ids
546 (nnmail-cache-close))))
16409b0b 547 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
6748645f
LMI
548 (unless result
549 (nnheader-report 'nnfolder "Couldn't store article"))
550 result)))
eec82323
LMI
551
552(deffoo nnfolder-request-replace-article (article group buffer)
553 (nnfolder-possibly-change-group group)
20a673b2 554 (with-current-buffer buffer
6748645f 555 (goto-char (point-min))
23f87bed
MB
556 (if (not (looking-at "X-From-Line: "))
557 (insert "From nobody " (current-time-string) "\n")
558 (replace-match "From ")
559 (forward-line 1)
560 (while (looking-at "[ \t]")
561 (delete-char -1)
562 (forward-line 1)))
6748645f 563 (nnfolder-normalize-buffer)
eec82323
LMI
564 (set-buffer nnfolder-current-buffer)
565 (goto-char (point-min))
6748645f 566 (if (not (nnfolder-goto-article article))
eec82323 567 nil
6748645f 568 (nnfolder-delete-mail)
eec82323 569 (insert-buffer-substring buffer)
23f87bed 570 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
20a673b2 571 (with-current-buffer buffer
23f87bed
MB
572 (let ((headers (nnfolder-parse-head article
573 (point-min) (point-max))))
574 (with-current-buffer (nnfolder-open-nov group)
575 (if (nnheader-find-nov-line article)
576 (delete-region (point) (progn (forward-line 1) (point))))
577 (nnheader-insert-nov headers)))))
eec82323
LMI
578 (nnfolder-save-buffer)
579 t)))
580
581(deffoo nnfolder-request-delete-group (group &optional force server)
582 (nnfolder-close-group group server t)
583 ;; Delete all articles in GROUP.
584 (if (not force)
585 () ; Don't delete the articles.
586 ;; Delete the file that holds the group.
23f87bed 587 (let ((data (nnfolder-group-pathname group))
89b163db 588 (nov (nnfolder-group-nov-pathname group)))
23f87bed 589 (ignore-errors (delete-file data))
89b163db 590 (ignore-errors (delete-file nov))))
eec82323
LMI
591 ;; Remove the group from all structures.
592 (setq nnfolder-group-alist
593 (delq (assoc group nnfolder-group-alist) nnfolder-group-alist)
594 nnfolder-current-group nil
595 nnfolder-current-buffer nil)
596 ;; Save the active file.
16409b0b 597 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
eec82323
LMI
598 t)
599
600(deffoo nnfolder-request-rename-group (group new-name &optional server)
601 (nnfolder-possibly-change-group group server)
20a673b2 602 (with-current-buffer nnfolder-current-buffer
eec82323
LMI
603 (and (file-writable-p buffer-file-name)
604 (ignore-errors
23f87bed
MB
605 (let ((new-file (nnfolder-group-pathname new-name)))
606 (gnus-make-directory (file-name-directory new-file))
607 (rename-file buffer-file-name new-file)
608 (when (file-exists-p (nnfolder-group-nov-pathname group))
609 (setq new-file (nnfolder-group-nov-pathname new-name))
610 (gnus-make-directory (file-name-directory new-file))
89b163db 611 (rename-file (nnfolder-group-nov-pathname group) new-file)))
eec82323
LMI
612 t)
613 ;; That went ok, so we change the internal structures.
614 (let ((entry (assoc group nnfolder-group-alist)))
615 (and entry (setcar entry new-name))
616 (setq nnfolder-current-buffer nil
617 nnfolder-current-group nil)
618 ;; Save the new group alist.
16409b0b 619 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
eec82323
LMI
620 ;; We kill the buffer instead of renaming it and stuff.
621 (kill-buffer (current-buffer))
622 t))))
623
23f87bed 624(deffoo nnfolder-request-regenerate (server)
a8151ef7
LMI
625 (nnfolder-possibly-change-group nil server)
626 (nnfolder-generate-active-file)
627 t)
628
eec82323
LMI
629\f
630;;; Internal functions.
631
632(defun nnfolder-adjust-min-active (group)
633 ;; Find the lowest active article in this group.
634 (let* ((active (cadr (assoc group nnfolder-group-alist)))
635 (marker (concat "\n" nnfolder-article-marker))
636 (number "[0-9]+")
637 (activemin (cdr active)))
20a673b2 638 (with-current-buffer nnfolder-current-buffer
eec82323
LMI
639 (goto-char (point-min))
640 (while (and (search-forward marker nil t)
641 (re-search-forward number nil t))
6748645f
LMI
642 (let ((newnum (string-to-number (match-string 0))))
643 (if (nnmail-within-headers-p)
644 (setq activemin (min activemin newnum)))))
eec82323
LMI
645 (setcar active activemin))))
646
647(defun nnfolder-article-string (article)
648 (if (numberp article)
649 (concat "\n" nnfolder-article-marker (int-to-string article) " ")
650 (concat "\nMessage-ID: " article)))
651
6748645f
LMI
652(defun nnfolder-goto-article (article)
653 "Place point at the start of the headers of ARTICLE.
654ARTICLE can be an article number or a Message-ID.
655Returns t if successful, nil otherwise."
656 (let ((art-string (nnfolder-article-string article))
657 start found)
658 ;; It is likely that we are at or before the delimiter line.
659 ;; We therefore go to the end of the previous line, and start
660 ;; searching from there.
661 (beginning-of-line)
662 (unless (bobp)
663 (forward-char -1))
664 (setq start (point))
665 ;; First search forward.
666 (while (and (setq found (search-forward art-string nil t))
667 (not (nnmail-within-headers-p))))
668 ;; If unsuccessful, search backward from where we started,
669 (unless found
670 (goto-char start)
671 (while (and (setq found (search-backward art-string nil t))
672 (not (nnmail-within-headers-p)))))
673 (when found
674 (nnmail-search-unix-mail-delim-backward))))
675
676(defun nnfolder-delete-mail (&optional leave-delim)
677 "Delete the message that point is in.
678If optional argument LEAVE-DELIM is t, then mailbox delimiter is not
679deleted. Point is left where the deleted region was."
16409b0b
GM
680 (save-restriction
681 (narrow-to-region
682 (save-excursion
683 ;; In case point is at the beginning of the message already.
684 (forward-line 1)
685 (nnmail-search-unix-mail-delim-backward)
686 (if leave-delim (progn (forward-line 1) (point))
687 (point)))
688 (progn
689 (forward-line 1)
690 (if (nnmail-search-unix-mail-delim)
691 (point)
692 (point-max))))
693 (run-hooks 'nnfolder-delete-mail-hook)
694 (delete-region (point-min) (point-max))))
eec82323
LMI
695
696(defun nnfolder-possibly-change-group (group &optional server dont-check)
697 ;; Change servers.
698 (when (and server
699 (not (nnfolder-server-opened server)))
700 (nnfolder-open-server server))
701 (unless (gnus-buffer-live-p nnfolder-current-buffer)
702 (setq nnfolder-current-buffer nil
703 nnfolder-current-group nil))
704 ;; Change group.
23f87bed
MB
705 (let ((file-name-coding-system nnmail-pathname-coding-system))
706 (when (and group
707 (not (equal group nnfolder-current-group))
708 (progn
709 (nnmail-activate 'nnfolder)
710 (and (assoc group nnfolder-group-alist)
711 (file-exists-p (nnfolder-group-pathname group)))))
58724016 712 (if dont-check
a8151ef7
LMI
713 (setq nnfolder-current-group group
714 nnfolder-current-buffer nil)
58724016 715 (let (inf file)
23f87bed
MB
716 ;; If we have to change groups, see if we don't already have
717 ;; the folder in memory. If we do, verify the modtime and
718 ;; destroy the folder if needed so we can rescan it.
a8151ef7
LMI
719 (setq nnfolder-current-buffer
720 (nth 1 (assoc group nnfolder-buffer-alist)))
721
23f87bed
MB
722 ;; If the buffer is not live, make sure it isn't in the
723 ;; alist. If it is live, verify that nobody else has
724 ;; touched the file since last time.
58724016
KH
725 (when (and nnfolder-current-buffer
726 (not (gnus-buffer-live-p nnfolder-current-buffer)))
727 (setq nnfolder-buffer-alist (delq inf nnfolder-buffer-alist)
728 nnfolder-current-buffer nil))
a8151ef7 729
58724016 730 (setq nnfolder-current-group group)
a8151ef7 731
58724016 732 (when (or (not nnfolder-current-buffer)
6748645f
LMI
733 (not (verify-visited-file-modtime
734 nnfolder-current-buffer)))
58724016
KH
735 (save-excursion
736 (setq file (nnfolder-group-pathname group))
737 ;; See whether we need to create the new file.
738 (unless (file-exists-p file)
739 (gnus-make-directory (file-name-directory file))
a1506d29 740 (let ((nnmail-file-coding-system
16409b0b
GM
741 (or nnfolder-file-coding-system-for-write
742 nnfolder-file-coding-system-for-write)))
be1ad767
SM
743 (nnmail-write-region (point-min) (point-min)
744 file t 'nomesg)))
58724016
KH
745 (when (setq nnfolder-current-buffer (nnfolder-read-folder group))
746 (set-buffer nnfolder-current-buffer)
747 (push (list group nnfolder-current-buffer)
748 nnfolder-buffer-alist)))))))))
eec82323
LMI
749
750(defun nnfolder-save-mail (group-art-list)
751 "Called narrowed to an article."
752 (let* (save-list group-art)
753 (goto-char (point-min))
754 ;; The From line may have been quoted by movemail.
16409b0b 755 (when (looking-at ">From")
eec82323
LMI
756 (delete-char 1))
757 ;; This might come from somewhere else.
16409b0b 758 (unless (looking-at "From ")
eec82323
LMI
759 (insert "From nobody " (current-time-string) "\n")
760 (goto-char (point-min)))
6748645f 761 ;; Quote all "From " lines in the article.
16409b0b 762 (forward-line 1)
eec82323
LMI
763 (let (case-fold-search)
764 (while (re-search-forward "^From " nil t)
765 (beginning-of-line)
766 (insert "> ")))
767 (setq save-list group-art-list)
768 (nnmail-insert-lines)
769 (nnmail-insert-xref group-art-list)
770 (run-hooks 'nnmail-prepare-save-mail-hook)
771 (run-hooks 'nnfolder-prepare-save-mail-hook)
772
773 ;; Insert the mail into each of the destination groups.
774 (while (setq group-art (pop group-art-list))
775 ;; Kill any previous newsgroup markers.
776 (goto-char (point-min))
16409b0b
GM
777 (if (search-forward "\n\n" nil t)
778 (forward-line -1)
779 (goto-char (point-max)))
eec82323
LMI
780 (while (search-backward (concat "\n" nnfolder-article-marker) nil t)
781 (delete-region (1+ (point)) (progn (forward-line 2) (point))))
782
783 ;; Insert the new newsgroup marker.
784 (nnfolder-insert-newsgroup-line group-art)
785
786 (save-excursion
787 (let ((beg (point-min))
788 (end (point-max))
789 (obuf (current-buffer)))
790 (nnfolder-possibly-change-folder (car group-art))
791 (let ((buffer-read-only nil))
6748645f 792 (nnfolder-normalize-buffer)
23f87bed
MB
793 (insert-buffer-substring obuf beg end))
794 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
795 (set-buffer obuf)
796 (nnfolder-add-nov (car group-art) (cdr group-art)
797 (nnfolder-parse-head nil beg end))))))
eec82323
LMI
798
799 ;; Did we save it anywhere?
800 save-list))
801
6748645f
LMI
802(defun nnfolder-normalize-buffer ()
803 "Make sure there are two newlines at the end of the buffer."
804 (goto-char (point-max))
805 (skip-chars-backward "\n")
806 (delete-region (point) (point-max))
23f87bed
MB
807 (unless (bobp)
808 (insert "\n\n")))
6748645f 809
eec82323
LMI
810(defun nnfolder-insert-newsgroup-line (group-art)
811 (save-excursion
812 (goto-char (point-min))
16409b0b
GM
813 (unless (search-forward "\n\n" nil t)
814 (goto-char (point-max))
815 (insert "\n"))
816 (forward-char -1)
817 (insert (format (concat nnfolder-article-marker "%d %s\n")
34128042 818 (cdr group-art) (message-make-date)))))
eec82323
LMI
819
820(defun nnfolder-active-number (group)
821 ;; Find the next article number in GROUP.
822 (let ((active (cadr (assoc group nnfolder-group-alist))))
823 (if active
824 (setcdr active (1+ (cdr active)))
825 ;; This group is new, so we create a new entry for it.
826 ;; This might be a bit naughty... creating groups on the drop of
827 ;; a hat, but I don't know...
828 (push (list group (setq active (cons 1 1)))
829 nnfolder-group-alist))
830 (cdr active)))
831
832(defun nnfolder-possibly-change-folder (group)
833 (let ((inf (assoc group nnfolder-buffer-alist)))
834 (if (and inf
835 (gnus-buffer-live-p (cadr inf)))
836 (set-buffer (cadr inf))
837 (when inf
838 (setq nnfolder-buffer-alist (delq inf nnfolder-buffer-alist)))
839 (when nnfolder-group-alist
16409b0b 840 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file))
eec82323
LMI
841 (push (list group (nnfolder-read-folder group))
842 nnfolder-buffer-alist))))
843
23f87bed
MB
844;; This method has a problem if you've accidentally let the active
845;; list get out of sync with the files. This could happen, say, if
846;; you've accidentally gotten new mail with something other than Gnus
847;; (but why would _that_ ever happen? :-). In that case, we will be
848;; in the middle of processing the file, ready to add new X-Gnus
849;; article number markers, and we'll run across a message with no ID
850;; yet - the active list _may_not_ be ready for us yet.
851
852;; To handle this, I'm modifying this routine to maintain the maximum
853;; ID seen so far, and when we hit a message with no ID, we will
854;; _manually_ scan the rest of the message looking for any more,
855;; possibly higher IDs. We'll assume the maximum that we find is the
856;; highest active. Note that this shouldn't cost us much extra time
857;; at all, but will be a lot less vulnerable to glitches between the
858;; mbox and the active file.
eec82323
LMI
859
860(defun nnfolder-read-folder (group)
861 (let* ((file (nnfolder-group-pathname group))
23f87bed 862 (nov (nnfolder-group-nov-pathname group))
16409b0b 863 (buffer (set-buffer
a1506d29 864 (let ((nnheader-file-coding-system
16409b0b 865 nnfolder-file-coding-system))
ff4d3926 866 (nnheader-find-file-noselect file t)))))
2c902422 867 (mm-enable-multibyte) ;; Use multibyte buffer for future copying.
d4755e04 868 (buffer-disable-undo)
eec82323
LMI
869 (if (equal (cadr (assoc group nnfolder-scantime-alist))
870 (nth 5 (file-attributes file)))
871 ;; This looks up-to-date, so we don't do any scanning.
6748645f
LMI
872 (if (file-exists-p file)
873 buffer
874 (push (list group buffer) nnfolder-buffer-alist)
875 (set-buffer-modified-p t)
16409b0b 876 (nnfolder-save-buffer))
eec82323
LMI
877 ;; Parse the damn thing.
878 (save-excursion
16409b0b
GM
879 (goto-char (point-min))
880 ;; Remove any blank lines at the start.
881 (while (eq (following-char) ?\n)
882 (delete-char 1))
eec82323
LMI
883 (nnmail-activate 'nnfolder)
884 ;; Read in the file.
16409b0b 885 (let ((delim "^From ")
eec82323
LMI
886 (marker (concat "\n" nnfolder-article-marker))
887 (number "[0-9]+")
888 (active (or (cadr (assoc group nnfolder-group-alist))
889 (cons 1 0)))
890 (scantime (assoc group nnfolder-scantime-alist))
548f737d
MB
891 (minid (or (and (boundp 'most-positive-fixnum)
892 most-positive-fixnum)
893 (lsh -1 -1)))
eec82323 894 maxid start end newscantime
23f87bed 895 novbuf articles newnum
eec82323 896 buffer-read-only)
eec82323 897 (setq maxid (cdr active))
23f87bed
MB
898
899 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil
900 (and (file-exists-p nov)
901 (file-newer-than-file-p nov file)))
902 (unless (file-exists-p nov)
903 (gnus-make-directory (file-name-directory nov)))
904 (with-current-buffer
905 (setq novbuf (nnfolder-open-nov group))
906 (goto-char (point-min))
907 (while (not (eobp))
908 (push (read novbuf) articles)
909 (forward-line 1))
910 (setq articles (nreverse articles))))
eec82323
LMI
911 (goto-char (point-min))
912
23f87bed
MB
913 ;; Anytime the active number is 1 or 0, it is suspect. In
914 ;; that case, search the file manually to find the active
915 ;; number. Or, of course, if we're being paranoid. (This
916 ;; would also be the place to build other lists from the
917 ;; header markers, such as expunge lists, etc., if we ever
918 ;; desired to abandon the active file entirely for mboxes.)
eec82323 919 (when (or nnfolder-ignore-active-file
23f87bed 920 novbuf
eec82323
LMI
921 (< maxid 2))
922 (while (and (search-forward marker nil t)
23f87bed
MB
923 (looking-at number))
924 (setq newnum (string-to-number (match-string 0)))
925 (when (nnmail-within-headers-p)
926 (setq maxid (max maxid newnum)
927 minid (min minid newnum))
928 (when novbuf
929 (if (memq newnum articles)
930 (setq articles (delq newnum articles))
931 (let ((headers (nnfolder-parse-head newnum)))
932 (with-current-buffer novbuf
933 (nnheader-find-nov-line newnum)
934 (nnheader-insert-nov headers)))))))
935 (when (and novbuf articles)
936 (with-current-buffer novbuf
937 (dolist (article articles)
938 (when (nnheader-find-nov-line article)
939 (delete-region (point)
940 (progn (forward-line 1) (point)))))))
eec82323
LMI
941 (setcar active (max 1 (min minid maxid)))
942 (setcdr active (max maxid (cdr active)))
943 (goto-char (point-min)))
944
23f87bed
MB
945 ;; As long as we trust that the user will only insert
946 ;; unmarked mail at the end, go to the end and search
947 ;; backwards for the last marker. Find the start of that
948 ;; message, and begin to search for unmarked messages from
949 ;; there.
eec82323
LMI
950 (when (not (or nnfolder-distrust-mbox
951 (< maxid 2)))
952 (goto-char (point-max))
953 (unless (re-search-backward marker nil t)
954 (goto-char (point-min)))
23f87bed
MB
955 ;;(when (nnmail-search-unix-mail-delim)
956 ;; (goto-char (point-min)))
957 )
eec82323 958
23f87bed
MB
959 ;; Keep track of the active number on our own, and insert it
960 ;; back into the active list when we're done. Also, prime
961 ;; the pump to cut down on the number of searches we do.
eec82323
LMI
962 (unless (nnmail-search-unix-mail-delim)
963 (goto-char (point-max)))
964 (setq end (point-marker))
965 (while (not (= end (point-max)))
966 (setq start (marker-position end))
967 (goto-char end)
23f87bed 968 ;; There may be more than one "From " line, so we skip past
eec82323
LMI
969 ;; them.
970 (while (looking-at delim)
971 (forward-line 1))
972 (set-marker end (if (nnmail-search-unix-mail-delim)
973 (point)
974 (point-max)))
975 (goto-char start)
976 (when (not (search-forward marker end t))
977 (narrow-to-region start end)
978 (nnmail-insert-lines)
979 (nnfolder-insert-newsgroup-line
23f87bed
MB
980 (cons nil
981 (setq newnum
982 (nnfolder-active-number group))))
983 (when novbuf
984 (let ((headers (nnfolder-parse-head newnum (point-min)
985 (point-max))))
986 (with-current-buffer novbuf
987 (goto-char (point-max))
988 (nnheader-insert-nov headers))))
eec82323
LMI
989 (widen)))
990
991 (set-marker end nil)
23f87bed
MB
992 ;; Make absolutely sure that the active list reflects
993 ;; reality!
16409b0b 994 (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
23f87bed 995
eec82323
LMI
996 ;; Set the scantime for this group.
997 (setq newscantime (visited-file-modtime))
998 (if scantime
999 (setcdr scantime (list newscantime))
23f87bed 1000 (push (list group newscantime)
eec82323 1001 nnfolder-scantime-alist))
23f87bed
MB
1002 ;; Save nov.
1003 (when novbuf
1004 (nnfolder-save-nov))
eec82323
LMI
1005 (current-buffer))))))
1006
4d1d3f07
LMI
1007(defun nnfolder-recursive-directory-files (dir prefix)
1008 (let ((files nil))
1009 (dolist (file (directory-files dir))
1010 (cond
1011 ((or (file-symlink-p (expand-file-name file dir))
1012 (member file '("." "..")))
1013 ;; Ignore
1014 )
1015 ((file-directory-p (expand-file-name file dir))
1016 (setq files (nconc (nnfolder-recursive-directory-files
1017 (expand-file-name file dir)
1018 (if prefix
1019 (concat prefix "." (directory-file-name file))
1020 (file-name-nondirectory file)))
1021 files)))
1022 ((file-regular-p (expand-file-name file dir))
1023 (push (if prefix
1024 (concat prefix "." file)
1025 file)
1026 files))))
1027 files))
1028
eec82323
LMI
1029;;;###autoload
1030(defun nnfolder-generate-active-file ()
16409b0b
GM
1031 "Look for mbox folders in the nnfolder directory and make them into groups.
1032This command does not work if you use short group names."
eec82323
LMI
1033 (interactive)
1034 (nnmail-activate 'nnfolder)
23f87bed
MB
1035 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
1036 (dolist (file (directory-files (or nnfolder-nov-directory
1037 nnfolder-directory)
1038 t
1039 (concat
1040 (regexp-quote nnfolder-nov-file-suffix)
1041 "$")))
1042 (when (not (message-mail-file-mbox-p file))
1043 (ignore-errors
1044 (delete-file file)))))
4d1d3f07
LMI
1045 (dolist (file (if nnmail-use-long-file-names
1046 (directory-files nnfolder-directory)
1047 (nnfolder-recursive-directory-files
1048 nnfolder-directory nil)))
eec82323 1049 (when (and (not (backup-file-name-p file))
23f87bed 1050 (message-mail-file-mbox-p
4d1d3f07 1051 (nnfolder-group-pathname file)))
23f87bed
MB
1052 (let ((oldgroup (assoc file nnfolder-group-alist)))
1053 (if oldgroup
1054 (nnheader-message 5 "Refreshing group %s..." file)
1055 (nnheader-message 5 "Adding group %s..." file))
03378294
RS
1056 (if oldgroup
1057 (setq nnfolder-group-alist
1058 (delq oldgroup (copy-sequence nnfolder-group-alist))))
23f87bed
MB
1059 (push (list file (cons 1 0)) nnfolder-group-alist)
1060 (nnfolder-possibly-change-folder file)
1061 (nnfolder-possibly-change-group file)
1062 (nnfolder-close-group file))))
01c52d31 1063 (nnheader-message 5 ""))
eec82323
LMI
1064
1065(defun nnfolder-group-pathname (group)
35ef97a5 1066 "Make file name for GROUP."
16409b0b
GM
1067 (setq group
1068 (mm-encode-coding-string group nnmail-pathname-coding-system))
eec82323
LMI
1069 (let ((dir (file-name-as-directory (expand-file-name nnfolder-directory))))
1070 ;; If this file exists, we use it directly.
1071 (if (or nnmail-use-long-file-names
1072 (file-exists-p (concat dir group)))
1073 (concat dir group)
1074 ;; If not, we translate dots into slashes.
1075 (concat dir (nnheader-replace-chars-in-string group ?. ?/)))))
1076
23f87bed
MB
1077(defun nnfolder-group-nov-pathname (group)
1078 "Make pathname for GROUP NOV."
1079 (let ((nnfolder-directory
1080 (or nnfolder-nov-directory nnfolder-directory)))
1081 (concat (nnfolder-group-pathname group) nnfolder-nov-file-suffix)))
1082
06b840e0
LI
1083(defvar copyright-update)
1084
eec82323
LMI
1085(defun nnfolder-save-buffer ()
1086 "Save the buffer."
89b163db
G
1087 (let ((delete-old-versions t))
1088 (when (buffer-modified-p)
1089 (run-hooks 'nnfolder-save-buffer-hook)
1090 (gnus-make-directory (file-name-directory (buffer-file-name)))
1091 (let ((coding-system-for-write
1092 (or nnfolder-file-coding-system-for-write
1093 nnfolder-file-coding-system)))
1094 (set (make-local-variable 'copyright-update) nil)
1095 (save-buffer)))
1096 (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
1097 (nnfolder-save-nov))))
16409b0b
GM
1098
1099(defun nnfolder-save-active (group-alist active-file)
1100 (let ((nnmail-active-file-coding-system
1101 (or nnfolder-active-file-coding-system-for-write
1102 nnfolder-active-file-coding-system)))
1103 (nnmail-save-active group-alist active-file)))
eec82323 1104
23f87bed
MB
1105(defun nnfolder-open-nov (group)
1106 (or (cdr (assoc group nnfolder-nov-buffer-alist))
1107 (let ((buffer (get-buffer-create (format " *nnfolder overview %s*" group))))
20a673b2 1108 (with-current-buffer buffer
23f87bed
MB
1109 (set (make-local-variable 'nnfolder-nov-buffer-file-name)
1110 (nnfolder-group-nov-pathname group))
1111 (erase-buffer)
1112 (when (file-exists-p nnfolder-nov-buffer-file-name)
1113 (nnheader-insert-file-contents nnfolder-nov-buffer-file-name)))
1114 (push (cons group buffer) nnfolder-nov-buffer-alist)
1115 buffer)))
1116
1117(defun nnfolder-save-nov ()
1118 (save-excursion
1119 (while nnfolder-nov-buffer-alist
1120 (when (buffer-name (cdar nnfolder-nov-buffer-alist))
1121 (set-buffer (cdar nnfolder-nov-buffer-alist))
1122 (when (buffer-modified-p)
1123 (gnus-make-directory (file-name-directory
1124 nnfolder-nov-buffer-file-name))
1125 (nnmail-write-region 1 (point-max) nnfolder-nov-buffer-file-name
1126 nil 'nomesg))
1127 (set-buffer-modified-p nil)
1128 (kill-buffer (current-buffer)))
1129 (setq nnfolder-nov-buffer-alist (cdr nnfolder-nov-buffer-alist)))))
1130
1131(defun nnfolder-nov-delete-article (group article)
20a673b2 1132 (with-current-buffer (nnfolder-open-nov group)
23f87bed
MB
1133 (when (nnheader-find-nov-line article)
1134 (delete-region (point) (progn (forward-line 1) (point))))
1135 t))
1136
1137(defun nnfolder-retrieve-headers-with-nov (articles &optional fetch-old)
1138 (if (or gnus-nov-is-evil nnfolder-nov-is-evil)
1139 nil
1140 (let ((nov (nnfolder-group-nov-pathname nnfolder-current-group)))
1141 (when (file-exists-p nov)
20a673b2 1142 (with-current-buffer nntp-server-buffer
23f87bed
MB
1143 (erase-buffer)
1144 (nnheader-insert-file-contents nov)
1145 (if (and fetch-old
1146 (not (numberp fetch-old)))
1147 t ; Don't remove anything.
1148 (nnheader-nov-delete-outside-range
1149 (if fetch-old (max 1 (- (car articles) fetch-old))
1150 (car articles))
1151 (car (last articles)))
1152 t))))))
1153
1154(defun nnfolder-parse-head (&optional number b e)
1155 "Parse the head of the current buffer."
1156 (let ((buf (current-buffer))
1157 chars)
1158 (save-excursion
1159 (unless b
1160 (setq b (if (nnmail-search-unix-mail-delim-backward)
1161 (point) (point-min)))
1162 (forward-line 1)
1163 (setq e (if (nnmail-search-unix-mail-delim)
1164 (point) (point-max))))
1165 (setq chars (- e b))
1166 (unless (zerop chars)
1167 (goto-char b)
1168 (if (search-forward "\n\n" e t) (setq e (1- (point)))))
1169 (with-temp-buffer
1170 (insert-buffer-substring buf b e)
1171 (let ((headers (nnheader-parse-naked-head)))
1172 (mail-header-set-chars headers chars)
1173 (mail-header-set-number headers number)
1174 headers)))))
1175
1176(defun nnfolder-add-nov (group article headers)
1177 "Add a nov line for the GROUP base."
20a673b2 1178 (with-current-buffer (nnfolder-open-nov group)
23f87bed
MB
1179 (goto-char (point-max))
1180 (mail-header-set-number headers article)
1181 (nnheader-insert-nov headers)))
1182
eec82323
LMI
1183(provide 'nnfolder)
1184
1185;;; nnfolder.el ends here