1
;;; elmo-maildir.el --- Maildir interface for ELMO.
3
;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
5
;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6
;; Keywords: mail, net news
8
;; This file is part of ELMO (Elisp Library for Message Orchestration).
10
;; This program is free software; you can redistribute it and/or modify
11
;; it under the terms of the GNU General Public License as published by
12
;; the Free Software Foundation; either version 2, or (at your option)
15
;; This program is distributed in the hope that it will be useful,
16
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
;; GNU General Public License for more details.
20
;; You should have received a copy of the GNU General Public License
21
;; along with GNU Emacs; see the file COPYING. If not, write to the
22
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23
;; Boston, MA 02111-1307, USA.
32
(eval-when-compile (require 'cl))
38
;;; ELMO Maildir folder
40
(luna-define-class elmo-maildir-folder
42
(directory unread-locations flagged-locations))
43
(luna-define-internal-accessors 'elmo-maildir-folder))
45
(luna-define-method elmo-folder-initialize ((folder
48
(if (file-name-absolute-p name)
49
(elmo-maildir-folder-set-directory-internal
51
(expand-file-name name))
52
(elmo-maildir-folder-set-directory-internal
56
elmo-maildir-folder-path)))
59
(luna-define-method elmo-folder-expand-msgdb-path ((folder
62
(elmo-replace-string-as-filename
63
(elmo-maildir-folder-directory-internal folder))
66
elmo-msgdb-directory)))
68
(defun elmo-maildir-message-file-name (folder location)
69
"Get a file name of the message from FOLDER which corresponded to
71
(let ((file (file-name-completion
75
(elmo-maildir-folder-directory-internal folder)))))
78
(if (eq file t) location file)
81
(elmo-maildir-folder-directory-internal folder))))))
83
(defsubst elmo-maildir-list-location (dir &optional child-dir)
84
(let* ((cur-dir (expand-file-name (or child-dir "cur") dir))
85
(cur (directory-files cur-dir
87
unread-locations flagged-locations seen flagged sym
92
(if (string-match "^\\([^:]+\\):\\([^:]+\\)$" x)
97
((string-match "S" (elmo-match-string 2 x))
99
((string-match "F" (elmo-match-string 2 x))
101
(setq sym (elmo-match-string 1 x))
102
(unless seen (setq unread-locations
103
(cons sym unread-locations)))
104
(if flagged (setq flagged-locations
105
(cons sym flagged-locations)))
109
(list locations unread-locations flagged-locations)))
111
(luna-define-method elmo-map-folder-list-message-locations
112
((folder elmo-maildir-folder))
113
(elmo-maildir-update-current folder)
114
(let ((locs (elmo-maildir-list-location
115
(elmo-maildir-folder-directory-internal folder))))
116
;; 0: locations, 1: unread-locations, 2: flagged-locations
117
(elmo-maildir-folder-set-unread-locations-internal folder (nth 1 locs))
118
(elmo-maildir-folder-set-flagged-locations-internal folder (nth 2 locs))
121
(luna-define-method elmo-map-folder-list-unreads
122
((folder elmo-maildir-folder))
123
(elmo-maildir-folder-unread-locations-internal folder))
125
(luna-define-method elmo-map-folder-list-importants
126
((folder elmo-maildir-folder))
127
(elmo-maildir-folder-flagged-locations-internal folder))
129
(luna-define-method elmo-folder-msgdb-create
130
((folder elmo-maildir-folder)
131
numbers new-mark already-mark seen-mark important-mark seen-list)
132
(let* ((unread-list (elmo-maildir-folder-unread-locations-internal folder))
133
(flagged-list (elmo-maildir-folder-flagged-locations-internal folder))
134
(len (length numbers))
136
overview number-alist mark-alist entity
138
(message "Creating msgdb...")
141
(setq location (elmo-map-message-location folder number))
143
(elmo-msgdb-create-overview-entity-from-file
145
(elmo-maildir-message-file-name folder location)))
148
(elmo-msgdb-append-element overview entity))
150
(elmo-msgdb-number-add number-alist
151
(elmo-msgdb-overview-entity-get-number
153
(elmo-msgdb-overview-entity-get-id
156
((member location unread-list)
157
(setq mark new-mark)) ; unread!
158
((member location flagged-list)
159
(setq mark important-mark)))
160
(if (setq mark (or (elmo-msgdb-global-mark-get
161
(elmo-msgdb-overview-entity-get-id
165
(elmo-msgdb-mark-append
167
(elmo-msgdb-overview-entity-get-number
170
(when (> len elmo-display-progress-threshold)
172
(elmo-display-progress
173
'elmo-maildir-msgdb-create "Creating msgdb..."
174
(/ (* i 100) len)))))
175
(message "Creating msgdb...done")
176
(elmo-msgdb-sort-by-date
177
(list overview number-alist mark-alist))))
179
(defun elmo-maildir-cleanup-temporal (dir)
180
;; Delete files in the tmp dir which are not accessed
181
;; for more than 36 hours.
182
(let ((cur-time (current-time))
187
(setq last-accessed (nth 4 (file-attributes file)))
188
(when (or (> (- (car cur-time)(car last-accessed)) 1)
189
(and (eq (- (car cur-time)(car last-accessed)) 1)
190
(> (- (cadr cur-time)(cadr last-accessed))
192
(message "Maildir: %d tmp file(s) are cleared."
193
(setq count (1+ count)))
194
(delete-file file))))
195
(directory-files (expand-file-name "tmp" dir)
199
(defun elmo-maildir-update-current (folder)
200
"Move all new msgs to cur in the maildir."
201
(let* ((maildir (elmo-maildir-folder-directory-internal folder))
202
(news (directory-files (expand-file-name "new"
206
;; cleanup tmp directory.
207
(elmo-maildir-cleanup-temporal maildir)
208
;; move new msgs to cur directory.
211
(expand-file-name (car news) (expand-file-name "new" maildir))
212
(expand-file-name (concat (car news) ":2,")
213
(expand-file-name "cur" maildir)))
214
(setq news (cdr news)))))
216
(defun elmo-maildir-set-mark (filename mark)
217
"Mark the FILENAME file in the maildir. MARK is a character."
218
(if (string-match "^\\([^:]+:[12],\\)\\(.*\\)$" filename)
219
(let ((flaglist (string-to-char-list (elmo-match-string
221
(unless (memq mark flaglist)
222
(setq flaglist (sort (cons mark flaglist) '<))
223
(rename-file filename
224
(concat (elmo-match-string 1 filename)
225
(char-list-to-string flaglist)))))
226
;; Rescue no info file in maildir.
227
(rename-file filename
228
(concat filename ":2," (char-to-string mark))))
231
(defun elmo-maildir-delete-mark (filename mark)
232
"Mark the FILENAME file in the maildir. MARK is a character."
233
(if (string-match "^\\([^:]+:2,\\)\\(.*\\)$" filename)
234
(let ((flaglist (string-to-char-list (elmo-match-string
236
(when (memq mark flaglist)
237
(setq flaglist (delq mark flaglist))
238
(rename-file filename
239
(concat (elmo-match-string 1 filename)
241
(char-list-to-string flaglist))))))))
243
(defsubst elmo-maildir-set-mark-msgs (folder locs mark)
245
(elmo-maildir-set-mark
246
(elmo-maildir-message-file-name folder loc)
250
(defsubst elmo-maildir-delete-mark-msgs (folder locs mark)
252
(elmo-maildir-delete-mark
253
(elmo-maildir-message-file-name folder loc)
257
(luna-define-method elmo-map-folder-mark-as-important ((folder elmo-maildir-folder)
259
(elmo-maildir-set-mark-msgs folder locs ?F))
261
(luna-define-method elmo-map-folder-unmark-important ((folder elmo-maildir-folder)
263
(elmo-maildir-delete-mark-msgs folder locs ?F))
265
(luna-define-method elmo-map-folder-mark-as-read ((folder elmo-maildir-folder)
267
(elmo-maildir-set-mark-msgs folder locs ?S))
269
(luna-define-method elmo-map-folder-unmark-read ((folder elmo-maildir-folder)
271
(elmo-maildir-delete-mark-msgs folder locs ?S))
273
(luna-define-method elmo-folder-list-subfolders
274
((folder elmo-maildir-folder) &optional one-level)
275
(let ((prefix (concat (elmo-folder-name-internal folder)
276
(unless (string= (elmo-folder-prefix-internal folder)
277
(elmo-folder-name-internal folder))
279
(elmo-list-subdirectories-ignore-regexp
280
"^\\(\\.\\.?\\|cur\\|tmp\\|new\\)$")
281
elmo-have-link-count)
283
(list (elmo-folder-name-internal folder))
284
(elmo-mapcar-list-of-list
285
(function (lambda (x) (concat prefix x)))
286
(elmo-list-subdirectories
287
(elmo-maildir-folder-directory-internal folder)
291
(defvar elmo-maildir-sequence-number-internal 0)
294
((>= emacs-major-version 19)
295
(defun elmo-maildir-make-unique-string ()
296
"This function generates a string that can be used as a unique
297
file name for maildir directories."
298
(let ((cur-time (current-time)))
299
(format "%.0f.%d_%d.%s"
301
(float 65536)) (cadr cur-time))
303
(incf elmo-maildir-sequence-number-internal)
305
((eq emacs-major-version 18)
306
;; A fake function for v18
307
(defun elmo-maildir-make-unique-string ()
308
"This function generates a string that can be used as a unique
309
file name for maildir directories."
310
(unless (fboundp 'float-to-string)
311
(load-library "float"))
312
(let ((time (current-time)))
316
(f+ (f* (f (car time))
321
(% (abs (random t)) 10000); dummy pid
324
(defun elmo-maildir-temporal-filename (basedir)
325
(let ((filename (expand-file-name
326
(concat "tmp/" (elmo-maildir-make-unique-string))
328
(unless (file-exists-p (file-name-directory filename))
329
(make-directory (file-name-directory filename)))
330
(while (file-exists-p filename)
331
;;; I don't want to wait.
335
(concat "tmp/" (elmo-maildir-make-unique-string))
339
(luna-define-method elmo-folder-append-buffer ((folder elmo-maildir-folder)
340
unread &optional number)
341
(let ((basedir (elmo-maildir-folder-directory-internal folder))
342
(src-buf (current-buffer))
346
(setq filename (elmo-maildir-temporal-filename basedir))
347
(setq dst-buf (current-buffer))
348
(with-current-buffer src-buf
349
(copy-to-buffer dst-buf (point-min) (point-max)))
350
(as-binary-output-file
351
(write-region (point-min) (point-max) filename nil 'no-msg))
352
;; add link from new.
353
(elmo-add-name-to-file
356
(concat "new/" (file-name-nondirectory filename))
359
;; If an error occured, return nil.
362
(luna-define-method elmo-folder-message-file-p ((folder elmo-maildir-folder))
365
(luna-define-method elmo-message-file-name ((folder elmo-maildir-folder)
367
(elmo-maildir-message-file-name
369
(elmo-map-message-location folder number)))
371
(luna-define-method elmo-folder-message-make-temp-file-p
372
((folder elmo-maildir-folder))
375
(luna-define-method elmo-folder-message-make-temp-files ((folder
380
(let ((temp-dir (elmo-folder-make-temporary-directory folder))
381
(cur-number (if start-number 0)))
382
(dolist (number numbers)
384
(elmo-message-file-name folder number)
386
(int-to-string (if start-number (incf cur-number) number))
390
(luna-define-method elmo-folder-append-messages :around
391
((folder elmo-maildir-folder)
392
src-folder numbers unread-marks &optional same-number)
393
(if (elmo-folder-message-file-p src-folder)
394
(let ((dir (elmo-maildir-folder-directory-internal folder))
397
(dolist (number numbers)
398
(setq filename (elmo-maildir-temporal-filename dir))
400
(elmo-message-file-name src-folder number)
402
(elmo-add-name-to-file
405
(concat "new/" (file-name-nondirectory filename))
407
(elmo-progress-notify 'elmo-folder-move-messages))
409
(luna-call-next-method)))
411
(luna-define-method elmo-map-folder-delete-messages
412
((folder elmo-maildir-folder) locations)
414
(dolist (location locations)
415
(setq file (elmo-maildir-message-file-name folder location))
417
(file-writable-p file)
418
(not (file-directory-p file)))
419
(delete-file file)))))
421
(luna-define-method elmo-map-message-fetch ((folder elmo-maildir-folder)
423
&optional section unseen)
424
(let ((file (elmo-maildir-message-file-name folder location)))
425
(when (file-exists-p file)
426
(insert-file-contents-as-binary file))))
428
(luna-define-method elmo-folder-exists-p ((folder elmo-maildir-folder))
429
(let ((basedir (elmo-maildir-folder-directory-internal folder)))
430
(and (file-directory-p (expand-file-name "new" basedir))
431
(file-directory-p (expand-file-name "cur" basedir))
432
(file-directory-p (expand-file-name "tmp" basedir)))))
434
(luna-define-method elmo-folder-diff ((folder elmo-maildir-folder)
436
(let* ((dir (elmo-maildir-folder-directory-internal folder))
437
(new-len (length (car (elmo-maildir-list-location dir "new"))))
438
(cur-len (length (car (elmo-maildir-list-location dir "cur")))))
439
(cons new-len (+ new-len cur-len))))
441
(luna-define-method elmo-folder-creatable-p ((folder elmo-maildir-folder))
444
(luna-define-method elmo-folder-writable-p ((folder elmo-maildir-folder))
447
(luna-define-method elmo-folder-create ((folder elmo-maildir-folder))
448
(let ((basedir (elmo-maildir-folder-directory-internal folder)))
451
(dolist (dir '("." "new" "cur" "tmp"))
452
(setq dir (expand-file-name dir basedir))
453
(or (file-directory-p dir)
455
(elmo-make-directory dir)
456
(set-file-modes dir 448))))
460
(luna-define-method elmo-folder-delete :before ((folder elmo-maildir-folder))
461
(let ((basedir (elmo-maildir-folder-directory-internal folder)))
463
(let ((tmp-files (directory-files
464
(expand-file-name "tmp" basedir)
466
;; Delete files in tmp.
467
(dolist (file tmp-files)
469
(dolist (dir '("new" "cur" "tmp" "."))
470
(setq dir (expand-file-name dir basedir))
471
(if (not (file-directory-p dir))
473
(elmo-delete-directory dir t)))
477
(luna-define-method elmo-folder-search ((folder elmo-maildir-folder)
478
condition &optional numbers)
480
(let* ((msgs (or numbers (elmo-folder-list-messages folder)))
482
case-fold-search matches
486
(setq number-list msgs)
487
(dolist (number numbers)
488
(if (elmo-file-field-condition-match
489
(elmo-message-file-name folder number)
490
condition number number-list)
491
(setq matches (cons number matches)))
493
(elmo-display-progress
494
'elmo-maildir-search "Searching..."
496
(nreverse matches))))
499
(product-provide (provide 'elmo-maildir) (require 'elmo-version))
501
;;; elmo-maildir.el ends here