Synch up with main trunk and so on.
[elisp/wanderlust.git] / elmo / elmo-localdir.el
1 ;;; elmo-localdir.el -- Localdir Interface for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
5 ;; Copyright (C) 1999,2000      Kenichi OKADA  <okada@opaopa.org>
6
7 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
8 ;;      Masahiro MURATA <muse@ba2.so-net.ne.jp>
9 ;;      Kenichi OKADA <okada@opaopa.org>
10 ;; Keywords: mail, net news
11
12 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
13
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; any later version.
18 ;;
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 ;; GNU General Public License for more details.
23 ;;
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
28 ;;
29
30 ;;; Commentary:
31 ;; 
32
33 ;;; Code:
34 ;; 
35 (eval-when-compile (require 'cl))
36
37 (require 'elmo-msgdb)
38 (require 'elmo)
39
40 (defcustom elmo-localdir-folder-path "~/Mail"
41   "*Local mail directory (MH format) path."
42   :type 'directory
43   :group 'elmo)
44
45 (defvar elmo-localdir-lockfile-list nil)
46
47 ;;; ELMO Local directory folder
48 (eval-and-compile
49   (luna-define-class elmo-localdir-folder (elmo-folder)
50                      (dir-name directory))
51   (luna-define-internal-accessors 'elmo-localdir-folder))
52
53 ;;; elmo-localdir specific methods.
54 (luna-define-generic elmo-localdir-folder-path (folder)
55   "Return local directory path of the FOLDER.")
56
57 (luna-define-generic elmo-localdir-folder-name (folder name)
58   "Return directory NAME for FOLDER.")
59
60 (luna-define-method elmo-localdir-folder-path ((folder elmo-localdir-folder))
61   elmo-localdir-folder-path)
62
63 (luna-define-method elmo-localdir-folder-name ((folder elmo-localdir-folder)
64                                                name)
65   name)
66
67 (luna-define-method elmo-folder-initialize ((folder
68                                              elmo-localdir-folder)
69                                             name)
70   (elmo-localdir-folder-set-dir-name-internal folder name)
71   (if (file-name-absolute-p name)
72       (elmo-localdir-folder-set-directory-internal
73        folder
74        (expand-file-name name))
75     (elmo-localdir-folder-set-directory-internal
76      folder
77      (expand-file-name
78       (elmo-localdir-folder-name folder name)
79       (elmo-localdir-folder-path folder))))
80   folder)
81
82 ;; open, check, commit, and close are generic.
83
84 (luna-define-method elmo-folder-exists-p ((folder elmo-localdir-folder))
85   (file-directory-p (elmo-localdir-folder-directory-internal folder)))
86
87 (luna-define-method elmo-folder-expand-msgdb-path ((folder
88                                                     elmo-localdir-folder))
89   (expand-file-name 
90    (elmo-replace-string-as-filename 
91     (elmo-localdir-folder-dir-name-internal folder))
92    (expand-file-name ;;"localdir"
93     (symbol-name (elmo-folder-type-internal folder))
94     elmo-msgdb-dir)))
95
96 (luna-define-method elmo-message-file-name ((folder
97                                              elmo-localdir-folder)
98                                             number)
99   (expand-file-name (int-to-string number)
100                     (elmo-localdir-folder-directory-internal folder)))
101
102 (luna-define-method elmo-folder-message-file-number-p ((folder
103                                                         elmo-localdir-folder))
104   t)
105
106 (luna-define-method elmo-folder-message-file-directory ((folder
107                                                          elmo-localdir-folder))
108   (elmo-localdir-folder-directory-internal folder))
109
110 (luna-define-method elmo-folder-message-make-temp-file-p
111   ((folder elmo-localdir-folder))
112   t)
113
114 (luna-define-method elmo-folder-message-make-temp-files ((folder
115                                                           elmo-localdir-folder)
116                                                          numbers
117                                                          &optional
118                                                          start-number)
119   (let ((temp-dir (elmo-folder-make-temp-dir folder))
120         (cur-number (if start-number 0)))
121     (dolist (number numbers)
122       (elmo-add-name-to-file
123        (expand-file-name
124         (int-to-string number)
125         (elmo-localdir-folder-directory-internal folder))
126        (expand-file-name
127         (int-to-string (if start-number (incf cur-number) number))
128         temp-dir)))
129     temp-dir))
130
131 (defun elmo-localdir-msgdb-create-entity (dir number)
132   (elmo-msgdb-create-overview-entity-from-file
133    number (expand-file-name (int-to-string number) dir)))
134
135 (luna-define-method elmo-folder-msgdb-create ((folder elmo-localdir-folder)
136                                               numbers
137                                               new-mark
138                                               already-mark
139                                               seen-mark
140                                               important-mark
141                                               seen-list)
142   (when numbers
143     (let ((dir (elmo-localdir-folder-directory-internal folder))
144           overview number-alist mark-alist entity message-id
145           num seen gmark
146           (i 0)
147           (len (length numbers)))
148       (message "Creating msgdb...")
149       (while numbers
150         (setq entity
151               (elmo-localdir-msgdb-create-entity
152                dir (car numbers)))
153         (if (null entity)
154             ()
155           (setq num (elmo-msgdb-overview-entity-get-number entity))
156           (setq overview
157                 (elmo-msgdb-append-element
158                  overview entity))
159           (setq message-id (elmo-msgdb-overview-entity-get-id entity))
160           (setq number-alist
161                 (elmo-msgdb-number-add number-alist
162                                        num
163                                        message-id))
164           (setq seen (member message-id seen-list))
165           (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
166                               (if (elmo-file-cache-exists-p message-id) ; XXX
167                                   (if seen
168                                       nil
169                                     already-mark)
170                                 (if seen
171                                     nil ;;seen-mark
172                                   new-mark))))
173               (setq mark-alist
174                     (elmo-msgdb-mark-append
175                      mark-alist
176                      num
177                      gmark))))
178         (when (> len elmo-display-progress-threshold)
179           (setq i (1+ i))
180           (elmo-display-progress
181            'elmo-localdir-msgdb-create-as-numbers "Creating msgdb..."
182            (/ (* i 100) len)))
183         (setq numbers (cdr numbers)))
184       (message "Creating msgdb...done")
185       (list overview number-alist mark-alist))))
186
187 (luna-define-method elmo-folder-list-subfolders ((folder elmo-localdir-folder)
188                                                  &optional one-level)
189   (mapcar
190    (lambda (x) (concat (elmo-folder-prefix-internal folder) x))
191    (elmo-list-subdirectories
192     (elmo-localdir-folder-path folder)
193     (or (elmo-localdir-folder-dir-name-internal folder) "")
194     one-level)))
195
196 (defsubst elmo-localdir-list-subr (folder &optional nonsort)
197   (let ((flist (mapcar 'string-to-int
198                        (directory-files 
199                         (elmo-localdir-folder-directory-internal folder)
200                         nil "^[0-9]+$" t)))
201         (killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))))
202     (if nonsort
203         (cons (or (elmo-max-of-list flist) 0)
204               (if killed
205                   (- (length flist)
206                      (elmo-msgdb-killed-list-length killed))
207                 (length flist)))
208       (sort flist '<))))
209
210 (luna-define-method elmo-folder-append-buffer ((folder elmo-localdir-folder)
211                                                unread
212                                                &optional number)
213   (let ((filename (elmo-message-file-name
214                    folder
215                    (or number
216                        (1+ (car (elmo-folder-status folder)))))))
217     (if (file-writable-p filename)
218         (write-region-as-binary
219          (point-min) (point-max) filename nil 'no-msg))
220     t))
221
222 (luna-define-method elmo-folder-append-messages :around ((folder elmo-localdir-folder)
223                                                          src-folder numbers
224                                                          unread-marks
225                                                          &optional same-number)
226   (if (elmo-folder-message-file-p src-folder)
227       (let ((dir (elmo-localdir-folder-directory-internal folder))
228             (succeeds numbers)
229             (next-num (1+ (car (elmo-folder-status folder)))))
230         (while numbers
231           (elmo-copy-file
232            (elmo-message-file-name src-folder (car numbers))
233            (expand-file-name
234             (int-to-string
235              (if same-number (car numbers) next-num))
236             dir))
237           (if (and (setq numbers (cdr numbers))
238                    (not same-number))
239               (setq next-num
240                     (if (elmo-localdir-locked-p)
241                         ;; MDA is running.
242                         (1+ (car (elmo-folder-status folder)))
243                       (1+ next-num)))))
244         succeeds)
245     (luna-call-next-method)))
246
247 (luna-define-method elmo-folder-delete-messages ((folder elmo-localdir-folder)
248                                                  numbers)
249   (dolist (number numbers)
250     (elmo-localdir-delete-message folder number))
251   t)
252
253 (defun elmo-localdir-delete-message (folder number)
254   "Delete message in the FOLDER with NUMBER."
255   (let ((filename (elmo-message-file-name folder number)))
256     (when (and (string-match "[0-9]+" filename) ; for safety.
257                (file-exists-p filename)
258                (file-writable-p filename)
259                (not (file-directory-p filename)))
260       (delete-file filename)
261       t)))
262
263 (luna-define-method elmo-message-fetch ((folder elmo-localdir-folder)
264                                         number strategy
265                                         &optional section outbuf unseen)
266   ;; strategy, section, unseen is ignored.
267   (if outbuf
268       (with-current-buffer outbuf
269         (erase-buffer)
270         (when (file-exists-p (elmo-message-file-name folder number))
271           (insert-file-contents-as-binary
272            (elmo-message-file-name folder number))
273           (elmo-delete-cr-buffer))
274         t)
275     (with-temp-buffer
276       (when (file-exists-p (elmo-message-file-name folder number))
277         (insert-file-contents-as-binary (elmo-message-file-name folder number))
278         (elmo-delete-cr-buffer))
279       (buffer-string))))
280
281 (luna-define-method elmo-folder-list-messages-internal
282   ((folder elmo-localdir-folder) &optional nohide)
283   (elmo-localdir-list-subr folder))
284
285 (luna-define-method elmo-folder-status ((folder elmo-localdir-folder))
286   (elmo-localdir-list-subr folder t))
287
288 (luna-define-method elmo-folder-creatable-p ((folder elmo-localdir-folder))
289   t)
290
291 (luna-define-method elmo-folder-create ((folder elmo-localdir-folder))
292   (let ((dir (elmo-localdir-folder-directory-internal folder)))
293     (if (file-directory-p dir)
294         ()
295       (if (file-exists-p dir)
296           (error "Create folder failed")
297         (elmo-make-directory dir))
298       t)))
299
300 (luna-define-method elmo-folder-delete ((folder elmo-localdir-folder))
301   (let ((dir (elmo-localdir-folder-directory-internal folder)))
302     (if (not (file-directory-p dir))
303         (error "No such directory: %s" dir)
304       (elmo-delete-directory dir t)
305       t)))
306
307 (luna-define-method elmo-folder-rename-internal ((folder elmo-localdir-folder)
308                                                  new-folder)
309   (let* ((old (elmo-localdir-folder-directory-internal folder))
310          (new (elmo-localdir-folder-directory-internal folder))
311          (new-dir (directory-file-name (file-name-directory new))))
312     (if (not (file-directory-p old))
313         (error "No such directory: %s" old)
314       (if (file-exists-p new)
315           (error "Already exists directory: %s" new)
316         (if (not (file-exists-p new-dir))
317             (elmo-make-directory new-dir))
318         (rename-file old new)
319         t))))
320
321 (defsubst elmo-localdir-field-condition-match (folder condition
322                                                       number number-list)
323   (elmo-file-field-condition-match
324    (expand-file-name (int-to-string number)
325                      (elmo-localdir-folder-directory-internal folder))
326    condition number number-list))
327
328 (luna-define-method elmo-folder-search ((folder elmo-localdir-folder)
329                                         condition &optional numbers)
330   (let* ((msgs (or numbers (elmo-folder-list-messages folder)))
331          (num (length msgs))
332          (i 0)
333          number-list case-fold-search ret-val)
334     (setq number-list msgs)
335     (while msgs
336       (if (elmo-localdir-field-condition-match folder condition
337                                                (car msgs) number-list)
338           (setq ret-val (cons (car msgs) ret-val)))
339       (when (> num elmo-display-progress-threshold)
340         (setq i (1+ i))
341         (elmo-display-progress
342          'elmo-localdir-search "Searching..."
343          (/ (* i 100) num)))
344       (setq msgs (cdr msgs)))
345     (nreverse ret-val)))
346
347 (luna-define-method elmo-folder-pack-numbers ((folder elmo-localdir-folder))
348   (let* ((dir (elmo-localdir-folder-directory-internal folder))
349          (msgdb (elmo-folder-msgdb folder))
350          (onum-alist (elmo-msgdb-get-number-alist msgdb))
351          (omark-alist (elmo-msgdb-get-mark-alist (elmo-folder-msgdb msgdb)))
352          (new-number 1)                 ; first ordinal position in localdir
353          flist onum mark new-mark-alist total)
354     (setq flist
355           (if elmo-pack-number-check-strict
356               (elmo-folder-list-messages folder) ; allow localnews
357             (mapcar 'car onum-alist)))
358     (setq total (length flist))
359     (while flist
360       (when (> total elmo-display-progress-threshold)
361         (elmo-display-progress
362          'elmo-folder-pack-numbers "Packing..."
363          (/ (* new-number 100) total)))
364       (setq onum (car flist))
365       (when (not (eq onum new-number))          ; why \=() is wrong..
366         (elmo-bind-directory
367          dir
368          ;; xxx  nfs,hardlink
369          (rename-file (int-to-string onum) (int-to-string new-number) t))
370         ;; update overview
371         (elmo-msgdb-overview-entity-set-number
372          (elmo-msgdb-overview-get-entity onum msgdb)
373          new-number)
374         ;; update number-alist
375         (setcar (assq onum onum-alist) new-number))
376       ;; update mark-alist
377       (when (setq mark (cadr (assq onum omark-alist)))
378         (setq new-mark-alist
379               (elmo-msgdb-mark-append
380                new-mark-alist
381                new-number mark)))
382       (setq new-number (1+ new-number))
383       (setq flist (cdr flist)))
384     (message "Packing...done")
385     (elmo-folder-set-msgdb-internal
386      folder
387      (list (elmo-msgdb-get-overview msgdb)
388            onum-alist
389            new-mark-alist
390            ;; remake hash table
391            (elmo-msgdb-make-overview-hashtb
392             (elmo-msgdb-get-overview msgdb))))))
393
394 (luna-define-method elmo-folder-message-file-p ((folder elmo-localdir-folder))
395   t)
396
397 (luna-define-method elmo-message-file-name ((folder elmo-localdir-folder)
398                                             number)
399   (expand-file-name
400    (int-to-string number)
401    (elmo-localdir-folder-directory-internal folder)))
402
403 (defun elmo-localdir-locked-p ()
404   (if elmo-localdir-lockfile-list
405       (let ((lock elmo-localdir-lockfile-list))
406         (catch 'found
407           (while lock
408             (if (file-exists-p (car lock))
409                 (throw 'found t))
410             (setq lock (cdr lock)))))))
411
412 (require 'product)
413 (product-provide (provide 'elmo-localdir) (require 'elmo-version))
414
415 ;;; elmo-localdir.el ends here