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))))))
121 (defun elmo-msgdb-set-cached (msgdb number cached)
122 "Set message cache status."
123 (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
125 ((string= cur-mark elmo-msgdb-important-mark)
127 ((member cur-mark (elmo-msgdb-answered-marks))
129 ((not (member cur-mark (elmo-msgdb-unread-marks)))
131 (cur-cached (not (member cur-mark (elmo-msgdb-uncached-marks)))))
132 (unless (eq (not cached) (not cur-cached))
135 (elmo-msgdb-set-mark msgdb number
137 elmo-msgdb-read-uncached-mark)))
140 (elmo-msgdb-set-mark msgdb number
142 elmo-msgdb-answered-cached-mark
143 elmo-msgdb-answered-uncached-mark)))
145 (elmo-msgdb-set-mark msgdb number
147 elmo-msgdb-unread-cached-mark
148 elmo-msgdb-unread-uncached-mark)))))))
150 (defun elmo-msgdb-set-status (msgdb folder number status)
152 MSGDB is the ELMO msgdb.
153 FOLDER is a ELMO folder structure.
154 NUMBER is a message number to be set status.
155 STATUS is a symbol which is one of the following:
156 `read' ... Messages which are already read.
157 `important' ... Messages which are marked as important.
158 `answered' ... Messages which are marked as answered."
159 (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
160 (use-cache (elmo-message-use-cache-p folder number))
162 ((string= cur-mark elmo-msgdb-important-mark)
164 ((member cur-mark (elmo-msgdb-answered-marks))
166 ((not (member cur-mark (elmo-msgdb-unread-marks)))
168 (cur-cached (not (member cur-mark (elmo-msgdb-uncached-marks))))
173 ((read important answered))
174 (t (elmo-msgdb-set-mark msgdb number
175 (if (and use-cache cur-cached)
178 elmo-msgdb-read-uncached-mark)))
179 (setq mark-modified t))))
181 (unless (eq cur-status 'important)
182 (elmo-msgdb-set-mark msgdb number elmo-msgdb-important-mark)
183 (setq mark-modified t)))
185 (unless (or (eq cur-status 'answered) (eq cur-status 'important))
186 (elmo-msgdb-set-mark msgdb number
189 elmo-msgdb-answered-cached-mark
190 elmo-msgdb-answered-uncached-mark)
191 elmo-msgdb-answered-uncached-mark)))
192 (setq mark-modified t)))
193 (if mark-modified (elmo-folder-set-mark-modified-internal folder t))))
195 (defun elmo-msgdb-unset-status (msgdb folder number status)
196 "Unset message status.
197 MSGDB is the ELMO msgdb.
198 FOLDER is a ELMO folder structure.
199 NUMBER is a message number to be set status.
200 STATUS is a symbol which is one of the following:
201 `read' ... Messages which are already read.
202 `important' ... Messages which are marked as important.
203 `answered' ... Messages which are marked as answered."
204 (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
205 (use-cache (elmo-message-use-cache-p folder number))
207 ((string= cur-mark elmo-msgdb-important-mark)
209 ((member cur-mark (elmo-msgdb-answered-marks))
211 ((not (member cur-mark (elmo-msgdb-unread-marks)))
213 (cur-cached (not (member cur-mark (elmo-msgdb-uncached-marks)))))
216 (if (eq cur-status 'read)
217 (elmo-msgdb-set-mark msgdb number
218 (if (and cur-cached use-cache)
219 elmo-msgdb-unread-cached-mark
220 elmo-msgdb-unread-uncached-mark))))
222 (if (eq cur-status 'important)
223 (elmo-msgdb-set-mark msgdb number nil)))
225 (if (eq cur-status 'answered)
226 (elmo-msgdb-set-mark msgdb number
227 (if (and cur-cached (not use-cache))
228 elmo-msgdb-read-uncached-mark)))))))
230 (defvar elmo-msgdb-unread-marks-internal nil)
231 (defsubst elmo-msgdb-unread-marks ()
232 "Return an unread mark list"
233 (or elmo-msgdb-unread-marks-internal
234 (setq elmo-msgdb-unread-marks-internal
235 (list elmo-msgdb-new-mark
236 elmo-msgdb-unread-uncached-mark
237 elmo-msgdb-unread-cached-mark))))
239 (defvar elmo-msgdb-answered-marks-internal nil)
240 (defsubst elmo-msgdb-answered-marks ()
241 "Return an answered mark list"
242 (or elmo-msgdb-answered-marks-internal
243 (setq elmo-msgdb-answered-marks-internal
244 (list elmo-msgdb-answered-cached-mark
245 elmo-msgdb-answered-uncached-mark))))
247 (defvar elmo-msgdb-uncached-marks-internal nil)
248 (defsubst elmo-msgdb-uncached-marks ()
249 (or elmo-msgdb-uncached-marks-internal
250 (setq elmo-msgdb-uncached-marks-internal
251 (list elmo-msgdb-answered-uncached-mark
252 elmo-msgdb-unread-uncached-mark
253 elmo-msgdb-read-uncached-mark))))
255 (defsubst elmo-msgdb-count-marks (msgdb)
259 (dolist (elem (elmo-msgdb-get-mark-alist msgdb))
261 ((string= (cadr elem) elmo-msgdb-new-mark)
263 ((member (cadr elem) (elmo-msgdb-unread-marks))
265 ((member (cadr elem) (elmo-msgdb-answered-marks))
267 (list new unreads answered)))
269 (defsubst elmo-msgdb-get-number (msgdb message-id)
270 "Get number of the message which corrensponds to MESSAGE-ID from MSGDB."
271 (elmo-msgdb-overview-entity-get-number
272 (elmo-msgdb-overview-get-entity message-id msgdb)))
274 (defsubst elmo-msgdb-get-field (msgdb number field)
275 "Get FIELD value of the message with NUMBER from MSGDB."
277 (message-id (elmo-msgdb-overview-entity-get-id
278 (elmo-msgdb-overview-get-entity
280 (subject (elmo-msgdb-overview-entity-get-subject
281 (elmo-msgdb-overview-get-entity
283 (size (elmo-msgdb-overview-entity-get-size
284 (elmo-msgdb-overview-get-entity
286 (date (elmo-msgdb-overview-entity-get-date
287 (elmo-msgdb-overview-get-entity
289 (to (elmo-msgdb-overview-entity-get-to
290 (elmo-msgdb-overview-get-entity
292 (cc (elmo-msgdb-overview-entity-get-cc
293 (elmo-msgdb-overview-get-entity
296 (defsubst elmo-msgdb-append (msgdb msgdb-append)
298 (nconc (car msgdb) (car msgdb-append))
299 (nconc (cadr msgdb) (cadr msgdb-append))
300 (nconc (caddr msgdb) (caddr msgdb-append))
301 (elmo-msgdb-make-index
303 (elmo-msgdb-get-overview msgdb-append)
304 (elmo-msgdb-get-mark-alist msgdb-append))))
306 (defsubst elmo-msgdb-clear (&optional msgdb)
310 (setcar (cdr msgdb) nil)
311 (setcar (cddr msgdb) nil)
312 (setcar (nthcdr 3 msgdb) nil))
313 (list nil nil nil nil)))
315 (defun elmo-msgdb-delete-msgs (msgdb msgs)
316 "Delete MSGS from MSGDB
317 content of MSGDB is changed."
318 (let* ((overview (car msgdb))
319 (number-alist (cadr msgdb))
320 (mark-alist (caddr msgdb))
321 (index (elmo-msgdb-get-index msgdb))
322 (newmsgdb (list overview number-alist mark-alist index))
324 ;; remove from current database.
329 (elmo-msgdb-overview-get-entity (car msgs) newmsgdb))
331 (setq number-alist (delq (assq (car msgs) number-alist) number-alist))
332 (setq mark-alist (delq (assq (car msgs) mark-alist) mark-alist))
334 (when index (elmo-msgdb-clear-index msgdb ov-entity))
335 (setq msgs (cdr msgs)))
336 (setcar msgdb overview)
337 (setcar (cdr msgdb) number-alist)
338 (setcar (cddr msgdb) mark-alist)
339 (setcar (nthcdr 3 msgdb) index)
342 (defun elmo-msgdb-sort-by-date (msgdb)
343 (message "Sorting...")
344 (let ((overview (elmo-msgdb-get-overview msgdb)))
345 (setq overview (elmo-msgdb-overview-sort-by-date overview))
346 (message "Sorting...done")
347 (list overview (nth 1 msgdb)(nth 2 msgdb))))
349 (defun elmo-msgdb-make-entity (&rest args)
350 "Make an msgdb entity."
351 (cons (plist-get args :message-id)
352 (vector (plist-get args :number)
353 (plist-get args :references)
354 (plist-get args :from)
355 (plist-get args :subject)
356 (plist-get args :date)
359 (plist-get args :size)
360 (plist-get args :extra))))
363 (defsubst elmo-msgdb-append-element (list element)
365 ;;; (append list (list element))
366 (nconc list (list element))
370 (defsubst elmo-msgdb-get-overview (msgdb)
372 (defsubst elmo-msgdb-get-number-alist (msgdb)
374 (defsubst elmo-msgdb-get-mark-alist (msgdb)
376 ;(defsubst elmo-msgdb-get-location (msgdb)
379 (defsubst elmo-msgdb-get-index (msgdb)
382 (defsubst elmo-msgdb-get-entity-hashtb (msgdb)
385 (defsubst elmo-msgdb-get-mark-hashtb (msgdb)
389 ;; number <-> Message-ID handling
391 (defsubst elmo-msgdb-number-add (alist number id)
392 (let ((ret-val alist))
394 (elmo-msgdb-append-element ret-val (cons number id)))
398 ;; parsistent mark handling
401 (defvar elmo-msgdb-global-mark-alist nil)
403 (defun elmo-msgdb-global-mark-delete (msgid)
404 (let* ((path (expand-file-name
405 elmo-msgdb-global-mark-filename
406 elmo-msgdb-directory))
407 (malist (or elmo-msgdb-global-mark-alist
408 (setq elmo-msgdb-global-mark-alist
409 (elmo-object-load path))))
411 (when (setq match (assoc msgid malist))
412 (setq elmo-msgdb-global-mark-alist
413 (delete match elmo-msgdb-global-mark-alist))
414 (elmo-object-save path elmo-msgdb-global-mark-alist))))
416 (defun elmo-msgdb-global-mark-set (msgid mark)
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 (if (setq match (assoc msgid malist))
426 (setq elmo-msgdb-global-mark-alist
427 (nconc elmo-msgdb-global-mark-alist
428 (list (cons msgid mark)))))
429 (elmo-object-save path elmo-msgdb-global-mark-alist)))
431 (defun elmo-msgdb-global-mark-get (msgid)
432 (cdr (assoc msgid (or elmo-msgdb-global-mark-alist
433 (setq elmo-msgdb-global-mark-alist
436 elmo-msgdb-global-mark-filename
437 elmo-msgdb-directory)))))))
440 ;; persistent mark handling
443 (defun elmo-msgdb-mark-append (alist id mark)
445 (setq alist (elmo-msgdb-append-element alist
448 (defun elmo-msgdb-seen-list (msgdb)
449 "Get SEEN-MSGID-LIST from MSGDB."
450 (let ((ov (elmo-msgdb-get-overview msgdb))
453 (if (setq mark (elmo-msgdb-get-mark
455 (elmo-msgdb-overview-entity-get-number (car ov))))
456 (if (and mark (not (member mark
457 (elmo-msgdb-unread-marks))))
458 (setq seen-list (cons
459 (elmo-msgdb-overview-entity-get-id (car ov))
461 (setq seen-list (cons
462 (elmo-msgdb-overview-entity-get-id (car ov))
470 (defvar elmo-msgdb-decoded-cache-hashtb nil)
471 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
473 (defsubst elmo-msgdb-get-decoded-cache (string)
474 (if elmo-use-decoded-cache
475 (let ((hashtb (or elmo-msgdb-decoded-cache-hashtb
476 (setq elmo-msgdb-decoded-cache-hashtb
477 (elmo-make-hash 2048))))
479 (or (elmo-get-hash-val string hashtb)
484 (decode-mime-charset-string string elmo-mime-charset))
487 (decode-mime-charset-string string elmo-mime-charset)))
493 (defsubst elmo-msgdb-get-field-value (field-name beg end buffer)
497 (narrow-to-region beg end)
498 (elmo-field-body field-name))))
500 (defun elmo-multiple-field-body (name &optional boundary)
503 (std11-narrow-to-header boundary)
504 (goto-char (point-min))
505 (let ((case-fold-search t)
507 (while (re-search-forward (concat "^" name ":[ \t]*") nil t)
510 (list (buffer-substring-no-properties
511 (match-end 0) (std11-field-end))))))
514 (defun elmo-multiple-fields-body-list (field-names &optional boundary)
515 "Return list of each field-bodies of FIELD-NAMES of the message header
516 in current buffer. If BOUNDARY is not nil, it is used as message
520 (std11-narrow-to-header boundary)
521 (let* ((case-fold-search t)
523 field-name field-body)
524 (while (setq field-name (car s-rest))
525 (goto-char (point-min))
526 (while (re-search-forward (concat "^" field-name ":[ \t]*") nil t)
529 (list (buffer-substring-no-properties
530 (match-end 0) (std11-field-end))))))
531 (setq s-rest (cdr s-rest)))
534 (defsubst elmo-msgdb-remove-field-string (string)
535 (if (string-match (concat std11-field-head-regexp "[ \t]*") string)
536 (substring string (match-end 0))
539 (defsubst elmo-msgdb-get-last-message-id (string)
545 (goto-char (point-max))
546 (when (search-backward "<" nil t)
548 (if (search-forward ">" nil t)
549 (elmo-replace-in-string
550 (buffer-substring beg (point)) "\n[ \t]*" ""))))))))
552 (defun elmo-msgdb-number-load (dir)
554 (expand-file-name elmo-msgdb-number-filename dir)))
556 (defun elmo-msgdb-overview-load (dir)
558 (expand-file-name elmo-msgdb-overview-filename dir)))
560 (defun elmo-msgdb-mark-load (dir)
562 (expand-file-name elmo-msgdb-mark-filename dir)))
564 (defsubst elmo-msgdb-seen-load (dir)
565 (elmo-object-load (expand-file-name
566 elmo-msgdb-seen-filename
569 (defun elmo-msgdb-number-save (dir obj)
571 (expand-file-name elmo-msgdb-number-filename dir)
574 (defun elmo-msgdb-mark-save (dir obj)
576 (expand-file-name elmo-msgdb-mark-filename dir)
579 (defun elmo-msgdb-change-mark (msgdb before after)
580 "Set the BEFORE marks to AFTER."
581 (let ((mark-alist (elmo-msgdb-get-mark-alist msgdb))
584 (setq entity (car mark-alist))
585 (when (string= (cadr entity) before)
586 (setcar (cdr entity) after))
587 (setq mark-alist (cdr mark-alist)))))
589 (defsubst elmo-msgdb-mark (status cached)
593 elmo-msgdb-unread-cached-mark
594 elmo-msgdb-unread-uncached-mark))
596 elmo-msgdb-important-mark)
599 elmo-msgdb-answered-cached-mark
600 elmo-msgdb-answered-uncached-mark))))
602 (defsubst elmo-msgdb-seen-save (dir obj)
604 (expand-file-name elmo-msgdb-seen-filename dir)
607 (defsubst elmo-msgdb-overview-save (dir overview)
609 (expand-file-name elmo-msgdb-overview-filename dir)
612 (defun elmo-msgdb-match-condition-primitive (condition entity numbers)
614 (let ((key (elmo-filter-key condition))
618 ((string= key "last")
619 (setq result (<= (length (memq
620 (elmo-msgdb-overview-entity-get-number
623 (string-to-int (elmo-filter-value condition)))))
624 ((string= key "first")
628 (elmo-msgdb-overview-entity-get-number
631 (string-to-int (elmo-filter-value condition)))))
632 ((string= key "from")
633 (setq result (string-match
634 (elmo-filter-value condition)
635 (elmo-msgdb-overview-entity-get-from entity))))
636 ((string= key "subject")
637 (setq result (string-match
638 (elmo-filter-value condition)
639 (elmo-msgdb-overview-entity-get-subject entity))))
641 (setq result (string-match
642 (elmo-filter-value condition)
643 (elmo-msgdb-overview-entity-get-to entity))))
645 (setq result (string-match
646 (elmo-filter-value condition)
647 (elmo-msgdb-overview-entity-get-cc entity))))
648 ((or (string= key "since")
649 (string= key "before"))
650 (let ((field-date (elmo-date-make-sortable-string
652 (elmo-msgdb-overview-entity-get-date entity)
653 (current-time-zone) nil)))
655 (elmo-date-make-sortable-string
656 (elmo-date-get-datevec
657 (elmo-filter-value condition)))))
658 (setq result (if (string= key "since")
659 (or (string= specified-date field-date)
660 (string< specified-date field-date))
661 (string< field-date specified-date)))))
662 ((member key elmo-msgdb-extra-fields)
663 (let ((extval (elmo-msgdb-overview-entity-get-extra-field entity key)))
664 (when (stringp extval)
665 (setq result (string-match
666 (elmo-filter-value condition)
669 (throw 'unresolved condition)))
670 (if (eq (elmo-filter-type condition) 'unmatch)
674 (defun elmo-msgdb-match-condition (condition entity numbers)
677 (elmo-msgdb-match-condition-primitive condition entity numbers))
678 ((eq (car condition) 'and)
679 (let ((lhs (elmo-msgdb-match-condition (nth 1 condition)
682 ((elmo-filter-condition-p lhs)
683 (let ((rhs (elmo-msgdb-match-condition (nth 2 condition)
685 (cond ((elmo-filter-condition-p rhs)
690 (elmo-msgdb-match-condition (nth 2 condition)
692 ((eq (car condition) 'or)
693 (let ((lhs (elmo-msgdb-match-condition (nth 1 condition)
696 ((elmo-filter-condition-p lhs)
697 (let ((rhs (elmo-msgdb-match-condition (nth 2 condition)
699 (cond ((elmo-filter-condition-p rhs)
708 (elmo-msgdb-match-condition (nth 2 condition)
709 entity numbers)))))))
711 (defsubst elmo-msgdb-set-overview (msgdb overview)
712 (setcar msgdb overview))
714 (defsubst elmo-msgdb-set-number-alist (msgdb number-alist)
715 (setcar (cdr msgdb) number-alist))
717 (defsubst elmo-msgdb-set-mark-alist (msgdb mark-alist)
718 (setcar (cddr msgdb) mark-alist))
720 (defsubst elmo-msgdb-set-index (msgdb index)
721 (setcar (cdddr msgdb) index))
723 (defsubst elmo-msgdb-overview-entity-get-references (entity)
724 (and entity (aref (cdr entity) 1)))
726 (defsubst elmo-msgdb-overview-entity-set-references (entity references)
727 (and entity (aset (cdr entity) 1 references))
730 ;; entity -> parent-entity
731 (defsubst elmo-msgdb-overview-get-parent-entity (entity database)
732 (setq entity (elmo-msgdb-overview-entity-get-references entity))
733 ;; entity is parent-id.
734 (and entity (assoc entity database)))
736 (defsubst elmo-msgdb-get-parent-entity (entity msgdb)
737 (setq entity (elmo-msgdb-overview-entity-get-references entity))
738 ;; entity is parent-id.
739 (and entity (elmo-msgdb-overview-get-entity entity msgdb)))
741 (defsubst elmo-msgdb-overview-entity-get-number (entity)
742 (and entity (aref (cdr entity) 0)))
744 (defsubst elmo-msgdb-overview-entity-get-from-no-decode (entity)
745 (and entity (aref (cdr entity) 2)))
747 (defsubst elmo-msgdb-overview-entity-get-from (entity)
749 (aref (cdr entity) 2)
750 (elmo-msgdb-get-decoded-cache (aref (cdr entity) 2))))
752 (defsubst elmo-msgdb-overview-entity-set-number (entity number)
753 (and entity (aset (cdr entity) 0 number))
755 ;;;(setcar (cadr entity) number) entity)
757 (defsubst elmo-msgdb-overview-entity-set-from (entity from)
758 (and entity (aset (cdr entity) 2 from))
761 (defsubst elmo-msgdb-overview-entity-get-subject (entity)
763 (aref (cdr entity) 3)
764 (elmo-msgdb-get-decoded-cache (aref (cdr entity) 3))))
766 (defsubst elmo-msgdb-overview-entity-get-subject-no-decode (entity)
767 (and entity (aref (cdr entity) 3)))
769 (defsubst elmo-msgdb-overview-entity-set-subject (entity subject)
770 (and entity (aset (cdr entity) 3 subject))
773 (defsubst elmo-msgdb-overview-entity-get-date (entity)
774 (and entity (aref (cdr entity) 4)))
776 (defsubst elmo-msgdb-overview-entity-set-date (entity date)
777 (and entity (aset (cdr entity) 4 date))
780 (defsubst elmo-msgdb-overview-entity-get-to (entity)
781 (and entity (aref (cdr entity) 5)))
783 (defsubst elmo-msgdb-overview-entity-get-cc (entity)
784 (and entity (aref (cdr entity) 6)))
786 (defsubst elmo-msgdb-overview-entity-get-size (entity)
787 (and entity (aref (cdr entity) 7)))
789 (defsubst elmo-msgdb-overview-entity-set-size (entity size)
790 (and entity (aset (cdr entity) 7 size))
793 (defsubst elmo-msgdb-overview-entity-get-id (entity)
794 (and entity (car entity)))
796 (defsubst elmo-msgdb-overview-entity-get-extra-field (entity field-name)
797 (let ((extra (and entity (aref (cdr entity) 8))))
799 (cdr (assoc field-name extra)))))
801 (defsubst elmo-msgdb-overview-entity-set-extra-field (entity field-name value)
802 (let ((extras (and entity (aref (cdr entity) 8)))
804 (if (setq extra (assoc field-name extras))
806 (elmo-msgdb-overview-entity-set-extra
808 (cons (cons field-name value) extras)))))
810 (defsubst elmo-msgdb-overview-entity-get-extra (entity)
811 (and entity (aref (cdr entity) 8)))
813 (defsubst elmo-msgdb-overview-entity-set-extra (entity extra)
814 (and entity (aset (cdr entity) 8 extra))
817 (defun elmo-msgdb-overview-get-entity-by-number (database number)
822 (if (eq (elmo-msgdb-overview-entity-get-number (car db)) number)
823 (setq entity (car db)
828 (defun elmo-msgdb-overview-get-entity (id msgdb)
830 (let ((ht (elmo-msgdb-get-entity-hashtb msgdb)))
832 (if (stringp id) ;; ID is message-id
833 (elmo-get-hash-val id ht)
834 (elmo-get-hash-val (format "#%d" id) ht))))))
837 ;; deleted message handling
839 (defun elmo-msgdb-killed-list-load (dir)
841 (expand-file-name elmo-msgdb-killed-filename dir)
844 (defun elmo-msgdb-killed-list-save (dir killed-list)
846 (expand-file-name elmo-msgdb-killed-filename dir)
849 (defun elmo-msgdb-killed-message-p (killed-list msg)
850 (elmo-number-set-member msg killed-list))
852 (defun elmo-msgdb-set-as-killed (killed-list msg)
853 (elmo-number-set-append killed-list msg))
855 (defun elmo-msgdb-append-to-killed-list (folder msgs)
856 (elmo-folder-set-killed-list-internal
858 (elmo-number-set-append-list
859 (elmo-folder-killed-list-internal folder)
862 (defun elmo-msgdb-killed-list-length (killed-list)
863 (let ((killed killed-list)
866 (if (consp (car killed))
867 (setq ret-val (+ ret-val 1 (- (cdar killed) (caar killed))))
868 (setq ret-val (+ ret-val 1)))
869 (setq killed (cdr killed)))
872 (defun elmo-msgdb-max-of-killed (killed-list)
873 (let ((klist killed-list)
879 (if (consp (car klist))
883 (setq klist (cdr klist)))
886 (defun elmo-living-messages (messages killed-list)
889 (mapcar (lambda (number)
890 (unless (elmo-number-set-member number killed-list)
895 (defun elmo-msgdb-finfo-load ()
896 (elmo-object-load (expand-file-name
897 elmo-msgdb-finfo-filename
898 elmo-msgdb-directory)
899 elmo-mime-charset t))
901 (defun elmo-msgdb-finfo-save (finfo)
902 (elmo-object-save (expand-file-name
903 elmo-msgdb-finfo-filename
904 elmo-msgdb-directory)
905 finfo elmo-mime-charset))
907 (defun elmo-msgdb-flist-load (fname)
908 (let ((flist-file (expand-file-name
909 elmo-msgdb-flist-filename
911 (elmo-safe-filename fname)
912 (expand-file-name "folder" elmo-msgdb-directory)))))
913 (elmo-object-load flist-file elmo-mime-charset t)))
915 (defun elmo-msgdb-flist-save (fname flist)
916 (let ((flist-file (expand-file-name
917 elmo-msgdb-flist-filename
919 (elmo-safe-filename fname)
920 (expand-file-name "folder" elmo-msgdb-directory)))))
921 (elmo-object-save flist-file flist elmo-mime-charset)))
923 (defun elmo-crosspost-alist-load ()
924 (elmo-object-load (expand-file-name
925 elmo-crosspost-alist-filename
926 elmo-msgdb-directory)
929 (defun elmo-crosspost-alist-save (alist)
930 (elmo-object-save (expand-file-name
931 elmo-crosspost-alist-filename
932 elmo-msgdb-directory)
935 (defun elmo-msgdb-add-msgs-to-seen-list (msgs msgdb seen-list)
939 (if (setq mark (elmo-msgdb-get-mark msgdb (car msgs)))
940 (unless (member mark (elmo-msgdb-unread-marks)) ;; not unread mark
943 (elmo-msgdb-get-field msgdb (car msgs) 'message-id)
945 ;; no mark ... seen...
948 (elmo-msgdb-get-field msgdb (car msgs) 'message-id)
950 (setq msgs (cdr msgs)))
953 (defun elmo-msgdb-get-message-id-from-buffer ()
954 (or (elmo-field-body "message-id")
955 ;; no message-id, so put dummy msgid.
956 (concat "<" (timezone-make-date-sortable
957 (elmo-field-body "date"))
958 (nth 1 (eword-extract-address-components
959 (or (elmo-field-body "from") "nobody"))) ">")))
961 (defsubst elmo-msgdb-create-overview-from-buffer (number &optional size time)
962 "Create overview entity from current buffer.
963 Header region is supposed to be narrowed."
965 (let ((extras elmo-msgdb-extra-fields)
966 (default-mime-charset default-mime-charset)
967 message-id references from subject to cc date
968 extra field-body charset)
969 (elmo-set-buffer-multibyte default-enable-multibyte-characters)
970 (setq message-id (elmo-msgdb-get-message-id-from-buffer))
971 (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
972 (setq charset (intern-soft charset))
973 (setq default-mime-charset charset))
975 (or (elmo-msgdb-get-last-message-id
976 (elmo-field-body "in-reply-to"))
977 (elmo-msgdb-get-last-message-id
978 (elmo-field-body "references"))))
979 (setq from (elmo-replace-in-string
980 (elmo-mime-string (or (elmo-field-body "from")
983 subject (elmo-replace-in-string
984 (elmo-mime-string (or (elmo-field-body "subject")
987 (setq date (or (elmo-field-body "date") time))
988 (setq to (mapconcat 'identity (elmo-multiple-field-body "to") ","))
989 (setq cc (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
991 (if (setq size (elmo-field-body "content-length"))
992 (setq size (string-to-int size))
993 (setq size 0)));; No mean...
995 (if (setq field-body (elmo-field-body (car extras)))
996 (setq extra (cons (cons (downcase (car extras))
998 (setq extras (cdr extras)))
999 (cons message-id (vector number references
1000 from subject date to cc
1004 (defun elmo-msgdb-copy-overview-entity (entity)
1006 (copy-sequence (cdr entity))))
1008 (defsubst elmo-msgdb-insert-file-header (file)
1009 "Insert the header of the article."
1011 insert-file-contents-pre-hook ; To avoid autoconv-xmas...
1012 insert-file-contents-post-hook
1014 (when (file-exists-p file)
1015 ;; Read until header separator is found.
1016 (while (and (eq elmo-msgdb-file-header-chop-length
1018 (insert-file-contents-as-binary
1020 (incf beg elmo-msgdb-file-header-chop-length))))
1021 (prog1 (not (search-forward "\n\n" nil t))
1022 (goto-char (point-max))))))))
1024 (defsubst elmo-msgdb-create-overview-entity-from-file (number file)
1025 (let (insert-file-contents-pre-hook ; To avoid autoconv-xmas...
1026 insert-file-contents-post-hook header-end
1027 (attrib (file-attributes file))
1030 (if (not (file-exists-p file))
1032 (setq size (nth 7 attrib))
1033 (setq mtime (timezone-make-date-arpa-standard
1034 (current-time-string (nth 5 attrib)) (current-time-zone)))
1035 ;; insert header from file.
1038 (elmo-msgdb-insert-file-header file)
1039 (error (throw 'done nil)))
1040 (goto-char (point-min))
1042 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
1045 (narrow-to-region (point-min) header-end)
1046 (elmo-msgdb-create-overview-from-buffer number size mtime))))))
1048 (defun elmo-msgdb-overview-sort-by-date (overview)
1054 (timezone-make-date-sortable
1055 (elmo-msgdb-overview-entity-get-date x))
1056 (timezone-make-date-sortable
1057 (elmo-msgdb-overview-entity-get-date y)))
1060 (defun elmo-msgdb-clear-index (msgdb entity)
1061 (let ((ehash (elmo-msgdb-get-entity-hashtb msgdb))
1062 (mhash (elmo-msgdb-get-mark-hashtb msgdb))
1064 (when (and entity ehash)
1065 (and (setq number (elmo-msgdb-overview-entity-get-number entity))
1066 (elmo-clear-hash-val (format "#%d" number) ehash))
1067 (and (car entity) ;; message-id
1068 (elmo-clear-hash-val (car entity) ehash)))
1069 (when (and entity mhash)
1070 (and (setq number (elmo-msgdb-overview-entity-get-number entity))
1071 (elmo-clear-hash-val (format "#%d" number) mhash)))))
1073 (defun elmo-msgdb-make-index (msgdb &optional overview mark-alist)
1074 "Append OVERVIEW and MARK-ALIST to the index of MSGDB.
1075 If OVERVIEW and MARK-ALIST are nil, make index for current MSGDB.
1076 Return the updated INDEX."
1078 (let* ((overview (or overview (elmo-msgdb-get-overview msgdb)))
1079 (mark-alist (or mark-alist (elmo-msgdb-get-mark-alist msgdb)))
1080 (index (elmo-msgdb-get-index msgdb))
1081 (ehash (or (car index) ;; append
1082 (elmo-make-hash (length overview))))
1083 (mhash (or (cdr index) ;; append
1084 (elmo-make-hash (length overview)))))
1086 ;; key is message-id
1088 (elmo-set-hash-val (caar overview) (car overview) ehash))
1092 (elmo-msgdb-overview-entity-get-number (car overview)))
1093 (car overview) ehash)
1094 (setq overview (cdr overview)))
1098 (format "#%d" (car (car mark-alist)))
1099 (car mark-alist) mhash)
1100 (setq mark-alist (cdr mark-alist)))
1101 (setq index (or index (cons ehash mhash)))
1102 (elmo-msgdb-set-index msgdb index)
1105 (defsubst elmo-folder-get-info (folder &optional hashtb)
1106 (elmo-get-hash-val folder
1107 (or hashtb elmo-folder-info-hashtb)))
1109 (defun elmo-folder-get-info-max (folder)
1110 "Get folder info from cache."
1111 (nth 3 (elmo-folder-get-info folder)))
1113 (defun elmo-folder-get-info-length (folder)
1114 (nth 2 (elmo-folder-get-info folder)))
1116 (defun elmo-folder-get-info-unread (folder)
1117 (nth 1 (elmo-folder-get-info folder)))
1119 (defsubst elmo-msgdb-location-load (dir)
1122 elmo-msgdb-location-filename
1125 (defsubst elmo-msgdb-location-add (alist number location)
1126 (let ((ret-val alist))
1128 (elmo-msgdb-append-element ret-val (cons number location)))
1131 (defsubst elmo-msgdb-location-save (dir alist)
1134 elmo-msgdb-location-filename
1137 (put 'elmo-msgdb-do-each-entity 'lisp-indent-function '1)
1138 (def-edebug-spec elmo-msgdb-do-each-entity
1139 ((symbolp form &rest form) &rest form))
1140 (defmacro elmo-msgdb-do-each-entity (spec &rest form)
1141 `(dolist (,(car spec) (elmo-msgdb-get-overview ,(car (cdr spec))))
1145 (product-provide (provide 'elmo-msgdb) (require 'elmo-version))
1147 ;;; elmo-msgdb.el ends here