1 ;;; elmo-msgdb.el --- Message Database for ELMO.
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;; Masahiro MURATA <muse@ba2.so-net.ne.jp>
8 ;; Keywords: mail, net news
10 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
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)
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.
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.
34 (eval-when-compile (require 'cl))
41 (defcustom elmo-msgdb-new-mark "N"
42 "Mark for new message."
43 :type '(string :tag "Mark")
46 (defcustom elmo-msgdb-unread-uncached-mark "U"
47 "Mark for unread and uncached message."
48 :type '(string :tag "Mark")
51 (defcustom elmo-msgdb-unread-cached-mark "!"
52 "Mark for unread but already cached message."
53 :type '(string :tag "Mark")
56 (defcustom elmo-msgdb-read-uncached-mark "u"
57 "Mark for read but uncached message."
58 :type '(string :tag "Mark")
61 ;; Not implemented yet.
62 (defcustom elmo-msgdb-answered-cached-mark "&"
63 "Mark for answered and cached message."
64 :type '(string :tag "Mark")
67 (defcustom elmo-msgdb-answered-uncached-mark "A"
68 "Mark for answered but cached message."
69 :type '(string :tag "Mark")
72 (defcustom elmo-msgdb-important-mark"$"
73 "Mark for important message."
74 :type '(string :tag "Mark")
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))))
85 (defun elmo-make-msgdb (&optional overview number-alist mark-alist)
87 (let ((msgdb (list overview number-alist mark-alist nil)))
88 (elmo-msgdb-make-index msgdb)
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))))
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))))
103 ;; Set mark of the elem
104 (setcar (cdr elem) mark)
105 ;; Delete elem from mark-alist
106 (elmo-msgdb-set-mark-alist
108 (delq elem (elmo-msgdb-get-mark-alist msgdb)))
109 (elmo-clear-hash-val (format "#%d" number)
110 (elmo-msgdb-get-mark-hashtb msgdb)))
112 ;; Append new element.
113 (elmo-msgdb-set-mark-alist
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))))
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))))
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))
133 ((string= cur-mark elmo-msgdb-important-mark)
135 ((member cur-mark (elmo-msgdb-answered-marks))
137 ((not (member cur-mark (elmo-msgdb-unread-marks)))
139 (cur-cached (elmo-file-cache-exists-p
140 (elmo-msgdb-get-field msgdb number 'message-id))))
141 (unless (eq cached cur-cached)
144 (elmo-msgdb-set-mark msgdb number
145 (if (and use-cache (not cached))
146 elmo-msgdb-read-uncached-mark)))
149 (elmo-msgdb-set-mark msgdb number
151 elmo-msgdb-answered-cached-mark
152 elmo-msgdb-answered-uncached-mark)))
154 (elmo-msgdb-set-mark msgdb number
156 elmo-msgdb-unread-cached-mark
157 elmo-msgdb-unread-uncached-mark)))))))
159 (defun elmo-msgdb-set-flag (msgdb folder number 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))
171 ((string= cur-mark elmo-msgdb-important-mark)
173 ((member cur-mark (elmo-msgdb-answered-marks))
175 ((not (member cur-mark (elmo-msgdb-unread-marks)))
177 (cur-cached (elmo-file-cache-exists-p
178 (elmo-msgdb-get-field msgdb number 'message-id)))
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))))
189 (unless (eq cur-flag 'important)
190 (elmo-msgdb-set-mark msgdb number elmo-msgdb-important-mark)
191 (setq mark-modified t)))
193 (unless (or (eq cur-flag 'answered) (eq cur-flag 'important))
194 (elmo-msgdb-set-mark msgdb number
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))))
201 (defun elmo-msgdb-unset-flag (msgdb folder number 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))
213 ((string= cur-mark elmo-msgdb-important-mark)
215 ((member cur-mark (elmo-msgdb-answered-marks))
217 ((not (member cur-mark (elmo-msgdb-unread-marks)))
219 (cur-cached (elmo-file-cache-exists-p
220 (elmo-msgdb-get-field msgdb number 'message-id)))
224 (when (eq cur-flag 'read)
225 (elmo-msgdb-set-mark msgdb number
227 elmo-msgdb-unread-cached-mark
228 elmo-msgdb-unread-uncached-mark))
229 (setq mark-modified t)))
231 (when (eq cur-flag 'important)
232 (elmo-msgdb-set-mark msgdb number nil)
233 (setq mark-modified t)))
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))))
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))))
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))))
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))))
268 (defsubst elmo-msgdb-count-marks (msgdb)
272 (dolist (elem (elmo-msgdb-get-mark-alist msgdb))
274 ((string= (cadr elem) elmo-msgdb-new-mark)
276 ((member (cadr elem) (elmo-msgdb-unread-marks))
278 ((member (cadr elem) (elmo-msgdb-answered-marks))
280 (list new unreads answered)))
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)))
287 (defsubst elmo-msgdb-get-field (msgdb number field)
288 "Get FIELD value of the message with NUMBER from MSGDB."
290 (message-id (elmo-msgdb-overview-entity-get-id
291 (elmo-msgdb-overview-get-entity
293 (subject (elmo-msgdb-overview-entity-get-subject
294 (elmo-msgdb-overview-get-entity
296 (size (elmo-msgdb-overview-entity-get-size
297 (elmo-msgdb-overview-get-entity
299 (date (elmo-msgdb-overview-entity-get-date
300 (elmo-msgdb-overview-get-entity
302 (to (elmo-msgdb-overview-entity-get-to
303 (elmo-msgdb-overview-get-entity
305 (cc (elmo-msgdb-overview-entity-get-cc
306 (elmo-msgdb-overview-get-entity
309 (defsubst elmo-msgdb-append (msgdb msgdb-append)
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
316 (elmo-msgdb-get-overview msgdb-append)
317 (elmo-msgdb-get-mark-alist msgdb-append))))
319 (defsubst elmo-msgdb-clear (&optional msgdb)
323 (setcar (cdr msgdb) nil)
324 (setcar (cddr msgdb) nil)
325 (setcar (nthcdr 3 msgdb) nil))
326 (list nil nil nil nil)))
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))
337 ;; remove from current database.
342 (elmo-msgdb-overview-get-entity (car msgs) newmsgdb))
344 (setq number-alist (delq (assq (car msgs) number-alist) number-alist))
345 (setq mark-alist (delq (assq (car msgs) mark-alist) mark-alist))
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)
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))))
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)
372 (plist-get args :size)
373 (plist-get args :extra))))
376 (defsubst elmo-msgdb-append-element (list element)
378 ;;; (append list (list element))
379 (nconc list (list element))
383 (defsubst elmo-msgdb-get-overview (msgdb)
385 (defsubst elmo-msgdb-get-number-alist (msgdb)
387 (defsubst elmo-msgdb-get-mark-alist (msgdb)
389 ;(defsubst elmo-msgdb-get-location (msgdb)
392 (defsubst elmo-msgdb-get-index (msgdb)
395 (defsubst elmo-msgdb-get-entity-hashtb (msgdb)
398 (defsubst elmo-msgdb-get-mark-hashtb (msgdb)
402 ;; number <-> Message-ID handling
404 (defsubst elmo-msgdb-number-add (alist number id)
405 (let ((ret-val alist))
407 (elmo-msgdb-append-element ret-val (cons number id)))
411 ;; parsistent mark handling
414 (defvar elmo-msgdb-global-mark-alist nil)
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))))
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))))
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))))
437 (if (setq match (assoc msgid malist))
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)))
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
449 elmo-msgdb-global-mark-filename
450 elmo-msgdb-directory)))))))
453 ;; persistent mark handling
456 (defun elmo-msgdb-mark-append (alist id mark)
458 (setq alist (elmo-msgdb-append-element alist
461 (defun elmo-msgdb-seen-list (msgdb)
462 "Get SEEN-MSGID-LIST from MSGDB."
463 (let ((ov (elmo-msgdb-get-overview msgdb))
466 (if (setq mark (elmo-msgdb-get-mark
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))
474 (setq seen-list (cons
475 (elmo-msgdb-overview-entity-get-id (car ov))
483 (defvar elmo-msgdb-decoded-cache-hashtb nil)
484 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
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))))
492 (or (elmo-get-hash-val string hashtb)
497 (decode-mime-charset-string string elmo-mime-charset))
500 (decode-mime-charset-string string elmo-mime-charset)))
506 (defsubst elmo-msgdb-get-field-value (field-name beg end buffer)
510 (narrow-to-region beg end)
511 (elmo-field-body field-name))))
513 (defun elmo-multiple-field-body (name &optional boundary)
516 (std11-narrow-to-header boundary)
517 (goto-char (point-min))
518 (let ((case-fold-search t)
520 (while (re-search-forward (concat "^" name ":[ \t]*") nil t)
523 (list (buffer-substring-no-properties
524 (match-end 0) (std11-field-end))))))
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
533 (std11-narrow-to-header boundary)
534 (let* ((case-fold-search t)
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)
542 (list (buffer-substring-no-properties
543 (match-end 0) (std11-field-end))))))
544 (setq s-rest (cdr s-rest)))
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))
552 (defsubst elmo-msgdb-get-last-message-id (string)
558 (goto-char (point-max))
559 (when (search-backward "<" nil t)
561 (if (search-forward ">" nil t)
562 (elmo-replace-in-string
563 (buffer-substring beg (point)) "\n[ \t]*" ""))))))))
565 (defun elmo-msgdb-number-load (dir)
567 (expand-file-name elmo-msgdb-number-filename dir)))
569 (defun elmo-msgdb-overview-load (dir)
571 (expand-file-name elmo-msgdb-overview-filename dir)))
573 (defun elmo-msgdb-mark-load (dir)
575 (expand-file-name elmo-msgdb-mark-filename dir)))
577 (defsubst elmo-msgdb-seen-load (dir)
578 (elmo-object-load (expand-file-name
579 elmo-msgdb-seen-filename
582 (defun elmo-msgdb-number-save (dir obj)
584 (expand-file-name elmo-msgdb-number-filename dir)
587 (defun elmo-msgdb-mark-save (dir obj)
589 (expand-file-name elmo-msgdb-mark-filename dir)
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))
597 (setq entity (car mark-alist))
598 (when (string= (cadr entity) before)
599 (setcar (cdr entity) after))
600 (setq mark-alist (cdr mark-alist)))))
602 (defsubst elmo-msgdb-mark (flag cached)
606 elmo-msgdb-unread-cached-mark
607 elmo-msgdb-unread-uncached-mark))
609 elmo-msgdb-important-mark)
612 elmo-msgdb-answered-cached-mark
613 elmo-msgdb-answered-uncached-mark))))
615 (defsubst elmo-msgdb-seen-save (dir obj)
617 (expand-file-name elmo-msgdb-seen-filename dir)
620 (defsubst elmo-msgdb-overview-save (dir overview)
622 (expand-file-name elmo-msgdb-overview-filename dir)
625 (defun elmo-msgdb-match-condition-primitive (condition mark entity numbers)
627 (let ((key (elmo-filter-key condition))
631 ((string= key "last")
632 (setq result (<= (length (memq
633 (elmo-msgdb-overview-entity-get-number
636 (string-to-int (elmo-filter-value condition)))))
637 ((string= key "first")
641 (elmo-msgdb-overview-entity-get-number
644 (string-to-int (elmo-filter-value condition)))))
645 ((string= key "flag")
648 ((string= (elmo-filter-value condition) "any")
650 (string= mark elmo-msgdb-read-uncached-mark))))
651 ((string= (elmo-filter-value condition) "digest")
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))))
674 (setq result (string-match
675 (elmo-filter-value condition)
676 (elmo-msgdb-overview-entity-get-to entity))))
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
685 (elmo-msgdb-overview-entity-get-date entity)
686 (current-time-zone) nil)))
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)
702 (throw 'unresolved condition)))
703 (if (eq (elmo-filter-type condition) 'unmatch)
707 (defun elmo-msgdb-match-condition-internal (condition mark entity numbers)
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)))
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)
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)))
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)
741 (elmo-msgdb-match-condition-internal (nth 2 condition)
742 mark entity numbers)))))))
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)))
753 (elmo-msgdb-match-condition-internal condition
754 (elmo-msgdb-get-mark msgdb number)
758 (defsubst elmo-msgdb-set-overview (msgdb overview)
759 (setcar msgdb overview))
761 (defsubst elmo-msgdb-set-number-alist (msgdb number-alist)
762 (setcar (cdr msgdb) number-alist))
764 (defsubst elmo-msgdb-set-mark-alist (msgdb mark-alist)
765 (setcar (cddr msgdb) mark-alist))
767 (defsubst elmo-msgdb-set-index (msgdb index)
768 (setcar (cdddr msgdb) index))
770 (defsubst elmo-msgdb-overview-entity-get-references (entity)
771 (and entity (aref (cdr entity) 1)))
773 (defsubst elmo-msgdb-overview-entity-set-references (entity references)
774 (and entity (aset (cdr entity) 1 references))
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)))
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)))
788 (defsubst elmo-msgdb-overview-entity-get-number (entity)
789 (and entity (aref (cdr entity) 0)))
791 (defsubst elmo-msgdb-overview-entity-get-from-no-decode (entity)
792 (and entity (aref (cdr entity) 2)))
794 (defsubst elmo-msgdb-overview-entity-get-from (entity)
796 (aref (cdr entity) 2)
797 (elmo-msgdb-get-decoded-cache (aref (cdr entity) 2))))
799 (defsubst elmo-msgdb-overview-entity-set-number (entity number)
800 (and entity (aset (cdr entity) 0 number))
802 ;;;(setcar (cadr entity) number) entity)
804 (defsubst elmo-msgdb-overview-entity-set-from (entity from)
805 (and entity (aset (cdr entity) 2 from))
808 (defsubst elmo-msgdb-overview-entity-get-subject (entity)
810 (aref (cdr entity) 3)
811 (elmo-msgdb-get-decoded-cache (aref (cdr entity) 3))))
813 (defsubst elmo-msgdb-overview-entity-get-subject-no-decode (entity)
814 (and entity (aref (cdr entity) 3)))
816 (defsubst elmo-msgdb-overview-entity-set-subject (entity subject)
817 (and entity (aset (cdr entity) 3 subject))
820 (defsubst elmo-msgdb-overview-entity-get-date (entity)
821 (and entity (aref (cdr entity) 4)))
823 (defsubst elmo-msgdb-overview-entity-set-date (entity date)
824 (and entity (aset (cdr entity) 4 date))
827 (defsubst elmo-msgdb-overview-entity-get-to (entity)
828 (and entity (aref (cdr entity) 5)))
830 (defsubst elmo-msgdb-overview-entity-get-cc (entity)
831 (and entity (aref (cdr entity) 6)))
833 (defsubst elmo-msgdb-overview-entity-get-size (entity)
834 (and entity (aref (cdr entity) 7)))
836 (defsubst elmo-msgdb-overview-entity-set-size (entity size)
837 (and entity (aset (cdr entity) 7 size))
840 (defsubst elmo-msgdb-overview-entity-get-id (entity)
841 (and entity (car entity)))
843 (defsubst elmo-msgdb-overview-entity-get-extra-field (entity field-name)
844 (let ((extra (and entity (aref (cdr entity) 8))))
846 (cdr (assoc field-name extra)))))
848 (defsubst elmo-msgdb-overview-entity-set-extra-field (entity field-name value)
849 (let ((extras (and entity (aref (cdr entity) 8)))
851 (if (setq extra (assoc field-name extras))
853 (elmo-msgdb-overview-entity-set-extra
855 (cons (cons field-name value) extras)))))
857 (defsubst elmo-msgdb-overview-entity-get-extra (entity)
858 (and entity (aref (cdr entity) 8)))
860 (defsubst elmo-msgdb-overview-entity-set-extra (entity extra)
861 (and entity (aset (cdr entity) 8 extra))
864 (defun elmo-msgdb-overview-get-entity-by-number (database number)
869 (if (eq (elmo-msgdb-overview-entity-get-number (car db)) number)
870 (setq entity (car db)
875 (defun elmo-msgdb-overview-get-entity (id msgdb)
877 (let ((ht (elmo-msgdb-get-entity-hashtb msgdb)))
879 (if (stringp id) ;; ID is message-id
880 (elmo-get-hash-val id ht)
881 (elmo-get-hash-val (format "#%d" id) ht))))))
884 ;; deleted message handling
886 (defun elmo-msgdb-killed-list-load (dir)
888 (expand-file-name elmo-msgdb-killed-filename dir)
891 (defun elmo-msgdb-killed-list-save (dir killed-list)
893 (expand-file-name elmo-msgdb-killed-filename dir)
896 (defun elmo-msgdb-killed-message-p (killed-list msg)
897 (elmo-number-set-member msg killed-list))
899 (defun elmo-msgdb-set-as-killed (killed-list msg)
900 (elmo-number-set-append killed-list msg))
902 (defun elmo-msgdb-append-to-killed-list (folder msgs)
903 (elmo-folder-set-killed-list-internal
905 (elmo-number-set-append-list
906 (elmo-folder-killed-list-internal folder)
909 (defun elmo-msgdb-killed-list-length (killed-list)
910 (let ((killed killed-list)
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)))
919 (defun elmo-msgdb-max-of-killed (killed-list)
920 (let ((klist killed-list)
926 (if (consp (car klist))
930 (setq klist (cdr klist)))
933 (defun elmo-living-messages (messages killed-list)
936 (mapcar (lambda (number)
937 (unless (elmo-number-set-member number killed-list)
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))
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))
954 (defun elmo-msgdb-flist-load (fname)
955 (let ((flist-file (expand-file-name
956 elmo-msgdb-flist-filename
958 (elmo-safe-filename fname)
959 (expand-file-name "folder" elmo-msgdb-directory)))))
960 (elmo-object-load flist-file elmo-mime-charset t)))
962 (defun elmo-msgdb-flist-save (fname flist)
963 (let ((flist-file (expand-file-name
964 elmo-msgdb-flist-filename
966 (elmo-safe-filename fname)
967 (expand-file-name "folder" elmo-msgdb-directory)))))
968 (elmo-object-save flist-file flist elmo-mime-charset)))
970 (defun elmo-crosspost-alist-load ()
971 (elmo-object-load (expand-file-name
972 elmo-crosspost-alist-filename
973 elmo-msgdb-directory)
976 (defun elmo-crosspost-alist-save (alist)
977 (elmo-object-save (expand-file-name
978 elmo-crosspost-alist-filename
979 elmo-msgdb-directory)
982 (defun elmo-msgdb-add-msgs-to-seen-list (msgs msgdb seen-list)
986 (if (setq mark (elmo-msgdb-get-mark msgdb (car msgs)))
987 (unless (member mark (elmo-msgdb-unread-marks)) ;; not unread mark
990 (elmo-msgdb-get-field msgdb (car msgs) 'message-id)
992 ;; no mark ... seen...
995 (elmo-msgdb-get-field msgdb (car msgs) 'message-id)
997 (setq msgs (cdr msgs)))
1000 (defun elmo-msgdb-get-message-id-from-buffer ()
1001 (let ((msgid (elmo-field-body "message-id")))
1003 (if (string-match "<\\(.+\\)>$" 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"))) ">"))))
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."
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))
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")
1034 subject (elmo-replace-in-string
1035 (elmo-mime-string (or (elmo-field-body "subject")
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") ","))
1042 (if (setq size (elmo-field-body "content-length"))
1043 (setq size (string-to-int size))
1044 (setq size 0)));; No mean...
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
1055 (defun elmo-msgdb-copy-overview-entity (entity)
1057 (copy-sequence (cdr entity))))
1059 (defsubst elmo-msgdb-insert-file-header (file)
1060 "Insert the header of the article."
1062 insert-file-contents-pre-hook ; To avoid autoconv-xmas...
1063 insert-file-contents-post-hook
1065 (when (file-exists-p file)
1066 ;; Read until header separator is found.
1067 (while (and (eq elmo-msgdb-file-header-chop-length
1069 (insert-file-contents-as-binary
1071 (incf beg elmo-msgdb-file-header-chop-length))))
1072 (prog1 (not (search-forward "\n\n" nil t))
1073 (goto-char (point-max))))))))
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))
1081 (if (not (file-exists-p file))
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.
1089 (elmo-msgdb-insert-file-header file)
1090 (error (throw 'done nil)))
1091 (goto-char (point-min))
1093 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
1096 (narrow-to-region (point-min) header-end)
1097 (elmo-msgdb-create-overview-from-buffer number size mtime))))))
1099 (defun elmo-msgdb-overview-sort-by-date (overview)
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)))
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))
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)))))
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."
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)))))
1137 ;; key is message-id
1139 (elmo-set-hash-val (caar overview) (car overview) ehash))
1143 (elmo-msgdb-overview-entity-get-number (car overview)))
1144 (car overview) ehash)
1145 (setq overview (cdr overview)))
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)
1156 (defsubst elmo-folder-get-info (folder &optional hashtb)
1157 (elmo-get-hash-val folder
1158 (or hashtb elmo-folder-info-hashtb)))
1160 (defun elmo-folder-get-info-max (folder)
1161 "Get folder info from cache."
1162 (nth 3 (elmo-folder-get-info folder)))
1164 (defun elmo-folder-get-info-length (folder)
1165 (nth 2 (elmo-folder-get-info folder)))
1167 (defun elmo-folder-get-info-unread (folder)
1168 (nth 1 (elmo-folder-get-info folder)))
1170 (defsubst elmo-msgdb-location-load (dir)
1173 elmo-msgdb-location-filename
1176 (defsubst elmo-msgdb-location-add (alist number location)
1177 (let ((ret-val alist))
1179 (elmo-msgdb-append-element ret-val (cons number location)))
1182 (defsubst elmo-msgdb-location-save (dir alist)
1185 elmo-msgdb-location-filename
1188 (defun elmo-msgdb-list-flagged (msgdb flag)
1189 (let ((case-fold-search nil)
1190 mark-regexp matched)
1193 (setq mark-regexp (regexp-quote elmo-msgdb-new-mark)))
1195 (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-unread-marks))))
1197 (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-unread-marks))))
1199 (setq mark-regexp (regexp-quote elmo-msgdb-important-mark)))
1201 (setq mark-regexp (elmo-regexp-opt (elmo-msgdb-unread-marks))))
1203 (setq mark-regexp (elmo-regexp-opt
1204 (append (elmo-msgdb-unread-marks)
1205 (list elmo-msgdb-important-mark)))))
1207 (setq mark-regexp (elmo-regexp-opt
1209 (elmo-msgdb-unread-marks)
1210 (elmo-msgdb-answered-marks)
1211 (list elmo-msgdb-important-mark))))))
1214 (dolist (number (elmo-msgdb-get-number-alist msgdb))
1215 (unless (string-match mark-regexp (elmo-msgdb-get-mark
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))))))
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))))
1231 (product-provide (provide 'elmo-msgdb) (require 'elmo-version))
1233 ;;; elmo-msgdb.el ends here