46e4b6179157d155be6439e192e1277df84f985d
[elisp/wanderlust.git] / elmo / modb-standard.el
1 ;;; modb-standard.el --- Standartd Implement of MODB.
2
3 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;;      Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news
8
9 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
10
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)
14 ;; any later version.
15 ;;
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.
20 ;;
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.
25 ;;
26
27 ;;; Commentary:
28 ;;
29
30 ;;; Code:
31 ;;
32 (eval-when-compile (require 'cl))
33
34 (require 'elmo-util)
35 (require 'modb)
36
37 (defcustom modb-standard-divide-number 500
38   "*Standard modb divide entity number."
39   :type '(choice (const :tag "Not divide" nil)
40                  number)
41   :group 'elmo)
42
43 (defcustom modb-standard-economize-entity-size t
44   "*Economize message entity size.
45 When non-nil, redundunt message-id string are not saved."
46   :type 'boolean
47   :group 'elmo)
48
49 (defvar modb-standard-entity-filename "entity"
50   "Message entity database.")
51
52 (defvar modb-standard-flag-filename "flag"
53   "Message number <=> Flag status database.")
54
55 (defvar modb-standard-msgid-filename "msgid"
56   "Message number <=> Message-Id database.")
57
58 (eval-and-compile
59   (luna-define-class modb-standard (modb-generic)
60                      (number-list       ; sorted list of message numbers.
61                       entity-map        ; number, msg-id -> entity mapping.
62                       flag-map          ; number -> flag-list mapping
63                       flag-count        ; list of (FLAG . COUNT)
64                       ))
65   (luna-define-internal-accessors 'modb-standard))
66
67 ;; for internal use only
68 (defsubst modb-standard-key (number)
69   (concat "#" (number-to-string number)))
70
71 (defsubst modb-standard-entity-id (entity)
72   (if (eq 'autoload (car-safe entity))
73       (cddr entity)
74     (elmo-msgdb-message-entity-field
75      (elmo-message-entity-handler entity)
76      entity 'message-id)))
77
78 (defsubst modb-standard-entity-map (modb)
79   (or (modb-standard-entity-map-internal modb)
80       (modb-standard-set-entity-map-internal
81        modb
82        (elmo-make-hash (elmo-msgdb-length modb)))))
83
84 (defsubst modb-standard-flag-map (modb)
85   (or (modb-standard-flag-map-internal modb)
86       (modb-standard-set-flag-map-internal
87        modb
88        (elmo-make-hash (elmo-msgdb-length modb)))))
89
90 (defsubst modb-standard-set-message-modified (modb number)
91   (if modb-standard-divide-number
92       (let ((section (/ number modb-standard-divide-number))
93             (modified (modb-generic-message-modified-internal modb)))
94         (unless (memq section modified)
95           (modb-generic-set-message-modified-internal
96            modb (cons section modified))))
97     (modb-generic-set-message-modified-internal modb t)))
98
99 (defsubst modb-standard-set-flag-modified (modb number)
100   (modb-generic-set-flag-modified-internal modb t))
101
102 (defsubst modb-standard-message-flags (modb number)
103   (cdr (elmo-get-hash-val (modb-standard-key number)
104                           (modb-standard-flag-map-internal modb))))
105
106 (defsubst modb-standard-match-flags (check-flags flags)
107   (catch 'done
108     (while check-flags
109       (when (memq (car check-flags) flags)
110         (throw 'done t))
111       (setq check-flags (cdr check-flags)))))
112
113 (defsubst modb-standard-countup-flags (modb flags &optional delta)
114   (let ((flag-count (modb-standard-flag-count-internal modb))
115         (delta (or delta 1))
116         elem)
117     (dolist (flag flags)
118       (if (setq elem (assq flag flag-count))
119           (setcdr elem (+ (cdr elem) delta))
120         (setq flag-count (cons (cons flag delta) flag-count))))
121     (modb-standard-set-flag-count-internal modb flag-count)))
122
123 ;; save and load functions
124 (defun modb-standard-load-msgid (modb path)
125   (let* ((alist (elmo-object-load
126                  (expand-file-name modb-standard-msgid-filename path)))
127          (table (or (modb-standard-entity-map-internal modb)
128                     (elmo-make-hash (length alist))))
129          numbers info)
130     (dolist (pair alist)
131       (setq info (cons 'autoload pair))
132       (elmo-set-hash-val (modb-standard-key (car pair)) info table)
133       (elmo-set-hash-val (cdr pair) info table)
134       (setq numbers (cons (car pair) numbers)))
135     (modb-standard-set-number-list-internal modb (nreverse numbers))
136     (modb-standard-set-entity-map-internal modb table)))
137
138 (defun modb-standard-save-msgid (modb path)
139   (let ((table (modb-standard-entity-map-internal modb))
140         entity alist)
141     (dolist (number (modb-standard-number-list-internal modb))
142       (setq entity (elmo-get-hash-val (modb-standard-key number) table))
143       (setq alist (cons (cons number (modb-standard-entity-id entity))
144                         alist)))
145     (elmo-object-save
146      (expand-file-name modb-standard-msgid-filename path)
147      (nreverse alist))))
148
149 (defun modb-standard-load-flag (modb path)
150   (let ((table (or (modb-standard-flag-map-internal modb)
151                    (elmo-make-hash (elmo-msgdb-length modb)))))
152     (dolist (info (elmo-object-load
153                    (expand-file-name modb-standard-flag-filename path)))
154       (modb-standard-countup-flags modb (cdr info))
155       (elmo-set-hash-val (modb-standard-key (car info)) info table))
156     (modb-standard-set-flag-map-internal modb table)))
157
158 (defun modb-standard-save-flag (modb path)
159   (let (table flist info)
160     (when (setq table (modb-standard-flag-map-internal modb))
161       (mapatoms
162        (lambda (atom)
163          (setq info (symbol-value atom))
164          (when (cdr info)
165            (setq flist (cons info flist))))
166        table))
167     (elmo-object-save
168      (expand-file-name modb-standard-flag-filename path)
169      flist)))
170
171 (defsubst modb-standard-entity-filename (section)
172   (if section
173       (concat modb-standard-entity-filename
174               "-"
175               (number-to-string section))
176     modb-standard-entity-filename))
177
178 (defsubst modb-standard-loaded-message-id (msgdb number)
179   "Get message-id for autoloaded entity."
180   (let ((ret (elmo-get-hash-val
181               (modb-standard-key number)
182               (modb-standard-entity-map-internal msgdb))))
183     (cond
184      ((and ret (eq (car-safe ret) 'autoload))
185       (cdr (cdr ret))) ; message-id.
186      ((and ret (stringp (car-safe ret)))
187       ;; Already loaded.
188       (car ret))
189      ((null ret)
190       ;; Garbage entity.
191       (elmo-clear-hash-val (modb-standard-key number)
192                            (modb-standard-entity-map-internal msgdb))
193       nil)                              ; return nil.
194      (t (error "Internal error: invalid msgdb status")))))
195
196 (defun modb-standard-load-entity (modb path &optional section)
197   (let ((table (or (modb-standard-entity-map-internal modb)
198                    (elmo-make-hash (elmo-msgdb-length modb))))
199         number msgid)
200     (dolist (entity (elmo-object-load
201                      (expand-file-name
202                       (modb-standard-entity-filename section)
203                       path)))
204       (setq number (elmo-msgdb-message-entity-number
205                     (elmo-message-entity-handler entity)
206                     entity)
207             msgid (modb-standard-loaded-message-id modb number))
208       (when msgid
209         (setcar entity msgid)
210         (elmo-set-hash-val msgid entity table)
211         (elmo-set-hash-val (modb-standard-key number) entity table)))
212     (modb-standard-set-entity-map-internal modb table)))
213
214 (defsubst modb-standard-save-entity-1 (modb path &optional section)
215   (let ((table (modb-standard-entity-map-internal modb))
216         (filename (expand-file-name
217                    (modb-standard-entity-filename section) path))
218         entity entities)
219     (dolist (number (modb-standard-number-list-internal modb))
220       (when (and (or (null section)
221                      (= section (/ number modb-standard-divide-number)))
222                  (setq entity (elmo-msgdb-message-entity modb number)))
223         (when modb-standard-economize-entity-size
224           (when (stringp (car entity))
225             (setq entity (cons t (cdr entity)))))
226         (setq entities (cons entity entities))))
227     (if entities
228         (elmo-object-save filename entities)
229       (ignore-errors (delete-file filename)))))
230
231 (defun modb-standard-save-entity (modb path)
232   (let ((sections (modb-generic-message-modified-internal modb)))
233     (cond ((listp sections)
234            (dolist (section sections)
235              (modb-standard-save-entity-1 modb path section)))
236           (sections
237            (modb-standard-save-entity-1 modb path)))))
238
239 ;;; Implement
240 ;;
241 (luna-define-method elmo-msgdb-load ((msgdb modb-standard))
242   (let ((inhibit-quit t)
243         (path (elmo-msgdb-location msgdb)))
244     (when (file-exists-p (expand-file-name modb-standard-flag-filename path))
245       (modb-standard-load-msgid msgdb path)
246       (modb-standard-load-flag msgdb path)
247       (unless modb-standard-divide-number
248         (modb-standard-load-entity msgdb path))
249       t)))
250
251 (luna-define-method elmo-msgdb-save ((msgdb modb-standard))
252   (let ((path (elmo-msgdb-location msgdb))
253         (inhibit-quit t))
254     (when (elmo-msgdb-message-modified-p msgdb)
255       (modb-standard-save-msgid  msgdb path)
256       (modb-standard-save-entity msgdb path)
257       (modb-generic-set-message-modified-internal msgdb nil))
258     (when (elmo-msgdb-flag-modified-p msgdb)
259       (modb-standard-save-flag msgdb path)
260       (modb-generic-set-flag-modified-internal msgdb nil))))
261
262 (luna-define-method elmo-msgdb-append :around ((msgdb modb-standard)
263                                                msgdb-append)
264   (when (> (elmo-msgdb-length msgdb-append) 0)
265     (if (eq (luna-class-name msgdb-append) 'modb-standard)
266         (let ((numbers (modb-standard-number-list-internal msgdb-append))
267               duplicates)
268           ;; number-list
269           (modb-standard-set-number-list-internal
270            msgdb
271            (nconc (modb-standard-number-list-internal msgdb)
272                   numbers))
273           ;; entity-map
274           (let ((table (modb-standard-entity-map msgdb))
275                 entity msg-id)
276             (dolist (number numbers)
277               (setq entity (elmo-msgdb-message-entity msgdb-append number)
278                     msg-id (modb-standard-entity-id entity))
279               (if (elmo-get-hash-val msg-id table)
280                   (setq duplicates (cons number duplicates))
281                 (elmo-set-hash-val msg-id entity table))
282               (elmo-set-hash-val (modb-standard-key number)
283                                  entity
284                                  table)))
285           ;; flag-map
286           (let ((table (modb-standard-flag-map msgdb)))
287             (mapatoms
288              (lambda (atom)
289                (elmo-set-hash-val (symbol-name atom)
290                                   (symbol-value atom)
291                                   table))
292              (modb-standard-flag-map msgdb-append)))
293           ;; flag-count
294           (dolist (pair (modb-standard-flag-count-internal msgdb-append))
295             (modb-standard-countup-flags msgdb (list (car pair)) (cdr pair)))
296           ;; modification flags
297           (dolist (number (modb-standard-number-list-internal msgdb-append))
298             (modb-standard-set-message-modified msgdb number)
299             (modb-standard-set-flag-modified msgdb number))
300           duplicates)
301       (luna-call-next-method))))
302
303 (luna-define-method elmo-msgdb-clear :after ((msgdb modb-standard))
304   (modb-standard-set-number-list-internal msgdb nil)
305   (modb-standard-set-entity-map-internal msgdb nil)
306   (modb-standard-set-flag-map-internal msgdb nil)
307   (modb-standard-set-flag-count-internal msgdb nil))
308
309 (luna-define-method elmo-msgdb-length ((msgdb modb-standard))
310   (length (modb-standard-number-list-internal msgdb)))
311
312 (luna-define-method elmo-msgdb-flag-available-p ((msgdb modb-standard) flag)
313   t)
314
315 (luna-define-method elmo-msgdb-flags ((msgdb modb-standard) number)
316   (modb-standard-message-flags msgdb number))
317
318 (luna-define-method elmo-msgdb-set-flag ((msgdb modb-standard)
319                                          number flag)
320   (case flag
321     (read
322      (elmo-msgdb-unset-flag msgdb number 'unread))
323     (uncached
324      (elmo-msgdb-unset-flag msgdb number 'cached))
325     (t
326      (let ((cur-flags (modb-standard-message-flags msgdb number))
327            new-flags diff)
328        (unless (memq flag cur-flags)
329          (setq new-flags (cons flag cur-flags))
330          (setq diff (elmo-list-diff-nonsortable new-flags cur-flags))
331          (modb-standard-countup-flags msgdb (car diff))
332          (modb-standard-countup-flags msgdb (cadr diff) -1)
333          (elmo-set-hash-val (modb-standard-key number)
334                             (cons number new-flags)
335                             (modb-standard-flag-map msgdb))
336          (modb-standard-set-flag-modified msgdb number))))))
337
338 (luna-define-method elmo-msgdb-unset-flag ((msgdb modb-standard)
339                                            number flag)
340   (case flag
341     (read
342      (elmo-msgdb-set-flag msgdb number 'unread))
343     (uncached
344      (elmo-msgdb-set-flag msgdb number 'cached))
345     (all
346      (modb-standard-countup-flags msgdb
347                                   (modb-standard-message-flags msgdb number)
348                                   -1)
349      (elmo-clear-hash-val (modb-standard-key number)
350                           (modb-standard-flag-map msgdb)))
351     (t
352      (let ((cur-flags (modb-standard-message-flags msgdb number))
353            (inhibit-quit t)
354            new-flags diff)
355        (when (memq flag cur-flags)
356          (setq new-flags (delq flag (copy-sequence cur-flags)))
357          (setq diff (elmo-list-diff-nonsortable new-flags cur-flags))
358          (modb-standard-countup-flags msgdb (car diff))
359          (modb-standard-countup-flags msgdb (cadr diff) -1)
360          (elmo-set-hash-val (modb-standard-key number)
361                             (cons number new-flags)
362                             (modb-standard-flag-map msgdb))
363          (modb-standard-set-flag-modified msgdb number))
364        (when (eq flag 'unread)
365          (elmo-msgdb-unset-flag msgdb number 'new))))))
366
367 (luna-define-method elmo-msgdb-flag-count ((msgdb modb-standard))
368   (modb-standard-flag-count-internal msgdb))
369
370 (luna-define-method elmo-msgdb-list-messages ((msgdb modb-standard))
371   (copy-sequence
372    (modb-standard-number-list-internal msgdb)))
373
374 (luna-define-method elmo-msgdb-list-flagged ((msgdb modb-standard) flag)
375   (let (entry matched)
376     (case flag
377       (read
378        (dolist (number (modb-standard-number-list-internal msgdb))
379          (unless (memq 'unread (modb-standard-message-flags msgdb number))
380            (setq matched (cons number matched)))))
381       (uncached
382        (dolist (number (modb-standard-number-list-internal msgdb))
383          (unless (memq 'cached (modb-standard-message-flags msgdb number))
384            (setq matched (cons number matched)))))
385       (any
386        (mapatoms
387         (lambda (atom)
388           (setq entry (symbol-value atom))
389           (unless (and (eq (length (cdr entry)) 1)
390                        (eq (car (cdr entry)) 'cached))
391             ;; If there is a flag other than cached, then the message
392             ;; matches to `any'.
393             (setq matched (cons (car entry) matched))))
394         (modb-standard-flag-map msgdb)))
395       (digest
396        (let ((flags (append elmo-digest-flags
397                             (elmo-get-global-flags t t))))
398          (mapatoms
399           (lambda (atom)
400             (setq entry (symbol-value atom))
401             (when (modb-standard-match-flags flags (cdr entry))
402               (setq matched (cons (car entry) matched))))
403           (modb-standard-flag-map msgdb))))
404       (t
405        (mapatoms
406         (lambda (atom)
407           (setq entry (symbol-value atom))
408           (when (memq flag (cdr entry))
409             (setq matched (cons (car entry) matched))))
410         (modb-standard-flag-map msgdb))))
411     matched))
412
413 (luna-define-method elmo-msgdb-search ((msgdb modb-standard)
414                                        condition &optional numbers)
415   (if (vectorp condition)
416       (let ((key (elmo-filter-key condition))
417             results)
418         (cond
419          ((and (string= key "flag")
420                (eq (elmo-filter-type condition) 'match))
421           (setq results (elmo-msgdb-list-flagged
422                          msgdb
423                          (intern (elmo-filter-value condition))))
424           (if numbers
425               (elmo-list-filter numbers results)
426             results))
427          ((member key '("first" "last"))
428           (let* ((numbers (or numbers
429                               (modb-standard-number-list-internal msgdb)))
430                  (len (length numbers))
431                  (lastp (string= key "last"))
432                  (value (string-to-number (elmo-filter-value condition))))
433             (when (eq (elmo-filter-type condition) 'unmatch)
434               (setq lastp (not lastp)
435                     value (- len value)))
436             (if lastp
437                 (nthcdr (max (- len value) 0) numbers)
438               (when (> value 0)
439                 (let* ((numbers (copy-sequence numbers))
440                        (last (nthcdr (1- value) numbers)))
441                   (when last
442                     (setcdr last nil))
443                   numbers)))))
444          (t
445           t)))
446     t))
447
448 (luna-define-method elmo-msgdb-append-entity ((msgdb modb-standard)
449                                               entity &optional flags)
450   (when entity
451     (let ((number (elmo-msgdb-message-entity-number
452                    (elmo-message-entity-handler entity) entity))
453           (msg-id (elmo-msgdb-message-entity-field
454                    (elmo-message-entity-handler entity) entity 'message-id))
455           duplicate)
456       (when msg-id
457         ;; number-list
458         (modb-standard-set-number-list-internal
459          msgdb
460          (nconc (modb-standard-number-list-internal msgdb)
461                 (list number)))
462         ;; entity-map
463         (let ((table (modb-standard-entity-map msgdb)))
464           (setq duplicate (elmo-get-hash-val msg-id table))
465           (elmo-set-hash-val (modb-standard-key number) entity table)
466           (elmo-set-hash-val msg-id entity table))
467         ;; modification flags
468         (modb-standard-set-message-modified msgdb number)
469         ;; flag-map
470         (when flags
471           (elmo-set-hash-val
472            (modb-standard-key number)
473            (cons number flags)
474            (modb-standard-flag-map msgdb))
475           (modb-standard-countup-flags msgdb flags)
476           (modb-standard-set-flag-modified msgdb number))
477         duplicate))))
478
479 (luna-define-method elmo-msgdb-update-entity ((msgdb modb-standard)
480                                               entity values)
481   (let ((handler (elmo-message-entity-handler entity)))
482     (when (elmo-msgdb-message-entity-update-fields handler entity values)
483       (modb-standard-set-message-modified
484        msgdb
485        (elmo-msgdb-message-entity-number handler entity))
486       t)))
487
488 (luna-define-method elmo-msgdb-delete-messages ((msgdb modb-standard)
489                                                 numbers)
490   (let ((number-list (modb-standard-number-list-internal msgdb))
491         (entity-map (modb-standard-entity-map-internal msgdb))
492         (flag-map (modb-standard-flag-map-internal msgdb))
493         key entity)
494     (dolist (number numbers)
495       (setq key (modb-standard-key number)
496             entity (elmo-get-hash-val key entity-map))
497       (when entity
498         ;; number-list
499         (setq number-list (delq number number-list))
500         ;; entity-map
501         (elmo-clear-hash-val key entity-map)
502         (elmo-clear-hash-val (modb-standard-entity-id entity) entity-map)
503         ;; flag-count (must be BEFORE flag-map)
504         (modb-standard-countup-flags
505          msgdb
506          (modb-standard-message-flags msgdb number)
507          -1)
508         ;; flag-map
509         (elmo-clear-hash-val key flag-map)
510         (modb-standard-set-message-modified msgdb number)
511         (modb-standard-set-flag-modified msgdb number)))
512     (modb-standard-set-number-list-internal msgdb number-list)
513     (modb-standard-set-entity-map-internal msgdb entity-map)
514     (modb-standard-set-flag-map-internal msgdb flag-map)
515     t))
516
517 (luna-define-method elmo-msgdb-sort-entities ((msgdb modb-standard)
518                                               predicate &optional app-data)
519   (message "Sorting...")
520   (let ((numbers (modb-standard-number-list-internal msgdb)))
521     (modb-standard-set-number-list-internal
522      msgdb
523      (sort numbers (lambda (a b)
524                      (funcall predicate
525                               (elmo-msgdb-message-entity msgdb a)
526                               (elmo-msgdb-message-entity msgdb b)
527                               app-data))))
528     (message "Sorting...done")
529     msgdb))
530
531 (defun modb-standard-message-entity (msgdb key load)
532   (let ((ret (elmo-get-hash-val
533               key
534               (modb-standard-entity-map-internal msgdb)))
535         (inhibit-quit t))
536     (if (eq 'autoload (car-safe ret))
537         (when (and load modb-standard-divide-number)
538           (modb-standard-load-entity
539            msgdb
540            (elmo-msgdb-location msgdb)
541            (/ (nth 1 ret) modb-standard-divide-number))
542           (modb-standard-message-entity msgdb key nil))
543       ret)))
544
545 (luna-define-method elmo-msgdb-message-number ((msgdb modb-standard)
546                                                message-id)
547   (let ((ret (elmo-get-hash-val
548               message-id
549               (modb-standard-entity-map-internal msgdb))))
550     (if (eq 'autoload (car-safe ret))
551         ;; Not loaded yet but can return number.
552         (nth 1 ret)
553       (elmo-message-entity-number ret))))
554
555 (luna-define-method elmo-msgdb-message-field ((msgdb modb-standard)
556                                               number field)
557   (let ((ret (elmo-get-hash-val
558               (modb-standard-key number)
559               (modb-standard-entity-map-internal msgdb))))
560     (if (and (eq 'autoload (car-safe ret)) (eq field 'message-id))
561         ;; Not loaded yet but can return message-id
562         (cdr (cdr ret))
563       (elmo-message-entity-field (elmo-msgdb-message-entity
564                                   msgdb (modb-standard-key number))
565                                  field))))
566
567 (luna-define-method elmo-msgdb-message-entity ((msgdb modb-standard) key)
568   (when key
569     (modb-standard-message-entity
570      msgdb
571      (cond ((stringp key) key)
572            ((numberp key) (modb-standard-key key)))
573      'autoload)))
574
575 (require 'product)
576 (product-provide (provide 'modb-standard) (require 'elmo-version))
577
578 ;;; modb-standard.el ends here