1 ;;; elmo-maildir.el -- Maildir interface for ELMO.
3 ;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7 ;; Time-stamp: <00/04/24 10:19:24 teranisi>
9 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
33 (eval-when-compile (require 'cl))
35 (require 'elmo-localdir)
37 (defvar elmo-maildir-sequence-number-internal 0
38 "Sequence number for the pid part of unique filename.
39 This variable should not be used in elsewhere.")
41 (defsubst elmo-maildir-get-folder-directory (spec)
42 (if (file-name-absolute-p (nth 1 spec))
43 (nth 1 spec) ; already full path.
44 (expand-file-name (nth 1 spec)
45 elmo-maildir-folder-path)))
47 (defun elmo-maildir-number-to-filename (dir number loc-alist)
48 (let ((location (cdr (assq number loc-alist))))
49 (and location (elmo-maildir-get-filename location dir))))
51 (defun elmo-maildir-get-filename (location dir)
52 "Get a filename that is corresponded to LOCATION in DIR."
54 (let ((file (file-name-completion (symbol-name location)
55 (expand-file-name "cur" dir))))
56 (if (eq file t) location file))
57 (expand-file-name "cur" dir)))
59 (defsubst elmo-maildir-list-location (dir &optional child-dir)
60 (let* ((cur-dir (expand-file-name (or child-dir "cur") dir))
61 (cur (directory-files cur-dir
63 seen-list seen sym list)
67 (if (string-match "^\\([^:]+\\):\\([^:]+\\)$" x)
73 (elmo-match-string 2 x))
75 (setq sym (intern (elmo-match-string 1 x)))
77 (setq seen-list (cons sym seen-list)))
81 (cons list seen-list)))
83 (defun elmo-maildir-msgdb-create-entity (dir number loc-alist)
84 (elmo-localdir-msgdb-create-overview-entity-from-file
86 (elmo-maildir-number-to-filename dir number loc-alist)))
88 (defun elmo-maildir-cleanup-temporal (dir)
89 ;; Delete files in the tmp dir which are not accessed
90 ;; for more than 36 hours.
91 (let ((cur-time (current-time))
96 (setq last-accessed (nth 4 (file-attributes file)))
97 (when (or (> (- (car cur-time)(car last-accessed)) 1)
98 (and (eq (- (car cur-time)(car last-accessed)) 1)
99 (> (- (cadr cur-time)(cadr last-accessed))
101 (message "Maildir: %d tmp file(s) are cleared."
102 (setq count (1+ count)))
103 (delete-file file))))
104 (directory-files (expand-file-name "tmp" dir)
108 (defun elmo-maildir-update-current (spec)
109 "Move all new msgs to cur in the maildir"
110 (let* ((maildir (elmo-maildir-get-folder-directory spec))
111 (news (directory-files (expand-file-name "new"
115 ;; cleanup tmp directory.
116 (elmo-maildir-cleanup-temporal maildir)
117 ;; move new msgs to cur directory.
120 (expand-file-name x (expand-file-name "new" maildir))
121 (expand-file-name (concat x ":2,")
122 (expand-file-name "cur" maildir))))
125 (defun elmo-maildir-set-mark (filename mark)
126 "Mark the file in the maildir. MARK is a character."
127 (if (string-match "^\\([^:]+:2,\\)\\(.*\\)$" filename)
128 (let ((flaglist (string-to-char-list (elmo-match-string
130 (unless (memq mark flaglist)
131 (setq flaglist (sort (cons mark flaglist) '<))
132 (rename-file filename
133 (concat (elmo-match-string 1 filename)
134 (char-list-to-string flaglist)))))))
136 (defun elmo-maildir-delete-mark (filename mark)
137 "Mark the file in the maildir. MARK is a character."
138 (if (string-match "^\\([^:]+:2,\\)\\(.*\\)$" filename)
139 (let ((flaglist (string-to-char-list (elmo-match-string
141 (when (memq mark flaglist)
142 (setq flaglist (delq mark flaglist))
143 (rename-file filename
144 (concat (elmo-match-string 1 filename)
146 (char-list-to-string flaglist))))))))
148 (defsubst elmo-maildir-set-mark-msgs (spec mark msgs msgdb)
149 (let ((dir (elmo-maildir-get-folder-directory spec))
151 (elmo-msgdb-get-location msgdb)
152 (elmo-msgdb-location-load (elmo-msgdb-expand-path nil spec))))
155 (if (setq file (elmo-maildir-number-to-filename dir (car msgs) locs))
156 (elmo-maildir-set-mark file mark))
157 (setq msgs (cdr msgs)))))
159 (defsubst elmo-maildir-delete-mark-msgs (spec mark msgs msgdb)
160 (let ((dir (elmo-maildir-get-folder-directory spec))
162 (elmo-msgdb-get-location msgdb)
163 (elmo-msgdb-location-load (elmo-msgdb-expand-path nil spec))))
166 (if (setq file (elmo-maildir-number-to-filename dir (car msgs) locs))
167 (elmo-maildir-delete-mark file mark))
168 (setq msgs (cdr msgs)))))
170 (defun elmo-maildir-mark-as-important (spec msgs &optional msgdb)
171 (elmo-maildir-set-mark-msgs spec ?F msgs msgdb))
173 (defun elmo-maildir-unmark-important (spec msgs &optional msgdb)
174 (elmo-maildir-delete-mark-msgs spec ?F msgs msgdb))
176 (defun elmo-maildir-mark-as-read (spec msgs &optional msgdb)
177 (elmo-maildir-set-mark-msgs spec ?S msgs msgdb))
179 (defun elmo-maildir-mark-as-unread (spec msgs &optional msgdb)
180 (elmo-maildir-delete-mark-msgs spec ?S msgs msgdb))
182 (defun elmo-maildir-msgdb-create (spec numlist new-mark
183 already-mark seen-mark
188 (let* ((dir (elmo-maildir-get-folder-directory spec))
189 (loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
190 (elmo-msgdb-location-load (elmo-msgdb-expand-path
192 (loc-seen (elmo-maildir-list-location dir))
193 (loc-list (car loc-seen))
194 (seen-list (cdr loc-seen))
195 overview number-alist mark-alist entity
196 i percent num location pair)
197 (setq num (length numlist))
199 (message "Creating msgdb...")
202 (elmo-maildir-msgdb-create-entity
203 dir (car numlist) loc-alist))
207 (elmo-msgdb-append-element
210 (elmo-msgdb-number-add number-alist
211 (elmo-msgdb-overview-entity-get-number
213 (elmo-msgdb-overview-entity-get-id
215 (setq location (cdr (assq (car numlist) loc-alist)))
216 (unless (member location seen-list)
218 (elmo-msgdb-mark-append
220 (elmo-msgdb-overview-entity-get-number
222 (or (elmo-msgdb-global-mark-get
223 (elmo-msgdb-overview-entity-get-id
227 (setq percent (/ (* i 100) num))
228 (elmo-display-progress
229 'elmo-maildir-msgdb-create "Creating msgdb..."
231 (setq numlist (cdr numlist)))
232 (message "Creating msgdb...done.")
233 (elmo-msgdb-sort-by-date
234 (list overview number-alist mark-alist loc-alist)))))
236 (defalias 'elmo-maildir-msgdb-create-as-numlist 'elmo-maildir-msgdb-create)
238 (defun elmo-maildir-list-folders (spec &optional hierarchy)
239 (let ((elmo-localdir-folder-path elmo-maildir-folder-path)
240 (elmo-localdir-list-folders-spec-string ".")
241 (elmo-localdir-list-folders-filter-regexp
242 "^\\(\\.\\.?\\|cur\\|tmp\\|new\\)$")
243 elmo-have-link-count folders)
244 (setq folders (elmo-localdir-list-folders spec hierarchy))
245 (if (eq (length (nth 1 spec)) 0) ; top
246 (setq folders (append
247 (list (concat elmo-localdir-list-folders-spec-string
251 (function (lambda (folder)
252 (not (or (listp folder) (elmo-folder-exists-p folder)))))
256 ((>= emacs-major-version 19)
257 (defun elmo-maildir-make-unique-string ()
258 "This function generates a string that can be used as a unique
259 file name for maildir directories."
260 (let ((cur-time (current-time)))
261 (format "%.0f.%d_%d.%s"
263 (float 65536)) (cadr cur-time))
265 (incf elmo-maildir-sequence-number-internal)
267 ((eq emacs-major-version 18)
268 ;; A fake function for v18
269 (defun elmo-maildir-make-unique-string ()
270 "This function generates a string that can be used as a unique
271 file name for maildir directories."
272 (unless (fboundp 'float-to-string)
273 (load-library "float"))
274 (let ((time (current-time)))
278 (f+ (f* (f (car time))
283 (% (abs (random t)) 10000); dummy pid
286 (defun elmo-maildir-temporal-filename (basedir)
287 (let ((filename (expand-file-name
288 (concat "tmp/" (elmo-maildir-make-unique-string))
290 (unless (file-exists-p (file-name-directory filename))
291 (make-directory (file-name-directory filename)))
292 (while (file-exists-p filename)
293 ;; (sleep-for 2) ; I don't want to wait.
296 (concat "tmp/" (elmo-maildir-make-unique-string))
300 (defun elmo-maildir-append-msg (spec string &optional msg no-see)
301 (let ((basedir (elmo-maildir-get-folder-directory spec))
305 (setq filename (elmo-maildir-temporal-filename basedir))
307 (as-binary-output-file
308 (write-region (point-min) (point-max) filename nil 'no-msg))
309 ;; add link from new.
310 (elmo-add-name-to-file
313 (concat "new/" (file-name-nondirectory filename))
316 ;; If an error occured, return nil.
319 (defun elmo-maildir-delete-msg (spec number loc-alist)
320 (let ((dir (elmo-maildir-get-folder-directory spec))
322 (setq file (elmo-maildir-number-to-filename dir number loc-alist))
323 (if (and (file-writable-p file)
324 (not (file-directory-p file)))
325 (progn (delete-file file)
328 (defun elmo-maildir-read-msg (spec number outbuf &optional msgdb)
330 (let* ((loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
331 (elmo-msgdb-location-load (elmo-msgdb-expand-path
333 (dir (elmo-maildir-get-folder-directory spec))
334 (file (elmo-maildir-number-to-filename dir number loc-alist)))
337 (when (file-exists-p file)
338 (as-binary-input-file (insert-file-contents file))
339 (elmo-delete-cr-get-content-type)))))
341 (defun elmo-maildir-delete-msgs (spec msgs &optional msgdb)
342 (let ((loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
343 (elmo-msgdb-location-load (elmo-msgdb-expand-path
345 (mapcar '(lambda (msg) (elmo-maildir-delete-msg spec msg
349 (defsubst elmo-maildir-list-folder-subr (spec &optional nonsort)
350 (let* ((dir (elmo-maildir-get-folder-directory spec))
351 (flist (elmo-list-folder-by-location
353 (car (elmo-maildir-list-location dir))))
354 (news (car (elmo-maildir-list-location dir "new"))))
356 (cons (+ (or (elmo-max-of-list flist) 0) (length news))
357 (+ (length flist) (length news)))
360 (defun elmo-maildir-list-folder (spec)
361 (elmo-maildir-update-current spec)
362 (elmo-maildir-list-folder-subr spec))
364 (defun elmo-maildir-max-of-folder (spec)
365 (elmo-maildir-list-folder-subr spec t))
367 (defalias 'elmo-maildir-check-validity 'elmo-localdir-check-validity)
369 (defalias 'elmo-maildir-sync-validity 'elmo-localdir-sync-validity)
371 (defun elmo-maildir-folder-exists-p (spec)
372 (let ((basedir (elmo-maildir-get-folder-directory spec)))
373 (and (file-directory-p (expand-file-name "new" basedir))
374 (file-directory-p (expand-file-name "cur" basedir))
375 (file-directory-p (expand-file-name "tmp" basedir)))))
377 (defun elmo-maildir-folder-creatable-p (spec)
380 (defun elmo-maildir-create-folder (spec)
381 (let ((basedir (elmo-maildir-get-folder-directory spec)))
384 (mapcar (function (lambda (dir)
385 (setq dir (expand-file-name dir basedir))
386 (or (file-directory-p dir)
388 (elmo-make-directory dir)
389 (set-file-modes dir 448)))))
390 '("." "new" "cur" "tmp"))
394 (defun elmo-maildir-delete-folder (spec)
395 (let ((basedir (elmo-maildir-get-folder-directory spec)))
397 (let ((tmp-files (directory-files
398 (expand-file-name "tmp" basedir)
400 ;; Delete files in tmp.
401 (and tmp-files (mapcar 'delete-file tmp-files))
405 (setq dir (expand-file-name dir basedir))
406 (if (not (file-directory-p dir))
408 (elmo-delete-directory dir t))))
409 '("new" "cur" "tmp" "."))
413 (defun elmo-maildir-search (spec condition &optional from-msgs msgdb)
415 (let* ((msgs (or from-msgs (elmo-maildir-list-folder spec)))
416 (loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
417 (elmo-msgdb-location-load (elmo-msgdb-expand-path
419 (dir (elmo-maildir-get-folder-directory spec))
421 case-fold-search ret-val
426 (setq msg-num (car msgs))
427 (if (elmo-file-field-condition-match
428 (elmo-maildir-number-to-filename
429 dir (car msgs) loc-alist)
431 (setq ret-val (append ret-val (list msg-num))))
433 (setq percent (/ (* i 100) num))
434 (elmo-display-progress
435 'elmo-maildir-search "Searching..."
437 (setq msgs (cdr msgs)))
440 ;;; (maildir) -> maildir
441 (defun elmo-maildir-copy-msgs (dst-spec msgs src-spec
442 &optional loc-alist same-number)
446 (elmo-maildir-get-msg-filename src-spec (car msgs) loc-alist))
452 (file-name-nondirectory srcfile)
453 (concat (elmo-maildir-get-folder-directory dst-spec) "/cur")))
454 (setq msgs (cdr msgs))))
457 (defun elmo-maildir-use-cache-p (spec number)
460 (defun elmo-maildir-local-file-p (spec number)
463 (defun elmo-maildir-get-msg-filename (spec number &optional loc-alist)
464 (elmo-maildir-number-to-filename
465 (elmo-maildir-get-folder-directory spec)
466 number (or loc-alist (elmo-msgdb-location-load
467 (elmo-msgdb-expand-path
470 (defalias 'elmo-maildir-sync-number-alist
471 'elmo-generic-sync-number-alist)
472 (defalias 'elmo-maildir-list-folder-unread
473 'elmo-generic-list-folder-unread)
474 (defalias 'elmo-maildir-list-folder-important
475 'elmo-generic-list-folder-important)
477 (provide 'elmo-maildir)
479 ;;; elmo-maildir.el ends here