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