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