Fixed last change.
[elisp/wanderlust.git] / elmo / elmo-msgdb.el
1 ;;; elmo-msgdb.el --- Message Database for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 2000           Masahiro MURATA <muse@ba2.so-net.ne.jp>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;;      Masahiro MURATA <muse@ba2.so-net.ne.jp>
8 ;; Keywords: mail, net news
9
10 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16 ;;
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21 ;;
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27
28 ;;; Commentary:
29 ;;
30
31 ;;; Code:
32 ;;
33
34 (eval-when-compile (require 'cl))
35 (require 'elmo-vars)
36 (require 'elmo-util)
37 (require 'emu)
38 (require 'std11)
39 (require 'mime)
40
41 (defcustom elmo-msgdb-new-mark "N"
42   "Mark for new message."
43   :type '(string :tag "Mark")
44   :group 'elmo)
45
46 (defcustom elmo-msgdb-unread-uncached-mark "U"
47   "Mark for unread and uncached message."
48   :type '(string :tag "Mark")
49   :group 'elmo)
50
51 (defcustom elmo-msgdb-unread-cached-mark "!"
52   "Mark for unread but already cached message."
53   :type '(string :tag "Mark")
54   :group 'elmo)
55
56 (defcustom elmo-msgdb-read-uncached-mark "u"
57   "Mark for read but uncached message."
58   :type '(string :tag "Mark")
59   :group 'elmo)
60
61 ;; Not implemented yet.
62 (defcustom elmo-msgdb-answered-cached-mark "&"
63   "Mark for answered and cached message."
64   :type '(string :tag "Mark")
65   :group 'elmo)
66
67 (defcustom elmo-msgdb-answered-uncached-mark "A"
68   "Mark for answered but cached message."
69   :type '(string :tag "Mark")
70   :group 'elmo)
71
72 (defcustom elmo-msgdb-important-mark"$"
73   "Mark for important message."
74   :type '(string :tag "Mark")
75   :group 'elmo)
76
77 ;;; MSGDB interface.
78 ;;
79 ;; MSGDB elmo-load-msgdb PATH
80 ;; MARK elmo-msgdb-get-mark MSGDB NUMBER 
81
82 ;; CACHED elmo-msgdb-get-cached MSGDB NUMBER
83 ;; VOID elmo-msgdb-set-cached MSGDB NUMBER CACHED USE-CACHE
84 ;; VOID elmo-msgdb-set-flag MSGDB FOLDER NUMBER FLAG
85 ;; VOID elmo-msgdb-unset-flag MSGDB FOLDER NUMBER FLAG
86
87 ;; LIST-OF-NUMBERS elmo-msgdb-count-marks MSGDB
88 ;; NUMBER elmo-msgdb-get-number MSGDB MESSAGE-ID
89 ;; FIELD-VALUE elmo-msgdb-get-field MSGDB NUMBER FIELD
90 ;; MSGDB elmo-msgdb-append MSGDB MSGDB-APPEND
91 ;; MSGDB elmo-msgdb-clear MSGDB
92 ;; elmo-msgdb-delete-msgs MSGDB NUMBERS
93 ;; elmo-msgdb-sort-by-date MSGDB
94
95 ;;;
96 ;; LIST-OF-NUMBERS elmo-msgdb-list-messages MSGDB
97
98 ;; elmo-flag-table-load
99 ;; elmo-flag-table-set
100 ;; elmo-flag-table-get
101 ;; elmo-flag-table-save
102
103 ;; elmo-msgdb-append-entity
104 ;; msgdb entity flag-table
105
106 ;; ENTITY elmo-msgdb-make-entity ARGS
107 ;; VALUE elmo-msgdb-entity-field ENTITY
108 ;; 
109
110 ;; OVERVIEW elmo-msgdb-get-overview MSGDB
111 ;; NUMBER-ALIST elmo-msgdb-get-number-alist MSGDB
112 ;; MARK-ALIST elmo-msgdb-get-mark-alist MSGDB
113 ;; elmo-msgdb-change-mark MSGDB BEFORE AFTER
114
115 ;; (for internal use?)
116 ;; LIST-OF-MARKS elmo-msgdb-unread-marks 
117 ;; LIST-OF-MARKS elmo-msgdb-answered-marks 
118 ;; LIST-OF-MARKS elmo-msgdb-uncached-marks 
119 ;; elmo-msgdb-overview-save DIR OBJ
120
121 ;; elmo-msgdb-message-entity MSGDB KEY
122
123 ;;; Abolish
124 ;; elmo-msgdb-overview-entity-get-references ENTITY
125 ;; elmo-msgdb-overview-entity-set-references ENTITY
126 ;; elmo-msgdb-get-parent-entity ENTITY MSGDB
127 ;; elmo-msgdb-overview-enitty-get-number ENTITY
128 ;; elmo-msgdb-overview-enitty-get-from-no-decode ENTITY
129 ;; elmo-msgdb-overview-enitty-get-from ENTITY
130 ;; elmo-msgdb-overview-enitty-get-subject-no-decode ENTITY
131 ;; elmo-msgdb-overview-enitty-get-subject ENTITY
132 ;; elmo-msgdb-overview-enitty-get-date ENTITY
133 ;; elmo-msgdb-overview-enitty-get-to ENTITY
134 ;; elmo-msgdb-overview-enitty-get-cc ENTITY
135 ;; elmo-msgdb-overview-enitty-get-size ENTITY
136 ;; elmo-msgdb-overview-enitty-get-id ENTITY
137 ;; elmo-msgdb-overview-enitty-get-extra-field ENTITY
138 ;; elmo-msgdb-overview-enitty-get-extra ENTITY
139 ;; elmo-msgdb-overview-get-entity ID MSGDB
140
141 ;; elmo-msgdb-killed-list-load DIR
142 ;; elmo-msgdb-killed-list-save DIR
143 ;; elmo-msgdb-append-to-killed-list FOLDER MSG
144 ;; elmo-msgdb-killed-list-length KILLED-LIST
145 ;; elmo-msgdb-max-of-killed KILLED-LIST
146 ;; elmo-msgdb-killed-message-p KILLED-LIST MSG
147 ;; elmo-living-messages MESSAGES KILLED-LIST
148 ;; elmo-msgdb-finfo-load
149 ;; elmo-msgdb-finfo-save
150 ;; elmo-msgdb-flist-load
151 ;; elmo-msgdb-flist-save
152
153 ;; elmo-crosspost-alist-load
154 ;; elmo-crosspost-alist-save
155
156 ;; elmo-msgdb-create-overview-from-buffer NUMBER SIZE TIME
157 ;; elmo-msgdb-copy-overview-entity ENTITY
158 ;; elmo-msgdb-create-overview-entity-from-file NUMBER FILE
159 ;; elmo-msgdb-overview-sort-by-date OVERVIEW
160 ;; elmo-msgdb-clear-index
161
162 ;; elmo-folder-get-info
163 ;; elmo-folder-get-info-max
164 ;; elmo-folder-get-info-length
165 ;; elmo-folder-get-info-unread
166
167 ;; elmo-msgdb-list-flagged MSGDB FLAG
168 ;; (MACRO) elmo-msgdb-do-each-entity 
169
170 (defun elmo-load-msgdb (path)
171   "Load the MSGDB from PATH."
172   (let ((inhibit-quit t))
173     (elmo-make-msgdb (elmo-msgdb-overview-load path)
174                      (elmo-msgdb-number-load path)
175                      (elmo-msgdb-mark-load path)
176                      path)))
177
178 (defun elmo-make-msgdb (&optional overview number-alist mark-alist path)
179   "Make a MSGDB."
180   (let ((msgdb (list overview number-alist mark-alist nil path)))
181     (elmo-msgdb-make-index msgdb)
182     msgdb))
183
184 (defun elmo-msgdb-list-messages (msgdb-or-path)
185   "Return a list of message numbers in the msgdb.
186 If MSGDB-OR-PATH is a msgdb structure, use it as a msgdb.
187 If argument is a string, use it as a path to load message entities."
188   (mapcar 'elmo-msgdb-overview-entity-get-number
189           (if (stringp msgdb-or-path)
190               (elmo-msgdb-overview-load msgdb-or-path)
191             (elmo-msgdb-get-overview msgdb-or-path))))
192
193 (defsubst elmo-msgdb-get-mark (msgdb number)
194   "Get mark string from MSGDB which corresponds to the message with NUMBER."
195   (cadr (elmo-get-hash-val (format "#%d" number)
196                            (elmo-msgdb-get-mark-hashtb msgdb))))
197
198 (defsubst elmo-msgdb-set-mark (msgdb number mark)
199   "Set MARK of the message with NUMBER in the MSGDB.
200 if MARK is nil, mark is removed."
201   (let ((elem (elmo-get-hash-val (format "#%d" number)
202                                  (elmo-msgdb-get-mark-hashtb msgdb))))
203     (if elem
204         (if mark
205             ;; Set mark of the elem
206             (setcar (cdr elem) mark)
207           ;; Delete elem from mark-alist
208           (elmo-msgdb-set-mark-alist
209            msgdb
210            (delq elem (elmo-msgdb-get-mark-alist msgdb)))
211           (elmo-clear-hash-val (format "#%d" number)
212                                (elmo-msgdb-get-mark-hashtb msgdb)))
213       (when mark
214         ;; Append new element.
215         (elmo-msgdb-set-mark-alist
216          msgdb
217          (nconc
218           (elmo-msgdb-get-mark-alist msgdb)
219           (list (setq elem (list number mark)))))
220         (elmo-set-hash-val (format "#%d" number) elem
221                            (elmo-msgdb-get-mark-hashtb msgdb))))
222     ;; return value.
223     t))
224
225 (defun elmo-msgdb-get-cached (msgdb number)
226   "Return non-nil if message is cached."
227   (not (member (elmo-msgdb-get-mark msgdb number)
228                (elmo-msgdb-uncached-marks))))
229
230 (defun elmo-msgdb-set-cached (msgdb number cached use-cache)
231   "Set message cache status.
232 If mark is changed, return non-nil."
233   (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
234          (cur-flag (cond
235                       ((string= cur-mark elmo-msgdb-important-mark)
236                        'important)
237                       ((member cur-mark (elmo-msgdb-answered-marks))
238                        'answered)
239                       ((not (member cur-mark (elmo-msgdb-unread-marks)))
240                        'read)))
241          (cur-cached (elmo-file-cache-exists-p
242                       (elmo-msgdb-get-field msgdb number 'message-id))))
243     (unless (eq cached cur-cached)
244       (case cur-flag
245         (read
246          (elmo-msgdb-set-mark msgdb number
247                               (if (and use-cache (not cached))
248                                   elmo-msgdb-read-uncached-mark)))
249         (important nil)
250         (answered
251          (elmo-msgdb-set-mark msgdb number
252                               (if cached
253                                   elmo-msgdb-answered-cached-mark
254                                 elmo-msgdb-answered-uncached-mark)))
255         (t
256          (elmo-msgdb-set-mark msgdb number
257                               (if cached
258                                   elmo-msgdb-unread-cached-mark
259                                 elmo-msgdb-unread-uncached-mark)))))))
260
261 (defun elmo-msgdb-set-flag (msgdb folder number flag)
262   "Set message flag.
263 MSGDB is the ELMO msgdb.
264 FOLDER is a ELMO folder structure.
265 NUMBER is a message number to set flag.
266 FLAG is a symbol which is one of the following:
267 `read'      ... Messages which are already read.
268 `important' ... Messages which are marked as important.
269 `answered'  ... Messages which are marked as answered."
270   (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
271          (use-cache (elmo-message-use-cache-p folder number))
272          (cur-flag (cond
273                     ((string= cur-mark elmo-msgdb-important-mark)
274                      'important)
275                     ((member cur-mark (elmo-msgdb-answered-marks))
276                      'answered)
277                     ((not (member cur-mark (elmo-msgdb-unread-marks)))
278                      'read)))
279          (cur-cached (elmo-file-cache-exists-p
280                       (elmo-msgdb-get-field msgdb number 'message-id)))
281          mark-modified)
282     (case flag
283       (read
284        (case cur-flag
285          ((read important)) ; answered mark is overriden.
286          (t (elmo-msgdb-set-mark msgdb number
287                                  (if (and use-cache (not cur-cached))
288                                      elmo-msgdb-read-uncached-mark))
289             (setq mark-modified t))))
290       (important
291        (unless (eq cur-flag 'important)
292          (elmo-msgdb-set-mark msgdb number elmo-msgdb-important-mark)
293          (setq mark-modified t)))
294       (answered
295        (unless (or (eq cur-flag 'answered) (eq cur-flag 'important))
296          (elmo-msgdb-set-mark msgdb number
297                               (if cur-cached
298                                   elmo-msgdb-answered-cached-mark
299                                 elmo-msgdb-answered-uncached-mark)))
300        (setq mark-modified t)))
301     (if mark-modified (elmo-folder-set-mark-modified-internal folder t))))
302
303 (defun elmo-msgdb-unset-flag (msgdb folder number flag)
304   "Unset message flag.
305 MSGDB is the ELMO msgdb.
306 FOLDER is a ELMO folder structure.
307 NUMBER is a message number to be set flag.
308 FLAG is a symbol which is one of the following:
309 `read'      ... Messages which are already read.
310 `important' ... Messages which are marked as important.
311 `answered'  ... Messages which are marked as answered."
312   (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
313          (use-cache (elmo-message-use-cache-p folder number))
314          (cur-flag (cond
315                     ((string= cur-mark elmo-msgdb-important-mark)
316                      'important)
317                     ((member cur-mark (elmo-msgdb-answered-marks))
318                      'answered)
319                     ((not (member cur-mark (elmo-msgdb-unread-marks)))
320                      'read)))
321          (cur-cached (elmo-file-cache-exists-p
322                       (elmo-msgdb-get-field msgdb number 'message-id)))
323          mark-modified)
324     (case flag
325       (read
326        (when (or (eq cur-flag 'read) (eq cur-flag 'answered))
327          (elmo-msgdb-set-mark msgdb number
328                               (if cur-cached
329                                   elmo-msgdb-unread-cached-mark
330                                 elmo-msgdb-unread-uncached-mark))
331          (setq mark-modified t)))
332       (important
333        (when (eq cur-flag 'important)
334          (elmo-msgdb-set-mark msgdb number nil)
335          (setq mark-modified t)))
336       (answered
337        (when (eq cur-flag 'answered)
338          (elmo-msgdb-set-mark msgdb number
339                               (if (and use-cache (not cur-cached))
340                                   elmo-msgdb-read-uncached-mark))
341          (setq mark-modified t))))
342     (if mark-modified (elmo-folder-set-mark-modified-internal folder t))))
343
344 (defvar elmo-msgdb-unread-marks-internal nil)
345 (defsubst elmo-msgdb-unread-marks ()
346   "Return an unread mark list"
347   (or elmo-msgdb-unread-marks-internal
348       (setq elmo-msgdb-unread-marks-internal
349             (list elmo-msgdb-new-mark
350                   elmo-msgdb-unread-uncached-mark
351                   elmo-msgdb-unread-cached-mark))))
352
353 (defvar elmo-msgdb-answered-marks-internal nil)
354 (defsubst elmo-msgdb-answered-marks ()
355   "Return an answered mark list"
356   (or elmo-msgdb-answered-marks-internal
357       (setq elmo-msgdb-answered-marks-internal
358             (list elmo-msgdb-answered-cached-mark
359                   elmo-msgdb-answered-uncached-mark))))
360
361 (defvar elmo-msgdb-uncached-marks-internal nil)
362 (defsubst elmo-msgdb-uncached-marks ()
363   (or elmo-msgdb-uncached-marks-internal
364       (setq elmo-msgdb-uncached-marks-internal
365             (list elmo-msgdb-new-mark
366                   elmo-msgdb-answered-uncached-mark
367                   elmo-msgdb-unread-uncached-mark
368                   elmo-msgdb-read-uncached-mark))))
369
370 (defsubst elmo-msgdb-get-number (msgdb message-id)
371   "Get number of the message which corrensponds to MESSAGE-ID from MSGDB."
372   (elmo-msgdb-overview-entity-get-number
373    (elmo-msgdb-overview-get-entity message-id msgdb)))
374
375 (defsubst elmo-msgdb-get-field (msgdb number field)
376   "Get FIELD value of the message with NUMBER from MSGDB."
377   (case field
378     (message-id (elmo-msgdb-overview-entity-get-id
379                  (elmo-msgdb-overview-get-entity
380                   number msgdb)))
381     (subject (elmo-msgdb-overview-entity-get-subject
382               (elmo-msgdb-overview-get-entity
383                number msgdb)))
384     (size (elmo-msgdb-overview-entity-get-size
385            (elmo-msgdb-overview-get-entity
386             number msgdb)))
387     (date (elmo-msgdb-overview-entity-get-date
388            (elmo-msgdb-overview-get-entity
389             number msgdb)))
390     (to (elmo-msgdb-overview-entity-get-to
391          (elmo-msgdb-overview-get-entity
392           number msgdb)))
393     (cc (elmo-msgdb-overview-entity-get-cc
394          (elmo-msgdb-overview-get-entity
395           number msgdb)))))
396
397 (defsubst elmo-msgdb-append (msgdb msgdb-append)
398   (list
399    (nconc (car msgdb) (car msgdb-append))
400    (nconc (cadr msgdb) (cadr msgdb-append))
401    (nconc (caddr msgdb) (caddr msgdb-append))
402    (elmo-msgdb-make-index
403     msgdb
404     (elmo-msgdb-get-overview msgdb-append)
405     (elmo-msgdb-get-mark-alist msgdb-append))
406    (nth 4 msgdb)))
407
408 (defsubst elmo-msgdb-clear (&optional msgdb)
409   (if msgdb
410       (list
411        (setcar msgdb nil)
412        (setcar (cdr msgdb) nil)
413        (setcar (cddr msgdb) nil)
414        (setcar (nthcdr 3 msgdb) nil)
415        (setcar (nthcdr 4 msgdb) nil))
416     (list nil nil nil nil nil)))
417
418 (defun elmo-msgdb-delete-msgs (msgdb msgs)
419   "Delete MSGS from MSGDB
420 content of MSGDB is changed."
421   (let* ((overview (car msgdb))
422          (number-alist (cadr msgdb))
423          (mark-alist (caddr msgdb))
424          (index (elmo-msgdb-get-index msgdb))
425          (newmsgdb (list overview number-alist mark-alist index
426                          (nth 4 msgdb)))
427          ov-entity)
428     ;; remove from current database.
429     (while msgs
430       (setq overview
431             (delq
432              (setq ov-entity
433                    (elmo-msgdb-overview-get-entity (car msgs) newmsgdb))
434              overview))
435       (setq number-alist (delq (assq (car msgs) number-alist) number-alist))
436       (setq mark-alist (delq (assq (car msgs) mark-alist) mark-alist))
437       ;;
438       (when index (elmo-msgdb-clear-index msgdb ov-entity))
439       (setq msgs (cdr msgs)))
440     (setcar msgdb overview)
441     (setcar (cdr msgdb) number-alist)
442     (setcar (cddr msgdb) mark-alist)
443     (setcar (nthcdr 3 msgdb) index)
444     t)) ;return value
445
446 (defun elmo-msgdb-sort-by-date (msgdb)
447   (message "Sorting...")
448   (let ((overview (elmo-msgdb-get-overview msgdb)))
449     (setq overview (elmo-msgdb-overview-sort-by-date overview))
450     (message "Sorting...done")
451     (list overview (nth 1 msgdb)(nth 2 msgdb))))
452
453 ;;;
454 (defsubst elmo-msgdb-append-element (list element)
455   (if list
456 ;;;   (append list (list element))
457       (nconc list (list element))
458     ;; list is nil
459     (list element)))
460
461 (defsubst elmo-msgdb-get-overview (msgdb)
462   (car msgdb))
463 (defsubst elmo-msgdb-get-number-alist (msgdb)
464   (cadr msgdb))
465 (defsubst elmo-msgdb-get-mark-alist (msgdb)
466   (caddr msgdb))
467 ;(defsubst elmo-msgdb-get-location (msgdb)
468 ;  (cadddr msgdb))
469
470 (defsubst elmo-msgdb-get-index (msgdb)
471   (nth 3 msgdb))
472
473 (defsubst elmo-msgdb-get-entity-hashtb (msgdb)
474   (car (nth 3 msgdb)))
475
476 (defsubst elmo-msgdb-get-mark-hashtb (msgdb)
477   (cdr (nth 3 msgdb)))
478
479 (defsubst elmo-msgdb-get-path (msgdb)
480   (nth 4 msgdb))
481
482 ;;
483 ;; number <-> Message-ID handling
484 ;;
485 (defsubst elmo-msgdb-number-add (alist number id)
486   (let ((ret-val alist))
487     (setq ret-val
488           (elmo-msgdb-append-element ret-val (cons number id)))
489     ret-val))
490
491 ;;; flag table
492 ;;
493 (defvar elmo-flag-table-filename "flag-table")
494 (defun elmo-flag-table-load (dir)
495   "Load flag hashtable for MSGDB."
496   (let ((table (elmo-make-hash))
497         ;; For backward compatibility
498         (seen-file (expand-file-name elmo-msgdb-seen-filename dir))
499         seen-list)
500     (when (file-exists-p seen-file)
501       (setq seen-list (elmo-object-load seen-file))
502       (delete-file seen-file))
503     (dolist (msgid seen-list)
504       (elmo-set-hash-val msgid 'read table))
505     (dolist (pair (elmo-object-load
506                    (expand-file-name elmo-flag-table-filename dir)))
507       (elmo-set-hash-val (car pair) (cdr pair) table))
508     table))
509
510 (defun elmo-flag-table-set (flag-table msg-id flag)
511   (elmo-set-hash-val msg-id flag flag-table))
512
513 (defun elmo-flag-table-get (flag-table msg-id)
514   (elmo-get-hash-val msg-id flag-table))
515
516 (defun elmo-flag-table-save (dir flag-table)
517   (elmo-object-save
518    (expand-file-name elmo-flag-table-filename dir)
519    (if flag-table
520        (let (list)
521          (mapatoms (lambda (atom)
522                      (setq list (cons (cons (symbol-name atom)
523                                             (symbol-value atom))
524                                       list)))
525                    flag-table)
526          list))))
527 ;;;
528 ;; persistent mark handling
529 ;; (for each folder)
530
531 (defun elmo-msgdb-mark-append (alist id mark)
532   "Append mark."
533   (setq alist (elmo-msgdb-append-element alist
534                                          (list id mark))))
535
536 (defsubst elmo-msgdb-length (msgdb)
537   (length (elmo-msgdb-get-overview msgdb)))
538
539 (defun elmo-msgdb-flag-table (msgdb &optional flag-table)
540   ;; Make a table of msgid flag (read, answered)
541   (let ((flag-table (or flag-table (elmo-make-hash (elmo-msgdb-length msgdb))))
542         mark)
543     (dolist (ov (elmo-msgdb-get-overview msgdb))
544       (setq mark (elmo-msgdb-get-mark
545                   msgdb
546                   (elmo-msgdb-overview-entity-get-number ov)))
547       (cond 
548        ((null mark)
549         (elmo-set-hash-val
550          (elmo-msgdb-overview-entity-get-id ov)
551          'read
552          flag-table))
553        ((and mark (member mark (elmo-msgdb-answered-marks)))
554         (elmo-set-hash-val
555          (elmo-msgdb-overview-entity-get-id ov)
556          'answered
557          flag-table))
558        ((and mark (not (member mark
559                                (elmo-msgdb-unread-marks))))
560         (elmo-set-hash-val
561          (elmo-msgdb-overview-entity-get-id ov)
562          'read
563          flag-table))))
564     flag-table))
565
566 ;;
567 ;; mime decode cache
568
569 (defvar elmo-msgdb-decoded-cache-hashtb nil)
570 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
571
572 (defsubst elmo-msgdb-get-decoded-cache (string)
573   (if elmo-use-decoded-cache
574       (let ((hashtb (or elmo-msgdb-decoded-cache-hashtb
575                         (setq elmo-msgdb-decoded-cache-hashtb
576                               (elmo-make-hash 2048))))
577             decoded)
578         (or (elmo-get-hash-val string hashtb)
579             (progn
580               (elmo-set-hash-val
581                string
582                (setq decoded
583                      (decode-mime-charset-string string elmo-mime-charset))
584                hashtb)
585               decoded)))
586     (decode-mime-charset-string string elmo-mime-charset)))
587
588 ;;
589 ;; overview handling
590 ;;
591 (defun elmo-multiple-field-body (name &optional boundary)
592   (save-excursion
593     (save-restriction
594       (std11-narrow-to-header boundary)
595       (goto-char (point-min))
596       (let ((case-fold-search t)
597             (field-body nil))
598         (while (re-search-forward (concat "^" name ":[ \t]*") nil t)
599           (setq field-body
600                 (nconc field-body
601                        (list (buffer-substring-no-properties
602                               (match-end 0) (std11-field-end))))))
603         field-body))))
604
605 (defun elmo-multiple-fields-body-list (field-names &optional boundary)
606   "Return list of each field-bodies of FIELD-NAMES of the message header
607 in current buffer. If BOUNDARY is not nil, it is used as message
608 header separator."
609   (save-excursion
610     (save-restriction
611       (std11-narrow-to-header boundary)
612       (let* ((case-fold-search t)
613              (s-rest field-names)
614              field-name field-body)
615         (while (setq field-name (car s-rest))
616           (goto-char (point-min))
617           (while (re-search-forward (concat "^" field-name ":[ \t]*") nil t)
618             (setq field-body
619                   (nconc field-body
620                          (list (buffer-substring-no-properties
621                                 (match-end 0) (std11-field-end))))))
622           (setq s-rest (cdr s-rest)))
623         field-body))))
624
625 (defsubst elmo-msgdb-remove-field-string (string)
626   (if (string-match (concat std11-field-head-regexp "[ \t]*") string)
627       (substring string (match-end 0))
628     string))
629
630 (defsubst elmo-msgdb-get-last-message-id (string)
631   (if string
632       (save-match-data
633         (let (beg)
634           (elmo-set-work-buf
635            (insert string)
636            (goto-char (point-max))
637            (when (search-backward "<" nil t)
638              (setq beg (point))
639              (if (search-forward ">" nil t)
640                  (elmo-replace-in-string
641                   (buffer-substring beg (point)) "\n[ \t]*" ""))))))))
642
643 (defun elmo-msgdb-number-load (dir)
644   (elmo-object-load
645    (expand-file-name elmo-msgdb-number-filename dir)))
646
647 (defun elmo-msgdb-overview-load (dir)
648   (elmo-object-load
649    (expand-file-name elmo-msgdb-overview-filename dir)))
650
651 (defun elmo-msgdb-mark-load (dir)
652   (elmo-object-load
653    (expand-file-name elmo-msgdb-mark-filename dir)))
654
655 (defsubst elmo-msgdb-seen-load (dir)
656   (elmo-object-load (expand-file-name
657                      elmo-msgdb-seen-filename
658                      dir)))
659
660 (defun elmo-msgdb-number-save (dir obj)
661   (elmo-object-save
662    (expand-file-name elmo-msgdb-number-filename dir)
663    obj))
664
665 (defun elmo-msgdb-mark-save (dir obj)
666   (elmo-object-save
667    (expand-file-name elmo-msgdb-mark-filename dir)
668    obj))
669
670 (defun elmo-msgdb-change-mark (msgdb before after)
671   "Set the BEFORE marks to AFTER."
672   (let ((mark-alist (elmo-msgdb-get-mark-alist msgdb))
673         entity)
674     (while mark-alist
675       (setq entity (car mark-alist))
676       (when (string= (cadr entity) before)
677         (setcar (cdr entity) after))
678       (setq mark-alist (cdr mark-alist)))))
679
680 (defsubst elmo-msgdb-mark (flag cached &optional new)
681   (if new
682       (case flag
683         (read
684          (if cached
685              nil
686            elmo-msgdb-read-uncached-mark))
687         (important
688          elmo-msgdb-important-mark)
689         (answered
690          (if cached
691              elmo-msgdb-answered-cached-mark
692            elmo-msgdb-answered-uncached-mark))
693         (t
694          (if cached
695              elmo-msgdb-unread-cached-mark
696            elmo-msgdb-new-mark)))
697     (case flag
698       (unread
699        (if cached
700            elmo-msgdb-unread-cached-mark
701          elmo-msgdb-unread-uncached-mark))
702       (important
703        elmo-msgdb-important-mark)
704       (answered
705        (if cached
706            elmo-msgdb-answered-cached-mark
707          elmo-msgdb-answered-uncached-mark)))))
708
709 (defsubst elmo-msgdb-overview-save (dir overview)
710   (elmo-object-save
711    (expand-file-name elmo-msgdb-overview-filename dir)
712    overview))
713
714 (defun elmo-msgdb-match-condition-primitive (condition mark entity numbers)
715   (catch 'unresolved
716     (let ((key (elmo-filter-key condition))
717           (case-fold-search t)
718           result)
719       (cond
720        ((string= key "last")
721         (setq result (<= (length (memq
722                                   (elmo-msgdb-overview-entity-get-number
723                                    entity)
724                                   numbers))
725                          (string-to-int (elmo-filter-value condition)))))
726        ((string= key "first")
727         (setq result (< (-
728                          (length numbers)
729                          (length (memq
730                                   (elmo-msgdb-overview-entity-get-number
731                                    entity)
732                                   numbers)))
733                         (string-to-int (elmo-filter-value condition)))))
734        ((string= key "flag")
735         (setq result
736               (cond
737                ((string= (elmo-filter-value condition) "any")
738                 (not (or (null mark)
739                          (string= mark elmo-msgdb-read-uncached-mark))))
740                ((string= (elmo-filter-value condition) "digest")
741                 (not (or (null mark)
742                          (string= mark elmo-msgdb-read-uncached-mark)
743                          (string= mark elmo-msgdb-answered-cached-mark)
744                          (string= mark elmo-msgdb-answered-uncached-mark))))
745 ;;        (member mark (append (elmo-msgdb-answered-marks)
746 ;;                             (list elmo-msgdb-important-mark)
747 ;;                             (elmo-msgdb-unread-marks))))
748                ((string= (elmo-filter-value condition) "unread")
749                 (member mark (elmo-msgdb-unread-marks)))
750                ((string= (elmo-filter-value condition) "important")
751                 (string= mark elmo-msgdb-important-mark))
752                ((string= (elmo-filter-value condition) "answered")
753                 (member mark (elmo-msgdb-answered-marks))))))
754        ((string= key "from")
755         (setq result (string-match
756                       (elmo-filter-value condition)
757                       (elmo-msgdb-overview-entity-get-from entity))))
758        ((string= key "subject")
759         (setq result (string-match
760                       (elmo-filter-value condition)
761                       (elmo-msgdb-overview-entity-get-subject entity))))
762        ((string= key "to")
763         (setq result (string-match
764                       (elmo-filter-value condition)
765                       (elmo-msgdb-overview-entity-get-to entity))))
766        ((string= key "cc")
767         (setq result (string-match
768                       (elmo-filter-value condition)
769                       (elmo-msgdb-overview-entity-get-cc entity))))
770        ((or (string= key "since")
771             (string= key "before"))
772         (let ((field-date (elmo-date-make-sortable-string
773                            (timezone-fix-time
774                             (elmo-msgdb-overview-entity-get-date entity)
775                             (current-time-zone) nil)))
776               (specified-date
777                (elmo-date-make-sortable-string
778                 (elmo-date-get-datevec
779                  (elmo-filter-value condition)))))
780           (setq result (if (string= key "since")
781                            (or (string= specified-date field-date)
782                                (string< specified-date field-date))
783                          (string< field-date specified-date)))))
784        ((member key elmo-msgdb-extra-fields)
785         (let ((extval (elmo-msgdb-overview-entity-get-extra-field entity key)))
786           (when (stringp extval)
787             (setq result (string-match
788                           (elmo-filter-value condition)
789                           extval)))))
790        (t
791         (throw 'unresolved condition)))
792       (if (eq (elmo-filter-type condition) 'unmatch)
793           (not result)
794         result))))
795
796 (defun elmo-msgdb-match-condition-internal (condition mark entity numbers)
797   (cond
798    ((vectorp condition)
799     (elmo-msgdb-match-condition-primitive condition mark entity numbers))
800    ((eq (car condition) 'and)
801     (let ((lhs (elmo-msgdb-match-condition-internal (nth 1 condition)
802                                                     mark entity numbers)))
803       (cond
804        ((elmo-filter-condition-p lhs)
805         (let ((rhs (elmo-msgdb-match-condition-internal
806                     (nth 2 condition) mark entity numbers)))
807           (cond ((elmo-filter-condition-p rhs)
808                  (list 'and lhs rhs))
809                 (rhs
810                  lhs))))
811        (lhs
812         (elmo-msgdb-match-condition-internal (nth 2 condition)
813                                              mark entity numbers)))))
814    ((eq (car condition) 'or)
815     (let ((lhs (elmo-msgdb-match-condition-internal (nth 1 condition)
816                                                     mark entity numbers)))
817       (cond
818        ((elmo-filter-condition-p lhs)
819         (let ((rhs (elmo-msgdb-match-condition-internal (nth 2 condition)
820                                                         mark entity numbers)))
821           (cond ((elmo-filter-condition-p rhs)
822                  (list 'or lhs rhs))
823                 (rhs
824                  t)
825                 (t
826                  lhs))))
827        (lhs
828         t)
829        (t
830         (elmo-msgdb-match-condition-internal (nth 2 condition)
831                                              mark entity numbers)))))))
832
833 (defun elmo-msgdb-match-condition (msgdb condition number numbers)
834   "Check whether the condition of the message is satisfied or not.
835 MSGDB is the msgdb to search from.
836 CONDITION is the search condition.
837 NUMBER is the message number to check.
838 NUMBERS is the target message number list.
839 Return CONDITION itself if no entity exists in msgdb."
840   (let ((entity (elmo-msgdb-overview-get-entity number msgdb)))
841     (if entity
842         (elmo-msgdb-match-condition-internal condition
843                                              (elmo-msgdb-get-mark msgdb number)
844                                              entity numbers)
845       condition)))
846
847 (defsubst elmo-msgdb-set-overview (msgdb overview)
848   (setcar msgdb overview))
849
850 (defsubst elmo-msgdb-set-number-alist (msgdb number-alist)
851   (setcar (cdr msgdb) number-alist))
852
853 (defsubst elmo-msgdb-set-mark-alist (msgdb mark-alist)
854   (setcar (cddr msgdb) mark-alist))
855
856 (defsubst elmo-msgdb-set-index (msgdb index)
857   (setcar (cdddr msgdb) index))
858
859 (defsubst elmo-msgdb-set-path (msgdb path)
860   (setcar (cddddr msgdb) path))
861
862 (defsubst elmo-msgdb-overview-entity-get-references (entity)
863   (and entity (aref (cdr entity) 1)))
864
865 (defsubst elmo-msgdb-overview-entity-set-references (entity references)
866   (and entity (aset (cdr entity) 1 references))
867   entity)
868
869 ;; entity -> parent-entity
870 (defsubst elmo-msgdb-overview-get-parent-entity (entity database)
871   (setq entity (elmo-msgdb-overview-entity-get-references entity))
872   ;; entity is parent-id.
873   (and entity (assoc entity database)))
874
875 (defsubst elmo-msgdb-get-parent-entity (entity msgdb)
876   (setq entity (elmo-msgdb-overview-entity-get-references entity))
877   ;; entity is parent-id.
878   (and entity (elmo-msgdb-overview-get-entity entity msgdb)))
879
880 (defsubst elmo-msgdb-overview-entity-get-number (entity)
881   (and entity (aref (cdr entity) 0)))
882
883 (defsubst elmo-msgdb-overview-entity-get-from-no-decode (entity)
884   (and entity (aref (cdr entity) 2)))
885
886 (defsubst elmo-msgdb-overview-entity-get-from (entity)
887   (and entity
888        (aref (cdr entity) 2)
889        (elmo-msgdb-get-decoded-cache (aref (cdr entity) 2))))
890
891 (defsubst elmo-msgdb-overview-entity-set-number (entity number)
892   (and entity (aset (cdr entity) 0 number))
893   entity)
894 ;;;(setcar (cadr entity) number) entity)
895
896 (defsubst elmo-msgdb-overview-entity-set-from (entity from)
897   (and entity (aset (cdr entity) 2 from))
898   entity)
899
900 (defsubst elmo-msgdb-overview-entity-get-subject (entity)
901   (and entity
902        (aref (cdr entity) 3)
903        (elmo-msgdb-get-decoded-cache (aref (cdr entity) 3))))
904
905 (defsubst elmo-msgdb-overview-entity-get-subject-no-decode (entity)
906   (and entity (aref (cdr entity) 3)))
907
908 (defsubst elmo-msgdb-overview-entity-set-subject (entity subject)
909   (and entity (aset (cdr entity) 3 subject))
910   entity)
911
912 (defsubst elmo-msgdb-overview-entity-get-date (entity)
913   (and entity (aref (cdr entity) 4)))
914
915 (defsubst elmo-msgdb-overview-entity-set-date (entity date)
916   (and entity (aset (cdr entity) 4 date))
917   entity)
918
919 (defsubst elmo-msgdb-overview-entity-get-to (entity)
920   (and entity (aref (cdr entity) 5)))
921
922 (defsubst elmo-msgdb-overview-entity-get-cc (entity)
923   (and entity (aref (cdr entity) 6)))
924
925 (defsubst elmo-msgdb-overview-entity-get-size (entity)
926   (and entity (aref (cdr entity) 7)))
927
928 (defsubst elmo-msgdb-overview-entity-set-size (entity size)
929   (and entity (aset (cdr entity) 7 size))
930   entity)
931
932 (defsubst elmo-msgdb-overview-entity-get-id (entity)
933   (and entity (car entity)))
934
935 (defsubst elmo-msgdb-overview-entity-get-extra-field (entity field-name)
936   (let ((field-name (downcase field-name))
937         (extra (and entity (aref (cdr entity) 8))))
938     (and extra
939          (cdr (assoc field-name extra)))))
940
941 (defsubst elmo-msgdb-overview-entity-set-extra-field (entity field-name value)
942   (let ((field-name (downcase field-name))
943         (extras (and entity (aref (cdr entity) 8)))
944         extra)
945     (if (setq extra (assoc field-name extras))
946         (setcdr extra value)
947       (elmo-msgdb-overview-entity-set-extra
948        entity
949        (cons (cons field-name value) extras)))))
950
951 (defsubst elmo-msgdb-overview-entity-get-extra (entity)
952   (and entity (aref (cdr entity) 8)))
953
954 (defsubst elmo-msgdb-overview-entity-set-extra (entity extra)
955   (and entity (aset (cdr entity) 8 extra))
956   entity)
957
958 ;;; New APIs
959 (defsubst elmo-msgdb-message-entity (msgdb key)
960   (elmo-get-hash-val 
961    (cond ((stringp key) key)
962          ((numberp key) (format "#%d" key)))
963    (elmo-msgdb-get-entity-hashtb msgdb)))
964
965 (defun elmo-msgdb-make-message-entity (&rest args)
966   "Make an message entity."
967   (cons (plist-get args :message-id)
968         (vector (plist-get args :number)
969                 (plist-get args :references)
970                 (plist-get args :from)
971                 (plist-get args :subject)
972                 (plist-get args :date)
973                 (plist-get args :to)
974                 (plist-get args :cc)
975                 (plist-get args :size)
976                 (plist-get args :extra))))
977
978 (defsubst elmo-msgdb-message-entity-field (entity field &optional decode)
979   (and entity
980        (let ((field-value
981               (case field
982                 (to (aref (cdr entity) 5))
983                 (cc (aref (cdr entity) 6))
984                 (date (aref (cdr entity) 4))
985                 (subject (aref (cdr entity) 3))
986                 (from (aref (cdr entity) 2))
987                 (message-id (car entity))
988                 (references (aref (cdr entity) 1))
989                 (size (aref (cdr entity) 7))
990                 (t (cdr (assoc (symbol-name field) (aref (cdr entity) 8)))))))
991          (if decode
992              (elmo-msgdb-get-decoded-cache field-value)
993            field-value))))
994
995 (defsubst elmo-msgdb-message-entity-set-field (entity field value)
996   (and entity
997        (case field
998          (to (aset (cdr entity) 5 value))
999          (cc (aset (cdr entity) 6 value))
1000          (date (aset (cdr entity) 4 value))
1001          (subject (aset (cdr entity) 3 value))
1002          (from (aset (cdr entity) 2 value))
1003          (message-id (setcar entity value))
1004          (references (aset (cdr entity) 1 value))
1005          (size (aset (cdr entity) 7 value))
1006          (t
1007           (let ((extras (and entity (aref (cdr entity) 8)))
1008                 extra)
1009             (if (setq extra (assoc field extras))
1010                 (setcdr extra value)
1011               (aset (cdr entity) 8 (cons (cons (symbol-name field)
1012                                                value) extras))))))))
1013
1014 ;;; 
1015 (defun elmo-msgdb-overview-get-entity (id msgdb)
1016   (when id
1017     (let ((ht (elmo-msgdb-get-entity-hashtb msgdb)))
1018       (if ht
1019           (if (stringp id) ;; ID is message-id
1020               (elmo-get-hash-val id ht)
1021             (elmo-get-hash-val (format "#%d" id) ht))))))
1022
1023 ;;
1024 ;; deleted message handling
1025 ;;
1026 (defun elmo-msgdb-killed-list-load (dir)
1027   (elmo-object-load
1028    (expand-file-name elmo-msgdb-killed-filename dir)
1029    nil t))
1030
1031 (defun elmo-msgdb-killed-list-save (dir killed-list)
1032   (elmo-object-save
1033    (expand-file-name elmo-msgdb-killed-filename dir)
1034    killed-list))
1035
1036 (defun elmo-msgdb-killed-message-p (killed-list msg)
1037   (elmo-number-set-member msg killed-list))
1038
1039 (defun elmo-msgdb-set-as-killed (killed-list msg)
1040   (elmo-number-set-append killed-list msg))
1041
1042 (defun elmo-msgdb-killed-list-length (killed-list)
1043   (let ((killed killed-list)
1044         (ret-val 0))
1045     (while (car killed)
1046       (if (consp (car killed))
1047           (setq ret-val (+ ret-val 1 (- (cdar killed) (caar killed))))
1048         (setq ret-val (+ ret-val 1)))
1049       (setq killed (cdr killed)))
1050     ret-val))
1051
1052 (defun elmo-msgdb-max-of-killed (killed-list)
1053   (let ((klist killed-list)
1054         (max 0)
1055         k)
1056     (while (car klist)
1057       (if (< max
1058              (setq k
1059                    (if (consp (car klist))
1060                        (cdar klist)
1061                      (car klist))))
1062           (setq max k))
1063       (setq klist (cdr klist)))
1064     max))
1065
1066 (defun elmo-living-messages (messages killed-list)
1067   (if killed-list
1068       (delq nil
1069             (mapcar (lambda (number)
1070                       (unless (elmo-number-set-member number killed-list)
1071                         number))
1072                     messages))
1073     messages))
1074
1075 (defun elmo-msgdb-finfo-load ()
1076   (elmo-object-load (expand-file-name
1077                      elmo-msgdb-finfo-filename
1078                      elmo-msgdb-directory)
1079                     elmo-mime-charset t))
1080
1081 (defun elmo-msgdb-finfo-save (finfo)
1082   (elmo-object-save (expand-file-name
1083                      elmo-msgdb-finfo-filename
1084                      elmo-msgdb-directory)
1085                     finfo elmo-mime-charset))
1086
1087 (defun elmo-msgdb-flist-load (fname)
1088   (let ((flist-file (expand-file-name
1089                      elmo-msgdb-flist-filename
1090                      (expand-file-name
1091                       (elmo-safe-filename fname)
1092                       (expand-file-name "folder" elmo-msgdb-directory)))))
1093     (elmo-object-load flist-file elmo-mime-charset t)))
1094
1095 (defun elmo-msgdb-flist-save (fname flist)
1096   (let ((flist-file (expand-file-name
1097                      elmo-msgdb-flist-filename
1098                      (expand-file-name
1099                       (elmo-safe-filename fname)
1100                       (expand-file-name "folder" elmo-msgdb-directory)))))
1101     (elmo-object-save flist-file flist elmo-mime-charset)))
1102
1103 (defun elmo-crosspost-alist-load ()
1104   (elmo-object-load (expand-file-name
1105                      elmo-crosspost-alist-filename
1106                      elmo-msgdb-directory)
1107                     nil t))
1108
1109 (defun elmo-crosspost-alist-save (alist)
1110   (elmo-object-save (expand-file-name
1111                      elmo-crosspost-alist-filename
1112                      elmo-msgdb-directory)
1113                     alist))
1114
1115 (defun elmo-msgdb-get-message-id-from-buffer ()
1116   (let ((msgid (elmo-field-body "message-id")))
1117     (if msgid
1118         (if (string-match "<\\(.+\\)>$" msgid)
1119             msgid
1120           (concat "<" msgid ">")) ; Invaild message-id.
1121       ;; no message-id, so put dummy msgid.
1122       (concat "<" (timezone-make-date-sortable
1123                    (elmo-field-body "date"))
1124               (nth 1 (eword-extract-address-components
1125                       (or (elmo-field-body "from") "nobody"))) ">"))))
1126
1127 (defsubst elmo-msgdb-create-overview-from-buffer (number &optional size time)
1128   "Create overview entity from current buffer.
1129 Header region is supposed to be narrowed."
1130   (save-excursion
1131     (let ((extras elmo-msgdb-extra-fields)
1132           (default-mime-charset default-mime-charset)
1133           message-id references from subject to cc date
1134           extra field-body charset)
1135       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
1136       (setq message-id (elmo-msgdb-get-message-id-from-buffer))
1137       (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
1138            (setq charset (intern-soft charset))
1139            (setq default-mime-charset charset))
1140       (setq references
1141             (or (elmo-msgdb-get-last-message-id
1142                  (elmo-field-body "in-reply-to"))
1143                 (elmo-msgdb-get-last-message-id
1144                  (elmo-field-body "references"))))
1145       (setq from (elmo-replace-in-string
1146                   (elmo-mime-string (or (elmo-field-body "from")
1147                                         elmo-no-from))
1148                   "\t" " ")
1149             subject (elmo-replace-in-string
1150                      (elmo-mime-string (or (elmo-field-body "subject")
1151                                            elmo-no-subject))
1152                      "\t" " "))
1153       (setq date (or (elmo-field-body "date") time))
1154       (setq to   (mapconcat 'identity (elmo-multiple-field-body "to") ","))
1155       (setq cc   (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
1156       (or size
1157           (if (setq size (elmo-field-body "content-length"))
1158               (setq size (string-to-int size))
1159             (setq size 0)));; No mean...
1160       (while extras
1161         (if (setq field-body (elmo-field-body (car extras)))
1162             (setq extra (cons (cons (downcase (car extras))
1163                                     field-body) extra)))
1164         (setq extras (cdr extras)))
1165       (cons message-id (vector number references
1166                                from subject date to cc
1167                                size extra))
1168       )))
1169
1170 (defun elmo-msgdb-copy-overview-entity (entity)
1171   (cons (car entity)
1172         (copy-sequence (cdr entity))))
1173
1174 (defsubst elmo-msgdb-insert-file-header (file)
1175   "Insert the header of the article."
1176   (let ((beg 0)
1177         insert-file-contents-pre-hook   ; To avoid autoconv-xmas...
1178         insert-file-contents-post-hook
1179         format-alist)
1180     (when (file-exists-p file)
1181       ;; Read until header separator is found.
1182       (while (and (eq elmo-msgdb-file-header-chop-length
1183                       (nth 1
1184                            (insert-file-contents-as-binary
1185                             file nil beg
1186                             (incf beg elmo-msgdb-file-header-chop-length))))
1187                   (prog1 (not (search-forward "\n\n" nil t))
1188                     (goto-char (point-max))))))))
1189
1190 (defsubst elmo-msgdb-create-overview-entity-from-file (number file)
1191   (let (insert-file-contents-pre-hook   ; To avoid autoconv-xmas...
1192         insert-file-contents-post-hook header-end
1193         (attrib (file-attributes file))
1194         ret-val size mtime)
1195     (with-temp-buffer
1196       (if (not (file-exists-p file))
1197           ()
1198         (setq size (nth 7 attrib))
1199         (setq mtime (timezone-make-date-arpa-standard
1200                      (current-time-string (nth 5 attrib)) (current-time-zone)))
1201         ;; insert header from file.
1202         (catch 'done
1203           (condition-case nil
1204               (elmo-msgdb-insert-file-header file)
1205             (error (throw 'done nil)))
1206           (goto-char (point-min))
1207           (setq header-end
1208                 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
1209                     (point)
1210                   (point-max)))
1211           (narrow-to-region (point-min) header-end)
1212           (elmo-msgdb-create-overview-from-buffer number size mtime))))))
1213
1214 (defun elmo-msgdb-overview-sort-by-date (overview)
1215   (sort overview
1216         (function
1217          (lambda (x y)
1218            (condition-case nil
1219                (string<
1220                 (timezone-make-date-sortable
1221                  (elmo-msgdb-overview-entity-get-date x))
1222                 (timezone-make-date-sortable
1223                  (elmo-msgdb-overview-entity-get-date y)))
1224              (error))))))
1225
1226 (defun elmo-msgdb-clear-index (msgdb entity)
1227   (let ((ehash (elmo-msgdb-get-entity-hashtb msgdb))
1228         (mhash (elmo-msgdb-get-mark-hashtb msgdb))
1229         number)
1230     (when (and entity ehash)
1231       (and (setq number (elmo-msgdb-overview-entity-get-number entity))
1232            (elmo-clear-hash-val (format "#%d" number) ehash))
1233       (and (car entity) ;; message-id
1234            (elmo-clear-hash-val (car entity) ehash)))
1235     (when (and entity mhash)
1236       (and (setq number (elmo-msgdb-overview-entity-get-number entity))
1237            (elmo-clear-hash-val (format "#%d" number) mhash)))))
1238
1239 (defun elmo-msgdb-make-index (msgdb &optional overview mark-alist)
1240   "Append OVERVIEW and MARK-ALIST to the index of MSGDB.
1241 If OVERVIEW and MARK-ALIST are nil, make index for current MSGDB.
1242 Return the updated INDEX."
1243   (when msgdb
1244     (let* ((overview (or overview (elmo-msgdb-get-overview msgdb)))
1245            (mark-alist (or mark-alist (elmo-msgdb-get-mark-alist msgdb)))
1246            (index (elmo-msgdb-get-index msgdb))
1247            (ehash (or (car index) ;; append
1248                       (elmo-make-hash (length overview))))
1249            (mhash (or (cdr index) ;; append
1250                       (elmo-make-hash (length overview)))))
1251       (while overview
1252         ;; key is message-id
1253         (if (caar overview)
1254             (elmo-set-hash-val (caar overview) (car overview) ehash))
1255         ;; key is number
1256         (elmo-set-hash-val
1257          (format "#%d"
1258                  (elmo-msgdb-overview-entity-get-number (car overview)))
1259          (car overview) ehash)
1260         (setq overview (cdr overview)))
1261       (while mark-alist
1262         ;; key is number
1263         (elmo-set-hash-val
1264          (format "#%d" (car (car mark-alist)))
1265          (car mark-alist) mhash)
1266         (setq mark-alist (cdr mark-alist)))
1267       (setq index (or index (cons ehash mhash)))
1268       (elmo-msgdb-set-index msgdb index)
1269       index)))
1270
1271 (defsubst elmo-folder-get-info (folder &optional hashtb)
1272   (elmo-get-hash-val folder
1273                      (or hashtb elmo-folder-info-hashtb)))
1274
1275 (defun elmo-folder-get-info-max (folder)
1276   "Get folder info from cache."
1277   (nth 3 (elmo-folder-get-info folder)))
1278
1279 (defun elmo-folder-get-info-length (folder)
1280   (nth 2 (elmo-folder-get-info folder)))
1281
1282 (defun elmo-folder-get-info-unread (folder)
1283   (nth 1 (elmo-folder-get-info folder)))
1284
1285 (defsubst elmo-msgdb-location-load (dir)
1286   (elmo-object-load
1287    (expand-file-name
1288     elmo-msgdb-location-filename
1289     dir)))
1290
1291 (defsubst elmo-msgdb-location-add (alist number location)
1292   (let ((ret-val alist))
1293     (setq ret-val
1294           (elmo-msgdb-append-element ret-val (cons number location)))
1295     ret-val))
1296
1297 (defsubst elmo-msgdb-location-save (dir alist)
1298   (elmo-object-save
1299    (expand-file-name
1300     elmo-msgdb-location-filename
1301     dir) alist))
1302
1303 (defun elmo-msgdb-list-flagged (msgdb flag)
1304   (let ((case-fold-search nil)
1305         mark-regexp matched)
1306     (case flag
1307       (new
1308        (setq mark-regexp (regexp-quote elmo-msgdb-new-mark)))
1309       (unread
1310        (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-unread-marks))))
1311       (answered
1312        (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-answered-marks))))
1313       (important
1314        (setq mark-regexp (regexp-quote elmo-msgdb-important-mark)))
1315       (read
1316        (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-unread-marks))))
1317       (digest
1318        (setq mark-regexp (elmo-regexp-opt
1319                           (append (elmo-msgdb-unread-marks)
1320                                   (list elmo-msgdb-important-mark)))))
1321       (any
1322        (setq mark-regexp (elmo-regexp-opt
1323                           (append
1324                            (elmo-msgdb-unread-marks)
1325                            (elmo-msgdb-answered-marks)
1326                            (list elmo-msgdb-important-mark))))))
1327     (when mark-regexp
1328       (if (eq flag 'read)
1329           (dolist (number (elmo-msgdb-list-messages msgdb))
1330             (let ((mark (elmo-msgdb-get-mark msgdb number)))
1331               (unless (and mark (string-match mark-regexp mark))
1332                 (setq matched (cons number matched)))))
1333         (dolist (elem (elmo-msgdb-get-mark-alist msgdb))
1334           (if (string-match mark-regexp (cadr elem))
1335               (setq matched (cons (car elem) matched))))))
1336     matched))
1337
1338 (require 'product)
1339 (product-provide (provide 'elmo-msgdb) (require 'elmo-version))
1340
1341 ;;; elmo-msgdb.el ends here