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