* wl-expire.el (wl-summary-archive): Fixed;
[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    (mapconcat
91     'identity
92     (mapcar
93      'elmo-replace-string-as-filename
94      (split-string (elmo-localdir-folder-dir-name-internal folder)
95                    "/"))
96     "/")
97    (expand-file-name ;;"localdir"
98     (symbol-name (elmo-folder-type-internal folder))
99     elmo-msgdb-dir)))
100
101 (luna-define-method elmo-message-file-name ((folder
102                                              elmo-localdir-folder)
103                                             number)
104   (expand-file-name (int-to-string number)
105                     (elmo-localdir-folder-directory-internal folder)))
106
107 (luna-define-method elmo-folder-message-file-number-p ((folder
108                                                         elmo-localdir-folder))
109   t)
110
111 (luna-define-method elmo-folder-message-file-directory ((folder
112                                                          elmo-localdir-folder))
113   (elmo-localdir-folder-directory-internal folder))
114
115 (luna-define-method elmo-folder-message-make-temp-file-p
116   ((folder elmo-localdir-folder))
117   t)
118
119 (luna-define-method elmo-folder-message-make-temp-files ((folder
120                                                           elmo-localdir-folder)
121                                                          numbers
122                                                          &optional
123                                                          start-number)
124   (let ((temp-dir (elmo-folder-make-temp-dir folder))
125         (cur-number (or start-number 0)))
126     (dolist (number numbers)
127       (elmo-add-name-to-file
128        (expand-file-name
129         (int-to-string number)
130         (elmo-localdir-folder-directory-internal folder))
131        (expand-file-name
132         (int-to-string (if start-number cur-number number))
133         temp-dir))
134       (incf cur-number))
135     temp-dir))
136
137 (defun elmo-localdir-msgdb-create-entity (dir number)
138   (elmo-msgdb-create-overview-entity-from-file
139    number (expand-file-name (int-to-string number) dir)))
140
141 (luna-define-method elmo-folder-msgdb-create ((folder elmo-localdir-folder)
142                                               numbers
143                                               new-mark
144                                               already-mark
145                                               seen-mark
146                                               important-mark
147                                               seen-list)
148   (when numbers
149     (let ((dir (elmo-localdir-folder-directory-internal folder))
150           overview number-alist mark-alist entity message-id
151           num seen gmark
152           (i 0)
153           (len (length numbers)))
154       (message "Creating msgdb...")
155       (while numbers
156         (setq entity
157               (elmo-localdir-msgdb-create-entity
158                dir (car numbers)))
159         (if (null entity)
160             ()
161           (setq num (elmo-msgdb-overview-entity-get-number entity))
162           (setq overview
163                 (elmo-msgdb-append-element
164                  overview entity))
165           (setq message-id (elmo-msgdb-overview-entity-get-id entity))
166           (setq number-alist
167                 (elmo-msgdb-number-add number-alist
168                                        num
169                                        message-id))
170           (setq seen (member message-id seen-list))
171           (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
172                               (if (elmo-file-cache-exists-p message-id) ; XXX
173                                   (if seen
174                                       nil
175                                     already-mark)
176                                 (if seen
177                                     nil ;;seen-mark
178                                   new-mark))))
179               (setq mark-alist
180                     (elmo-msgdb-mark-append
181                      mark-alist
182                      num
183                      gmark))))
184         (when (> len elmo-display-progress-threshold)
185           (setq i (1+ i))
186           (elmo-display-progress
187            'elmo-localdir-msgdb-create-as-numbers "Creating msgdb..."
188            (/ (* i 100) len)))
189         (setq numbers (cdr numbers)))
190       (message "Creating msgdb...done")
191       (list overview number-alist mark-alist))))
192
193 (luna-define-method elmo-folder-list-subfolders ((folder elmo-localdir-folder)
194                                                  &optional one-level)
195   (elmo-mapcar-list-of-list
196    (lambda (x) (concat (elmo-folder-prefix-internal folder) x))
197    (elmo-list-subdirectories
198     (elmo-localdir-folder-path folder)
199     (or (elmo-localdir-folder-dir-name-internal folder) "")
200     one-level)))
201
202 (defsubst elmo-localdir-list-subr (folder &optional nonsort)
203   (let ((flist (mapcar 'string-to-int
204                        (directory-files
205                         (elmo-localdir-folder-directory-internal folder)
206                         nil "^[0-9]+$" t)))
207         (killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))))
208     (if nonsort
209         (cons (or (elmo-max-of-list flist) 0)
210               (if killed
211                   (- (length flist)
212                      (elmo-msgdb-killed-list-length killed))
213                 (length flist)))
214       (sort flist '<))))
215
216 (luna-define-method elmo-folder-append-buffer ((folder elmo-localdir-folder)
217                                                unread
218                                                &optional number)
219   (let ((filename (elmo-message-file-name
220                    folder
221                    (or number
222                        (1+ (car (elmo-folder-status folder)))))))
223     (when (file-writable-p filename)
224       (write-region-as-binary
225        (point-min) (point-max) filename nil 'no-msg)
226       t)))
227
228 (luna-define-method elmo-folder-append-messages :around
229   ((folder elmo-localdir-folder)
230    src-folder numbers unread-marks &optional same-number)
231   (if (elmo-folder-message-file-p src-folder)
232       (let ((dir (elmo-localdir-folder-directory-internal folder))
233             (succeeds numbers)
234             (next-num (1+ (car (elmo-folder-status folder)))))
235         (while numbers
236           (elmo-copy-file
237            (elmo-message-file-name src-folder (car numbers))
238            (expand-file-name
239             (int-to-string
240              (if same-number (car numbers) next-num))
241             dir))
242           (elmo-progress-notify 'elmo-folder-move-messages)
243           (if (and (setq numbers (cdr numbers))
244                    (not same-number))
245               (setq next-num
246                     (if (elmo-localdir-locked-p)
247                         ;; MDA is running.
248                         (1+ (car (elmo-folder-status folder)))
249                       (1+ next-num)))))
250         succeeds)
251     (luna-call-next-method)))
252
253 (luna-define-method elmo-folder-delete-messages ((folder elmo-localdir-folder)
254                                                  numbers)
255   (dolist (number numbers)
256     (elmo-localdir-delete-message folder number))
257   t)
258
259 (defun elmo-localdir-delete-message (folder number)
260   "Delete message in the FOLDER with NUMBER."
261   (let ((filename (elmo-message-file-name folder number)))
262     (when (and (string-match "[0-9]+" filename) ; for safety.
263                (file-exists-p filename)
264                (file-writable-p filename)
265                (not (file-directory-p filename)))
266       (delete-file filename)
267       t)))
268
269 (luna-define-method elmo-message-fetch-internal ((folder elmo-localdir-folder)
270                                                  number strategy
271                                                  &optional section unread)
272   (when (file-exists-p (elmo-message-file-name folder number))
273     (insert-file-contents-as-binary
274      (elmo-message-file-name folder number))))
275
276 (luna-define-method elmo-folder-list-messages-internal
277   ((folder elmo-localdir-folder) &optional nohide)
278   (elmo-localdir-list-subr folder))
279
280 (luna-define-method elmo-folder-status ((folder elmo-localdir-folder))
281   (elmo-localdir-list-subr folder t))
282
283 (luna-define-method elmo-folder-creatable-p ((folder elmo-localdir-folder))
284   t)
285
286 (luna-define-method elmo-folder-writable-p ((folder elmo-localdir-folder))
287   t)
288
289 (luna-define-method elmo-folder-create ((folder elmo-localdir-folder))
290   (let ((dir (elmo-localdir-folder-directory-internal folder)))
291     (if (file-directory-p dir)
292         ()
293       (if (file-exists-p dir)
294           (error "Create folder failed")
295         (elmo-make-directory dir))
296       t)))
297
298 (luna-define-method elmo-folder-delete ((folder elmo-localdir-folder))
299   (let ((dir (elmo-localdir-folder-directory-internal folder)))
300     (if (not (file-directory-p dir))
301         (error "No such directory: %s" dir)
302       (elmo-delete-directory dir t)
303       t)))
304
305 (luna-define-method elmo-folder-rename-internal ((folder elmo-localdir-folder)
306                                                  new-folder)
307   (let* ((old (elmo-localdir-folder-directory-internal folder))
308          (new (elmo-localdir-folder-directory-internal new-folder))
309          (new-dir (directory-file-name (file-name-directory new))))
310     (if (not (file-directory-p old))
311         (error "No such directory: %s" old)
312       (if (file-exists-p new)
313           (error "Already exists directory: %s" new)
314         (if (not (file-exists-p new-dir))
315             (elmo-make-directory new-dir))
316         (rename-file old new)
317         t))))
318
319 (defsubst elmo-localdir-field-condition-match (folder condition
320                                                       number number-list)
321   (elmo-file-field-condition-match
322    (expand-file-name (int-to-string number)
323                      (elmo-localdir-folder-directory-internal folder))
324    condition number number-list))
325
326 (luna-define-method elmo-folder-search ((folder elmo-localdir-folder)
327                                         condition &optional numbers)
328   (let* ((msgs (or numbers (elmo-folder-list-messages folder)))
329          (num (length msgs))
330          (i 0)
331          last cur number-list case-fold-search ret-val)
332     (cond
333      ;; short cut.
334      ((and (vectorp condition)
335            (string= (elmo-filter-key condition) "last"))
336       (nthcdr (max (- (length msgs)
337                       (string-to-int (elmo-filter-value condition)))
338                    0)
339               msgs))
340      ((and (vectorp condition)
341            (string= (elmo-filter-key condition) "first"))
342       (let ((rest (nthcdr (string-to-int (elmo-filter-value condition) )
343                           msgs)))
344         (mapcar '(lambda (x)
345                    (delete x msgs)) rest))
346       msgs)
347      (t
348       (setq number-list msgs)
349       (while msgs
350         (if (elmo-localdir-field-condition-match folder condition
351                                                  (car msgs) number-list)
352             (setq ret-val (cons (car msgs) ret-val)))
353         (when (> num elmo-display-progress-threshold)
354           (setq i (1+ i))
355           (setq cur (/ (* i 100) num))
356           (unless (eq cur last)
357             (elmo-display-progress
358              'elmo-localdir-search "Searching..."
359              cur)
360             (setq last cur)))
361         (setq msgs (cdr msgs)))
362       (nreverse ret-val)))))
363
364 (luna-define-method elmo-folder-pack-numbers ((folder elmo-localdir-folder))
365   (let* ((dir (elmo-localdir-folder-directory-internal folder))
366          (msgdb (elmo-folder-msgdb folder))
367          (onum-alist (elmo-msgdb-get-number-alist msgdb))
368          (omark-alist (elmo-msgdb-get-mark-alist msgdb))
369          (new-number 1)                 ; first ordinal position in localdir
370          flist onum mark new-mark-alist total)
371     (setq flist
372           (if elmo-pack-number-check-strict
373               (elmo-folder-list-messages folder) ; allow localnews
374             (mapcar 'car onum-alist)))
375     (setq total (length flist))
376     (while flist
377       (when (> total elmo-display-progress-threshold)
378         (elmo-display-progress
379          'elmo-folder-pack-numbers "Packing..."
380          (/ (* new-number 100) total)))
381       (setq onum (car flist))
382       (when (not (eq onum new-number))          ; why \=() is wrong..
383         (elmo-bind-directory
384          dir
385          ;; xxx  nfs,hardlink
386          (rename-file (int-to-string onum) (int-to-string new-number) t))
387         ;; update overview
388         (elmo-msgdb-overview-entity-set-number
389          (elmo-msgdb-overview-get-entity onum msgdb)
390          new-number)
391         ;; update number-alist
392         (setcar (assq onum onum-alist) new-number))
393       ;; update mark-alist
394       (when (setq mark (cadr (assq onum omark-alist)))
395         (setq new-mark-alist
396               (elmo-msgdb-mark-append
397                new-mark-alist
398                new-number mark)))
399       (setq new-number (1+ new-number))
400       (setq flist (cdr flist)))
401     (message "Packing...done")
402     (elmo-folder-set-msgdb-internal
403      folder
404      (list (elmo-msgdb-get-overview msgdb)
405            onum-alist
406            new-mark-alist
407            ;; remake hash table
408            (elmo-msgdb-make-overview-hashtb
409             (elmo-msgdb-get-overview msgdb))))))
410
411 (luna-define-method elmo-folder-message-file-p ((folder elmo-localdir-folder))
412   t)
413
414 (luna-define-method elmo-message-file-name ((folder elmo-localdir-folder)
415                                             number)
416   (expand-file-name
417    (int-to-string number)
418    (elmo-localdir-folder-directory-internal folder)))
419
420 (defun elmo-localdir-locked-p ()
421   (if elmo-localdir-lockfile-list
422       (let ((lock elmo-localdir-lockfile-list))
423         (catch 'found
424           (while lock
425             (if (file-exists-p (car lock))
426                 (throw 'found t))
427             (setq lock (cdr lock)))))))
428
429 (require 'product)
430 (product-provide (provide 'elmo-localdir) (require 'elmo-version))
431
432 ;;; elmo-localdir.el ends here