+(require 'mime)
+
+(defcustom elmo-msgdb-new-mark "N"
+ "Mark for new message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+(defcustom elmo-msgdb-unread-uncached-mark "U"
+ "Mark for unread and uncached message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+(defcustom elmo-msgdb-unread-cached-mark "!"
+ "Mark for unread but already cached message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+(defcustom elmo-msgdb-read-uncached-mark "u"
+ "Mark for read but uncached message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+;; Not implemented yet.
+(defcustom elmo-msgdb-answered-cached-mark "&"
+ "Mark for answered and cached message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+(defcustom elmo-msgdb-answered-uncached-mark "A"
+ "Mark for answered but cached message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+(defcustom elmo-msgdb-important-mark"$"
+ "Mark for important message."
+ :type '(string :tag "Mark")
+ :group 'elmo)
+
+;;; MSGDB interface.
+(defun elmo-load-msgdb (path)
+ "Load the MSGDB from PATH."
+ (let ((inhibit-quit t))
+ (elmo-make-msgdb (elmo-msgdb-overview-load path)
+ (elmo-msgdb-number-load path)
+ (elmo-msgdb-mark-load path))))
+
+(defun elmo-make-msgdb (&optional overview number-alist mark-alist)
+ "Make a MSGDB."
+ (let ((msgdb (list overview number-alist mark-alist nil)))
+ (elmo-msgdb-make-index msgdb)
+ msgdb))
+
+(defsubst elmo-msgdb-get-mark (msgdb number)
+ "Get mark string from MSGDB which corresponds to the message with NUMBER."
+ (cadr (elmo-get-hash-val (format "#%d" number)
+ (elmo-msgdb-get-mark-hashtb msgdb))))
+
+(defsubst elmo-msgdb-set-mark (msgdb number mark)
+ "Set MARK of the message with NUMBER in the MSGDB.
+if MARK is nil, mark is removed."
+ (let ((elem (elmo-get-hash-val (format "#%d" number)
+ (elmo-msgdb-get-mark-hashtb msgdb))))
+ (if elem
+ (if mark
+ ;; Set mark of the elem
+ (setcar (cdr elem) mark)
+ ;; Delete elem from mark-alist
+ (elmo-msgdb-set-mark-alist
+ msgdb
+ (delq elem (elmo-msgdb-get-mark-alist msgdb)))
+ (elmo-clear-hash-val (format "#%d" number)
+ (elmo-msgdb-get-mark-hashtb msgdb)))
+ (when mark
+ ;; Append new element.
+ (elmo-msgdb-set-mark-alist
+ msgdb
+ (nconc
+ (elmo-msgdb-get-mark-alist msgdb)
+ (list (setq elem (list number mark)))))
+ (elmo-set-hash-val (format "#%d" number) elem
+ (elmo-msgdb-get-mark-hashtb msgdb))))
+ ;; return value.
+ t))
+
+(defun elmo-msgdb-get-cached (msgdb number)
+ "Return non-nil if message is cached."
+ (not (member (elmo-msgdb-get-mark msgdb number)
+ (elmo-msgdb-uncached-marks))))
+
+(defun elmo-msgdb-set-cached (msgdb number cached use-cache)
+ "Set message cache status.
+If mark is changed, return non-nil."
+ (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
+ (cur-flag (cond
+ ((string= cur-mark elmo-msgdb-important-mark)
+ 'important)
+ ((member cur-mark (elmo-msgdb-answered-marks))
+ 'answered)
+ ((not (member cur-mark (elmo-msgdb-unread-marks)))
+ 'read)))
+ (cur-cached (elmo-file-cache-exists-p
+ (elmo-msgdb-get-field msgdb number 'message-id))))
+ (unless (eq cached cur-cached)
+ (case cur-flag
+ (read
+ (elmo-msgdb-set-mark msgdb number
+ (if (and use-cache (not cached))
+ elmo-msgdb-read-uncached-mark)))
+ (important nil)
+ (answered
+ (elmo-msgdb-set-mark msgdb number
+ (if cached
+ elmo-msgdb-answered-cached-mark
+ elmo-msgdb-answered-uncached-mark)))
+ (t
+ (elmo-msgdb-set-mark msgdb number
+ (if cached
+ elmo-msgdb-unread-cached-mark
+ elmo-msgdb-unread-uncached-mark)))))))
+
+(defun elmo-msgdb-set-flag (msgdb folder number flag)
+ "Set message flag.
+MSGDB is the ELMO msgdb.
+FOLDER is a ELMO folder structure.
+NUMBER is a message number to set flag.
+FLAG is a symbol which is one of the following:
+`read' ... Messages which are already read.
+`important' ... Messages which are marked as important.
+`answered' ... Messages which are marked as answered."
+ (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
+ (use-cache (elmo-message-use-cache-p folder number))
+ (cur-flag (cond
+ ((string= cur-mark elmo-msgdb-important-mark)
+ 'important)
+ ((member cur-mark (elmo-msgdb-answered-marks))
+ 'answered)
+ ((not (member cur-mark (elmo-msgdb-unread-marks)))
+ 'read)))
+ (cur-cached (elmo-file-cache-exists-p
+ (elmo-msgdb-get-field msgdb number 'message-id)))
+ mark-modified)
+ (case flag
+ (read
+ (case cur-flag
+ ((read important answered))
+ (t (elmo-msgdb-set-mark msgdb number
+ (if (and use-cache (not cur-cached))
+ elmo-msgdb-read-uncached-mark))
+ (setq mark-modified t))))
+ (important
+ (unless (eq cur-flag 'important)
+ (elmo-msgdb-set-mark msgdb number elmo-msgdb-important-mark)
+ (setq mark-modified t)))
+ (answered
+ (unless (or (eq cur-flag 'answered) (eq cur-flag 'important))
+ (elmo-msgdb-set-mark msgdb number
+ (if cur-cached
+ elmo-msgdb-answered-cached-mark
+ elmo-msgdb-answered-uncached-mark)))
+ (setq mark-modified t)))
+ (if mark-modified (elmo-folder-set-mark-modified-internal folder t))))
+
+(defun elmo-msgdb-unset-flag (msgdb folder number flag)
+ "Unset message flag.
+MSGDB is the ELMO msgdb.
+FOLDER is a ELMO folder structure.
+NUMBER is a message number to be set flag.
+FLAG is a symbol which is one of the following:
+`read' ... Messages which are already read.
+`important' ... Messages which are marked as important.
+`answered' ... Messages which are marked as answered."
+ (let* ((cur-mark (elmo-msgdb-get-mark msgdb number))
+ (use-cache (elmo-message-use-cache-p folder number))
+ (cur-flag (cond
+ ((string= cur-mark elmo-msgdb-important-mark)
+ 'important)
+ ((member cur-mark (elmo-msgdb-answered-marks))
+ 'answered)
+ ((not (member cur-mark (elmo-msgdb-unread-marks)))
+ 'read)))
+ (cur-cached (elmo-file-cache-exists-p
+ (elmo-msgdb-get-field msgdb number 'message-id)))
+ mark-modified)
+ (case flag
+ (read
+ (when (eq cur-flag 'read)
+ (elmo-msgdb-set-mark msgdb number
+ (if cur-cached
+ elmo-msgdb-unread-cached-mark
+ elmo-msgdb-unread-uncached-mark))
+ (setq mark-modified t)))
+ (important
+ (when (eq cur-flag 'important)
+ (elmo-msgdb-set-mark msgdb number nil)
+ (setq mark-modified t)))
+ (answered
+ (when (eq cur-flag 'answered)
+ (elmo-msgdb-set-mark msgdb number
+ (if (and use-cache (not cur-cached))
+ elmo-msgdb-read-uncached-mark))
+ (setq mark-modified t))))
+ (if mark-modified (elmo-folder-set-mark-modified-internal folder t))))
+
+(defvar elmo-msgdb-unread-marks-internal nil)
+(defsubst elmo-msgdb-unread-marks ()
+ "Return an unread mark list"
+ (or elmo-msgdb-unread-marks-internal
+ (setq elmo-msgdb-unread-marks-internal
+ (list elmo-msgdb-new-mark
+ elmo-msgdb-unread-uncached-mark
+ elmo-msgdb-unread-cached-mark))))
+
+(defvar elmo-msgdb-answered-marks-internal nil)
+(defsubst elmo-msgdb-answered-marks ()
+ "Return an answered mark list"
+ (or elmo-msgdb-answered-marks-internal
+ (setq elmo-msgdb-answered-marks-internal
+ (list elmo-msgdb-answered-cached-mark
+ elmo-msgdb-answered-uncached-mark))))
+
+(defvar elmo-msgdb-uncached-marks-internal nil)
+(defsubst elmo-msgdb-uncached-marks ()
+ (or elmo-msgdb-uncached-marks-internal
+ (setq elmo-msgdb-uncached-marks-internal
+ (list elmo-msgdb-new-mark
+ elmo-msgdb-answered-uncached-mark
+ elmo-msgdb-unread-uncached-mark
+ elmo-msgdb-read-uncached-mark))))
+
+(defsubst elmo-msgdb-count-marks (msgdb)
+ (let ((new 0)
+ (unreads 0)
+ (answered 0))
+ (dolist (elem (elmo-msgdb-get-mark-alist msgdb))
+ (cond
+ ((string= (cadr elem) elmo-msgdb-new-mark)
+ (incf new))
+ ((member (cadr elem) (elmo-msgdb-unread-marks))
+ (incf unreads))
+ ((member (cadr elem) (elmo-msgdb-answered-marks))
+ (incf answered))))
+ (list new unreads answered)))
+
+(defsubst elmo-msgdb-get-number (msgdb message-id)
+ "Get number of the message which corrensponds to MESSAGE-ID from MSGDB."
+ (elmo-msgdb-overview-entity-get-number
+ (elmo-msgdb-overview-get-entity message-id msgdb)))
+
+(defsubst elmo-msgdb-get-field (msgdb number field)
+ "Get FIELD value of the message with NUMBER from MSGDB."
+ (case field
+ (message-id (elmo-msgdb-overview-entity-get-id
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))
+ (subject (elmo-msgdb-overview-entity-get-subject
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))
+ (size (elmo-msgdb-overview-entity-get-size
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))
+ (date (elmo-msgdb-overview-entity-get-date
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))
+ (to (elmo-msgdb-overview-entity-get-to
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))
+ (cc (elmo-msgdb-overview-entity-get-cc
+ (elmo-msgdb-overview-get-entity
+ number msgdb)))))
+
+(defsubst elmo-msgdb-append (msgdb msgdb-append)
+ (list
+ (nconc (car msgdb) (car msgdb-append))
+ (nconc (cadr msgdb) (cadr msgdb-append))
+ (nconc (caddr msgdb) (caddr msgdb-append))
+ (elmo-msgdb-make-index
+ msgdb
+ (elmo-msgdb-get-overview msgdb-append)
+ (elmo-msgdb-get-mark-alist msgdb-append))))
+
+(defsubst elmo-msgdb-clear (&optional msgdb)
+ (if msgdb
+ (list
+ (setcar msgdb nil)
+ (setcar (cdr msgdb) nil)
+ (setcar (cddr msgdb) nil)
+ (setcar (nthcdr 3 msgdb) nil))
+ (list nil nil nil nil)))
+
+(defun elmo-msgdb-delete-msgs (msgdb msgs)
+ "Delete MSGS from MSGDB
+content of MSGDB is changed."
+ (let* ((overview (car msgdb))
+ (number-alist (cadr msgdb))
+ (mark-alist (caddr msgdb))
+ (index (elmo-msgdb-get-index msgdb))
+ (newmsgdb (list overview number-alist mark-alist index))
+ ov-entity)
+ ;; remove from current database.
+ (while msgs
+ (setq overview
+ (delq
+ (setq ov-entity
+ (elmo-msgdb-overview-get-entity (car msgs) newmsgdb))
+ overview))
+ (setq number-alist (delq (assq (car msgs) number-alist) number-alist))
+ (setq mark-alist (delq (assq (car msgs) mark-alist) mark-alist))
+ ;;
+ (when index (elmo-msgdb-clear-index msgdb ov-entity))
+ (setq msgs (cdr msgs)))
+ (setcar msgdb overview)
+ (setcar (cdr msgdb) number-alist)
+ (setcar (cddr msgdb) mark-alist)
+ (setcar (nthcdr 3 msgdb) index)
+ t)) ;return value
+
+(defun elmo-msgdb-sort-by-date (msgdb)
+ (message "Sorting...")
+ (let ((overview (elmo-msgdb-get-overview msgdb)))
+ (setq overview (elmo-msgdb-overview-sort-by-date overview))
+ (message "Sorting...done")
+ (list overview (nth 1 msgdb)(nth 2 msgdb))))