1 ;;; nnmaildir.el --- maildir backend for Gnus
4 ;; Author: Paul Jarc <prj@po.cwru.edu>
6 ;; This file is part of GNU Emacs.
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)
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.
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.
25 ;; Maildir format is documented in the maildir(5) man page from qmail
26 ;; (available at <URL:http://multivac.cwru.edu./prj/maildir.5>) and at
27 ;; <URL:http://cr.yp.to/proto/maildir.html>. nnmaildir also stores
28 ;; extra information in the .nnmaildir/ directory within a maildir.
30 ;; Some goals of nnmaildir:
31 ;; * Everything Just Works, and correctly. E.g., stale NOV data is
32 ;; ignored; no need for -generate-nov-databases.
33 ;; * Perfect reliability: [C-g] will never corrupt its data in memory,
34 ;; and SIGKILL will never corrupt its data in the filesystem.
35 ;; * We use the filesystem as a database, so that, e.g., it's easy to
36 ;; manipulate marks from outside Gnus.
37 ;; * All information about a group is stored in the maildir, for easy
38 ;; backup, copying, restoring, etc.
41 ;; * Don't force article renumbering, so nnmaildir can be used with
42 ;; the cache and agent. Alternatively, completely rewrite the Gnus
43 ;; backend interface, which would have other advantages.
45 ;; See also <URL:http://multivac.cwru.edu./nnmaildir/> until that
46 ;; information is added to the Gnus manual.
62 (defconst nnmaildir-version "Gnus")
64 (defvar nnmaildir-article-file-name nil
65 "*The filename of the most recently requested article. This variable is set
66 by nnmaildir-request-article.")
68 ;; The filename of the article being moved/copied:
69 (defvar nnmaildir--file nil)
71 ;; Variables to generate filenames of messages being delivered:
72 (defvar nnmaildir--delivery-time "")
73 (defconst nnmaildir--delivery-pid (number-to-string (emacs-pid)))
74 (defvar nnmaildir--delivery-ct nil)
76 ;; An obarry containing symbols whose names are server names and whose values
78 (defvar nnmaildir--servers (make-vector 3 0))
79 ;; The current server:
80 (defvar nnmaildir--cur-server nil)
82 ;; A server is a vector:
85 "/expanded/path/to/directory/containing/symlinks/to/maildirs/"
86 directory-files-function
87 group-name-transformation-function
88 ;; An obarray containing symbols whose names are group names and whose values
91 ;; A group which has not necessarily been added to the group hash, or nil:
93 current-group ;; or nil
94 "Last error message, or nil"
96 get-new-mail-p ;; Should we split mail from mail-sources?
97 "new/group/creation/directory"]
99 ;; A group is a vector:
101 "prefixed:group.name"
102 ;; Modification times of the "new", and "cur" directories:
105 ;; A vector containing lists of articles:
106 [;; A list of articles, with article numbers in descending order, ending with
109 ;; An obarray containing symbols whose names are filename prefixes and whose
110 ;; values are articles:
112 ;; Same as above, but keyed on Message-ID:
114 ;; An article which has not necessarily been added to the file and msgid
117 ;; A vector containing nil, or articles with NOV data:
119 ;; The index of the next nov-cache entry to be replaced:
121 ;; An obarray containing symbols whose names are mark names and whose values
122 ;; are modtimes of mark directories:
125 ;; An article is a vector:
127 ":2,suffix" ;; or 'expire if expired
130 ;; A NOV data vector, or nil:
131 ["subject\tfrom\tdate"
132 "references\tchars\lines"
135 ;; The value of nnmail-extra-headers when this NOV data was parsed:
138 (defmacro nnmaildir--srv-new () '(make-vector 11 nil))
139 (defmacro nnmaildir--srv-get-name (server) `(aref ,server 0))
140 (defmacro nnmaildir--srv-get-method (server) `(aref ,server 1))
141 (defmacro nnmaildir--srv-get-dir (server) `(aref ,server 2))
142 (defmacro nnmaildir--srv-get-ls (server) `(aref ,server 3))
143 (defmacro nnmaildir--srv-get-groups (server) `(aref ,server 4))
144 (defmacro nnmaildir--srv-get-curgrp (server) `(aref ,server 6))
145 (defmacro nnmaildir--srv-get-error (server) `(aref ,server 7))
146 (defmacro nnmaildir--srv-get-mtime (server) `(aref ,server 8))
147 (defmacro nnmaildir--srv-get-gnm (server) `(aref ,server 9))
148 (defmacro nnmaildir--srv-get-create-dir (server) `(aref ,server 10))
149 (defmacro nnmaildir--srv-set-name (server val) `(aset ,server 0 ,val))
150 (defmacro nnmaildir--srv-set-method (server val) `(aset ,server 1 ,val))
151 (defmacro nnmaildir--srv-set-dir (server val) `(aset ,server 2 ,val))
152 (defmacro nnmaildir--srv-set-ls (server val) `(aset ,server 3 ,val))
153 (defmacro nnmaildir--srv-set-groups (server val) `(aset ,server 4 ,val))
154 (defmacro nnmaildir--srv-set-curgrp (server val) `(aset ,server 6 ,val))
155 (defmacro nnmaildir--srv-set-error (server val) `(aset ,server 7 ,val))
156 (defmacro nnmaildir--srv-set-mtime (server val) `(aset ,server 8 ,val))
157 (defmacro nnmaildir--srv-set-gnm (server val) `(aset ,server 9 ,val))
158 (defmacro nnmaildir--srv-set-create-dir (server val) `(aset ,server 10 ,val))
160 (defmacro nnmaildir--grp-new () '(make-vector 8 nil))
161 (defmacro nnmaildir--grp-get-name (group) `(aref ,group 0))
162 (defmacro nnmaildir--grp-get-pname (group) `(aref ,group 1))
163 (defmacro nnmaildir--grp-get-new (group) `(aref ,group 2))
164 (defmacro nnmaildir--grp-get-cur (group) `(aref ,group 3))
165 (defmacro nnmaildir--grp-get-lists (group) `(aref ,group 4))
166 (defmacro nnmaildir--grp-get-cache (group) `(aref ,group 5))
167 (defmacro nnmaildir--grp-get-index (group) `(aref ,group 6))
168 (defmacro nnmaildir--grp-get-mmth (group) `(aref ,group 7))
169 (defmacro nnmaildir--grp-set-name (group val) `(aset ,group 0 ,val))
170 (defmacro nnmaildir--grp-set-pname (group val) `(aset ,group 1 ,val))
171 (defmacro nnmaildir--grp-set-new (group val) `(aset ,group 2 ,val))
172 (defmacro nnmaildir--grp-set-cur (group val) `(aset ,group 3 ,val))
173 (defmacro nnmaildir--grp-set-lists (group val) `(aset ,group 4 ,val))
174 (defmacro nnmaildir--grp-set-cache (group val) `(aset ,group 5 ,val))
175 (defmacro nnmaildir--grp-set-index (group val) `(aset ,group 6 ,val))
176 (defmacro nnmaildir--grp-set-mmth (group val) `(aset ,group 7 ,val))
178 (defmacro nnmaildir--lists-new () '(make-vector 4 nil))
179 (defmacro nnmaildir--lists-get-nlist (lists) `(aref ,lists 0))
180 (defmacro nnmaildir--lists-get-flist (lists) `(aref ,lists 1))
181 (defmacro nnmaildir--lists-get-mlist (lists) `(aref ,lists 2))
182 (defmacro nnmaildir--lists-get-tmpart (lists) `(aref ,lists 3))
183 (defmacro nnmaildir--lists-set-nlist (lists val) `(aset ,lists 0 ,val))
184 (defmacro nnmaildir--lists-set-flist (lists val) `(aset ,lists 1 ,val))
185 (defmacro nnmaildir--lists-set-mlist (lists val) `(aset ,lists 2 ,val))
186 (defmacro nnmaildir--lists-set-tmpart (lists val) `(aset ,lists 3 ,val))
188 (defmacro nnmaildir--nlist-last-num (list)
189 `(if ,list (nnmaildir--art-get-num (car ,list)) 0))
190 (defmacro nnmaildir--nlist-art (list num)
192 (>= (nnmaildir--art-get-num (car ,list)) ,num)
193 (nth (- (nnmaildir--art-get-num (car ,list)) ,num) ,list)))
194 (defmacro nnmaildir--flist-art (list file)
195 `(symbol-value (intern-soft ,file ,list)))
196 (defmacro nnmaildir--mlist-art (list msgid)
197 `(symbol-value (intern-soft ,msgid ,list)))
199 (defmacro nnmaildir--art-new () '(make-vector 5 nil))
200 (defmacro nnmaildir--art-get-prefix (article) `(aref ,article 0))
201 (defmacro nnmaildir--art-get-suffix (article) `(aref ,article 1))
202 (defmacro nnmaildir--art-get-num (article) `(aref ,article 2))
203 (defmacro nnmaildir--art-get-msgid (article) `(aref ,article 3))
204 (defmacro nnmaildir--art-get-nov (article) `(aref ,article 4))
205 (defmacro nnmaildir--art-set-prefix (article val) `(aset ,article 0 ,val))
206 (defmacro nnmaildir--art-set-suffix (article val) `(aset ,article 1 ,val))
207 (defmacro nnmaildir--art-set-num (article val) `(aset ,article 2 ,val))
208 (defmacro nnmaildir--art-set-msgid (article val) `(aset ,article 3 ,val))
209 (defmacro nnmaildir--art-set-nov (article val) `(aset ,article 4 ,val))
211 (defmacro nnmaildir--nov-new () '(make-vector 5 nil))
212 (defmacro nnmaildir--nov-get-beg (nov) `(aref ,nov 0))
213 (defmacro nnmaildir--nov-get-mid (nov) `(aref ,nov 1))
214 (defmacro nnmaildir--nov-get-end (nov) `(aref ,nov 2))
215 (defmacro nnmaildir--nov-get-mtime (nov) `(aref ,nov 3))
216 (defmacro nnmaildir--nov-get-neh (nov) `(aref ,nov 4))
217 (defmacro nnmaildir--nov-set-beg (nov val) `(aset ,nov 0 ,val))
218 (defmacro nnmaildir--nov-set-mid (nov val) `(aset ,nov 1 ,val))
219 (defmacro nnmaildir--nov-set-end (nov val) `(aset ,nov 2 ,val))
220 (defmacro nnmaildir--nov-set-mtime (nov val) `(aset ,nov 3 ,val))
221 (defmacro nnmaildir--nov-set-neh (nov val) `(aset ,nov 4 ,val))
223 (defmacro nnmaildir--subdir (dir subdir)
224 `(file-name-as-directory (concat ,dir ,subdir)))
225 (defmacro nnmaildir--srv-grp-dir (srv-dir gname)
226 `(nnmaildir--subdir ,srv-dir ,gname))
227 (defmacro nnmaildir--tmp (dir) `(nnmaildir--subdir ,dir "tmp"))
228 (defmacro nnmaildir--new (dir) `(nnmaildir--subdir ,dir "new"))
229 (defmacro nnmaildir--cur (dir) `(nnmaildir--subdir ,dir "cur"))
230 (defmacro nnmaildir--nndir (dir)
231 `(nnmaildir--subdir ,dir ".nnmaildir"))
232 (defmacro nnmaildir--nov-dir (dir)
233 `(nnmaildir--subdir ,dir "nov"))
234 (defmacro nnmaildir--marks-dir (dir)
235 `(nnmaildir--subdir ,dir "marks"))
237 (defun nnmaildir--param (pgname param)
239 (gnus-group-find-parameter pgname param 'allow-list)
240 param (if (vectorp param) (aref param 0) param))
243 (defmacro nnmaildir--with-nntp-buffer (&rest body)
245 (set-buffer nntp-server-buffer)
247 (defmacro nnmaildir--with-work-buffer (&rest body)
249 (set-buffer (get-buffer-create " *nnmaildir work*"))
251 (defmacro nnmaildir--with-nov-buffer (&rest body)
253 (set-buffer (get-buffer-create " *nnmaildir nov*"))
255 (defmacro nnmaildir--with-move-buffer (&rest body)
257 (set-buffer (get-buffer-create " *nnmaildir move*"))
260 (defmacro nnmaildir--unlink (file-arg)
261 `(let ((file ,file-arg))
262 (if (file-attributes file) (delete-file file))))
263 (defun nnmaildir--mkdir (dir)
264 (or (file-exists-p (file-name-as-directory dir))
265 (make-directory-internal (directory-file-name dir))))
267 (defun nnmaildir--prepare (server group)
271 (or (setq server nnmaildir--cur-server)
273 (or (setq server (intern-soft server nnmaildir--servers))
275 (setq server (symbol-value server)
276 nnmaildir--cur-server server))
277 (or (setq groups (nnmaildir--srv-get-groups server))
279 (or (nnmaildir--srv-get-method server)
280 (setq x (concat "nnmaildir:" (nnmaildir--srv-get-name server))
281 x (gnus-server-to-method x)
282 x (or x (throw 'return nil))
283 x (nnmaildir--srv-set-method server x)))
285 (or (setq group (nnmaildir--srv-get-curgrp server))
287 (or (setq group (intern-soft group groups))
289 (setq group (symbol-value group)))
292 (defun nnmaildir--update-nov (srv-dir group article)
293 (let ((nnheader-file-coding-system 'binary)
294 dir gname pgname msgdir prefix suffix file attr mtime novdir novfile
295 nov msgid nov-beg nov-mid nov-end field pos extra val old-neh new-neh
298 (setq suffix (nnmaildir--art-get-suffix article))
299 (if (stringp suffix) nil
300 (nnmaildir--art-set-nov article nil)
302 (setq gname (nnmaildir--grp-get-name group)
303 pgname (nnmaildir--grp-get-pname group)
304 dir (nnmaildir--srv-grp-dir srv-dir gname)
305 msgdir (if (nnmaildir--param pgname 'read-only)
306 (nnmaildir--new dir) (nnmaildir--cur dir))
307 prefix (nnmaildir--art-get-prefix article)
308 file (concat msgdir prefix suffix)
309 attr (file-attributes file))
311 (nnmaildir--art-set-suffix article 'expire)
312 (nnmaildir--art-set-nov article nil)
314 (setq mtime (nth 5 attr)
316 nov (nnmaildir--art-get-nov article)
317 novdir (nnmaildir--nov-dir (nnmaildir--nndir dir))
318 novfile (concat novdir prefix))
319 (nnmaildir--with-nov-buffer
320 (when (file-exists-p novfile) ;; If not, force reparsing the message.
321 (if nov nil ;; It's already in memory.
322 ;; Else read the data from the NOV file.
324 (nnheader-insert-file-contents novfile)
325 (setq nov (read (current-buffer)))
326 (nnmaildir--art-set-msgid article (car nov))
327 (setq nov (cadr nov)))
328 ;; If the NOV's modtime matches the file's current modtime,
329 ;; and it has the right length (i.e., it wasn't produced by
330 ;; a too-much older version of nnmaildir), then we may use
331 ;; this NOV data rather than parsing the message file,
332 ;; unless nnmail-extra-headers has been augmented since this
333 ;; data was last parsed.
334 (when (and (equal mtime (nnmaildir--nov-get-mtime nov))
335 (= (length nov) (length (nnmaildir--nov-new))))
336 ;; This NOV data is potentially up-to-date.
337 (setq old-neh (nnmaildir--nov-get-neh nov)
338 new-neh nnmail-extra-headers)
339 (if (equal new-neh old-neh) (throw 'return nov)) ;; Common case.
340 ;; They're not equal, but maybe the new is a subset of the old...
341 (if (null new-neh) (throw 'return nov))
343 (if (memq (car new-neh) old-neh)
345 (setq new-neh (cdr new-neh))
346 (if new-neh nil (throw 'return nov)))
347 (setq new-neh nil)))))
348 ;; Parse the NOV data out of the message.
350 (nnheader-insert-file-contents file)
352 (goto-char (point-min))
354 (if (search-forward "\n\n" nil 'noerror)
356 (setq nov-mid (count-lines (point) (point-max)))
357 (narrow-to-region (point-min) (1- (point))))
359 (goto-char (point-min))
361 (nnheader-fold-continuation-lines)
362 (setq nov (nnheader-parse-head 'naked)
363 field (or (mail-header-lines nov) 0)))
364 (if (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:)) nil
365 (setq nov-mid field))
366 (setq nov-mid (number-to-string nov-mid)
367 nov-mid (concat (number-to-string attr) "\t" nov-mid)
368 field (or (mail-header-references nov) "")
371 (while (string-match "\t" field pos)
372 (aset field (match-beginning 0) ? )
373 (setq pos (match-end 0)))
374 (setq nov-mid (concat field "\t" nov-mid)
375 extra (mail-header-extra nov)
378 (setq field (car extra) extra (cdr extra)
379 val (cdr field) field (symbol-name (car field))
381 (while (string-match "\t" field pos)
382 (aset field (match-beginning 0) ? )
383 (setq pos (match-end 0)))
385 (while (string-match "\t" val pos)
386 (aset val (match-beginning 0) ? )
387 (setq pos (match-end 0)))
388 (setq nov-end (concat nov-end "\t" field ": " val)))
389 (setq nov-end (if (zerop (length nov-end)) "" (substring nov-end 1))
390 field (or (mail-header-subject nov) "")
392 (while (string-match "\t" field pos)
393 (aset field (match-beginning 0) ? )
394 (setq pos (match-end 0)))
396 field (or (mail-header-from nov) "")
398 (while (string-match "\t" field pos)
399 (aset field (match-beginning 0) ? )
400 (setq pos (match-end 0)))
401 (setq nov-beg (concat nov-beg "\t" field)
402 field (or (mail-header-date nov) "")
404 (while (string-match "\t" field pos)
405 (aset field (match-beginning 0) ? )
406 (setq pos (match-end 0)))
407 (setq nov-beg (concat nov-beg "\t" field)
408 field (mail-header-id nov)
410 (while (string-match "\t" field pos)
411 (aset field (match-beginning 0) ? )
412 (setq pos (match-end 0)))
414 (if (or (null msgid) (nnheader-fake-message-id-p msgid))
415 (setq msgid (concat "<" prefix "@nnmaildir>")))
417 (setq nov (nnmaildir--nov-new))
418 (nnmaildir--nov-set-beg nov nov-beg)
419 (nnmaildir--nov-set-mid nov nov-mid)
420 (nnmaildir--nov-set-end nov nov-end)
421 (nnmaildir--nov-set-mtime nov mtime)
422 (nnmaildir--nov-set-neh nov (copy-sequence nnmail-extra-headers))
423 (prin1 (list msgid nov) (current-buffer))
424 (setq file (concat novfile ":"))
425 (nnmaildir--unlink file)
426 (write-region (point-min) (point-max) file nil 'no-message))
427 (rename-file file novfile 'replace)
428 (nnmaildir--art-set-msgid article msgid)
431 (defun nnmaildir--cache-nov (group article nov)
432 (let ((cache (nnmaildir--grp-get-cache group))
433 (index (nnmaildir--grp-get-index group))
435 (if (nnmaildir--art-get-nov article) nil
436 (setq goner (aref cache index))
437 (if goner (nnmaildir--art-set-nov goner nil))
438 (aset cache index article)
439 (nnmaildir--grp-set-index group (% (1+ index) (length cache))))
440 (nnmaildir--art-set-nov article nov)))
442 (defun nnmaildir--grp-add-art (srv-dir group article)
443 (let ((nov (nnmaildir--update-nov srv-dir group article))
446 (setq old-lists (nnmaildir--grp-get-lists group)
447 new-lists (nnmaildir--lists-new))
448 (nnmaildir--lists-set-nlist
449 new-lists (cons article (nnmaildir--lists-get-nlist old-lists)))
450 (nnmaildir--lists-set-flist new-lists
451 (nnmaildir--lists-get-flist old-lists))
452 (nnmaildir--lists-set-mlist new-lists
453 (nnmaildir--lists-get-mlist old-lists))
454 (let ((inhibit-quit t))
455 (nnmaildir--grp-set-lists group new-lists)
456 (set (intern (nnmaildir--art-get-prefix article)
457 (nnmaildir--lists-get-flist new-lists))
459 (set (intern (nnmaildir--art-get-msgid article)
460 (nnmaildir--lists-get-mlist new-lists))
462 (nnmaildir--cache-nov group article nov)
465 (defun nnmaildir--group-ls (server pgname)
466 (or (nnmaildir--param pgname 'directory-files)
467 (nnmaildir--srv-get-ls server)))
469 (defun nnmaildir--article-count (group)
472 (setq group (nnmaildir--grp-get-lists group)
473 group (nnmaildir--lists-get-nlist group))
475 (if (stringp (nnmaildir--art-get-suffix (car group)))
477 min (nnmaildir--art-get-num (car group))))
478 (setq group (cdr group)))
481 (defun nnmaildir-article-number-to-file-name
482 (number group-name server-address-string)
483 (let ((group (nnmaildir--prepare server-address-string group-name))
484 list article suffix dir filename)
487 ;; The given group or server does not exist.
489 (setq list (nnmaildir--grp-get-lists group)
490 list (nnmaildir--lists-get-nlist list)
491 article (nnmaildir--nlist-art list number))
493 ;; The given article number does not exist in this group.
495 (setq suffix (nnmaildir--art-get-suffix article))
496 (if (not (stringp suffix))
497 ;; The article has expired.
499 (setq dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
500 dir (nnmaildir--srv-grp-dir dir group-name)
501 group (if (nnmaildir--param (nnmaildir--grp-get-pname group)
503 (nnmaildir--new dir) (nnmaildir--cur dir))
504 filename (concat group (nnmaildir--art-get-prefix article) suffix))
505 (if (file-exists-p filename)
507 ;; The article disappeared out from under us.
508 (nnmaildir--art-set-suffix article 'expire)
509 (nnmaildir--art-set-nov article nil)
512 (defun nnmaildir-article-number-to-base-name
513 (number group-name server-address-string)
514 (let ((group (nnmaildir--prepare server-address-string group-name))
515 list article suffix dir filename)
518 ;; The given group or server does not exist.
520 (setq list (nnmaildir--grp-get-lists group)
521 list (nnmaildir--lists-get-nlist list)
522 article (nnmaildir--nlist-art list number))
524 ;; The given article number does not exist in this group.
526 (setq suffix (nnmaildir--art-get-suffix article))
527 (if (not (stringp suffix))
528 ;; The article has expired.
530 (cons (nnmaildir--art-get-prefix article) suffix))))
532 (defun nnmaildir-base-name-to-article-number
533 (base-name group-name server-address-string)
534 (let ((group (nnmaildir--prepare server-address-string group-name))
535 list article suffix dir filename)
538 ;; The given group or server does not exist.
540 (setq list (nnmaildir--grp-get-lists group)
541 list (nnmaildir--lists-get-flist list)
542 article (nnmaildir--flist-art list base-name))
544 ;; The given article number does not exist in this group.
546 (nnmaildir--art-get-num article))))
548 (defun nnmaildir-request-type (group &optional article)
551 (defun nnmaildir-status-message (&optional server)
552 (nnmaildir--prepare server nil)
553 (nnmaildir--srv-get-error nnmaildir--cur-server))
555 (defun nnmaildir-server-opened (&optional server)
556 (and nnmaildir--cur-server
559 (nnmaildir--srv-get-name nnmaildir--cur-server))
561 (nnmaildir--srv-get-groups nnmaildir--cur-server)
564 (defun nnmaildir-open-server (server &optional defs)
568 (setq server (intern-soft x nnmaildir--servers))
570 (and (setq server (symbol-value server))
571 (nnmaildir--srv-get-groups server)
572 (setq nnmaildir--cur-server server)
574 (setq server (nnmaildir--srv-new))
575 (nnmaildir--srv-set-name server x)
576 (let ((inhibit-quit t))
577 (set (intern x nnmaildir--servers) server)))
578 (setq dir (assq 'directory defs))
580 (nnmaildir--srv-set-error
581 server "You must set \"directory\" in the select method")
585 dir (expand-file-name dir)
586 dir (file-name-as-directory dir))
587 (if (file-exists-p dir) nil
588 (nnmaildir--srv-set-error server (concat "No such directory: " dir))
590 (nnmaildir--srv-set-dir server dir)
591 (setq x (assq 'directory-files defs))
593 (setq x (symbol-function (if nnheader-directory-files-is-safe
595 'nnheader-directory-files-safe)))
597 (if (functionp x) nil
598 (nnmaildir--srv-set-error
599 server (concat "Not a function: " (prin1-to-string x)))
600 (throw 'return nil)))
601 (nnmaildir--srv-set-ls server x)
602 (setq x (funcall x dir nil "\\`[^.]" 'nosort)
605 (while (<= size x) (setq size (* 2 size)))
606 (if (/= size 1) (setq size (1- size)))
607 (and (setq x (assq 'get-new-mail defs))
610 (nnmaildir--srv-set-gnm server t)
612 (setq x (assq 'create-directory defs))
616 (nnmaildir--srv-set-create-dir server x))
617 (nnmaildir--srv-set-groups server (make-vector size 0))
618 (setq nnmaildir--cur-server server)
621 (defun nnmaildir--parse-filename (file)
622 (let ((prefix (car file))
625 "\\`\\([0-9]+\\)\\.\\([0-9]+\\)\\(_\\([0-9]+\\)\\)?\\(\\..*\\)\\'"
628 (setq timestamp (concat "0000" (match-string 1 prefix))
629 len (- (length timestamp) 4))
630 (vector (string-to-number (substring timestamp 0 len))
631 (string-to-number (substring timestamp len))
632 (string-to-number (match-string 2 prefix))
633 (string-to-number (or (match-string 4 prefix) "-1"))
634 (match-string 5 prefix)
638 (defun nnmaildir--sort-files (a b)
641 (throw 'return (and (consp b) (string-lessp (car a) (car b)))))
642 (if (consp b) (throw 'return t))
643 (if (< (aref a 0) (aref b 0)) (throw 'return t))
644 (if (> (aref a 0) (aref b 0)) (throw 'return nil))
645 (if (< (aref a 1) (aref b 1)) (throw 'return t))
646 (if (> (aref a 1) (aref b 1)) (throw 'return nil))
647 (if (< (aref a 2) (aref b 2)) (throw 'return t))
648 (if (> (aref a 2) (aref b 2)) (throw 'return nil))
649 (if (< (aref a 3) (aref b 3)) (throw 'return t))
650 (if (> (aref a 3) (aref b 3)) (throw 'return nil))
651 (string-lessp (aref a 4) (aref b 4))))
653 (defun nnmaildir--scan (gname scan-msgs groups method srv-dir srv-ls)
655 (let ((36h-ago (- (car (current-time)) 2))
656 absdir nndir tdir ndir cdir nattr cattr isnew pgname read-only ls
657 files file num dir flist group x)
658 (setq absdir (nnmaildir--srv-grp-dir srv-dir gname)
659 nndir (nnmaildir--nndir absdir))
660 (if (file-exists-p absdir) nil
661 (nnmaildir--srv-set-error nnmaildir--cur-server
662 (concat "No such directory: " absdir))
664 (setq tdir (nnmaildir--tmp absdir)
665 ndir (nnmaildir--new absdir)
666 cdir (nnmaildir--cur absdir)
667 nattr (file-attributes ndir)
668 cattr (file-attributes cdir))
669 (if (and (file-exists-p tdir) nattr cattr) nil
670 (nnmaildir--srv-set-error nnmaildir--cur-server
671 (concat "Not a maildir: " absdir))
673 (setq group (nnmaildir--prepare nil gname))
676 pgname (nnmaildir--grp-get-pname group))
678 group (nnmaildir--grp-new)
679 pgname (gnus-group-prefixed-name gname method))
680 (nnmaildir--grp-set-name group gname)
681 (nnmaildir--grp-set-pname group pgname)
682 (nnmaildir--grp-set-lists group (nnmaildir--lists-new))
683 (nnmaildir--grp-set-index group 0)
684 (nnmaildir--mkdir nndir)
685 (nnmaildir--mkdir (nnmaildir--nov-dir nndir))
686 (nnmaildir--mkdir (nnmaildir--marks-dir nndir))
687 (write-region "" nil (concat nndir "markfile") nil 'no-message))
688 (setq read-only (nnmaildir--param pgname 'read-only)
689 ls (or (nnmaildir--param pgname 'directory-files) srv-ls))
691 (setq x (nth 11 (file-attributes tdir)))
692 (if (and (= x (nth 11 nattr)) (= x (nth 11 cattr))) nil
693 (nnmaildir--srv-set-error nnmaildir--cur-server
694 (concat "Maildir spans filesystems: "
697 (setq files (funcall ls tdir 'full "\\`[^.]" 'nosort))
699 (setq file (car files) files (cdr files)
700 x (file-attributes file))
701 (if (or (< 1 (cadr x)) (> 36h-ago (car (nth 4 x))))
702 (delete-file file))))
706 (setq nattr (nth 5 nattr))
707 (if (equal nattr (nnmaildir--grp-get-new group))
709 (if read-only (setq dir (and (or isnew nattr) ndir))
710 (when (or isnew nattr)
711 (setq files (funcall ls ndir nil "\\`[^.]" 'nosort))
713 (setq file (car files) files (cdr files))
714 (rename-file (concat ndir file) (concat cdir file ":2,")))
715 (nnmaildir--grp-set-new group nattr))
716 (setq cattr (nth 5 (file-attributes cdir)))
717 (if (equal cattr (nnmaildir--grp-get-cur group))
719 (setq dir (and (or isnew cattr) cdir)))
720 (if dir nil (throw 'return t))
721 (setq files (funcall ls dir nil "\\`[^.]" 'nosort))
723 (setq x (length files)
725 (while (<= num x) (setq num (* 2 num)))
726 (if (/= num 1) (setq num (1- num)))
727 (setq x (nnmaildir--grp-get-lists group))
728 (nnmaildir--lists-set-flist x (make-vector num 0))
729 (nnmaildir--lists-set-mlist x (make-vector num 0))
730 (nnmaildir--grp-set-mmth group (make-vector 1 0))
731 (setq num (nnmaildir--param pgname 'nov-cache-size))
732 (if (numberp num) (if (< num 1) (setq num 1))
735 cdir (nnmaildir--marks-dir nndir)
736 ndir (nnmaildir--subdir cdir "tick")
737 cdir (nnmaildir--subdir cdir "read"))
739 (setq file (car x) x (cdr x))
740 (string-match "\\`\\([^:]*\\)\\(\\(:.*\\)?\\)\\'" file)
741 (setq file (match-string 1 file))
742 (if (or (not (file-exists-p (concat cdir file)))
743 (file-exists-p (concat ndir file)))
744 (setq num (1+ num)))))
745 (nnmaildir--grp-set-cache group (make-vector num nil))
746 (let ((inhibit-quit t))
747 (set (intern gname groups) group))
748 (or scan-msgs (throw 'return t)))
749 (setq flist (nnmaildir--grp-get-lists group)
750 num (nnmaildir--lists-get-nlist flist)
751 flist (nnmaildir--lists-get-flist flist)
752 num (nnmaildir--nlist-last-num num)
756 (setq file (car x) x (cdr x))
757 (string-match "\\`\\([^:]*\\)\\(\\(:.*\\)?\\)\\'" file)
758 (setq file (cons (match-string 1 file) (match-string 2 file)))
759 (if (nnmaildir--flist-art flist (car file)) nil
760 (setq files (cons file files))))
761 (setq files (mapcar 'nnmaildir--parse-filename files)
762 files (sort files 'nnmaildir--sort-files))
764 (setq file (car files) files (cdr files)
765 file (if (consp file) file (aref file 5))
766 x (nnmaildir--art-new))
767 (nnmaildir--art-set-prefix x (car file))
768 (nnmaildir--art-set-suffix x (cdr file))
769 (nnmaildir--art-set-num x (1+ num))
770 (if (nnmaildir--grp-add-art srv-dir group x)
771 (setq num (1+ num))))
772 (if read-only (nnmaildir--grp-set-new group nattr)
773 (nnmaildir--grp-set-cur group cattr)))
776 (defun nnmaildir-request-scan (&optional scan-group server)
777 (let ((coding-system-for-write nnheader-file-coding-system)
778 (buffer-file-coding-system nil)
779 (file-coding-system-alist nil)
780 (nnmaildir-get-new-mail t)
781 (nnmaildir-group-alist nil)
782 (nnmaildir-active-file nil)
783 x srv-ls srv-dir method groups group dirs grp-dir seen deactivate-mark)
784 (nnmaildir--prepare server nil)
785 (setq srv-ls (nnmaildir--srv-get-ls nnmaildir--cur-server)
786 srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
787 method (nnmaildir--srv-get-method nnmaildir--cur-server)
788 groups (nnmaildir--srv-get-groups nnmaildir--cur-server))
789 (nnmaildir--with-work-buffer
791 (if (stringp scan-group)
792 (if (nnmaildir--scan scan-group t groups method srv-dir srv-ls)
793 (if (nnmaildir--srv-get-gnm nnmaildir--cur-server)
794 (nnmail-get-new-mail 'nnmaildir nil nil scan-group))
795 (unintern scan-group groups))
796 (setq x (nth 5 (file-attributes srv-dir)))
797 (if (equal x (nnmaildir--srv-get-mtime nnmaildir--cur-server))
799 (mapatoms (lambda (sym)
800 (nnmaildir--scan (symbol-name sym) t groups
801 method srv-dir srv-ls))
803 (setq dirs (funcall srv-ls srv-dir nil "\\`[^.]" 'nosort)
806 (while (<= seen x) (setq seen (* 2 seen)))
807 (if (/= seen 1) (setq seen (1- seen)))
808 (setq seen (make-vector seen 0)
809 scan-group (null scan-group))
811 (setq grp-dir (car dirs) dirs (cdr dirs))
812 (if (nnmaildir--scan grp-dir scan-group groups method srv-dir
814 (intern grp-dir seen)))
816 (mapatoms (lambda (group)
817 (setq group (symbol-name group))
818 (if (intern-soft group seen) nil
819 (setq x (cons group x))))
822 (unintern (car x) groups)
824 (nnmaildir--srv-set-mtime nnmaildir--cur-server
825 (nth 5 (file-attributes srv-dir))))
826 (if (nnmaildir--srv-get-gnm nnmaildir--cur-server)
827 (nnmail-get-new-mail 'nnmaildir nil nil))))))
830 (defun nnmaildir-request-list (&optional server)
831 (nnmaildir-request-scan 'find-new-groups server)
832 (let (pgname ro ct-min deactivate-mark)
833 (nnmaildir--prepare server nil)
834 (nnmaildir--with-nntp-buffer
836 (mapatoms (lambda (group)
837 (setq group (symbol-value group)
838 ro (nnmaildir--param (nnmaildir--grp-get-pname group)
840 ct-min (nnmaildir--article-count group))
841 (insert (nnmaildir--grp-get-name group) " ")
842 (princ (nnmaildir--nlist-last-num
843 (nnmaildir--lists-get-nlist
844 (nnmaildir--grp-get-lists group)))
847 (princ (cdr ct-min) nntp-server-buffer)
848 (insert " " (if ro "n" "y") "\n"))
849 (nnmaildir--srv-get-groups nnmaildir--cur-server))))
852 (defun nnmaildir-request-newgroups (date &optional server)
853 (nnmaildir-request-list server))
855 (defun nnmaildir-retrieve-groups (groups &optional server)
856 (let (gname group ct-min deactivate-mark)
857 (nnmaildir--prepare server nil)
858 (nnmaildir--with-nntp-buffer
861 (setq gname (car groups) groups (cdr groups))
862 (nnmaildir-request-scan gname server)
863 (setq group (nnmaildir--prepare nil gname))
864 (if (null group) (insert "411 no such news group\n")
865 (setq ct-min (nnmaildir--article-count group))
867 (princ (car ct-min) nntp-server-buffer)
869 (princ (cdr ct-min) nntp-server-buffer)
871 (princ (nnmaildir--nlist-last-num
872 (nnmaildir--lists-get-nlist
873 (nnmaildir--grp-get-lists group)))
875 (insert " " gname "\n")))))
878 (defun nnmaildir-request-update-info (gname info &optional server)
879 (nnmaildir-request-scan gname server)
880 (let ((group (nnmaildir--prepare server gname))
881 pgname nlist flist last always-marks never-marks old-marks dotfile num
882 dir markdirs marks mark ranges articles article read end new-marks ls
883 old-mmth new-mmth mtime mark-sym deactivate-mark)
886 (nnmaildir--srv-set-error nnmaildir--cur-server
887 (concat "No such group: " gname))
889 (setq gname (nnmaildir--grp-get-name group)
890 pgname (nnmaildir--grp-get-pname group)
891 nlist (nnmaildir--grp-get-lists group)
892 flist (nnmaildir--lists-get-flist nlist)
893 nlist (nnmaildir--lists-get-nlist nlist))
895 (gnus-info-set-read info nil)
896 (gnus-info-set-marks info nil 'extend)
897 (throw 'return info))
898 (setq old-marks (cons 'read (gnus-info-read info))
899 old-marks (cons old-marks (gnus-info-marks info))
900 last (nnmaildir--nlist-last-num nlist)
901 always-marks (nnmaildir--param pgname 'always-marks)
902 never-marks (nnmaildir--param pgname 'never-marks)
903 dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
904 dir (nnmaildir--srv-grp-dir dir gname)
905 dir (nnmaildir--nndir dir)
906 dir (nnmaildir--marks-dir dir)
907 ls (nnmaildir--group-ls nnmaildir--cur-server pgname)
908 markdirs (funcall ls dir nil "\\`[^.]" 'nosort)
909 num (length markdirs)
911 (while (<= new-mmth num) (setq new-mmth (* 2 new-mmth)))
912 (if (/= new-mmth 1) (setq new-mmth (1- new-mmth)))
913 (setq new-mmth (make-vector new-mmth 0)
914 old-mmth (nnmaildir--grp-get-mmth group))
916 (setq mark (car markdirs) markdirs (cdr markdirs)
917 articles (nnmaildir--subdir dir mark)
918 mark-sym (intern mark)
921 (if (memq mark-sym never-marks) (throw 'got-ranges nil))
922 (when (memq mark-sym always-marks)
923 (setq ranges (list (cons 1 last)))
924 (throw 'got-ranges nil))
925 (setq mtime (nth 5 (file-attributes articles)))
926 (set (intern mark new-mmth) mtime)
927 (when (equal mtime (symbol-value (intern-soft mark old-mmth)))
928 (setq ranges (assq mark-sym old-marks))
929 (if ranges (setq ranges (cdr ranges)))
930 (throw 'got-ranges nil))
931 (setq articles (funcall ls articles nil "\\`[^.]" 'nosort))
933 (setq article (car articles) articles (cdr articles)
934 article (nnmaildir--flist-art flist article))
936 (setq num (nnmaildir--art-get-num article)
937 ranges (gnus-add-to-range ranges (list num))))))
938 (if (eq mark-sym 'read) (setq read ranges)
939 (if ranges (setq marks (cons (cons mark-sym ranges) marks)))))
940 (gnus-info-set-read info read)
941 (gnus-info-set-marks info marks 'extend)
942 (nnmaildir--grp-set-mmth group new-mmth)
945 (defun nnmaildir-request-group (gname &optional server fast)
946 (nnmaildir-request-scan gname server)
947 (let ((group (nnmaildir--prepare server gname))
948 ct-min deactivate-mark)
949 (nnmaildir--with-nntp-buffer
953 (insert "411 no such news group\n")
954 (nnmaildir--srv-set-error nnmaildir--cur-server
955 (concat "No such group: " gname))
957 (nnmaildir--srv-set-curgrp nnmaildir--cur-server group)
958 (if fast (throw 'return t))
959 (setq ct-min (nnmaildir--article-count group))
961 (princ (car ct-min) nntp-server-buffer)
963 (princ (cdr ct-min) nntp-server-buffer)
965 (princ (nnmaildir--nlist-last-num
966 (nnmaildir--lists-get-nlist
967 (nnmaildir--grp-get-lists group)))
969 (insert " " gname "\n")
972 (defun nnmaildir-request-create-group (gname &optional server args)
973 (nnmaildir--prepare server nil)
975 (let ((create-dir (nnmaildir--srv-get-create-dir nnmaildir--cur-server))
977 (when (zerop (length gname))
978 (nnmaildir--srv-set-error nnmaildir--cur-server
979 "Invalid (empty) group name")
981 (when (eq (aref "." 0) (aref gname 0))
982 (nnmaildir--srv-set-error nnmaildir--cur-server
983 "Group names may not start with \".\"")
985 (when (save-match-data (string-match "[\0/\t]" gname))
986 (nnmaildir--srv-set-error nnmaildir--cur-server
987 (concat "Illegal characters (null, tab, or /) in group name: "
990 (setq groups (nnmaildir--srv-get-groups nnmaildir--cur-server))
991 (when (intern-soft gname groups)
992 (nnmaildir--srv-set-error nnmaildir--cur-server
993 (concat "Group already exists: " gname))
995 (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server))
996 (if (file-name-absolute-p create-dir)
997 (setq dir (expand-file-name create-dir))
999 dir (file-truename dir)
1000 dir (concat dir create-dir)))
1001 (setq dir (nnmaildir--subdir (file-name-as-directory dir) gname))
1002 (nnmaildir--mkdir dir)
1003 (nnmaildir--mkdir (nnmaildir--tmp dir))
1004 (nnmaildir--mkdir (nnmaildir--new dir))
1005 (nnmaildir--mkdir (nnmaildir--cur dir))
1006 (setq create-dir (file-name-as-directory create-dir))
1007 (make-symbolic-link (concat create-dir gname) (concat srv-dir gname))
1008 (nnmaildir-request-scan 'find-new-groups))))
1010 (defun nnmaildir-request-rename-group (gname new-name &optional server)
1011 (let ((group (nnmaildir--prepare server gname))
1012 (coding-system-for-write nnheader-file-coding-system)
1013 (buffer-file-coding-system nil)
1014 (file-coding-system-alist nil)
1018 (nnmaildir--srv-set-error nnmaildir--cur-server
1019 (concat "No such group: " gname))
1020 (throw 'return nil))
1021 (when (zerop (length new-name))
1022 (nnmaildir--srv-set-error nnmaildir--cur-server
1023 "Invalid (empty) group name")
1024 (throw 'return nil))
1025 (when (eq (aref "." 0) (aref new-name 0))
1026 (nnmaildir--srv-set-error nnmaildir--cur-server
1027 "Group names may not start with \".\"")
1028 (throw 'return nil))
1029 (when (save-match-data (string-match "[\0/\t]" new-name))
1030 (nnmaildir--srv-set-error nnmaildir--cur-server
1031 (concat "Illegal characters (null, tab, or /) in group name: "
1033 (throw 'return nil))
1034 (if (string-equal gname new-name) (throw 'return t))
1035 (when (intern-soft new-name
1036 (nnmaildir--srv-get-groups nnmaildir--cur-server))
1037 (nnmaildir--srv-set-error nnmaildir--cur-server
1038 (concat "Group already exists: " new-name))
1039 (throw 'return nil))
1040 (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server))
1042 (rename-file (concat srv-dir gname)
1043 (concat srv-dir new-name))
1045 (nnmaildir--srv-set-error nnmaildir--cur-server
1046 (concat "Error renaming link: "
1047 (prin1-to-string err)))
1048 (throw 'return nil)))
1049 (setq x (nnmaildir--srv-get-groups nnmaildir--cur-server)
1050 groups (make-vector (length x) 0))
1051 (mapatoms (lambda (sym)
1052 (if (eq (symbol-value sym) group) nil
1053 (set (intern (symbol-name sym) groups)
1054 (symbol-value sym))))
1056 (setq group (copy-sequence group))
1057 (nnmaildir--grp-set-name group new-name)
1058 (set (intern new-name groups) group)
1059 (nnmaildir--srv-set-groups nnmaildir--cur-server groups)
1062 (defun nnmaildir-request-delete-group (gname force &optional server)
1063 (let ((group (nnmaildir--prepare server gname))
1064 pgname grp-dir dir dirs files ls deactivate-mark)
1067 (nnmaildir--srv-set-error nnmaildir--cur-server
1068 (concat "No such group: " gname))
1069 (throw 'return nil))
1070 (if (eq group (nnmaildir--srv-get-curgrp nnmaildir--cur-server))
1071 (nnmaildir--srv-set-curgrp nnmaildir--cur-server nil))
1072 (setq gname (nnmaildir--grp-get-name group)
1073 pgname (nnmaildir--grp-get-pname group))
1074 (unintern gname (nnmaildir--srv-get-groups nnmaildir--cur-server))
1075 (setq grp-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1076 grp-dir (nnmaildir--srv-grp-dir grp-dir gname))
1077 (if (not force) (setq grp-dir (directory-file-name grp-dir))
1078 (if (nnmaildir--param pgname 'read-only)
1079 (progn (delete-directory (nnmaildir--tmp grp-dir))
1080 (nnmaildir--unlink (nnmaildir--new grp-dir))
1081 (delete-directory (nnmaildir--cur grp-dir)))
1082 (nnmaildir--with-work-buffer
1084 (setq ls (nnmaildir--group-ls nnmaildir--cur-server pgname)
1085 files (funcall ls (nnmaildir--tmp grp-dir) 'full "\\`[^.]"
1088 (delete-file (car files))
1089 (setq files (cdr files)))
1090 (delete-directory (nnmaildir--tmp grp-dir))
1091 (setq files (funcall ls (nnmaildir--new grp-dir) 'full "\\`[^.]"
1094 (delete-file (car files))
1095 (setq files (cdr files)))
1096 (delete-directory (nnmaildir--new grp-dir))
1097 (setq files (funcall ls (nnmaildir--cur grp-dir) 'full "\\`[^.]"
1100 (delete-file (car files))
1101 (setq files (cdr files)))
1102 (delete-directory (nnmaildir--cur grp-dir))))
1103 (setq dir (nnmaildir--nndir grp-dir)
1104 dirs (cons (nnmaildir--nov-dir dir)
1105 (funcall ls (nnmaildir--marks-dir dir) 'full "\\`[^.]"
1108 (setq dir (car dirs) dirs (cdr dirs)
1109 files (funcall ls dir 'full "\\`[^.]" 'nosort))
1111 (delete-file (car files))
1112 (setq files (cdr files)))
1113 (delete-directory dir))
1114 (setq dir (nnmaildir--nndir grp-dir)
1115 files (concat dir "markfile"))
1116 (nnmaildir--unlink files)
1117 (delete-directory (nnmaildir--marks-dir dir))
1118 (delete-directory dir)
1119 (setq grp-dir (directory-file-name grp-dir)
1120 dir (car (file-attributes grp-dir)))
1121 (if (eq (aref "/" 0) (aref dir 0)) nil
1122 (setq dir (concat (file-truename
1123 (nnmaildir--srv-get-dir nnmaildir--cur-server))
1125 (delete-directory dir))
1126 (nnmaildir--unlink grp-dir)
1129 (defun nnmaildir-retrieve-headers (articles &optional gname server fetch-old)
1130 (let ((group (nnmaildir--prepare server gname))
1131 srv-dir dir nlist mlist article num stop nov nlist2 deactivate-mark)
1134 (nnmaildir--srv-set-error nnmaildir--cur-server
1135 (if gname (concat "No such group: " gname)
1136 "No current group"))
1137 (throw 'return nil))
1138 (nnmaildir--with-nntp-buffer
1140 (setq nlist (nnmaildir--grp-get-lists group)
1141 mlist (nnmaildir--lists-get-mlist nlist)
1142 nlist (nnmaildir--lists-get-nlist nlist)
1143 gname (nnmaildir--grp-get-name group)
1144 srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1145 dir (nnmaildir--srv-grp-dir srv-dir gname))
1148 ((and fetch-old (not (numberp fetch-old)))
1150 (setq article (car nlist) nlist (cdr nlist)
1151 nov (nnmaildir--update-nov srv-dir group article))
1153 (nnmaildir--cache-nov group article nov)
1154 (setq num (nnmaildir--art-get-num article))
1155 (princ num nntp-server-buffer)
1156 (insert "\t" (nnmaildir--nov-get-beg nov) "\t"
1157 (nnmaildir--art-get-msgid article) "\t"
1158 (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname
1160 (princ num nntp-server-buffer)
1161 (insert "\t" (nnmaildir--nov-get-end nov) "\n")
1162 (goto-char (point-min)))))
1164 ((stringp (car articles))
1166 (setq article (car articles) articles (cdr articles)
1167 article (nnmaildir--mlist-art mlist article))
1169 (setq nov (nnmaildir--update-nov srv-dir group
1171 (nnmaildir--cache-nov group article nov)
1172 (setq num (nnmaildir--art-get-num article))
1173 (princ num nntp-server-buffer)
1174 (insert "\t" (nnmaildir--nov-get-beg nov) "\t"
1175 (nnmaildir--art-get-msgid article) "\t"
1176 (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname
1178 (princ num nntp-server-buffer)
1179 (insert "\t" (nnmaildir--nov-get-end nov) "\n"))))
1182 ;; Assume the article range is sorted ascending
1183 (setq stop (car articles)
1184 num (car (last articles))
1185 stop (if (numberp stop) stop (car stop))
1186 num (if (numberp num) num (cdr num))
1187 stop (- stop fetch-old)
1188 stop (if (< stop 1) 1 stop)
1189 articles (list (cons stop num))))
1191 (setq stop (car articles) articles (cdr articles))
1192 (while (eq stop (car articles))
1193 (setq articles (cdr articles)))
1194 (if (numberp stop) (setq num stop)
1195 (setq num (cdr stop) stop (car stop)))
1196 (setq nlist2 (nthcdr (- (nnmaildir--art-get-num (car nlist)) num)
1199 (setq article (car nlist2)
1200 num (nnmaildir--art-get-num article))
1202 (setq nlist2 (cdr nlist2)
1203 nov (nnmaildir--update-nov srv-dir group article))
1205 (nnmaildir--cache-nov group article nov)
1206 (princ num nntp-server-buffer)
1207 (insert "\t" (nnmaildir--nov-get-beg nov) "\t"
1208 (nnmaildir--art-get-msgid article) "\t"
1209 (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname
1211 (princ num nntp-server-buffer)
1212 (insert "\t" (nnmaildir--nov-get-end nov) "\n")
1213 (goto-char (point-min)))))))
1214 (sort-numeric-fields 1 (point-min) (point-max))
1217 (defun nnmaildir-request-article (num-msgid &optional gname server to-buffer)
1218 (let ((group (nnmaildir--prepare server gname))
1219 (case-fold-search t)
1220 list article suffix dir deactivate-mark)
1223 (nnmaildir--srv-set-error nnmaildir--cur-server
1224 (if gname (concat "No such group: " gname)
1225 "No current group"))
1226 (throw 'return nil))
1227 (setq list (nnmaildir--grp-get-lists group))
1228 (if (numberp num-msgid)
1229 (setq list (nnmaildir--lists-get-nlist list)
1230 article (nnmaildir--nlist-art list num-msgid))
1231 (setq list (nnmaildir--lists-get-mlist list)
1232 article (nnmaildir--mlist-art list num-msgid))
1233 (if article (setq num-msgid (nnmaildir--art-get-num article))
1237 (setq group (symbol-value grp)
1238 list (nnmaildir--grp-get-lists group)
1239 list (nnmaildir--lists-get-mlist list)
1240 article (nnmaildir--mlist-art list num-msgid))
1242 (setq num-msgid (nnmaildir--art-get-num article))
1243 (throw 'found nil)))
1244 (nnmaildir--srv-get-groups nnmaildir--cur-server)))))
1246 (nnmaildir--srv-set-error nnmaildir--cur-server "No such article")
1247 (throw 'return nil))
1248 (if (stringp (setq suffix (nnmaildir--art-get-suffix article))) nil
1249 (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired")
1250 (throw 'return nil))
1251 (setq gname (nnmaildir--grp-get-name group)
1252 dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1253 dir (nnmaildir--srv-grp-dir dir gname)
1254 group (if (nnmaildir--param (nnmaildir--grp-get-pname group)
1256 (nnmaildir--new dir) (nnmaildir--cur dir))
1257 nnmaildir-article-file-name (concat group
1258 (nnmaildir--art-get-prefix
1261 (if (file-exists-p nnmaildir-article-file-name) nil
1262 (nnmaildir--art-set-suffix article 'expire)
1263 (nnmaildir--art-set-nov article nil)
1264 (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired")
1265 (throw 'return nil))
1267 (set-buffer (or to-buffer nntp-server-buffer))
1269 (nnheader-insert-file-contents nnmaildir-article-file-name))
1270 (cons gname num-msgid))))
1272 (defun nnmaildir-request-post (&optional server)
1273 (let (message-required-mail-headers)
1274 (funcall message-send-mail-function)))
1276 (defun nnmaildir-request-replace-article (article gname buffer)
1277 (let ((group (nnmaildir--prepare nil gname))
1278 (coding-system-for-write nnheader-file-coding-system)
1279 (buffer-file-coding-system nil)
1280 (file-coding-system-alist nil)
1281 file dir suffix tmpfile deactivate-mark)
1284 (nnmaildir--srv-set-error nnmaildir--cur-server
1285 (concat "No such group: " gname))
1286 (throw 'return nil))
1287 (when (nnmaildir--param (nnmaildir--grp-get-pname group) 'read-only)
1288 (nnmaildir--srv-set-error nnmaildir--cur-server
1289 (concat "Read-only group: " group))
1290 (throw 'return nil))
1291 (setq dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1292 dir (nnmaildir--srv-grp-dir dir gname)
1293 file (nnmaildir--grp-get-lists group)
1294 file (nnmaildir--lists-get-nlist file)
1295 file (nnmaildir--nlist-art file article))
1296 (if (and file (stringp (setq suffix (nnmaildir--art-get-suffix file))))
1298 (nnmaildir--srv-set-error nnmaildir--cur-server
1299 (format "No such article: %d" article))
1300 (throw 'return nil))
1304 file (nnmaildir--art-get-prefix article)
1305 tmpfile (concat (nnmaildir--tmp dir) file))
1306 (when (file-exists-p tmpfile)
1307 (nnmaildir--srv-set-error nnmaildir--cur-server
1308 (concat "File exists: " tmpfile))
1309 (throw 'return nil))
1310 (write-region (point-min) (point-max) tmpfile nil 'no-message nil
1311 'confirm-overwrite)) ;; error would be preferred :(
1312 (unix-sync) ;; no fsync :(
1313 (rename-file tmpfile (concat (nnmaildir--cur dir) file suffix) 'replace)
1316 (defun nnmaildir-request-move-article (article gname server accept-form
1318 (let ((group (nnmaildir--prepare server gname))
1319 pgname list suffix result nnmaildir--file deactivate-mark)
1322 (nnmaildir--srv-set-error nnmaildir--cur-server
1323 (concat "No such group: " gname))
1324 (throw 'return nil))
1325 (setq gname (nnmaildir--grp-get-name group)
1326 pgname (nnmaildir--grp-get-pname group)
1327 list (nnmaildir--grp-get-lists group)
1328 list (nnmaildir--lists-get-nlist list)
1329 article (nnmaildir--nlist-art list article))
1331 (nnmaildir--srv-set-error nnmaildir--cur-server "No such article")
1332 (throw 'return nil))
1333 (if (stringp (setq suffix (nnmaildir--art-get-suffix article))) nil
1334 (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired")
1335 (throw 'return nil))
1336 (setq nnmaildir--file (nnmaildir--srv-get-dir nnmaildir--cur-server)
1337 nnmaildir--file (nnmaildir--srv-grp-dir nnmaildir--file gname)
1338 nnmaildir--file (if (nnmaildir--param pgname 'read-only)
1339 (nnmaildir--new nnmaildir--file)
1340 (nnmaildir--cur nnmaildir--file))
1341 nnmaildir--file (concat nnmaildir--file
1342 (nnmaildir--art-get-prefix article)
1344 (if (file-exists-p nnmaildir--file) nil
1345 (nnmaildir--art-set-suffix article 'expire)
1346 (nnmaildir--art-set-nov article nil)
1347 (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired")
1348 (throw 'return nil))
1349 (nnmaildir--with-move-buffer
1351 (nnheader-insert-file-contents nnmaildir--file)
1352 (setq result (eval accept-form)))
1353 (if (or (null result) (nnmaildir--param pgname 'read-only)) nil
1354 (nnmaildir--unlink nnmaildir--file)
1355 (nnmaildir--art-set-suffix article 'expire)
1356 (nnmaildir--art-set-nov article nil))
1359 (defun nnmaildir-request-accept-article (gname &optional server last)
1360 (let ((group (nnmaildir--prepare server gname))
1361 (coding-system-for-write nnheader-file-coding-system)
1362 (buffer-file-coding-system nil)
1363 (file-coding-system-alist nil)
1364 srv-dir dir file tmpfile curfile 24h num article)
1367 (nnmaildir--srv-set-error nnmaildir--cur-server
1368 (concat "No such group: " gname))
1369 (throw 'return nil))
1370 (setq gname (nnmaildir--grp-get-name group))
1371 (when (nnmaildir--param (nnmaildir--grp-get-pname group) 'read-only)
1372 (nnmaildir--srv-set-error nnmaildir--cur-server
1373 (concat "Read-only group: " gname))
1374 (throw 'return nil))
1375 (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1376 dir (nnmaildir--srv-grp-dir srv-dir gname)
1377 file (format-time-string "%s" nil))
1378 (if (string-equal nnmaildir--delivery-time file) nil
1379 (setq nnmaildir--delivery-time file
1380 nnmaildir--delivery-ct 0))
1381 (setq file (concat file "." nnmaildir--delivery-pid))
1382 (if (zerop nnmaildir--delivery-ct) nil
1383 (setq file (concat file "_"
1384 (number-to-string nnmaildir--delivery-ct))))
1385 (setq file (concat file "." (system-name))
1386 tmpfile (concat (nnmaildir--tmp dir) file)
1387 curfile (concat (nnmaildir--cur dir) file ":2,"))
1388 (when (file-exists-p tmpfile)
1389 (nnmaildir--srv-set-error nnmaildir--cur-server
1390 (concat "File exists: " tmpfile))
1391 (throw 'return nil))
1392 (when (file-exists-p curfile)
1393 (nnmaildir--srv-set-error nnmaildir--cur-server
1394 (concat "File exists: " curfile))
1395 (throw 'return nil))
1396 (setq nnmaildir--delivery-ct (1+ nnmaildir--delivery-ct)
1397 24h (run-with-timer 86400 nil
1399 (nnmaildir--unlink tmpfile)
1400 (nnmaildir--srv-set-error
1401 nnmaildir--cur-server
1402 "24-hour timer expired")
1403 (throw 'return nil))))
1405 (add-name-to-file nnmaildir--file tmpfile)
1407 (write-region (point-min) (point-max) tmpfile nil 'no-message nil
1408 'confirm-overwrite) ;; error would be preferred :(
1409 (unix-sync))) ;; no fsync :(
1412 (add-name-to-file tmpfile curfile)
1414 (nnmaildir--srv-set-error nnmaildir--cur-server
1415 (concat "Error linking: "
1416 (prin1-to-string err)))
1417 (nnmaildir--unlink tmpfile)
1418 (throw 'return nil)))
1419 (nnmaildir--unlink tmpfile)
1420 (setq article (nnmaildir--art-new)
1421 num (nnmaildir--grp-get-lists group)
1422 num (nnmaildir--lists-get-nlist num)
1423 num (1+ (nnmaildir--nlist-last-num num)))
1424 (nnmaildir--art-set-prefix article file)
1425 (nnmaildir--art-set-suffix article ":2,")
1426 (nnmaildir--art-set-num article num)
1427 (if (nnmaildir--grp-add-art srv-dir group article) (cons gname num)))))
1429 (defun nnmaildir-save-mail (group-art)
1432 (throw 'return nil))
1433 (let ((ret group-art)
1434 ga gname x groups nnmaildir--file deactivate-mark)
1436 (goto-char (point-min))
1438 (while (looking-at "From ")
1439 (replace-match "X-From-Line: ")
1441 (setq groups (nnmaildir--srv-get-groups nnmaildir--cur-server)
1442 ga (car group-art) group-art (cdr group-art)
1444 (or (intern-soft gname groups)
1445 (nnmaildir-request-create-group gname)
1446 (throw 'return nil)) ;; not that nnmail bothers to check :(
1447 (if (nnmaildir-request-accept-article gname) nil
1448 (throw 'return nil))
1449 (setq x (nnmaildir--prepare nil gname)
1450 nnmaildir--file (nnmaildir--srv-get-dir nnmaildir--cur-server)
1451 nnmaildir--file (nnmaildir--subdir nnmaildir--file
1452 (nnmaildir--grp-get-name x))
1453 x (nnmaildir--grp-get-lists x)
1454 x (nnmaildir--lists-get-nlist x)
1456 nnmaildir--file (concat nnmaildir--file
1457 (nnmaildir--art-get-prefix x)
1458 (nnmaildir--art-get-suffix x)))
1460 (setq ga (car group-art) group-art (cdr group-art)
1462 (if (and (or (intern-soft gname groups)
1463 (nnmaildir-request-create-group gname))
1464 (nnmaildir-request-accept-article gname)) nil
1465 (setq ret (delq ga ret)))) ;; We'll still try the other groups
1468 (defun nnmaildir-active-number (group)
1469 (let ((x (nnmaildir--prepare nil group)))
1472 (nnmaildir--srv-set-error nnmaildir--cur-server
1473 (concat "No such group: " group))
1474 (throw 'return nil))
1475 (setq x (nnmaildir--grp-get-lists x)
1476 x (nnmaildir--lists-get-nlist x))
1479 x (nnmaildir--art-get-num x)
1483 (defun nnmaildir-request-expire-articles (ranges &optional gname server force)
1484 (let ((no-force (not force))
1485 (group (nnmaildir--prepare server gname))
1486 pgname time boundary time-iter bound-iter high low target dir nlist
1487 stop number article didnt suffix nnmaildir--file
1488 nnmaildir-article-file-name deactivate-mark)
1491 (nnmaildir--srv-set-error nnmaildir--cur-server
1492 (if gname (concat "No such group: " gname)
1493 "No current group"))
1494 (throw 'return (gnus-uncompress-range ranges)))
1495 (setq gname (nnmaildir--grp-get-name group)
1496 pgname (nnmaildir--grp-get-pname group))
1497 (if (nnmaildir--param pgname 'read-only)
1498 (throw 'return (gnus-uncompress-range ranges)))
1499 (setq time (or (nnmaildir--param pgname 'expire-age)
1500 (* 86400 ;; seconds per day
1501 (or (and nnmail-expiry-wait-function
1502 (funcall nnmail-expiry-wait-function gname))
1503 nnmail-expiry-wait))))
1504 (if (or force (integerp time)) nil
1505 (throw 'return (gnus-uncompress-range ranges)))
1506 (setq boundary (current-time)
1507 high (- (car boundary) (/ time 65536))
1508 low (- (cadr boundary) (% time 65536)))
1510 (setq low (+ low 65536)
1512 (setcar (cdr boundary) low)
1513 (setcar boundary high)
1514 (setq dir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1515 dir (nnmaildir--srv-grp-dir dir gname)
1516 dir (nnmaildir--cur dir)
1517 nlist (nnmaildir--grp-get-lists group)
1518 nlist (nnmaildir--lists-get-nlist nlist)
1519 ranges (reverse ranges))
1520 (nnmaildir--with-move-buffer
1522 (setq number (car ranges) ranges (cdr ranges))
1523 (while (eq number (car ranges))
1524 (setq ranges (cdr ranges)))
1525 (if (numberp number) (setq stop number)
1526 (setq stop (car number) number (cdr number)))
1527 (setq nlist (nthcdr (- (nnmaildir--art-get-num (car nlist)) number)
1530 (setq article (car nlist)
1531 number (nnmaildir--art-get-num article))
1533 (setq nlist (cdr nlist)
1534 suffix (nnmaildir--art-get-suffix article))
1536 (if (stringp suffix) nil
1537 (nnmaildir--art-set-suffix article 'expire)
1538 (nnmaildir--art-set-nov article nil)
1539 (throw 'continue nil))
1540 (setq nnmaildir--file (nnmaildir--art-get-prefix article)
1541 nnmaildir--file (concat dir nnmaildir--file suffix)
1542 time (file-attributes nnmaildir--file))
1544 (nnmaildir--art-set-suffix article 'expire)
1545 (nnmaildir--art-set-nov article nil)
1546 (throw 'continue nil))
1547 (setq time (nth 5 time)
1549 bound-iter boundary)
1552 (while (and bound-iter time-iter
1553 (= (car bound-iter) (car time-iter)))
1554 (setq bound-iter (cdr bound-iter)
1555 time-iter (cdr time-iter)))
1556 (and bound-iter time-iter
1557 (car-less-than-car bound-iter time-iter))))
1558 (setq didnt (cons number didnt))
1560 (setq nnmaildir-article-file-name nnmaildir--file
1561 target (nnmaildir--param pgname 'expire-group)))
1562 (when (and (stringp target)
1563 (not (string-equal target pgname))) ;; Move it.
1565 (nnheader-insert-file-contents nnmaildir--file)
1566 (gnus-request-accept-article target nil nil 'no-encode))
1567 (if (equal target pgname)
1568 (setq didnt (cons number didnt)) ;; Leave it here.
1569 (nnmaildir--unlink nnmaildir--file)
1570 (nnmaildir--art-set-suffix article 'expire)
1571 (nnmaildir--art-set-nov article nil))))))
1575 (defun nnmaildir-request-set-mark (gname actions &optional server)
1576 (let ((group (nnmaildir--prepare server gname))
1577 (coding-system-for-write nnheader-file-coding-system)
1578 (buffer-file-coding-system nil)
1579 (file-coding-system-alist nil)
1580 del-mark add-marks marksdir markfile action group-nlist nlist ranges
1581 begin end article all-marks todo-marks did-marks marks form mdir mfile
1582 pgname ls deactivate-mark)
1585 (setq mfile (nnmaildir--subdir marksdir (symbol-name (car marks)))
1586 mfile (concat mfile (nnmaildir--art-get-prefix article)))
1587 (nnmaildir--unlink mfile))
1591 (setq mdir (nnmaildir--subdir marksdir (symbol-name (car marks)))
1592 mfile (concat mdir (nnmaildir--art-get-prefix article)))
1593 (if (memq (car marks) did-marks) nil
1594 (nnmaildir--mkdir mdir)
1595 (setq did-marks (cons (car marks) did-marks)))
1596 (if (file-exists-p mfile) nil
1598 (add-name-to-file markfile mfile)
1599 (file-error ;; too many links, probably
1600 (if (file-exists-p mfile) nil
1601 (nnmaildir--unlink markfile)
1602 (write-region "" nil markfile nil 'no-message)
1603 (add-name-to-file markfile mfile
1604 'ok-if-already-exists)))))
1605 (setq marks (cdr marks)))))
1608 (nnmaildir--srv-set-error nnmaildir--cur-server
1609 (concat "No such group: " gname))
1611 (setq ranges (gnus-range-add ranges (caar actions))
1612 actions (cdr actions)))
1613 (throw 'return ranges))
1614 (setq group-nlist (nnmaildir--grp-get-lists group)
1615 group-nlist (nnmaildir--lists-get-nlist group-nlist)
1616 marksdir (nnmaildir--srv-get-dir nnmaildir--cur-server)
1617 marksdir (nnmaildir--srv-grp-dir marksdir gname)
1618 marksdir (nnmaildir--nndir marksdir)
1619 markfile (concat marksdir "markfile")
1620 marksdir (nnmaildir--marks-dir marksdir)
1621 gname (nnmaildir--grp-get-name group)
1622 pgname (nnmaildir--grp-get-pname group)
1623 ls (nnmaildir--group-ls nnmaildir--cur-server pgname)
1624 all-marks (funcall ls marksdir nil "\\`[^.]" 'nosort)
1627 (setcar marks (intern (car marks)))
1628 (setq marks (cdr marks)))
1630 (setq action (car actions) actions (cdr actions)
1633 todo-marks (caddr action)
1636 (if (memq (car marks) all-marks) nil
1637 (setq all-marks (cons (car marks) all-marks)))
1638 (setq marks (cdr marks)))
1641 ((eq 'del (cadr action))
1644 (setq marks (cdr marks))))
1645 ((eq 'add (cadr action)) '(funcall add-marks))
1649 (setq marks all-marks)
1651 (if (memq (car marks) todo-marks) nil
1653 (setq marks (cdr marks)))))))
1654 (if (numberp (cdr ranges)) (setq ranges (list ranges))
1655 (setq ranges (reverse ranges)))
1657 (setq begin (car ranges) ranges (cdr ranges))
1658 (while (eq begin (car ranges))
1659 (setq ranges (cdr ranges)))
1660 (if (numberp begin) (setq end begin)
1661 (setq end (cdr begin) begin (car begin)))
1662 (setq nlist (nthcdr (- (nnmaildir--art-get-num (car nlist)) end)
1665 (setq article (car nlist))
1666 (>= (nnmaildir--art-get-num article) begin))
1667 (setq nlist (cdr nlist))
1668 (when (stringp (nnmaildir--art-get-suffix article))
1669 (setq marks todo-marks)
1673 (defun nnmaildir-close-group (group &optional server)
1676 (defun nnmaildir-close-server (&optional server)
1677 (let (flist ls dirs dir files file x)
1678 (nnmaildir--prepare server nil)
1679 (setq server nnmaildir--cur-server)
1681 (setq nnmaildir--cur-server nil)
1685 (setq group (symbol-value group)
1686 x (nnmaildir--grp-get-pname group)
1687 ls (nnmaildir--group-ls server x)
1688 dir (nnmaildir--srv-get-dir server)
1689 dir (nnmaildir--srv-grp-dir
1690 dir (nnmaildir--grp-get-name group))
1691 x (nnmaildir--param x 'read-only)
1692 x (if x (nnmaildir--new dir) (nnmaildir--cur dir))
1693 files (funcall ls x nil "\\`[^.]" 'nosort)
1696 (while (<= flist x) (setq flist (* 2 flist)))
1697 (if (/= flist 1) (setq flist (1- flist)))
1698 (setq flist (make-vector flist 0))
1700 (setq file (car files) files (cdr files))
1701 (string-match "\\`\\([^:]*\\)\\(:.*\\)?\\'" file)
1702 (intern (match-string 1 file) flist))
1703 (setq dir (nnmaildir--nndir dir)
1704 dirs (cons (nnmaildir--nov-dir dir)
1705 (funcall ls (nnmaildir--marks-dir dir) 'full
1706 "\\`[^.]" 'nosort)))
1708 (setq dir (car dirs) dirs (cdr dirs)
1709 files (funcall ls dir nil "\\`[^.]" 'nosort)
1710 dir (file-name-as-directory dir))
1712 (setq file (car files) files (cdr files))
1713 (if (intern-soft file flist) nil
1714 (setq file (concat dir file))
1715 (delete-file file)))))
1716 (nnmaildir--srv-get-groups server)))
1717 (unintern (nnmaildir--srv-get-name server) nnmaildir--servers)))
1720 (defun nnmaildir-request-close ()
1721 (let (servers buffer)
1722 (mapatoms (lambda (server)
1723 (setq servers (cons (symbol-name server) servers)))
1726 (nnmaildir-close-server (car servers))
1727 (setq servers (cdr servers)))
1728 (setq buffer (get-buffer " *nnmaildir work*"))
1729 (if buffer (kill-buffer buffer))
1730 (setq buffer (get-buffer " *nnmaildir nov*"))
1731 (if buffer (kill-buffer buffer))
1732 (setq buffer (get-buffer " *nnmaildir move*"))
1733 (if buffer (kill-buffer buffer)))
1736 (defun nnmaildir--edit-prep ()
1737 (let ((extras '(mapcar mapatoms))
1741 (when (or (memq sym extras)
1743 (>= (length (setq name (symbol-name sym))) 10)
1744 (string-equal "nnmaildir-" (substring name 0 10))))
1745 (put sym 'lisp-indent-function 0))))
1748 (provide 'nnmaildir)
1751 ;; eval: (progn (require 'nnmaildir) (nnmaildir--edit-prep))
1754 ;;; nnmaildir.el ends here