Mercurial > emacs
comparison lisp/gnus/nnmaildir.el @ 82951:0fde48feb604
Import Gnus 5.10 from the v5_10 branch of the Gnus repository.
author | Andreas Schwab <schwab@suse.de> |
---|---|
date | Thu, 22 Jul 2004 16:45:51 +0000 |
parents | |
children | 7503b2a24a3c |
comparison
equal
deleted
inserted
replaced
56503:8bbd2323fbf2 | 82951:0fde48feb604 |
---|---|
1 ;;; nnmaildir.el --- maildir backend for Gnus | |
2 ;; Public domain. | |
3 | |
4 ;; Author: Paul Jarc <prj@po.cwru.edu> | |
5 | |
6 ;; This file is part of GNU Emacs. | |
7 | |
8 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
9 ;; it under the terms of the GNU General Public License as published by | |
10 ;; the Free Software Foundation; either version 2, or (at your option) | |
11 ;; any later version. | |
12 | |
13 ;; GNU Emacs is distributed in the hope that it will be useful, | |
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 ;; GNU General Public License for more details. | |
17 | |
18 ;; You should have received a copy of the GNU General Public License | |
19 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
20 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
21 ;; Boston, MA 02111-1307, USA. | |
22 | |
23 ;;; Commentary: | |
24 | |
25 ;; Maildir format is documented at <URL:http://cr.yp.to/proto/maildir.html> | |
26 ;; and in the maildir(5) man page from qmail (available at | |
27 ;; <URL:http://www.qmail.org/man/man5/maildir.html>). nnmaildir also stores | |
28 ;; extra information in the .nnmaildir/ directory within a maildir. | |
29 ;; | |
30 ;; Some goals of nnmaildir: | |
31 ;; * Everything Just Works, and correctly. E.g., NOV data is automatically | |
32 ;; regenerated when stale; no need for manually running | |
33 ;; *-generate-nov-databases. | |
34 ;; * Perfect reliability: [C-g] will never corrupt its data in memory, and | |
35 ;; SIGKILL will never corrupt its data in the filesystem. | |
36 ;; * Allow concurrent operation as much as possible. If files change out | |
37 ;; from under us, adapt to the changes or degrade gracefully. | |
38 ;; * We use the filesystem as a database, so that, e.g., it's easy to | |
39 ;; manipulate marks from outside Gnus. | |
40 ;; * All information about a group is stored in the maildir, for easy backup, | |
41 ;; copying, restoring, etc. | |
42 ;; | |
43 ;; Todo: | |
44 ;; * Add a hook for when moving messages from new/ to cur/, to support | |
45 ;; nnmail's duplicate detection. | |
46 ;; * Improve generated Xrefs, so crossposts are detectable. | |
47 ;; * Improve code readability. | |
48 | |
49 ;;; Code: | |
50 | |
51 ;; eval this before editing | |
52 [(progn | |
53 (put 'nnmaildir--with-nntp-buffer 'lisp-indent-function 0) | |
54 (put 'nnmaildir--with-work-buffer 'lisp-indent-function 0) | |
55 (put 'nnmaildir--with-nov-buffer 'lisp-indent-function 0) | |
56 (put 'nnmaildir--with-move-buffer 'lisp-indent-function 0) | |
57 ) | |
58 ] | |
59 | |
60 (eval-and-compile | |
61 (require 'nnheader) | |
62 (require 'gnus) | |
63 (require 'gnus-util) | |
64 (require 'gnus-range) | |
65 (require 'gnus-start) | |
66 (require 'gnus-int) | |
67 (require 'message)) | |
68 (eval-when-compile | |
69 (require 'cl) | |
70 (require 'nnmail)) | |
71 | |
72 (defconst nnmaildir-version "Gnus") | |
73 | |
74 (defvar nnmaildir-article-file-name nil | |
75 "*The filename of the most recently requested article. This variable is set | |
76 by nnmaildir-request-article.") | |
77 | |
78 ;; The filename of the article being moved/copied: | |
79 (defvar nnmaildir--file nil) | |
80 | |
81 ;; Variables to generate filenames of messages being delivered: | |
82 (defvar nnmaildir--delivery-time "") | |
83 (defconst nnmaildir--delivery-pid (concat "P" (number-to-string (emacs-pid)))) | |
84 (defvar nnmaildir--delivery-count nil) | |
85 | |
86 ;; An obarry containing symbols whose names are server names and whose values | |
87 ;; are servers: | |
88 (defvar nnmaildir--servers (make-vector 3 0)) | |
89 ;; The current server: | |
90 (defvar nnmaildir--cur-server nil) | |
91 | |
92 ;; A copy of nnmail-extra-headers | |
93 (defvar nnmaildir--extra nil) | |
94 | |
95 ;; A NOV structure looks like this (must be prin1-able, so no defstruct): | |
96 ["subject\tfrom\tdate" | |
97 "references\tchars\lines" | |
98 "To: you\tIn-Reply-To: <your.mess@ge>" | |
99 (12345 67890) ;; modtime of the corresponding article file | |
100 (to in-reply-to)] ;; contemporary value of nnmail-extra-headers | |
101 (defconst nnmaildir--novlen 5) | |
102 (defmacro nnmaildir--nov-new (beg mid end mtime extra) | |
103 `(vector ,beg ,mid ,end ,mtime ,extra)) | |
104 (defmacro nnmaildir--nov-get-beg (nov) `(aref ,nov 0)) | |
105 (defmacro nnmaildir--nov-get-mid (nov) `(aref ,nov 1)) | |
106 (defmacro nnmaildir--nov-get-end (nov) `(aref ,nov 2)) | |
107 (defmacro nnmaildir--nov-get-mtime (nov) `(aref ,nov 3)) | |
108 (defmacro nnmaildir--nov-get-extra (nov) `(aref ,nov 4)) | |
109 (defmacro nnmaildir--nov-set-beg (nov value) `(aset ,nov 0 ,value)) | |
110 (defmacro nnmaildir--nov-set-mid (nov value) `(aset ,nov 1 ,value)) | |
111 (defmacro nnmaildir--nov-set-end (nov value) `(aset ,nov 2 ,value)) | |
112 (defmacro nnmaildir--nov-set-mtime (nov value) `(aset ,nov 3 ,value)) | |
113 (defmacro nnmaildir--nov-set-extra (nov value) `(aset ,nov 4 ,value)) | |
114 | |
115 (defstruct nnmaildir--art | |
116 (prefix nil :type string) ;; "time.pid.host" | |
117 (suffix nil :type string) ;; ":2,flags" | |
118 (num nil :type natnum) ;; article number | |
119 (msgid nil :type string) ;; "<mess.age@id>" | |
120 (nov nil :type vector)) ;; cached nov structure, or nil | |
121 | |
122 (defstruct nnmaildir--grp | |
123 (name nil :type string) ;; "group.name" | |
124 (new nil :type list) ;; new/ modtime | |
125 (cur nil :type list) ;; cur/ modtime | |
126 (min 1 :type natnum) ;; minimum article number | |
127 (count 0 :type natnum) ;; count of articles | |
128 (nlist nil :type list) ;; list of articles, ordered descending by number | |
129 (flist nil :type vector) ;; obarray mapping filename prefix->article | |
130 (mlist nil :type vector) ;; obarray mapping message-id->article | |
131 (cache nil :type vector) ;; nov cache | |
132 (index nil :type natnum) ;; index of next cache entry to replace | |
133 (mmth nil :type vector)) ;; obarray mapping mark name->dir modtime | |
134 ; ("Mark Mod Time Hash") | |
135 | |
136 (defstruct nnmaildir--srv | |
137 (address nil :type string) ;; server address string | |
138 (method nil :type list) ;; (nnmaildir "address" ...) | |
139 (prefix nil :type string) ;; "nnmaildir+address:" | |
140 (dir nil :type string) ;; "/expanded/path/to/server/dir/" | |
141 (ls nil :type function) ;; directory-files function | |
142 (groups nil :type vector) ;; obarray mapping group name->group | |
143 (curgrp nil :type nnmaildir--grp) ;; current group, or nil | |
144 (error nil :type string) ;; last error message, or nil | |
145 (mtime nil :type list) ;; modtime of dir | |
146 (gnm nil) ;; flag: split from mail-sources? | |
147 (target-prefix nil :type string)) ;; symlink target prefix | |
148 | |
149 (defun nnmaildir--expired-article (group article) | |
150 (setf (nnmaildir--art-nov article) nil) | |
151 (let ((flist (nnmaildir--grp-flist group)) | |
152 (mlist (nnmaildir--grp-mlist group)) | |
153 (min (nnmaildir--grp-min group)) | |
154 (count (1- (nnmaildir--grp-count group))) | |
155 (prefix (nnmaildir--art-prefix article)) | |
156 (msgid (nnmaildir--art-msgid article)) | |
157 (new-nlist nil) | |
158 (nlist-pre '(nil . nil)) | |
159 nlist-post num) | |
160 (unless (zerop count) | |
161 (setq nlist-post (nnmaildir--grp-nlist group) | |
162 num (nnmaildir--art-num article)) | |
163 (if (eq num (caar nlist-post)) | |
164 (setq new-nlist (cdr nlist-post)) | |
165 (setq new-nlist nlist-post | |
166 nlist-pre nlist-post | |
167 nlist-post (cdr nlist-post)) | |
168 (while (/= num (caar nlist-post)) | |
169 (setq nlist-pre nlist-post | |
170 nlist-post (cdr nlist-post))) | |
171 (setq nlist-post (cdr nlist-post)) | |
172 (if (eq num min) | |
173 (setq min (caar nlist-pre))))) | |
174 (let ((inhibit-quit t)) | |
175 (setf (nnmaildir--grp-min group) min) | |
176 (setf (nnmaildir--grp-count group) count) | |
177 (setf (nnmaildir--grp-nlist group) new-nlist) | |
178 (setcdr nlist-pre nlist-post) | |
179 (unintern prefix flist) | |
180 (unintern msgid mlist)))) | |
181 | |
182 (defun nnmaildir--nlist-art (group num) | |
183 (let ((entry (assq num (nnmaildir--grp-nlist group)))) | |
184 (if entry | |
185 (cdr entry)))) | |
186 (defmacro nnmaildir--flist-art (list file) | |
187 `(symbol-value (intern-soft ,file ,list))) | |
188 (defmacro nnmaildir--mlist-art (list msgid) | |
189 `(symbol-value (intern-soft ,msgid ,list))) | |
190 | |
191 (defun nnmaildir--pgname (server gname) | |
192 (let ((prefix (nnmaildir--srv-prefix server))) | |
193 (if prefix (concat prefix gname) | |
194 (setq gname (gnus-group-prefixed-name gname | |
195 (nnmaildir--srv-method server))) | |
196 (setf (nnmaildir--srv-prefix server) (gnus-group-real-prefix gname)) | |
197 gname))) | |
198 | |
199 (defun nnmaildir--param (pgname param) | |
200 (setq param (gnus-group-find-parameter pgname param 'allow-list)) | |
201 (if (vectorp param) (setq param (aref param 0))) | |
202 (eval param)) | |
203 | |
204 (defmacro nnmaildir--with-nntp-buffer (&rest body) | |
205 `(save-excursion | |
206 (set-buffer nntp-server-buffer) | |
207 ,@body)) | |
208 (defmacro nnmaildir--with-work-buffer (&rest body) | |
209 `(save-excursion | |
210 (set-buffer (get-buffer-create " *nnmaildir work*")) | |
211 ,@body)) | |
212 (defmacro nnmaildir--with-nov-buffer (&rest body) | |
213 `(save-excursion | |
214 (set-buffer (get-buffer-create " *nnmaildir nov*")) | |
215 ,@body)) | |
216 (defmacro nnmaildir--with-move-buffer (&rest body) | |
217 `(save-excursion | |
218 (set-buffer (get-buffer-create " *nnmaildir move*")) | |
219 ,@body)) | |
220 | |
221 (defmacro nnmaildir--subdir (dir subdir) | |
222 `(file-name-as-directory (concat ,dir ,subdir))) | |
223 (defmacro nnmaildir--srvgrp-dir (srv-dir gname) | |
224 `(nnmaildir--subdir ,srv-dir ,gname)) | |
225 (defmacro nnmaildir--tmp (dir) `(nnmaildir--subdir ,dir "tmp")) | |
226 (defmacro nnmaildir--new (dir) `(nnmaildir--subdir ,dir "new")) | |
227 (defmacro nnmaildir--cur (dir) `(nnmaildir--subdir ,dir "cur")) | |
228 (defmacro nnmaildir--nndir (dir) `(nnmaildir--subdir ,dir ".nnmaildir")) | |
229 (defmacro nnmaildir--nov-dir (dir) `(nnmaildir--subdir ,dir "nov")) | |
230 (defmacro nnmaildir--marks-dir (dir) `(nnmaildir--subdir ,dir "marks")) | |
231 (defmacro nnmaildir--num-dir (dir) `(nnmaildir--subdir ,dir "num")) | |
232 (defmacro nnmaildir--num-file (dir) `(concat ,dir ":")) | |
233 | |
234 (defmacro nnmaildir--unlink (file-arg) | |
235 `(let ((file ,file-arg)) | |
236 (if (file-attributes file) (delete-file file)))) | |
237 (defun nnmaildir--mkdir (dir) | |
238 (or (file-exists-p (file-name-as-directory dir)) | |
239 (make-directory-internal (directory-file-name dir)))) | |
240 (defun nnmaildir--delete-dir-files (dir ls) | |
241 (when (file-attributes dir) | |
242 (mapcar 'delete-file (funcall ls dir 'full "\\`[^.]" 'nosort)) | |
243 (delete-directory dir))) | |
244 | |
245 (defun nnmaildir--group-maxnum (server group) | |
246 (if (zerop (nnmaildir--grp-count group)) 0 | |
247 (let ((x (nnmaildir--srvgrp-dir (nnmaildir--srv-dir server) | |
248 (nnmaildir--grp-name group)))) | |
249 (setq x (nnmaildir--nndir x) | |
250 x (nnmaildir--num-dir x) | |
251 x (nnmaildir--num-file x) | |
252 x (file-attributes x)) | |
253 (if x (1- (nth 1 x)) 0)))) | |
254 | |
255 ;; Make the given server, if non-nil, be the current server. Then make the | |
256 ;; given group, if non-nil, be the current group of the current server. Then | |
257 ;; return the group object for the current group. | |
258 (defun nnmaildir--prepare (server group) | |
259 (let (x groups) | |
260 (catch 'return | |
261 (if (null server) | |
262 (unless (setq server nnmaildir--cur-server) | |
263 (throw 'return nil)) | |
264 (unless (setq server (intern-soft server nnmaildir--servers)) | |
265 (throw 'return nil)) | |
266 (setq server (symbol-value server) | |
267 nnmaildir--cur-server server)) | |
268 (unless (setq groups (nnmaildir--srv-groups server)) | |
269 (throw 'return nil)) | |
270 (unless (nnmaildir--srv-method server) | |
271 (setq x (concat "nnmaildir:" (nnmaildir--srv-address server)) | |
272 x (gnus-server-to-method x)) | |
273 (unless x (throw 'return nil)) | |
274 (setf (nnmaildir--srv-method server) x)) | |
275 (if (null group) | |
276 (unless (setq group (nnmaildir--srv-curgrp server)) | |
277 (throw 'return nil)) | |
278 (unless (setq group (intern-soft group groups)) | |
279 (throw 'return nil)) | |
280 (setq group (symbol-value group))) | |
281 group))) | |
282 | |
283 (defun nnmaildir--tab-to-space (string) | |
284 (let ((pos 0)) | |
285 (while (string-match "\t" string pos) | |
286 (aset string (match-beginning 0) ? ) | |
287 (setq pos (match-end 0)))) | |
288 string) | |
289 | |
290 (defun nnmaildir--update-nov (server group article) | |
291 (let ((nnheader-file-coding-system 'binary) | |
292 (srv-dir (nnmaildir--srv-dir server)) | |
293 (storage-version 1) ;; [version article-number msgid [...nov...]] | |
294 dir gname pgname msgdir prefix suffix file attr mtime novdir novfile | |
295 nov msgid nov-beg nov-mid nov-end field val old-extra num numdir | |
296 deactivate-mark) | |
297 (catch 'return | |
298 (setq gname (nnmaildir--grp-name group) | |
299 pgname (nnmaildir--pgname server gname) | |
300 dir (nnmaildir--srvgrp-dir srv-dir gname) | |
301 msgdir (if (nnmaildir--param pgname 'read-only) | |
302 (nnmaildir--new dir) (nnmaildir--cur dir)) | |
303 prefix (nnmaildir--art-prefix article) | |
304 suffix (nnmaildir--art-suffix article) | |
305 file (concat msgdir prefix suffix) | |
306 attr (file-attributes file)) | |
307 (unless attr | |
308 (nnmaildir--expired-article group article) | |
309 (throw 'return nil)) | |
310 (setq mtime (nth 5 attr) | |
311 attr (nth 7 attr) | |
312 nov (nnmaildir--art-nov article) | |
313 dir (nnmaildir--nndir dir) | |
314 novdir (nnmaildir--nov-dir dir) | |
315 novfile (concat novdir prefix)) | |
316 (unless (equal nnmaildir--extra nnmail-extra-headers) | |
317 (setq nnmaildir--extra (copy-sequence nnmail-extra-headers))) | |
318 (nnmaildir--with-nov-buffer | |
319 ;; First we'll check for already-parsed NOV data. | |
320 (cond ((not (file-exists-p novfile)) | |
321 ;; The NOV file doesn't exist; we have to parse the message. | |
322 (setq nov nil)) | |
323 ((not nov) | |
324 ;; The file exists, but the data isn't in memory; read the file. | |
325 (erase-buffer) | |
326 (nnheader-insert-file-contents novfile) | |
327 (setq nov (read (current-buffer))) | |
328 (if (not (and (vectorp nov) | |
329 (/= 0 (length nov)) | |
330 (equal storage-version (aref nov 0)))) | |
331 ;; This NOV data seems to be in the wrong format. | |
332 (setq nov nil) | |
333 (unless (nnmaildir--art-num article) | |
334 (setf (nnmaildir--art-num article) (aref nov 1))) | |
335 (unless (nnmaildir--art-msgid article) | |
336 (setf (nnmaildir--art-msgid article) (aref nov 2))) | |
337 (setq nov (aref nov 3))))) | |
338 ;; Now check whether the already-parsed data (if we have any) is | |
339 ;; usable: if the message has been edited or if nnmail-extra-headers | |
340 ;; has been augmented since this data was parsed from the message, | |
341 ;; then we have to reparse. Otherwise it's up-to-date. | |
342 (when (and nov (equal mtime (nnmaildir--nov-get-mtime nov))) | |
343 ;; The timestamp matches. Now check nnmail-extra-headers. | |
344 (setq old-extra (nnmaildir--nov-get-extra nov)) | |
345 (when (equal nnmaildir--extra old-extra) ;; common case | |
346 ;; Save memory; use a single copy of the list value. | |
347 (nnmaildir--nov-set-extra nov nnmaildir--extra) | |
348 (throw 'return nov)) | |
349 ;; They're not equal, but maybe the new is a subset of the old. | |
350 (if (null nnmaildir--extra) | |
351 ;; The empty set is a subset of every set. | |
352 (throw 'return nov)) | |
353 (if (not (memq nil (mapcar (lambda (e) (memq e old-extra)) | |
354 nnmaildir--extra))) | |
355 (throw 'return nov))) | |
356 ;; Parse the NOV data out of the message. | |
357 (erase-buffer) | |
358 (nnheader-insert-file-contents file) | |
359 (insert "\n") | |
360 (goto-char (point-min)) | |
361 (save-restriction | |
362 (if (search-forward "\n\n" nil 'noerror) | |
363 (progn | |
364 (setq nov-mid (count-lines (point) (point-max))) | |
365 (narrow-to-region (point-min) (1- (point)))) | |
366 (setq nov-mid 0)) | |
367 (goto-char (point-min)) | |
368 (delete-char 1) | |
369 (setq nov (nnheader-parse-naked-head) | |
370 field (or (mail-header-lines nov) 0))) | |
371 (unless (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:)) | |
372 (setq nov-mid field)) | |
373 (setq nov-mid (number-to-string nov-mid) | |
374 nov-mid (concat (number-to-string attr) "\t" nov-mid)) | |
375 (save-match-data | |
376 (setq field (or (mail-header-references nov) "")) | |
377 (nnmaildir--tab-to-space field) | |
378 (setq nov-mid (concat field "\t" nov-mid) | |
379 nov-beg (mapconcat | |
380 (lambda (f) (nnmaildir--tab-to-space (or f ""))) | |
381 (list (mail-header-subject nov) | |
382 (mail-header-from nov) | |
383 (mail-header-date nov)) "\t") | |
384 nov-end (mapconcat | |
385 (lambda (extra) | |
386 (setq field (symbol-name (car extra)) | |
387 val (cdr extra)) | |
388 (nnmaildir--tab-to-space field) | |
389 (nnmaildir--tab-to-space val) | |
390 (concat field ": " val)) | |
391 (mail-header-extra nov) "\t"))) | |
392 (setq msgid (mail-header-id nov)) | |
393 (if (or (null msgid) (nnheader-fake-message-id-p msgid)) | |
394 (setq msgid (concat "<" prefix "@nnmaildir>"))) | |
395 (nnmaildir--tab-to-space msgid) | |
396 ;; The data is parsed; create an nnmaildir NOV structure. | |
397 (setq nov (nnmaildir--nov-new nov-beg nov-mid nov-end mtime | |
398 nnmaildir--extra) | |
399 num (nnmaildir--art-num article)) | |
400 (unless num | |
401 ;; Allocate a new article number. | |
402 (erase-buffer) | |
403 (setq numdir (nnmaildir--num-dir dir) | |
404 file (nnmaildir--num-file numdir) | |
405 num -1) | |
406 (nnmaildir--mkdir numdir) | |
407 (write-region "" nil file nil 'no-message) | |
408 (while file | |
409 ;; Get the number of links to file. | |
410 (setq attr (nth 1 (file-attributes file))) | |
411 (if (= attr num) | |
412 ;; We've already tried this number, in the previous loop | |
413 ;; iteration, and failed. | |
414 (signal 'error `("Corrupt internal nnmaildir data" ,numdir))) | |
415 ;; If attr is 123, try to link file to "123". This atomically | |
416 ;; increases the link count and creates the "123" link, failing | |
417 ;; if that link was already created by another Gnus, just after | |
418 ;; we stat()ed file. | |
419 (condition-case nil | |
420 (progn | |
421 (add-name-to-file file (concat numdir (format "%x" attr))) | |
422 (setq file nil)) ;; Stop looping. | |
423 (file-already-exists nil)) | |
424 (setq num attr)) | |
425 (setf (nnmaildir--art-num article) num)) | |
426 ;; Store this new NOV data in a file | |
427 (erase-buffer) | |
428 (prin1 (vector storage-version num msgid nov) (current-buffer)) | |
429 (setq file (concat novfile ":")) | |
430 (nnmaildir--unlink file) | |
431 (write-region (point-min) (point-max) file nil 'no-message nil 'excl)) | |
432 (rename-file file novfile 'replace) | |
433 (setf (nnmaildir--art-msgid article) msgid) | |
434 nov))) | |
435 | |
436 (defun nnmaildir--cache-nov (group article nov) | |
437 (let ((cache (nnmaildir--grp-cache group)) | |
438 (index (nnmaildir--grp-index group)) | |
439 goner) | |
440 (unless (nnmaildir--art-nov article) | |
441 (setq goner (aref cache index)) | |
442 (if goner (setf (nnmaildir--art-nov goner) nil)) | |
443 (aset cache index article) | |
444 (setf (nnmaildir--grp-index group) (% (1+ index) (length cache)))) | |
445 (setf (nnmaildir--art-nov article) nov))) | |
446 | |
447 (defun nnmaildir--grp-add-art (server group article) | |
448 (let ((nov (nnmaildir--update-nov server group article)) | |
449 count num min nlist nlist-cdr insert-nlist) | |
450 (when nov | |
451 (setq count (1+ (nnmaildir--grp-count group)) | |
452 num (nnmaildir--art-num article) | |
453 min (if (= count 1) num | |
454 (min num (nnmaildir--grp-min group))) | |
455 nlist (nnmaildir--grp-nlist group)) | |
456 (if (or (null nlist) (> num (caar nlist))) | |
457 (setq nlist (cons (cons num article) nlist)) | |
458 (setq insert-nlist t | |
459 nlist-cdr (cdr nlist)) | |
460 (while (and nlist-cdr (< num (caar nlist-cdr))) | |
461 (setq nlist nlist-cdr | |
462 nlist-cdr (cdr nlist)))) | |
463 (let ((inhibit-quit t)) | |
464 (setf (nnmaildir--grp-count group) count) | |
465 (setf (nnmaildir--grp-min group) min) | |
466 (if insert-nlist | |
467 (setcdr nlist (cons (cons num article) nlist-cdr)) | |
468 (setf (nnmaildir--grp-nlist group) nlist)) | |
469 (set (intern (nnmaildir--art-prefix article) | |
470 (nnmaildir--grp-flist group)) | |
471 article) | |
472 (set (intern (nnmaildir--art-msgid article) | |
473 (nnmaildir--grp-mlist group)) | |
474 article) | |
475 (set (intern (nnmaildir--grp-name group) | |
476 (nnmaildir--srv-groups server)) | |
477 group)) | |
478 (nnmaildir--cache-nov group article nov) | |
479 t))) | |
480 | |
481 (defun nnmaildir--group-ls (server pgname) | |
482 (or (nnmaildir--param pgname 'directory-files) | |
483 (nnmaildir--srv-ls server))) | |
484 | |
485 (defun nnmaildir-article-number-to-file-name | |
486 (number group-name server-address-string) | |
487 (let ((group (nnmaildir--prepare server-address-string group-name)) | |
488 article dir pgname) | |
489 (catch 'return | |
490 (unless group | |
491 ;; The given group or server does not exist. | |
492 (throw 'return nil)) | |
493 (setq article (nnmaildir--nlist-art group number)) | |
494 (unless article | |
495 ;; The given article number does not exist in this group. | |
496 (throw 'return nil)) | |
497 (setq pgname (nnmaildir--pgname nnmaildir--cur-server group-name) | |
498 dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
499 dir (nnmaildir--srvgrp-dir dir group-name) | |
500 dir (if (nnmaildir--param pgname 'read-only) | |
501 (nnmaildir--new dir) (nnmaildir--cur dir))) | |
502 (concat dir (nnmaildir--art-prefix article) | |
503 (nnmaildir--art-suffix article))))) | |
504 | |
505 (defun nnmaildir-article-number-to-base-name | |
506 (number group-name server-address-string) | |
507 (let ((x (nnmaildir--prepare server-address-string group-name))) | |
508 (when x | |
509 (setq x (nnmaildir--nlist-art x number)) | |
510 (and x (cons (nnmaildir--art-prefix x) | |
511 (nnmaildir--art-suffix x)))))) | |
512 | |
513 (defun nnmaildir-base-name-to-article-number | |
514 (base-name group-name server-address-string) | |
515 (let ((x (nnmaildir--prepare server-address-string group-name))) | |
516 (when x | |
517 (setq x (nnmaildir--grp-flist x) | |
518 x (nnmaildir--flist-art x base-name)) | |
519 (and x (nnmaildir--art-num x))))) | |
520 | |
521 (defun nnmaildir--nlist-iterate (nlist ranges func) | |
522 (let (entry high low nlist2) | |
523 (if (eq ranges 'all) | |
524 (setq ranges `((1 . ,(caar nlist))))) | |
525 (while ranges | |
526 (setq entry (car ranges) ranges (cdr ranges)) | |
527 (while (and ranges (eq entry (car ranges))) | |
528 (setq ranges (cdr ranges))) ;; skip duplicates | |
529 (if (numberp entry) | |
530 (setq low entry | |
531 high entry) | |
532 (setq low (car entry) | |
533 high (cdr entry))) | |
534 (setq nlist2 nlist) ;; Don't assume any sorting of ranges | |
535 (catch 'iterate-loop | |
536 (while nlist2 | |
537 (if (<= (caar nlist2) high) (throw 'iterate-loop nil)) | |
538 (setq nlist2 (cdr nlist2)))) | |
539 (catch 'iterate-loop | |
540 (while nlist2 | |
541 (setq entry (car nlist2) nlist2 (cdr nlist2)) | |
542 (if (< (car entry) low) (throw 'iterate-loop nil)) | |
543 (funcall func (cdr entry))))))) | |
544 | |
545 (defun nnmaildir--up2-1 (n) | |
546 (if (zerop n) 1 (1- (lsh 1 (1+ (logb n)))))) | |
547 | |
548 (defun nnmaildir--system-name () | |
549 (gnus-replace-in-string | |
550 (gnus-replace-in-string | |
551 (gnus-replace-in-string | |
552 (system-name) | |
553 "\\\\" "\\134" 'literal) | |
554 "/" "\\057" 'literal) | |
555 ":" "\\072" 'literal)) | |
556 | |
557 (defun nnmaildir-request-type (group &optional article) | |
558 'mail) | |
559 | |
560 (defun nnmaildir-status-message (&optional server) | |
561 (nnmaildir--prepare server nil) | |
562 (nnmaildir--srv-error nnmaildir--cur-server)) | |
563 | |
564 (defun nnmaildir-server-opened (&optional server) | |
565 (and nnmaildir--cur-server | |
566 (if server | |
567 (string-equal server (nnmaildir--srv-address nnmaildir--cur-server)) | |
568 t) | |
569 (nnmaildir--srv-groups nnmaildir--cur-server) | |
570 t)) | |
571 | |
572 (defun nnmaildir-open-server (server &optional defs) | |
573 (let ((x server) | |
574 dir size) | |
575 (catch 'return | |
576 (setq server (intern-soft x nnmaildir--servers)) | |
577 (if server | |
578 (and (setq server (symbol-value server)) | |
579 (nnmaildir--srv-groups server) | |
580 (setq nnmaildir--cur-server server) | |
581 (throw 'return t)) | |
582 (setq server (make-nnmaildir--srv :address x)) | |
583 (let ((inhibit-quit t)) | |
584 (set (intern x nnmaildir--servers) server))) | |
585 (setq dir (assq 'directory defs)) | |
586 (unless dir | |
587 (setf (nnmaildir--srv-error server) | |
588 "You must set \"directory\" in the select method") | |
589 (throw 'return nil)) | |
590 (setq dir (cadr dir) | |
591 dir (eval dir) | |
592 dir (expand-file-name dir) | |
593 dir (file-name-as-directory dir)) | |
594 (unless (file-exists-p dir) | |
595 (setf (nnmaildir--srv-error server) (concat "No such directory: " dir)) | |
596 (throw 'return nil)) | |
597 (setf (nnmaildir--srv-dir server) dir) | |
598 (setq x (assq 'directory-files defs)) | |
599 (if (null x) | |
600 (setq x (if nnheader-directory-files-is-safe 'directory-files | |
601 'nnheader-directory-files-safe)) | |
602 (setq x (cadr x)) | |
603 (unless (functionp x) | |
604 (setf (nnmaildir--srv-error server) | |
605 (concat "Not a function: " (prin1-to-string x))) | |
606 (throw 'return nil))) | |
607 (setf (nnmaildir--srv-ls server) x) | |
608 (setq size (length (funcall x dir nil "\\`[^.]" 'nosort)) | |
609 size (nnmaildir--up2-1 size)) | |
610 (and (setq x (assq 'get-new-mail defs)) | |
611 (setq x (cdr x)) | |
612 (car x) | |
613 (setf (nnmaildir--srv-gnm server) t) | |
614 (require 'nnmail)) | |
615 (setq x (assq 'target-prefix defs)) | |
616 (if x | |
617 (progn | |
618 (setq x (cadr x) | |
619 x (eval x)) | |
620 (setf (nnmaildir--srv-target-prefix server) x)) | |
621 (setq x (assq 'create-directory defs)) | |
622 (if x | |
623 (progn | |
624 (setq x (cadr x) | |
625 x (eval x) | |
626 x (file-name-as-directory x)) | |
627 (setf (nnmaildir--srv-target-prefix server) x)) | |
628 (setf (nnmaildir--srv-target-prefix server) ""))) | |
629 (setf (nnmaildir--srv-groups server) (make-vector size 0)) | |
630 (setq nnmaildir--cur-server server) | |
631 t))) | |
632 | |
633 (defun nnmaildir--parse-filename (file) | |
634 (let ((prefix (car file)) | |
635 timestamp len) | |
636 (if (string-match "\\`\\([0-9]+\\)\\(\\..*\\)\\'" prefix) | |
637 (progn | |
638 (setq timestamp (concat "0000" (match-string 1 prefix)) | |
639 len (- (length timestamp) 4)) | |
640 (vector (string-to-number (substring timestamp 0 len)) | |
641 (string-to-number (substring timestamp len)) | |
642 (match-string 2 prefix) | |
643 file)) | |
644 file))) | |
645 | |
646 (defun nnmaildir--sort-files (a b) | |
647 (catch 'return | |
648 (if (consp a) | |
649 (throw 'return (and (consp b) (string-lessp (car a) (car b))))) | |
650 (if (consp b) (throw 'return t)) | |
651 (if (< (aref a 0) (aref b 0)) (throw 'return t)) | |
652 (if (> (aref a 0) (aref b 0)) (throw 'return nil)) | |
653 (if (< (aref a 1) (aref b 1)) (throw 'return t)) | |
654 (if (> (aref a 1) (aref b 1)) (throw 'return nil)) | |
655 (string-lessp (aref a 2) (aref b 2)))) | |
656 | |
657 (defun nnmaildir--scan (gname scan-msgs groups method srv-dir srv-ls) | |
658 (catch 'return | |
659 (let ((36h-ago (- (car (current-time)) 2)) | |
660 absdir nndir tdir ndir cdir nattr cattr isnew pgname read-only ls | |
661 files num dir flist group x) | |
662 (setq absdir (nnmaildir--srvgrp-dir srv-dir gname) | |
663 nndir (nnmaildir--nndir absdir)) | |
664 (unless (file-exists-p absdir) | |
665 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
666 (concat "No such directory: " absdir)) | |
667 (throw 'return nil)) | |
668 (setq tdir (nnmaildir--tmp absdir) | |
669 ndir (nnmaildir--new absdir) | |
670 cdir (nnmaildir--cur absdir) | |
671 nattr (file-attributes ndir) | |
672 cattr (file-attributes cdir)) | |
673 (unless (and (file-exists-p tdir) nattr cattr) | |
674 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
675 (concat "Not a maildir: " absdir)) | |
676 (throw 'return nil)) | |
677 (setq group (nnmaildir--prepare nil gname) | |
678 pgname (nnmaildir--pgname nnmaildir--cur-server gname)) | |
679 (if group | |
680 (setq isnew nil) | |
681 (setq isnew t | |
682 group (make-nnmaildir--grp :name gname :index 0)) | |
683 (nnmaildir--mkdir nndir) | |
684 (nnmaildir--mkdir (nnmaildir--nov-dir nndir)) | |
685 (nnmaildir--mkdir (nnmaildir--marks-dir nndir)) | |
686 (write-region "" nil (concat nndir "markfile") nil 'no-message)) | |
687 (setq read-only (nnmaildir--param pgname 'read-only) | |
688 ls (or (nnmaildir--param pgname 'directory-files) srv-ls)) | |
689 (unless read-only | |
690 (setq x (nth 11 (file-attributes tdir))) | |
691 (unless (and (= x (nth 11 nattr)) (= x (nth 11 cattr))) | |
692 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
693 (concat "Maildir spans filesystems: " absdir)) | |
694 (throw 'return nil)) | |
695 (mapcar | |
696 (lambda (file) | |
697 (setq x (file-attributes file)) | |
698 (if (or (> (cadr x) 1) (< (car (nth 4 x)) 36h-ago)) | |
699 (delete-file file))) | |
700 (funcall ls tdir 'full "\\`[^.]" 'nosort))) | |
701 (or scan-msgs | |
702 isnew | |
703 (throw 'return t)) | |
704 (setq nattr (nth 5 nattr)) | |
705 (if (equal nattr (nnmaildir--grp-new group)) | |
706 (setq nattr nil)) | |
707 (if read-only (setq dir (and (or isnew nattr) ndir)) | |
708 (when (or isnew nattr) | |
709 (mapcar | |
710 (lambda (file) | |
711 (let ((path (concat ndir file))) | |
712 (and (time-less-p (nth 5 (file-attributes path)) (current-time)) | |
713 (rename-file path (concat cdir file ":2,"))))) | |
714 (funcall ls ndir nil "\\`[^.]" 'nosort)) | |
715 (setf (nnmaildir--grp-new group) nattr)) | |
716 (setq cattr (nth 5 (file-attributes cdir))) | |
717 (if (equal cattr (nnmaildir--grp-cur group)) | |
718 (setq cattr nil)) | |
719 (setq dir (and (or isnew cattr) cdir))) | |
720 (unless dir (throw 'return t)) | |
721 (setq files (funcall ls dir nil "\\`[^.]" 'nosort) | |
722 files (save-match-data | |
723 (mapcar | |
724 (lambda (f) | |
725 (string-match "\\`\\([^:]*\\)\\(\\(:.*\\)?\\)\\'" f) | |
726 (cons (match-string 1 f) (match-string 2 f))) | |
727 files))) | |
728 (when isnew | |
729 (setq num (nnmaildir--up2-1 (length files))) | |
730 (setf (nnmaildir--grp-flist group) (make-vector num 0)) | |
731 (setf (nnmaildir--grp-mlist group) (make-vector num 0)) | |
732 (setf (nnmaildir--grp-mmth group) (make-vector 1 0)) | |
733 (setq num (nnmaildir--param pgname 'nov-cache-size)) | |
734 (if (numberp num) (if (< num 1) (setq num 1)) | |
735 (setq num 16 | |
736 cdir (nnmaildir--marks-dir nndir) | |
737 ndir (nnmaildir--subdir cdir "tick") | |
738 cdir (nnmaildir--subdir cdir "read")) | |
739 (mapcar | |
740 (lambda (file) | |
741 (setq file (car file)) | |
742 (if (or (not (file-exists-p (concat cdir file))) | |
743 (file-exists-p (concat ndir file))) | |
744 (setq num (1+ num)))) | |
745 files)) | |
746 (setf (nnmaildir--grp-cache group) (make-vector num nil)) | |
747 (let ((inhibit-quit t)) | |
748 (set (intern gname groups) group)) | |
749 (or scan-msgs (throw 'return t))) | |
750 (setq flist (nnmaildir--grp-flist group) | |
751 files (mapcar | |
752 (lambda (file) | |
753 (and (null (nnmaildir--flist-art flist (car file))) | |
754 file)) | |
755 files) | |
756 files (delq nil files) | |
757 files (mapcar 'nnmaildir--parse-filename files) | |
758 files (sort files 'nnmaildir--sort-files)) | |
759 (mapcar | |
760 (lambda (file) | |
761 (setq file (if (consp file) file (aref file 3)) | |
762 x (make-nnmaildir--art :prefix (car file) :suffix (cdr file))) | |
763 (nnmaildir--grp-add-art nnmaildir--cur-server group x)) | |
764 files) | |
765 (if read-only (setf (nnmaildir--grp-new group) nattr) | |
766 (setf (nnmaildir--grp-cur group) cattr))) | |
767 t)) | |
768 | |
769 (defun nnmaildir-request-scan (&optional scan-group server) | |
770 (let ((coding-system-for-write nnheader-file-coding-system) | |
771 (buffer-file-coding-system nil) | |
772 (file-coding-system-alist nil) | |
773 (nnmaildir-get-new-mail t) | |
774 (nnmaildir-group-alist nil) | |
775 (nnmaildir-active-file nil) | |
776 x srv-ls srv-dir method groups target-prefix group dirs grp-dir seen | |
777 deactivate-mark) | |
778 (nnmaildir--prepare server nil) | |
779 (setq srv-ls (nnmaildir--srv-ls nnmaildir--cur-server) | |
780 srv-dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
781 method (nnmaildir--srv-method nnmaildir--cur-server) | |
782 groups (nnmaildir--srv-groups nnmaildir--cur-server) | |
783 target-prefix (nnmaildir--srv-target-prefix nnmaildir--cur-server)) | |
784 (nnmaildir--with-work-buffer | |
785 (save-match-data | |
786 (if (stringp scan-group) | |
787 (if (nnmaildir--scan scan-group t groups method srv-dir srv-ls) | |
788 (if (nnmaildir--srv-gnm nnmaildir--cur-server) | |
789 (nnmail-get-new-mail 'nnmaildir nil nil scan-group)) | |
790 (unintern scan-group groups)) | |
791 (setq x (nth 5 (file-attributes srv-dir)) | |
792 scan-group (null scan-group)) | |
793 (if (equal x (nnmaildir--srv-mtime nnmaildir--cur-server)) | |
794 (if scan-group | |
795 (mapatoms (lambda (sym) | |
796 (nnmaildir--scan (symbol-name sym) t groups | |
797 method srv-dir srv-ls)) | |
798 groups)) | |
799 (setq dirs (funcall srv-ls srv-dir nil "\\`[^.]" 'nosort) | |
800 dirs (if (zerop (length target-prefix)) | |
801 dirs | |
802 (gnus-remove-if | |
803 (lambda (dir) | |
804 (and (>= (length dir) (length target-prefix)) | |
805 (string= (substring dir 0 | |
806 (length target-prefix)) | |
807 target-prefix))) | |
808 dirs)) | |
809 seen (nnmaildir--up2-1 (length dirs)) | |
810 seen (make-vector seen 0)) | |
811 (mapcar | |
812 (lambda (grp-dir) | |
813 (if (nnmaildir--scan grp-dir scan-group groups method srv-dir | |
814 srv-ls) | |
815 (intern grp-dir seen))) | |
816 dirs) | |
817 (setq x nil) | |
818 (mapatoms (lambda (group) | |
819 (setq group (symbol-name group)) | |
820 (unless (intern-soft group seen) | |
821 (setq x (cons group x)))) | |
822 groups) | |
823 (mapcar (lambda (grp) (unintern grp groups)) x) | |
824 (setf (nnmaildir--srv-mtime nnmaildir--cur-server) | |
825 (nth 5 (file-attributes srv-dir)))) | |
826 (and scan-group | |
827 (nnmaildir--srv-gnm nnmaildir--cur-server) | |
828 (nnmail-get-new-mail 'nnmaildir nil nil)))))) | |
829 t) | |
830 | |
831 (defun nnmaildir-request-list (&optional server) | |
832 (nnmaildir-request-scan 'find-new-groups server) | |
833 (let (pgname ro deactivate-mark) | |
834 (nnmaildir--prepare server nil) | |
835 (nnmaildir--with-nntp-buffer | |
836 (erase-buffer) | |
837 (mapatoms (lambda (group) | |
838 (setq pgname (symbol-name group) | |
839 pgname (nnmaildir--pgname nnmaildir--cur-server pgname) | |
840 group (symbol-value group) | |
841 ro (nnmaildir--param pgname 'read-only)) | |
842 (insert (nnmaildir--grp-name group) " ") | |
843 (princ (nnmaildir--group-maxnum nnmaildir--cur-server group) | |
844 nntp-server-buffer) | |
845 (insert " ") | |
846 (princ (nnmaildir--grp-min group) nntp-server-buffer) | |
847 (insert " " (if ro "n" "y") "\n")) | |
848 (nnmaildir--srv-groups nnmaildir--cur-server)))) | |
849 t) | |
850 | |
851 (defun nnmaildir-request-newgroups (date &optional server) | |
852 (nnmaildir-request-list server)) | |
853 | |
854 (defun nnmaildir-retrieve-groups (groups &optional server) | |
855 (let (group deactivate-mark) | |
856 (nnmaildir--prepare server nil) | |
857 (nnmaildir--with-nntp-buffer | |
858 (erase-buffer) | |
859 (mapcar | |
860 (lambda (gname) | |
861 (setq group (nnmaildir--prepare nil gname)) | |
862 (if (null group) (insert "411 no such news group\n") | |
863 (insert "211 ") | |
864 (princ (nnmaildir--grp-count group) nntp-server-buffer) | |
865 (insert " ") | |
866 (princ (nnmaildir--grp-min group) nntp-server-buffer) | |
867 (insert " ") | |
868 (princ (nnmaildir--group-maxnum nnmaildir--cur-server group) | |
869 nntp-server-buffer) | |
870 (insert " " gname "\n"))) | |
871 groups))) | |
872 'group) | |
873 | |
874 (defun nnmaildir-request-update-info (gname info &optional server) | |
875 (let ((group (nnmaildir--prepare server gname)) | |
876 pgname flist always-marks never-marks old-marks dotfile num dir | |
877 markdirs marks mark ranges markdir article read end new-marks ls | |
878 old-mmth new-mmth mtime mark-sym existing missing deactivate-mark) | |
879 (catch 'return | |
880 (unless group | |
881 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
882 (concat "No such group: " gname)) | |
883 (throw 'return nil)) | |
884 (setq gname (nnmaildir--grp-name group) | |
885 pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
886 flist (nnmaildir--grp-flist group)) | |
887 (when (zerop (nnmaildir--grp-count group)) | |
888 (gnus-info-set-read info nil) | |
889 (gnus-info-set-marks info nil 'extend) | |
890 (throw 'return info)) | |
891 (setq old-marks (cons 'read (gnus-info-read info)) | |
892 old-marks (cons old-marks (gnus-info-marks info)) | |
893 always-marks (nnmaildir--param pgname 'always-marks) | |
894 never-marks (nnmaildir--param pgname 'never-marks) | |
895 existing (nnmaildir--grp-nlist group) | |
896 existing (mapcar 'car existing) | |
897 existing (nreverse existing) | |
898 existing (gnus-compress-sequence existing 'always-list) | |
899 missing (list (cons 1 (nnmaildir--group-maxnum | |
900 nnmaildir--cur-server group))) | |
901 missing (gnus-range-difference missing existing) | |
902 dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
903 dir (nnmaildir--srvgrp-dir dir gname) | |
904 dir (nnmaildir--nndir dir) | |
905 dir (nnmaildir--marks-dir dir) | |
906 ls (nnmaildir--group-ls nnmaildir--cur-server pgname) | |
907 markdirs (funcall ls dir nil "\\`[^.]" 'nosort) | |
908 new-mmth (nnmaildir--up2-1 (length markdirs)) | |
909 new-mmth (make-vector new-mmth 0) | |
910 old-mmth (nnmaildir--grp-mmth group)) | |
911 (mapcar | |
912 (lambda (mark) | |
913 (setq markdir (nnmaildir--subdir dir mark) | |
914 mark-sym (intern mark) | |
915 ranges nil) | |
916 (catch 'got-ranges | |
917 (if (memq mark-sym never-marks) (throw 'got-ranges nil)) | |
918 (when (memq mark-sym always-marks) | |
919 (setq ranges existing) | |
920 (throw 'got-ranges nil)) | |
921 (setq mtime (nth 5 (file-attributes markdir))) | |
922 (set (intern mark new-mmth) mtime) | |
923 (when (equal mtime (symbol-value (intern-soft mark old-mmth))) | |
924 (setq ranges (assq mark-sym old-marks)) | |
925 (if ranges (setq ranges (cdr ranges))) | |
926 (throw 'got-ranges nil)) | |
927 (mapcar | |
928 (lambda (prefix) | |
929 (setq article (nnmaildir--flist-art flist prefix)) | |
930 (if article | |
931 (setq ranges | |
932 (gnus-add-to-range ranges | |
933 `(,(nnmaildir--art-num article)))))) | |
934 (funcall ls markdir nil "\\`[^.]" 'nosort))) | |
935 (if (eq mark-sym 'read) (setq read ranges) | |
936 (if ranges (setq marks (cons (cons mark-sym ranges) marks))))) | |
937 markdirs) | |
938 (gnus-info-set-read info (gnus-range-add read missing)) | |
939 (gnus-info-set-marks info marks 'extend) | |
940 (setf (nnmaildir--grp-mmth group) new-mmth) | |
941 info))) | |
942 | |
943 (defun nnmaildir-request-group (gname &optional server fast) | |
944 (let ((group (nnmaildir--prepare server gname)) | |
945 deactivate-mark) | |
946 (catch 'return | |
947 (unless group | |
948 ;; (insert "411 no such news group\n") | |
949 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
950 (concat "No such group: " gname)) | |
951 (throw 'return nil)) | |
952 (setf (nnmaildir--srv-curgrp nnmaildir--cur-server) group) | |
953 (if fast (throw 'return t)) | |
954 (nnmaildir--with-nntp-buffer | |
955 (erase-buffer) | |
956 (insert "211 ") | |
957 (princ (nnmaildir--grp-count group) nntp-server-buffer) | |
958 (insert " ") | |
959 (princ (nnmaildir--grp-min group) nntp-server-buffer) | |
960 (insert " ") | |
961 (princ (nnmaildir--group-maxnum nnmaildir--cur-server group) | |
962 nntp-server-buffer) | |
963 (insert " " gname "\n") | |
964 t)))) | |
965 | |
966 (defun nnmaildir-request-create-group (gname &optional server args) | |
967 (nnmaildir--prepare server nil) | |
968 (catch 'return | |
969 (let ((target-prefix (nnmaildir--srv-target-prefix nnmaildir--cur-server)) | |
970 srv-dir dir groups) | |
971 (when (zerop (length gname)) | |
972 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
973 "Invalid (empty) group name") | |
974 (throw 'return nil)) | |
975 (when (eq (aref "." 0) (aref gname 0)) | |
976 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
977 "Group names may not start with \".\"") | |
978 (throw 'return nil)) | |
979 (when (save-match-data (string-match "[\0/\t]" gname)) | |
980 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
981 (concat "Illegal characters (null, tab, or /) in group name: " | |
982 gname)) | |
983 (throw 'return nil)) | |
984 (setq groups (nnmaildir--srv-groups nnmaildir--cur-server)) | |
985 (when (intern-soft gname groups) | |
986 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
987 (concat "Group already exists: " gname)) | |
988 (throw 'return nil)) | |
989 (setq srv-dir (nnmaildir--srv-dir nnmaildir--cur-server)) | |
990 (if (file-name-absolute-p target-prefix) | |
991 (setq dir (expand-file-name target-prefix)) | |
992 (setq dir srv-dir | |
993 dir (file-truename dir) | |
994 dir (concat dir target-prefix))) | |
995 (setq dir (nnmaildir--subdir dir gname)) | |
996 (nnmaildir--mkdir dir) | |
997 (nnmaildir--mkdir (nnmaildir--tmp dir)) | |
998 (nnmaildir--mkdir (nnmaildir--new dir)) | |
999 (nnmaildir--mkdir (nnmaildir--cur dir)) | |
1000 (unless (string= target-prefix "") | |
1001 (make-symbolic-link (concat target-prefix gname) | |
1002 (concat srv-dir gname))) | |
1003 (nnmaildir-request-scan 'find-new-groups)))) | |
1004 | |
1005 (defun nnmaildir-request-rename-group (gname new-name &optional server) | |
1006 (let ((group (nnmaildir--prepare server gname)) | |
1007 (coding-system-for-write nnheader-file-coding-system) | |
1008 (buffer-file-coding-system nil) | |
1009 (file-coding-system-alist nil) | |
1010 srv-dir x groups) | |
1011 (catch 'return | |
1012 (unless group | |
1013 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1014 (concat "No such group: " gname)) | |
1015 (throw 'return nil)) | |
1016 (when (zerop (length new-name)) | |
1017 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1018 "Invalid (empty) group name") | |
1019 (throw 'return nil)) | |
1020 (when (eq (aref "." 0) (aref new-name 0)) | |
1021 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1022 "Group names may not start with \".\"") | |
1023 (throw 'return nil)) | |
1024 (when (save-match-data (string-match "[\0/\t]" new-name)) | |
1025 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1026 (concat "Illegal characters (null, tab, or /) in group name: " | |
1027 new-name)) | |
1028 (throw 'return nil)) | |
1029 (if (string-equal gname new-name) (throw 'return t)) | |
1030 (when (intern-soft new-name | |
1031 (nnmaildir--srv-groups nnmaildir--cur-server)) | |
1032 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1033 (concat "Group already exists: " new-name)) | |
1034 (throw 'return nil)) | |
1035 (setq srv-dir (nnmaildir--srv-dir nnmaildir--cur-server)) | |
1036 (condition-case err | |
1037 (rename-file (concat srv-dir gname) | |
1038 (concat srv-dir new-name)) | |
1039 (error | |
1040 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1041 (concat "Error renaming link: " (prin1-to-string err))) | |
1042 (throw 'return nil))) | |
1043 (setq x (nnmaildir--srv-groups nnmaildir--cur-server) | |
1044 groups (make-vector (length x) 0)) | |
1045 (mapatoms (lambda (sym) | |
1046 (unless (eq (symbol-value sym) group) | |
1047 (set (intern (symbol-name sym) groups) | |
1048 (symbol-value sym)))) | |
1049 x) | |
1050 (setq group (copy-sequence group)) | |
1051 (setf (nnmaildir--grp-name group) new-name) | |
1052 (set (intern new-name groups) group) | |
1053 (setf (nnmaildir--srv-groups nnmaildir--cur-server) groups) | |
1054 t))) | |
1055 | |
1056 (defun nnmaildir-request-delete-group (gname force &optional server) | |
1057 (let ((group (nnmaildir--prepare server gname)) | |
1058 pgname grp-dir target dir ls deactivate-mark) | |
1059 (catch 'return | |
1060 (unless group | |
1061 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1062 (concat "No such group: " gname)) | |
1063 (throw 'return nil)) | |
1064 (setq gname (nnmaildir--grp-name group) | |
1065 pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
1066 grp-dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1067 target (car (file-attributes (concat grp-dir gname))) | |
1068 grp-dir (nnmaildir--srvgrp-dir grp-dir gname)) | |
1069 (unless (or force (stringp target)) | |
1070 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1071 (concat "Not a symlink: " gname)) | |
1072 (throw 'return nil)) | |
1073 (if (eq group (nnmaildir--srv-curgrp nnmaildir--cur-server)) | |
1074 (setf (nnmaildir--srv-curgrp nnmaildir--cur-server) nil)) | |
1075 (unintern gname (nnmaildir--srv-groups nnmaildir--cur-server)) | |
1076 (if (not force) | |
1077 (progn | |
1078 (setq grp-dir (directory-file-name grp-dir)) | |
1079 (nnmaildir--unlink grp-dir)) | |
1080 (setq ls (nnmaildir--group-ls nnmaildir--cur-server pgname)) | |
1081 (if (nnmaildir--param pgname 'read-only) | |
1082 (progn (delete-directory (nnmaildir--tmp grp-dir)) | |
1083 (nnmaildir--unlink (nnmaildir--new grp-dir)) | |
1084 (delete-directory (nnmaildir--cur grp-dir))) | |
1085 (nnmaildir--delete-dir-files (nnmaildir--tmp grp-dir) ls) | |
1086 (nnmaildir--delete-dir-files (nnmaildir--new grp-dir) ls) | |
1087 (nnmaildir--delete-dir-files (nnmaildir--cur grp-dir) ls)) | |
1088 (setq dir (nnmaildir--nndir grp-dir)) | |
1089 (mapcar (lambda (subdir) (nnmaildir--delete-dir-files subdir ls)) | |
1090 `(,(nnmaildir--nov-dir dir) ,(nnmaildir--num-dir dir) | |
1091 ,@(funcall ls (nnmaildir--marks-dir dir) 'full "\\`[^.]" | |
1092 'nosort))) | |
1093 (setq dir (nnmaildir--nndir grp-dir)) | |
1094 (nnmaildir--unlink (concat dir "markfile")) | |
1095 (nnmaildir--unlink (concat dir "markfile{new}")) | |
1096 (delete-directory (nnmaildir--marks-dir dir)) | |
1097 (delete-directory dir) | |
1098 (if (not (stringp target)) | |
1099 (delete-directory grp-dir) | |
1100 (setq grp-dir (directory-file-name grp-dir) | |
1101 dir target) | |
1102 (unless (eq (aref "/" 0) (aref dir 0)) | |
1103 (setq dir (concat (file-truename | |
1104 (nnmaildir--srv-dir nnmaildir--cur-server)) | |
1105 dir))) | |
1106 (delete-directory dir) | |
1107 (nnmaildir--unlink grp-dir))) | |
1108 t))) | |
1109 | |
1110 (defun nnmaildir-retrieve-headers (articles &optional gname server fetch-old) | |
1111 (let ((group (nnmaildir--prepare server gname)) | |
1112 srv-dir dir nlist mlist article num start stop nov nlist2 insert-nov | |
1113 deactivate-mark) | |
1114 (setq insert-nov | |
1115 (lambda (article) | |
1116 (setq nov (nnmaildir--update-nov nnmaildir--cur-server group | |
1117 article)) | |
1118 (when nov | |
1119 (nnmaildir--cache-nov group article nov) | |
1120 (setq num (nnmaildir--art-num article)) | |
1121 (princ num nntp-server-buffer) | |
1122 (insert "\t" (nnmaildir--nov-get-beg nov) "\t" | |
1123 (nnmaildir--art-msgid article) "\t" | |
1124 (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " | |
1125 gname ":") | |
1126 (princ num nntp-server-buffer) | |
1127 (insert "\t" (nnmaildir--nov-get-end nov) "\n")))) | |
1128 (catch 'return | |
1129 (unless group | |
1130 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1131 (if gname (concat "No such group: " gname) "No current group")) | |
1132 (throw 'return nil)) | |
1133 (nnmaildir--with-nntp-buffer | |
1134 (erase-buffer) | |
1135 (setq mlist (nnmaildir--grp-mlist group) | |
1136 nlist (nnmaildir--grp-nlist group) | |
1137 gname (nnmaildir--grp-name group) | |
1138 srv-dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1139 dir (nnmaildir--srvgrp-dir srv-dir gname)) | |
1140 (cond | |
1141 ((null nlist)) | |
1142 ((and fetch-old (not (numberp fetch-old))) | |
1143 (nnmaildir--nlist-iterate nlist 'all insert-nov)) | |
1144 ((null articles)) | |
1145 ((stringp (car articles)) | |
1146 (mapcar | |
1147 (lambda (msgid) | |
1148 (setq article (nnmaildir--mlist-art mlist msgid)) | |
1149 (if article (funcall insert-nov article))) | |
1150 articles)) | |
1151 (t | |
1152 (if fetch-old | |
1153 ;; Assume the article range list is sorted ascending | |
1154 (setq stop (car articles) | |
1155 start (car (last articles)) | |
1156 stop (if (numberp stop) stop (car stop)) | |
1157 start (if (numberp start) start (cdr start)) | |
1158 stop (- stop fetch-old) | |
1159 stop (if (< stop 1) 1 stop) | |
1160 articles (list (cons stop start)))) | |
1161 (nnmaildir--nlist-iterate nlist articles insert-nov))) | |
1162 (sort-numeric-fields 1 (point-min) (point-max)) | |
1163 'nov)))) | |
1164 | |
1165 (defun nnmaildir-request-article (num-msgid &optional gname server to-buffer) | |
1166 (let ((group (nnmaildir--prepare server gname)) | |
1167 (case-fold-search t) | |
1168 list article dir pgname deactivate-mark) | |
1169 (catch 'return | |
1170 (unless group | |
1171 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1172 (if gname (concat "No such group: " gname) "No current group")) | |
1173 (throw 'return nil)) | |
1174 (if (numberp num-msgid) | |
1175 (setq article (nnmaildir--nlist-art group num-msgid)) | |
1176 (setq list (nnmaildir--grp-mlist group) | |
1177 article (nnmaildir--mlist-art list num-msgid)) | |
1178 (if article (setq num-msgid (nnmaildir--art-num article)) | |
1179 (catch 'found | |
1180 (mapatoms | |
1181 (lambda (group-sym) | |
1182 (setq group (symbol-value group-sym) | |
1183 list (nnmaildir--grp-mlist group) | |
1184 article (nnmaildir--mlist-art list num-msgid)) | |
1185 (when article | |
1186 (setq num-msgid (nnmaildir--art-num article)) | |
1187 (throw 'found nil))) | |
1188 (nnmaildir--srv-groups nnmaildir--cur-server)))) | |
1189 (unless article | |
1190 (setf (nnmaildir--srv-error nnmaildir--cur-server) "No such article") | |
1191 (throw 'return nil))) | |
1192 (setq gname (nnmaildir--grp-name group) | |
1193 pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
1194 dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1195 dir (nnmaildir--srvgrp-dir dir gname) | |
1196 dir (if (nnmaildir--param pgname 'read-only) | |
1197 (nnmaildir--new dir) (nnmaildir--cur dir)) | |
1198 nnmaildir-article-file-name | |
1199 (concat dir | |
1200 (nnmaildir--art-prefix article) | |
1201 (nnmaildir--art-suffix article))) | |
1202 (unless (file-exists-p nnmaildir-article-file-name) | |
1203 (nnmaildir--expired-article group article) | |
1204 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1205 "Article has expired") | |
1206 (throw 'return nil)) | |
1207 (save-excursion | |
1208 (set-buffer (or to-buffer nntp-server-buffer)) | |
1209 (erase-buffer) | |
1210 (nnheader-insert-file-contents nnmaildir-article-file-name)) | |
1211 (cons gname num-msgid)))) | |
1212 | |
1213 (defun nnmaildir-request-post (&optional server) | |
1214 (let (message-required-mail-headers) | |
1215 (funcall message-send-mail-function))) | |
1216 | |
1217 (defun nnmaildir-request-replace-article (number gname buffer) | |
1218 (let ((group (nnmaildir--prepare nil gname)) | |
1219 (coding-system-for-write nnheader-file-coding-system) | |
1220 (buffer-file-coding-system nil) | |
1221 (file-coding-system-alist nil) | |
1222 dir file article suffix tmpfile deactivate-mark) | |
1223 (catch 'return | |
1224 (unless group | |
1225 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1226 (concat "No such group: " gname)) | |
1227 (throw 'return nil)) | |
1228 (when (nnmaildir--param (nnmaildir--pgname nnmaildir--cur-server gname) | |
1229 'read-only) | |
1230 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1231 (concat "Read-only group: " group)) | |
1232 (throw 'return nil)) | |
1233 (setq dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1234 dir (nnmaildir--srvgrp-dir dir gname) | |
1235 article (nnmaildir--nlist-art group number)) | |
1236 (unless article | |
1237 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1238 (concat "No such article: " (number-to-string number))) | |
1239 (throw 'return nil)) | |
1240 (setq suffix (nnmaildir--art-suffix article) | |
1241 file (nnmaildir--art-prefix article) | |
1242 tmpfile (concat (nnmaildir--tmp dir) file)) | |
1243 (when (file-exists-p tmpfile) | |
1244 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1245 (concat "File exists: " tmpfile)) | |
1246 (throw 'return nil)) | |
1247 (save-excursion | |
1248 (set-buffer buffer) | |
1249 (write-region (point-min) (point-max) tmpfile nil 'no-message nil | |
1250 'excl)) | |
1251 (unix-sync) ;; no fsync :( | |
1252 (rename-file tmpfile (concat (nnmaildir--cur dir) file suffix) 'replace) | |
1253 t))) | |
1254 | |
1255 (defun nnmaildir-request-move-article (article gname server accept-form | |
1256 &optional last) | |
1257 (let ((group (nnmaildir--prepare server gname)) | |
1258 pgname suffix result nnmaildir--file deactivate-mark) | |
1259 (catch 'return | |
1260 (unless group | |
1261 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1262 (concat "No such group: " gname)) | |
1263 (throw 'return nil)) | |
1264 (setq gname (nnmaildir--grp-name group) | |
1265 pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
1266 article (nnmaildir--nlist-art group article)) | |
1267 (unless article | |
1268 (setf (nnmaildir--srv-error nnmaildir--cur-server) "No such article") | |
1269 (throw 'return nil)) | |
1270 (setq suffix (nnmaildir--art-suffix article) | |
1271 nnmaildir--file (nnmaildir--srv-dir nnmaildir--cur-server) | |
1272 nnmaildir--file (nnmaildir--srvgrp-dir nnmaildir--file gname) | |
1273 nnmaildir--file (if (nnmaildir--param pgname 'read-only) | |
1274 (nnmaildir--new nnmaildir--file) | |
1275 (nnmaildir--cur nnmaildir--file)) | |
1276 nnmaildir--file (concat nnmaildir--file | |
1277 (nnmaildir--art-prefix article) | |
1278 suffix)) | |
1279 (unless (file-exists-p nnmaildir--file) | |
1280 (nnmaildir--expired-article group article) | |
1281 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1282 "Article has expired") | |
1283 (throw 'return nil)) | |
1284 (nnmaildir--with-move-buffer | |
1285 (erase-buffer) | |
1286 (nnheader-insert-file-contents nnmaildir--file) | |
1287 (setq result (eval accept-form))) | |
1288 (unless (or (null result) (nnmaildir--param pgname 'read-only)) | |
1289 (nnmaildir--unlink nnmaildir--file) | |
1290 (nnmaildir--expired-article group article)) | |
1291 result))) | |
1292 | |
1293 (defun nnmaildir-request-accept-article (gname &optional server last) | |
1294 (let ((group (nnmaildir--prepare server gname)) | |
1295 (coding-system-for-write nnheader-file-coding-system) | |
1296 (buffer-file-coding-system nil) | |
1297 (file-coding-system-alist nil) | |
1298 srv-dir dir file time tmpfile curfile 24h article) | |
1299 (catch 'return | |
1300 (unless group | |
1301 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1302 (concat "No such group: " gname)) | |
1303 (throw 'return nil)) | |
1304 (setq gname (nnmaildir--grp-name group)) | |
1305 (when (nnmaildir--param (nnmaildir--pgname nnmaildir--cur-server gname) | |
1306 'read-only) | |
1307 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1308 (concat "Read-only group: " gname)) | |
1309 (throw 'return nil)) | |
1310 (setq srv-dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1311 dir (nnmaildir--srvgrp-dir srv-dir gname) | |
1312 time (current-time) | |
1313 file (format-time-string "%s." time)) | |
1314 (unless (string-equal nnmaildir--delivery-time file) | |
1315 (setq nnmaildir--delivery-time file | |
1316 nnmaildir--delivery-count 0)) | |
1317 (when (and (consp (cdr time)) | |
1318 (consp (cddr time))) | |
1319 (setq file (concat file "M" (number-to-string (caddr time))))) | |
1320 (setq file (concat file nnmaildir--delivery-pid) | |
1321 file (concat file "Q" (number-to-string nnmaildir--delivery-count)) | |
1322 file (concat file "." (nnmaildir--system-name)) | |
1323 tmpfile (concat (nnmaildir--tmp dir) file) | |
1324 curfile (concat (nnmaildir--cur dir) file ":2,")) | |
1325 (when (file-exists-p tmpfile) | |
1326 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1327 (concat "File exists: " tmpfile)) | |
1328 (throw 'return nil)) | |
1329 (when (file-exists-p curfile) | |
1330 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1331 (concat "File exists: " curfile)) | |
1332 (throw 'return nil)) | |
1333 (setq nnmaildir--delivery-count (1+ nnmaildir--delivery-count) | |
1334 24h (run-with-timer 86400 nil | |
1335 (lambda () | |
1336 (nnmaildir--unlink tmpfile) | |
1337 (setf (nnmaildir--srv-error | |
1338 nnmaildir--cur-server) | |
1339 "24-hour timer expired") | |
1340 (throw 'return nil)))) | |
1341 (condition-case nil | |
1342 (add-name-to-file nnmaildir--file tmpfile) | |
1343 (error | |
1344 (write-region (point-min) (point-max) tmpfile nil 'no-message nil | |
1345 'excl) | |
1346 (unix-sync))) ;; no fsync :( | |
1347 (cancel-timer 24h) | |
1348 (condition-case err | |
1349 (add-name-to-file tmpfile curfile) | |
1350 (error | |
1351 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1352 (concat "Error linking: " (prin1-to-string err))) | |
1353 (nnmaildir--unlink tmpfile) | |
1354 (throw 'return nil))) | |
1355 (nnmaildir--unlink tmpfile) | |
1356 (setq article (make-nnmaildir--art :prefix file :suffix ":2,")) | |
1357 (if (nnmaildir--grp-add-art nnmaildir--cur-server group article) | |
1358 (cons gname (nnmaildir--art-num article)))))) | |
1359 | |
1360 (defun nnmaildir-save-mail (group-art) | |
1361 (catch 'return | |
1362 (unless group-art | |
1363 (throw 'return nil)) | |
1364 (let (ga gname x groups nnmaildir--file deactivate-mark) | |
1365 (save-excursion | |
1366 (goto-char (point-min)) | |
1367 (save-match-data | |
1368 (while (looking-at "From ") | |
1369 (replace-match "X-From-Line: ") | |
1370 (forward-line 1)))) | |
1371 (setq groups (nnmaildir--srv-groups nnmaildir--cur-server) | |
1372 ga (car group-art) group-art (cdr group-art) | |
1373 gname (car ga)) | |
1374 (or (intern-soft gname groups) | |
1375 (nnmaildir-request-create-group gname) | |
1376 (throw 'return nil)) ;; not that nnmail bothers to check :( | |
1377 (unless (nnmaildir-request-accept-article gname) | |
1378 (throw 'return nil)) | |
1379 (setq nnmaildir--file (nnmaildir--srv-dir nnmaildir--cur-server) | |
1380 nnmaildir--file (nnmaildir--srvgrp-dir nnmaildir--file gname) | |
1381 x (nnmaildir--prepare nil gname) | |
1382 x (nnmaildir--grp-nlist x) | |
1383 x (cdar x) | |
1384 nnmaildir--file (concat nnmaildir--file | |
1385 (nnmaildir--art-prefix x) | |
1386 (nnmaildir--art-suffix x))) | |
1387 (delq nil | |
1388 (mapcar | |
1389 (lambda (ga) | |
1390 (setq gname (car ga)) | |
1391 (and (or (intern-soft gname groups) | |
1392 (nnmaildir-request-create-group gname)) | |
1393 (nnmaildir-request-accept-article gname) | |
1394 ga)) | |
1395 group-art))))) | |
1396 | |
1397 (defun nnmaildir-active-number (gname) | |
1398 0) | |
1399 | |
1400 (defun nnmaildir-request-expire-articles (ranges &optional gname server force) | |
1401 (let ((no-force (not force)) | |
1402 (group (nnmaildir--prepare server gname)) | |
1403 pgname time boundary bound-iter high low target dir nlist nlist2 | |
1404 stop article didnt nnmaildir--file nnmaildir-article-file-name | |
1405 deactivate-mark) | |
1406 (catch 'return | |
1407 (unless group | |
1408 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1409 (if gname (concat "No such group: " gname) "No current group")) | |
1410 (throw 'return (gnus-uncompress-range ranges))) | |
1411 (setq gname (nnmaildir--grp-name group) | |
1412 pgname (nnmaildir--pgname nnmaildir--cur-server gname)) | |
1413 (if (nnmaildir--param pgname 'read-only) | |
1414 (throw 'return (gnus-uncompress-range ranges))) | |
1415 (setq time (nnmaildir--param pgname 'expire-age)) | |
1416 (unless time | |
1417 (setq time (or (and nnmail-expiry-wait-function | |
1418 (funcall nnmail-expiry-wait-function gname)) | |
1419 nnmail-expiry-wait)) | |
1420 (if (eq time 'immediate) | |
1421 (setq time 0) | |
1422 (if (numberp time) | |
1423 (setq time (* time 86400))))) | |
1424 (when no-force | |
1425 (unless (integerp time) ;; handle 'never | |
1426 (throw 'return (gnus-uncompress-range ranges))) | |
1427 (setq boundary (current-time) | |
1428 high (- (car boundary) (/ time 65536)) | |
1429 low (- (cadr boundary) (% time 65536))) | |
1430 (if (< low 0) | |
1431 (setq low (+ low 65536) | |
1432 high (1- high))) | |
1433 (setcar (cdr boundary) low) | |
1434 (setcar boundary high)) | |
1435 (setq dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1436 dir (nnmaildir--srvgrp-dir dir gname) | |
1437 dir (nnmaildir--cur dir) | |
1438 nlist (nnmaildir--grp-nlist group) | |
1439 ranges (reverse ranges)) | |
1440 (nnmaildir--with-move-buffer | |
1441 (nnmaildir--nlist-iterate | |
1442 nlist ranges | |
1443 (lambda (article) | |
1444 (setq nnmaildir--file (nnmaildir--art-prefix article) | |
1445 nnmaildir--file (concat dir nnmaildir--file | |
1446 (nnmaildir--art-suffix article)) | |
1447 time (file-attributes nnmaildir--file)) | |
1448 (cond | |
1449 ((null time) | |
1450 (nnmaildir--expired-article group article)) | |
1451 ((and no-force | |
1452 (progn | |
1453 (setq time (nth 5 time) | |
1454 bound-iter boundary) | |
1455 (while (and bound-iter time | |
1456 (= (car bound-iter) (car time))) | |
1457 (setq bound-iter (cdr bound-iter) | |
1458 time (cdr time))) | |
1459 (and bound-iter time | |
1460 (car-less-than-car bound-iter time)))) | |
1461 (setq didnt (cons (nnmaildir--art-num article) didnt))) | |
1462 (t | |
1463 (setq nnmaildir-article-file-name nnmaildir--file | |
1464 target (if force nil | |
1465 (save-excursion | |
1466 (save-restriction | |
1467 (nnmaildir--param pgname 'expire-group))))) | |
1468 (when (and (stringp target) | |
1469 (not (string-equal target pgname))) ;; Move it. | |
1470 (erase-buffer) | |
1471 (nnheader-insert-file-contents nnmaildir--file) | |
1472 (gnus-request-accept-article target nil nil 'no-encode)) | |
1473 (if (equal target pgname) | |
1474 ;; Leave it here. | |
1475 (setq didnt (cons (nnmaildir--art-num article) didnt)) | |
1476 (nnmaildir--unlink nnmaildir--file) | |
1477 (nnmaildir--expired-article group article)))))) | |
1478 (erase-buffer)) | |
1479 didnt))) | |
1480 | |
1481 (defun nnmaildir-request-set-mark (gname actions &optional server) | |
1482 (let ((group (nnmaildir--prepare server gname)) | |
1483 (coding-system-for-write nnheader-file-coding-system) | |
1484 (buffer-file-coding-system nil) | |
1485 (file-coding-system-alist nil) | |
1486 del-mark del-action add-action set-action marksdir markfile nlist | |
1487 ranges begin end article all-marks todo-marks did-marks mdir mfile | |
1488 pgname ls permarkfile deactivate-mark) | |
1489 (setq del-mark | |
1490 (lambda (mark) | |
1491 (setq mfile (nnmaildir--subdir marksdir (symbol-name mark)) | |
1492 mfile (concat mfile (nnmaildir--art-prefix article))) | |
1493 (nnmaildir--unlink mfile)) | |
1494 del-action (lambda (article) (mapcar del-mark todo-marks)) | |
1495 add-action | |
1496 (lambda (article) | |
1497 (mapcar | |
1498 (lambda (mark) | |
1499 (setq mdir (nnmaildir--subdir marksdir (symbol-name mark)) | |
1500 permarkfile (concat mdir ":") | |
1501 mfile (concat mdir (nnmaildir--art-prefix article))) | |
1502 (unless (memq mark did-marks) | |
1503 (setq did-marks (cons mark did-marks)) | |
1504 (nnmaildir--mkdir mdir) | |
1505 (unless (file-attributes permarkfile) | |
1506 (condition-case nil | |
1507 (add-name-to-file markfile permarkfile) | |
1508 (file-error | |
1509 ;; AFS can't make hard links in separate directories | |
1510 (write-region "" nil permarkfile nil 'no-message))))) | |
1511 (unless (file-exists-p mfile) | |
1512 (add-name-to-file permarkfile mfile))) | |
1513 todo-marks)) | |
1514 set-action (lambda (article) | |
1515 (funcall add-action) | |
1516 (mapcar (lambda (mark) | |
1517 (unless (memq mark todo-marks) | |
1518 (funcall del-mark mark))) | |
1519 all-marks))) | |
1520 (catch 'return | |
1521 (unless group | |
1522 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1523 (concat "No such group: " gname)) | |
1524 (mapcar (lambda (action) | |
1525 (setq ranges (gnus-range-add ranges (car action)))) | |
1526 actions) | |
1527 (throw 'return ranges)) | |
1528 (setq nlist (nnmaildir--grp-nlist group) | |
1529 marksdir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1530 marksdir (nnmaildir--srvgrp-dir marksdir gname) | |
1531 marksdir (nnmaildir--nndir marksdir) | |
1532 markfile (concat marksdir "markfile") | |
1533 marksdir (nnmaildir--marks-dir marksdir) | |
1534 gname (nnmaildir--grp-name group) | |
1535 pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
1536 ls (nnmaildir--group-ls nnmaildir--cur-server pgname) | |
1537 all-marks (funcall ls marksdir nil "\\`[^.]" 'nosort) | |
1538 all-marks (mapcar 'intern all-marks)) | |
1539 (mapcar | |
1540 (lambda (action) | |
1541 (setq ranges (car action) | |
1542 todo-marks (caddr action)) | |
1543 (mapcar (lambda (mark) (add-to-list 'all-marks mark)) todo-marks) | |
1544 (if (numberp (cdr ranges)) (setq ranges (list ranges))) | |
1545 (nnmaildir--nlist-iterate nlist ranges | |
1546 (cond ((eq 'del (cadr action)) del-action) | |
1547 ((eq 'add (cadr action)) add-action) | |
1548 (t set-action)))) | |
1549 actions) | |
1550 nil))) | |
1551 | |
1552 (defun nnmaildir-close-group (gname &optional server) | |
1553 (let ((group (nnmaildir--prepare server gname)) | |
1554 pgname ls dir msgdir files flist dirs) | |
1555 (if (null group) | |
1556 (progn | |
1557 (setf (nnmaildir--srv-error nnmaildir--cur-server) | |
1558 (concat "No such group: " gname)) | |
1559 nil) | |
1560 (setq pgname (nnmaildir--pgname nnmaildir--cur-server gname) | |
1561 ls (nnmaildir--group-ls nnmaildir--cur-server pgname) | |
1562 dir (nnmaildir--srv-dir nnmaildir--cur-server) | |
1563 dir (nnmaildir--srvgrp-dir dir gname) | |
1564 msgdir (if (nnmaildir--param pgname 'read-only) | |
1565 (nnmaildir--new dir) (nnmaildir--cur dir)) | |
1566 dir (nnmaildir--nndir dir) | |
1567 dirs (cons (nnmaildir--nov-dir dir) | |
1568 (funcall ls (nnmaildir--marks-dir dir) 'full "\\`[^.]" | |
1569 'nosort)) | |
1570 dirs (mapcar | |
1571 (lambda (dir) | |
1572 (cons dir (funcall ls dir nil "\\`[^.]" 'nosort))) | |
1573 dirs) | |
1574 files (funcall ls msgdir nil "\\`[^.]" 'nosort) | |
1575 flist (nnmaildir--up2-1 (length files)) | |
1576 flist (make-vector flist 0)) | |
1577 (save-match-data | |
1578 (mapcar | |
1579 (lambda (file) | |
1580 (string-match "\\`\\([^:]*\\)\\(:.*\\)?\\'" file) | |
1581 (intern (match-string 1 file) flist)) | |
1582 files)) | |
1583 (mapcar | |
1584 (lambda (dir) | |
1585 (setq files (cdr dir) | |
1586 dir (file-name-as-directory (car dir))) | |
1587 (mapcar | |
1588 (lambda (file) | |
1589 (unless (or (intern-soft file flist) (string= file ":")) | |
1590 (setq file (concat dir file)) | |
1591 (delete-file file))) | |
1592 files)) | |
1593 dirs) | |
1594 t))) | |
1595 | |
1596 (defun nnmaildir-close-server (&optional server) | |
1597 (let (flist ls dirs dir files file x) | |
1598 (nnmaildir--prepare server nil) | |
1599 (when nnmaildir--cur-server | |
1600 (setq server nnmaildir--cur-server | |
1601 nnmaildir--cur-server nil) | |
1602 (unintern (nnmaildir--srv-address server) nnmaildir--servers))) | |
1603 t) | |
1604 | |
1605 (defun nnmaildir-request-close () | |
1606 (let (servers buffer) | |
1607 (mapatoms (lambda (server) | |
1608 (setq servers (cons (symbol-name server) servers))) | |
1609 nnmaildir--servers) | |
1610 (mapcar 'nnmaildir-close-server servers) | |
1611 (setq buffer (get-buffer " *nnmaildir work*")) | |
1612 (if buffer (kill-buffer buffer)) | |
1613 (setq buffer (get-buffer " *nnmaildir nov*")) | |
1614 (if buffer (kill-buffer buffer)) | |
1615 (setq buffer (get-buffer " *nnmaildir move*")) | |
1616 (if buffer (kill-buffer buffer))) | |
1617 t) | |
1618 | |
1619 (provide 'nnmaildir) | |
1620 | |
1621 ;; Local Variables: | |
1622 ;; indent-tabs-mode: t | |
1623 ;; fill-column: 77 | |
1624 ;; End: | |
1625 | |
1626 ;;; arch-tag: 0c4e44cd-dfde-4040-888e-5597ec771849 | |
1627 ;;; nnmaildir.el ends here |