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