* test-elmo-util.el (test-elmo-number-set-delete-1): New testcase.
[elisp/wanderlust.git] / elmo / elmo.el
index 09dc513..b111675 100644 (file)
@@ -46,7 +46,8 @@
 
 (defcustom elmo-message-fetch-threshold 30000
   "Fetch threshold."
-  :type 'integer
+  :type '(choice (integer :tag "Threshold (bytes)")
+                (const :tag "No limitation" nil))
   :group 'elmo)
 
 (defcustom elmo-message-fetch-confirm t
@@ -79,6 +80,19 @@ Otherwise, entire fetching of the message is aborted without confirmation."
 (elmo-define-error 'elmo-authenticate-error "Login failed" 'elmo-open-error)
 (elmo-define-error 'elmo-imap4-bye-error "IMAP4 session was terminated" 'elmo-open-error)
 
+;; autoloads
+(eval-and-compile
+  (autoload 'elmo-dop-queue-flush "elmo-dop")
+  (autoload 'elmo-nntp-post "elmo-nntp")
+  (autoload 'elmo-global-flag-p "elmo-flag")
+  (autoload 'elmo-local-flag-p "elmo-flag")
+  (autoload 'elmo-global-flag-detach "elmo-flag")
+  (autoload 'elmo-global-flag-detach-messages "elmo-flag")
+  (autoload 'elmo-global-flag-set "elmo-flag")
+  (autoload 'elmo-get-global-flags "elmo-flag")
+  (autoload 'elmo-global-mark-migrate "elmo-flag")
+  (autoload 'elmo-folder-list-global-flag-messages "elmo-flag"))
+
 (defun elmo-define-folder (prefix backend)
   "Define a folder.
 If a folder name begins with PREFIX, use BACKEND."
@@ -92,8 +106,8 @@ If a folder name begins with PREFIX, use BACKEND."
 
 (defmacro elmo-folder-type (name)
   "Get folder type from NAME string."
-  (` (and (stringp (, name))
-         (cdr (assoc (string-to-char (, name)) elmo-folder-type-alist)))))
+  `(and (stringp ,name)
+       (cdr (assoc (string-to-char ,name) elmo-folder-type-alist))))
 
 ;;; ELMO folder
 ;; A elmo folder provides uniformed (orchestrated) access
@@ -105,6 +119,7 @@ If a folder name begins with PREFIX, use BACKEND."
                                     path   ; directory path for msgdb.
                                     msgdb  ; msgdb (may be nil).
                                     killed-list  ; killed list.
+                                    flag-table   ; flag table.
                                     persistent   ; non-nil if persistent.
                                     process-duplicates  ; read or hide
                                     biff   ; folder for biff
@@ -128,8 +143,8 @@ If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved."
     (setq original (elmo-string name))
     (if type
        (progn
-         (setq prefix (substring name 0 1))
-         (setq name (substring name 1)))
+         (setq prefix (elmo-string (substring name 0 1)))
+         (setq name (elmo-string (substring name 1))))
       (setq type (intern (car (setq split (split-string name ":")))))
       (if (>= (length split) 2)
          (setq name (substring name (+ 1 (length (car split)))))
@@ -220,15 +235,17 @@ If second optional IN-MSGDB is non-nil, only messages in the msgdb are listed.")
       (setq list (elmo-msgdb-list-messages (elmo-folder-msgdb folder))))
     (if visible-only
        (elmo-living-messages list killed-list)
-      (elmo-uniq-list
-       (nconc (elmo-number-set-to-number-list killed-list) list)))))
+      (if (and in-msgdb killed-list)
+         (elmo-uniq-sorted-list
+          (sort (nconc (elmo-number-set-to-number-list killed-list) list) #'<)
+          #'eq)
+       list))))
 
-(luna-define-generic elmo-folder-list-unreads (folder)
-  "Return a list of unread message numbers contained in FOLDER.")
-(luna-define-generic elmo-folder-list-importants (folder)
-  "Return a list of important message numbers contained in FOLDER.")
-(luna-define-generic elmo-folder-list-answereds (folder)
-  "Return a list of answered message numbers contained in FOLDER.")
+(luna-define-generic elmo-folder-list-messages-internal (folder &optional
+                                                               visible-only)
+  ;; Return a list of message numbers contained in FOLDER.
+  ;; Return t if the message list is not available.
+  )
 
 (luna-define-generic elmo-folder-list-flagged (folder flag &optional in-msgdb)
   "List messages in the FOLDER with FLAG.
@@ -239,30 +256,30 @@ FLAG is a symbol which is one of the following:
   `important'  (marked as important)
 'sugar' flags:
   `read'       (not unread)
-  `digest'     (unread + important)
-  `any'        (digest + answered)
-
+  `digest'     (unread + important + other flags)
+  `any'        (digest + answered + other flags)
 If optional IN-MSGDB is non-nil, retrieve flag information from msgdb.")
 
 (luna-define-method elmo-folder-list-flagged ((folder elmo-folder) flag
                                              &optional in-msgdb)
-  ;; Currently, only in-msgdb is implemented.
-  (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag))
-
-(luna-define-method elmo-folder-list-unreads ((folder elmo-folder))
-  (elmo-folder-list-flagged folder 'unread))
-
-(luna-define-method elmo-folder-list-importants ((folder elmo-folder))
-  (elmo-folder-list-flagged folder 'important))
+  (let ((msgs (if in-msgdb
+                 t
+               (elmo-folder-list-flagged-internal folder flag))))
+    (unless (listp msgs)
+      (setq msgs (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag)))
+    (if in-msgdb
+       msgs
+      (elmo-uniq-list
+       (nconc (elmo-folder-list-global-flag-messages folder flag) msgs)
+       #'delq))))
 
-(luna-define-method elmo-folder-list-answereds ((folder elmo-folder))
-  (elmo-folder-list-flagged folder 'answered))
+(luna-define-generic elmo-folder-list-flagged-internal (folder flag)
+  "Return a list of message in the FOLDER with FLAG.
+Return t if the message list is not available.")
 
-(luna-define-generic elmo-folder-list-messages-internal (folder &optional
-                                                               visible-only)
-  ;; Return a list of message numbers contained in FOLDER.
-  ;; Return t if the message list is not available.
-  )
+(luna-define-method elmo-folder-list-flagged-internal ((folder elmo-folder)
+                                                      flag)
+  t)
 
 (luna-define-generic elmo-folder-list-subfolders (folder &optional one-level)
   "Returns a list of subfolders contained in FOLDER.
@@ -298,12 +315,18 @@ Otherwise, all descendent folders are returned.")
   "Rename FOLDER to NEW-NAME (string).")
 
 (luna-define-generic elmo-folder-delete-messages (folder numbers)
-  "Delete messages.
+  "Delete messages with msgdb entity.
 FOLDER is the ELMO folder structure.
 NUMBERS is a list of message numbers to be deleted.
 It is not recommended to use this function other than internal use.
 Use `elmo-folder-move-messages' with dst-folder 'null instead.")
 
+(luna-define-generic elmo-folder-delete-messages-internal (folder numbers)
+  "Delete messages, but no delete msgdb entity.
+FOLDER is the ELMO folder structure.
+NUMBERS is a list of message numbers to be deleted.
+Override this method by each implement of `elmo-folder'.")
+
 (luna-define-generic elmo-folder-search (folder condition &optional numbers)
   "Search and return list of message numbers.
 FOLDER is the ELMO folder structure.
@@ -325,55 +348,52 @@ FOLDER is the ELMO folder structure.
 NUMBERS is a list of message numbers to create msgdb.
 FLAG-TABLE is a hashtable of message-id and flag.")
 
-(luna-define-generic elmo-folder-unflag-important (folder
-                                                  numbers
-                                                  &optional is-local)
-  "Un-flag messages as important.
-FOLDER is the ELMO folder structure.
-NUMBERS is a list of message numbers to be processed.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+(luna-define-generic elmo-folder-set-flag (folder numbers flag
+                                                 &optional is-local)
+  "Set messages flag.
+FOLDER is a ELMO folder structure.
+NUMBERS is a list of message number to set flag.
 
-(luna-define-generic elmo-folder-flag-as-important (folder
-                                                   numbers
-                                                   &optional is-local)
-  "Flag messages as important.
-FOLDER is the ELMO folder structure.
-NUMBERS is a list of message numbers to be processed.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+FLAG is a symbol which is one of the following:
+  `unread'    (set the message as unread)
+  `answered'  (set the message as answered)
+  `important' (set the message as important)
+'sugar' flag:
+  `read'      (remove new and unread flags)
+If optional IS-LOCAL is non-nil, update only local (not server) status.")
 
-(luna-define-generic elmo-folder-unflag-read (folder numbers
-                                                    &optional is-local)
-  "Un-flag messages as read.
-FOLDER is the ELMO folder structure.
-NUMBERS is a list of message numbers to be processed.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+(luna-define-generic elmo-folder-unset-flag (folder numbers flag
+                                                   &optional is-local)
+  "Unset messages flag.
+FOLDER is a ELMO folder structure.
+NUMBERS is a list of message number to unset flag.
 
-(luna-define-generic elmo-folder-flag-as-read (folder numbers
-                                                     &optional is-local)
-  "Flag messages as read.
-FOLDER is the ELMO folder structure.
-NUMBERS is a list of message numbers to be processed.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+FLAG is a symbol which is one of the following:
+  `unread'    (remove unread and new flag)
+  `answered'  (remove answered flag)
+  `important' (remove important flag)
+'sugar' flag:
+  `read'      (set unread flag)
+  `all'       (remove all flags)
+If optional IS-LOCAL is non-nil, update only local (not server) status.")
 
-(luna-define-generic elmo-folder-unflag-answered (folder numbers
-                                                        &optional is-local)
-  "Un-flag messages as answered.
-FOLDER is the ELMO folder structure.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+(luna-define-generic elmo-message-flag-available-p (folder number flag)
+  "Return non-nil when a message in the FOLDER with NUMBER treats FLAG.")
 
-(luna-define-generic elmo-folder-flag-as-answered (folder numbers
-                                                         &optional is-local)
-  "Flag messages as answered.
-FOLDER is the ELMO folder structure.
-If IS-LOCAL is non-nil, only the local flag is updated.")
+(luna-define-generic elmo-folder-next-message-number (folder)
+  "The next message number that will be assigned to a new message.
+FOLDER is the ELMO folder structure.")
 
-(luna-define-generic elmo-folder-append-buffer (folder &optional flag
+(luna-define-generic elmo-folder-append-buffer (folder &optional flags
                                                       number)
   "Append current buffer as a new message.
-FOLDER is the destination folder(ELMO folder structure).
-FLAG is the status of appended message.
+FOLDER is the destination folder (ELMO folder structure).
+FLAGS is the flag list for the appended message (list of symbols).
+If FLAGS contain `read', the message is appended as `not-unread'.
+If it is nil, the appended message will be treated as `new'.
 If optional argument NUMBER is specified, the new message number is set
-\(if possible\).")
+\(if possible\).
+Return nil on failure.")
 
 (luna-define-generic elmo-folder-append-messages (folder
                                                  src-folder
@@ -387,7 +407,8 @@ Caller should make sure FOLDER is `writable'.
 SRC-FOLDER is the source ELMO folder structure.
 NUMBERS is the message numbers to be appended in the SRC-FOLDER.
 If second optional argument SAME-NUMBER is specified,
-message number is preserved \(if possible\).")
+message number is preserved \(if possible\).
+Returns a list of message numbers successfully appended.")
 
 (luna-define-generic elmo-folder-pack-numbers (folder)
   "Pack message numbers of FOLDER.")
@@ -445,6 +466,29 @@ Return newly created temporary directory name which contains temporary files.")
 (luna-define-generic elmo-message-file-p (folder number)
   "Return t if message in the FOLDER with NUMBER is a file.")
 
+(luna-define-generic elmo-message-flags (folder number)
+  "Return a list of flags.
+FOLDER is a ELMO folder structure.
+NUMBER is a number of the message.")
+
+(luna-define-method elmo-message-flag-available-p ((folder elmo-folder) number
+                                                  flag)
+  (elmo-msgdb-flag-available-p (elmo-folder-msgdb folder) flag))
+
+(luna-define-method elmo-message-flags ((folder elmo-folder) number)
+  (elmo-msgdb-flags (elmo-folder-msgdb folder) number))
+
+(defsubst elmo-message-flagged-p (folder number flag)
+  "Return non-nil if the message is set FLAG.
+FOLDER is a ELMO folder structure.
+NUMBER is a message number to test."
+  (let ((cur-flags (elmo-message-flags folder number)))
+    (case flag
+      (read
+       (not (memq 'unread cur-flags)))
+      (t
+       (memq flag cur-flags)))))
+
 (luna-define-generic elmo-find-fetch-strategy
   (folder entity &optional ignore-cache)
 ;; Returns the message fetching strategy suitable for the message.
@@ -554,6 +598,9 @@ If READ is non-nil, message is flaged as read.")
      nil nil (not read))
     path))
 
+(luna-define-generic elmo-message-fetch-bodystructure (folder number strategy)
+  "Fetch bodystructure of the message in FOLDER with NUMBER using STRATEGY.")
+
 (luna-define-generic elmo-message-fetch (folder number strategy
                                                &optional
                                                section
@@ -655,16 +702,17 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
   (elmo-generic-folder-commit folder))
 
 (defun elmo-generic-folder-commit (folder)
-  (let ((msgdb (elmo-folder-msgdb-internal folder)))
-    (when (and msgdb (elmo-folder-persistent-p folder))
-      (when (elmo-msgdb-message-modified-p msgdb)
-       (elmo-folder-set-info-max-by-numdb
-        folder
-        (elmo-folder-list-messages folder nil 'in-msgdb))
-       (elmo-msgdb-killed-list-save
-        (elmo-folder-msgdb-path folder)
-        (elmo-folder-killed-list-internal folder)))
-      (elmo-msgdb-save msgdb))))
+  (when (elmo-folder-persistent-p folder)
+    (let ((msgdb (elmo-folder-msgdb-internal folder)))
+      (when msgdb
+       (when (elmo-msgdb-message-modified-p msgdb)
+         (elmo-folder-set-info-max-by-numdb
+          folder
+          (elmo-folder-list-messages folder nil 'in-msgdb))
+       (elmo-msgdb-save msgdb))))
+    (elmo-msgdb-killed-list-save
+     (elmo-folder-msgdb-path folder)
+     (elmo-folder-killed-list-internal folder))))
 
 (luna-define-method elmo-folder-close-internal ((folder elmo-folder))
   ;; do nothing.
@@ -715,104 +763,68 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
     (elmo-folder-send folder 'elmo-folder-rename-internal new-folder)
     (elmo-msgdb-rename-path folder new-folder)))
 
-(defsubst elmo-folder-search-fast (folder condition numbers)
-  "Search and return list of message numbers.
-Return t if CONDITION is not treated.
-FOLDER is the ELMO folder structure.
-CONDITION is a condition structure for searching.
-NUMBERS is a list of message numbers, messages are searched from the list."
-  (if (and numbers
-          (vectorp condition))
-      (cond
-       ((string= (elmo-filter-key condition) "flag")
-       (let ((msgdb (elmo-folder-msgdb folder)))
-         ;; msgdb should be synchronized at this point.
-         (cond
-          ((string= (elmo-filter-value condition) "unread")
-           (elmo-folder-list-unreads folder))
-          ((string= (elmo-filter-value condition) "important")
-           (elmo-folder-list-importants folder))
-          ((string= (elmo-filter-value condition) "answered")
-           (elmo-folder-list-answereds folder))
-          ((string= (elmo-filter-value condition) "digest")
-           (nconc (elmo-folder-list-unreads folder)
-                  (elmo-folder-list-importants folder)))
-          ((string= (elmo-filter-value condition) "any")
-           (nconc (elmo-folder-list-unreads folder)
-                  (elmo-folder-list-importants folder)
-                  (elmo-folder-list-answereds folder))))))
-       ((member (elmo-filter-key condition) '("first" "last"))
-       (let ((len (length numbers))
-             (lastp (string= (elmo-filter-key condition) "last"))
-             (value (string-to-number (elmo-filter-value condition))))
-         (when (eq (elmo-filter-type condition) 'unmatch)
-           (setq lastp (not lastp)
-                 value  (- len value)))
-         (if lastp
-             (nthcdr (max (- len value) 0) numbers)
-           (when (> value 0)
-             (let* ((numbers (copy-sequence numbers))
-                    (last (nthcdr (1- value) numbers)))
-               (when last
-                 (setcdr last nil))
-               numbers)))))
-       (t
-       t))
-    t))
+(luna-define-method elmo-folder-delete-messages ((folder elmo-folder)
+                                                numbers)
+  (and (elmo-folder-delete-messages-internal folder numbers)
+       (elmo-folder-detach-messages folder numbers)))
 
 (luna-define-method elmo-folder-search ((folder elmo-folder)
                                        condition
                                        &optional numbers)
   (let ((numbers (or numbers (elmo-folder-list-messages folder)))
+       (msgdb (elmo-folder-msgdb folder))
        results)
-    (if (listp (setq results (elmo-folder-search-fast folder
-                                                     condition
-                                                     numbers)))
+    (setq results (elmo-msgdb-search msgdb condition numbers))
+    (if (listp results)
        results
-      (let ((msgdb (elmo-folder-msgdb folder))
-           (len (length numbers))
+      (let ((len (length numbers))
            matched)
-       (when (> len elmo-display-progress-threshold)
-         (elmo-progress-set 'elmo-folder-search len "Searching..."))
-       (unwind-protect
-           (dolist (number numbers)
-             (let (result)
-               (setq result (elmo-msgdb-match-condition
-                             msgdb
-                             condition
-                             number
-                             numbers))
-               (when (elmo-filter-condition-p result)
-                 (setq result (elmo-message-match-condition
-                               folder
-                               number
-                               condition
-                               numbers)))
-               (when result
-                 (setq matched (cons number matched))))
-             (elmo-progress-notify 'elmo-folder-search))
-         (elmo-progress-clear 'elmo-folder-search))
+       (elmo-with-progress-display (> len elmo-display-progress-threshold)
+           (elmo-folder-search len "Searching...")
+         (dolist (number numbers)
+           (let (result)
+             (setq result (elmo-msgdb-match-condition msgdb
+                                                      condition
+                                                      number
+                                                      numbers))
+             (when (elmo-filter-condition-p result)
+               (setq result (elmo-message-match-condition folder
+                                                          number
+                                                          condition
+                                                          numbers)))
+             (when result
+               (setq matched (cons number matched))))
+           (elmo-progress-notify 'elmo-folder-search)))
+       (message "Searching...done")
        (nreverse matched)))))
 
 (luna-define-method elmo-message-match-condition ((folder elmo-folder)
                                                  number condition
                                                  numbers)
-  (let ((filename (cond
-                  ((elmo-message-file-name folder number))
-                  ((let* ((cache (elmo-file-cache-get
-                                  (elmo-message-field folder number
-                                                      'message-id)))
-                          (cache-path (elmo-file-cache-path cache)))
-                     (when (and cache-path
-                                (not (elmo-cache-path-section-p cache-path)))
-                       cache-path))))))
-    (when (and filename
-              (file-readable-p filename))
+  (let* (cache cache-path
+        (filename (cond
+                   ((elmo-message-file-name folder number))
+                   ((progn
+                      (setq cache (elmo-file-cache-get
+                                   (elmo-message-field folder number
+                                                       'message-id)))
+                      (setq cache-path (elmo-file-cache-path cache))
+                      (and cache-path
+                           (not (elmo-cache-path-section-p cache-path))))
+                    cache-path))))
+    (when (and filename (file-readable-p filename))
       (with-temp-buffer
-       (insert-file-contents-as-binary filename)
-       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
-       ;; Should consider charset?
-       (decode-mime-charset-region (point-min) (point-max) elmo-mime-charset)
+       (set-buffer-multibyte nil)
+       ;;(insert-file-contents-as-binary filename)
+       (elmo-message-fetch folder number
+                           (elmo-make-fetch-strategy 'entire
+                                                     (and cache t)
+                                                     nil
+                                                     cache-path)
+                           nil (current-buffer) t)
+       (set-buffer-multibyte default-enable-multibyte-characters)
+       (decode-coding-region (point-min) (point-max)
+                             elmo-mime-display-as-is-coding-system)
        (elmo-buffer-field-condition-match condition number numbers)))))
 
 (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder))
@@ -840,6 +852,39 @@ NUMBERS is a list of message numbers, messages are searched from the list."
 (luna-define-method elmo-folder-have-subfolder-p ((folder elmo-folder))
   t)
 
+;; Flag table
+(luna-define-generic elmo-folder-flag-table (folder &optional if-exists)
+  "Return the flag-table of FOLDER.
+If optional argument IF-EXISTS is nil, load on demand.
+\(For internal use only.\)")
+
+(luna-define-generic elmo-folder-close-flag-table (folder)
+  "Close flag-table of FOLDER.")
+
+(luna-define-method elmo-folder-flag-table ((folder elmo-folder)
+                                           &optional if-exists)
+  (or (elmo-folder-flag-table-internal folder)
+      (unless if-exists
+       (elmo-folder-set-flag-table-internal
+        folder
+        (elmo-flag-table-load (elmo-folder-msgdb-path folder))))))
+
+(luna-define-method elmo-folder-close-flag-table ((folder elmo-folder))
+  (elmo-flag-table-save (elmo-folder-msgdb-path folder)
+                       (elmo-folder-flag-table folder))
+  (elmo-folder-set-flag-table-internal folder nil))
+
+(defun elmo-folder-preserve-flags (folder msgid flags)
+  "Preserve FLAGS into FOLDER for a message that has MSGID."
+  (when (and msgid flags)
+    (let ((flag-table (elmo-folder-flag-table folder 'if-exists))
+         load-now)
+      (when (setq load-now (null flag-table))
+       (setq flag-table (elmo-folder-flag-table folder)))
+      (elmo-flag-table-set flag-table msgid flags)
+      (when load-now
+       (elmo-folder-close-flag-table folder)))))
+
 ;;; Folder info
 ;; Folder info is a message number information cache (hashtable)
 (defsubst elmo-folder-get-info (folder &optional hashtb)
@@ -863,7 +908,7 @@ NUMBERS is a list of message numbers, messages are searched from the list."
   "Set FOLDER info by MSGDB-NUMBER in msgdb."
   (elmo-folder-set-info-hashtb
    folder
-   (or (car (sort numbers '>)) 0)
+   (if numbers (apply #'max numbers) 0)
    nil ;;(length num-db)
    ))
 
@@ -910,8 +955,7 @@ NUMBERS is a list of message numbers, messages are searched from the list."
 
 (defsubst elmo-strict-folder-diff (folder)
   "Return folder diff information strictly from FOLDER."
-  (let ((in-db (sort (elmo-msgdb-list-messages (elmo-folder-msgdb folder))
-                    '<))
+  (let ((in-db (sort (elmo-folder-list-messages folder nil 'in-msgdb) '<))
        (in-folder  (elmo-folder-list-messages folder))
        append-list delete-list diff)
     (cons (if (equal in-folder in-db)
@@ -942,9 +986,8 @@ NUMBERS is a list of message numbers, messages are searched from the list."
          (let ((number-list (elmo-folder-list-messages folder
                                                        nil 'in-msgdb)))
            ;; No info-cache.
-           (setq in-db (sort number-list '<))
-           (setq in-db-max (or (nth (max 0 (1- (length in-db))) in-db)
-                               0))
+           (setq in-db number-list)
+           (setq in-db-max (if in-db (apply #'max in-db) 0))
            (elmo-folder-set-info-hashtb folder in-db-max nil))
        (setq in-db-max cached-in-db-max))
       (setq unsync (if (and in-db (car in-folder))
@@ -972,6 +1015,10 @@ NUMBERS is a list of message numbers, messages are searched from the list."
 (luna-define-method elmo-folder-contains-type ((folder elmo-folder) type)
   (eq (elmo-folder-type-internal folder) type))
 
+(luna-define-method elmo-folder-next-message-number ((folder elmo-folder))
+  (+ 1 (elmo-max-of-list (or (elmo-folder-list-messages folder)
+                            '(0)))))
+
 (luna-define-method elmo-folder-append-messages ((folder elmo-folder)
                                                 src-folder
                                                 numbers
@@ -983,17 +1030,16 @@ NUMBERS is a list of message numbers, messages are searched from the list."
 (defun elmo-generic-folder-append-messages (folder src-folder numbers
                                                   same-number)
   (let ((src-msgdb-exists (not (zerop (elmo-folder-length src-folder))))
-       unseen table flags
+       unseen table
        succeed-numbers failure cache id)
-    (setq table (elmo-flag-table-load (elmo-folder-msgdb-path folder)))
+    (setq table (elmo-folder-flag-table folder))
     (with-temp-buffer
       (set-buffer-multibyte nil)
       (while numbers
        (setq failure nil
              id (and src-msgdb-exists
                      (elmo-message-field src-folder (car numbers)
-                                         'message-id))
-             flags (elmo-message-flags src-folder (car numbers)))
+                                         'message-id)))
        (condition-case nil
            (setq cache (elmo-file-cache-get id)
                  failure
@@ -1017,18 +1063,22 @@ NUMBERS is a list of message numbers, messages are searched from the list."
                    (> (buffer-size) 0)
                    (elmo-folder-append-buffer
                     folder
-                    (or flags '(read))
+                    (let ((this-id (elmo-message-field src-folder (car numbers)
+                                                       'message-id)))
+                      (and this-id
+                           (string= this-id
+                                    (elmo-msgdb-get-message-id-from-buffer))
+                           (or (elmo-message-flags src-folder (car numbers))
+                               '(read))))
                     (if same-number (car numbers))))))
          (error (setq failure t)))
        ;; FETCH & APPEND finished
        (unless failure
-         (when id
-           (elmo-flag-table-set table id flags))
          (setq succeed-numbers (cons (car numbers) succeed-numbers)))
        (elmo-progress-notify 'elmo-folder-move-messages)
        (setq numbers (cdr numbers)))
       (when (elmo-folder-persistent-p folder)
-       (elmo-flag-table-save (elmo-folder-msgdb-path folder) table))
+       (elmo-folder-close-flag-table folder))
       succeed-numbers)))
 
 ;; Arguments should be reduced.
@@ -1058,8 +1108,7 @@ NUMBERS is a list of message numbers, messages are searched from the list."
          (elmo-folder-close dst-folder)))
       (if (and (not no-delete) succeeds)
          (progn
-           (if (and (elmo-folder-delete-messages src-folder succeeds)
-                    (elmo-folder-detach-messages src-folder succeeds))
+           (if (elmo-folder-delete-messages src-folder succeeds)
                (progn
                  (elmo-global-flag-detach-messages
                   src-folder succeeds (eq dst-folder 'null))
@@ -1114,6 +1163,12 @@ If CACHED is t, message is set as cached.")
   (elmo-msgdb-copy-message-entity (elmo-message-entity-handler entity)
                                  entity))
 
+(luna-define-generic elmo-message-number (folder message-id)
+  "Get message number from MSGDB which corresponds to MESSAGE-ID.")
+
+(luna-define-method elmo-message-number ((folder elmo-folder) message-id)
+  (elmo-msgdb-message-number (elmo-folder-msgdb folder) message-id))
+
 (luna-define-generic elmo-message-entity (folder key)
   "Return the message-entity structure which matches to the KEY.
 KEY is a number or a string.
@@ -1148,71 +1203,16 @@ ENTITY is the message-entity to get the parent.")
   `(dolist (,(car spec) (elmo-folder-list-message-entities ,(car (cdr spec))))
      ,@form))
 
-(defmacro elmo-message-entity-number (entity)
-  `(elmo-msgdb-message-entity-number (elmo-message-entity-handler ,entity)
-                                    ,entity))
-
-(defmacro elmo-message-entity-set-number (entity number)
-  `(elmo-msgdb-message-entity-set-number (elmo-message-entity-handler ,entity)
-                                        ,entity
-                                        ,number))
-
-(defun elmo-message-entity-field (entity field &optional decode)
-  "Get message entity field value.
-ENTITY is the message entity structure obtained by `elmo-message-entity'.
-FIELD is the symbol of the field name.
-if optional DECODE is non-nil, returned value is decoded."
-  (elmo-msgdb-message-entity-field
-   (elmo-message-entity-handler entity)
-   entity field decode))
-
-(defun elmo-message-entity-set-field (entity field value)
-  "Set message entity field value.
-ENTITY is the message entity structure.
-FIELD is the symbol of the field name.
-VALUE is the field value (raw)."
-  (elmo-msgdb-message-entity-set-field
-   (elmo-message-entity-handler entity)
-   entity field value))
-
 (luna-define-generic elmo-folder-count-flags (folder)
   "Count flagged message number in the msgdb of the FOLDER.
-Return a list of numbers (`new' `unread' `answered')")
+Return alist of flag and numbers.
+Example:
+\(\(new . 10\)
+  \(unread . 20\)
+  \(answered . 3\)\)")
 
 (luna-define-method elmo-folder-count-flags ((folder elmo-folder))
-  (let ((new 0)
-       (unreads 0)
-       (answered 0)
-       flags)
-    (dolist (number (elmo-folder-list-messages folder 'visible 'in-msgdb))
-      (setq flags (elmo-message-flags folder number))
-      (cond
-       ((memq 'new flags)
-       (incf new))
-       ((memq 'unread flags)
-       (incf unreads))
-       ((memq 'answered flags)
-       (incf answered))))
-    (list new unreads answered)))
-
-(luna-define-generic elmo-message-flags (folder number)
-  "Return a list of flags.
-FOLDER is a ELMO folder structure.
-NUMBER is a number of the message.")
-
-(luna-define-method elmo-message-flags ((folder elmo-folder) number)
-  (elmo-msgdb-flags (elmo-folder-msgdb folder) number))
-
-(defsubst elmo-message-flagged-p (folder number flag)
-  "Return non-nil if the message is set FLAG.
-FOLDER is a ELMO folder structure.
-NUMBER is a message number to test."
-  (let ((cur-flags (elmo-message-flags folder number)))
-    (case flag
-      (read
-       (not (memq 'unread cur-flags)))
-      (t
-       (memq flag cur-flags)))))
+  (elmo-msgdb-flag-count (elmo-folder-msgdb folder)))
 
 (defun elmo-message-set-flag (folder number flag &optional is-local)
   "Set message flag.
@@ -1227,15 +1227,7 @@ FLAG is a symbol which is one of the following:
   `read'      (remove new and unread flags)
 If optional IS-LOCAL is non-nil, update only local (not server) status."
   ;; XXX Transitional implementation.
-  (case flag
-    (unread
-     (elmo-folder-unflag-read folder (list number) is-local))
-    (read
-     (elmo-folder-flag-as-read folder (list number) is-local))
-    (answered
-     (elmo-folder-flag-as-answered folder (list number) is-local))
-    (important
-     (elmo-folder-flag-as-important folder (list number) is-local))))
+  (elmo-folder-set-flag folder (list number) flag is-local))
 
 (defun elmo-message-unset-flag (folder number flag &optional is-local)
   "Unset message flag.
@@ -1250,15 +1242,7 @@ FLAG is a symbol which is one of the following:
   `read'      (set unread flag)
 If optional IS-LOCAL is non-nil, update only local (not server) status."
   ;; XXX Transitional implementation.
-  (case flag
-    (unread
-     (elmo-folder-flag-as-read folder (list number) is-local))
-    (read
-     (elmo-folder-unflag-read folder (list number) is-local))
-    (answered
-     (elmo-folder-unflag-answered folder (list number) is-local))
-    (important
-     (elmo-folder-unflag-important folder (list number) is-local))))
+  (elmo-folder-unset-flag folder (list number) flag is-local))
 
 (luna-define-generic elmo-message-field (folder number field)
   "Get message field value in the msgdb.
@@ -1267,9 +1251,19 @@ NUMBER is a number of the message.
 FIELD is a symbol of the field.")
 
 (luna-define-method elmo-message-field ((folder elmo-folder) number field)
-  (when (zerop (elmo-folder-length folder))
-    (error "Cannot treat this folder correctly."))
-  (elmo-message-entity-field (elmo-message-entity folder number) field))
+  (elmo-msgdb-message-field (elmo-folder-msgdb folder) number field))
+
+(luna-define-generic elmo-message-set-field (folder number field value)
+  "Set message field value in the msgdb.
+FOLDER is the ELMO folder structure.
+NUMBER is a number of the message.
+FIELD is a symbol of the field.
+VALUE is a value to set.")
+
+(luna-define-method elmo-message-set-field ((folder elmo-folder) number
+                                           field value)
+  (elmo-message-entity-set-field (elmo-message-entity folder number)
+                                field value))
 
 (luna-define-method elmo-message-use-cache-p ((folder elmo-folder) number)
   nil) ; default is not use cache.
@@ -1277,70 +1271,56 @@ FIELD is a symbol of the field.")
 (luna-define-method elmo-message-folder ((folder elmo-folder) number)
   folder) ; default is folder
 
-(luna-define-method elmo-folder-unflag-important ((folder elmo-folder)
-                                                 numbers
-                                                 &optional is-local)
-  (when (elmo-folder-msgdb-internal folder)
-    (dolist (number numbers)
-      (when (elmo-global-flag-p 'important)
-       (elmo-global-flag-detach 'important folder number 'remove-if-none))
-      (elmo-msgdb-unset-flag (elmo-folder-msgdb folder)
-                            number
-                            'important))))
-
-(luna-define-method elmo-folder-flag-as-important ((folder elmo-folder)
-                                                  numbers
-                                                  &optional is-local)
-  (let (path message-id)
-    (when (elmo-folder-msgdb-internal folder)
-      (dolist (number numbers)
-       ;; important message should always be a read message.
-       (if (eq (elmo-file-cache-exists-p
-                (setq message-id
-                      (elmo-message-field folder number 'message-id)))
-               'entire)
-           (elmo-folder-flag-as-read folder (list number)))
-       (when (elmo-global-flag-p 'important)
-         (elmo-global-flag-set 'important folder number message-id))
-       (elmo-msgdb-set-flag (elmo-folder-msgdb folder)
-                            number
-                            'important)))))
-
-(luna-define-method elmo-folder-unflag-read ((folder elmo-folder)
-                                            numbers
-                                            &optional is-local)
-  (when (elmo-folder-msgdb-internal folder)
-    (dolist (number numbers)
-      (elmo-msgdb-unset-flag (elmo-folder-msgdb folder)
-                            number
-                            'read))))
-
-(luna-define-method elmo-folder-flag-as-read ((folder elmo-folder)
-                                             numbers
-                                             &optional is-local)
+(luna-define-method elmo-folder-set-flag ((folder elmo-folder)
+                                         numbers
+                                         flag
+                                         &optional is-local)
   (when (elmo-folder-msgdb-internal folder)
     (dolist (number numbers)
+      (when (elmo-global-flag-p flag)
+       (let ((message-id (elmo-message-field folder number 'message-id)))
+         (elmo-global-flag-set flag folder number message-id)))
       (elmo-msgdb-set-flag (elmo-folder-msgdb folder)
                           number
-                          'read))))
-
-(luna-define-method elmo-folder-unflag-answered ((folder elmo-folder)
-                                                numbers
-                                                &optional is-local)
+                          flag))))
+
+(defun elmo-message-has-global-flag-p (folder number)
+  "Return non-nil when the message in the FOLDER with NUMBER has global flag."
+  (let ((flags (elmo-message-flags folder number))
+       result)
+    (while flags
+      (when (and (elmo-global-flag-p (car flags))
+                (not (memq (car flags) '(answered unread cached))))
+       (setq result t
+             flags nil))
+      (setq flags (cdr flags)))
+    result))
+
+(defun elmo-message-set-global-flags (folder number flags &optional local)
+  "Set global flags of the message in the FOLDER with NUMBER as FLAGS.
+If Optional LOCAL is non-nil, don't update server flag."
+  (dolist (flag flags)
+    (unless (elmo-global-flag-p flag)
+      (error "Not a global flag")))
+  (let ((old-flags (elmo-get-global-flags (elmo-message-flags folder number))))
+    (dolist (flag flags)
+      (unless (memq flag old-flags)
+       (elmo-message-set-flag folder number flag local)))
+    (dolist (flag old-flags)
+      (unless (memq flag flags)
+       (elmo-message-unset-flag folder number flag local)))))
+
+(luna-define-method elmo-folder-unset-flag ((folder elmo-folder)
+                                           numbers
+                                           flag
+                                           &optional is-local)
   (when (elmo-folder-msgdb-internal folder)
     (dolist (number numbers)
+      (when (elmo-global-flag-p flag)
+       (elmo-global-flag-detach flag folder number 'always))
       (elmo-msgdb-unset-flag (elmo-folder-msgdb folder)
                             number
-                            'answered))))
-
-(luna-define-method elmo-folder-flag-as-answered ((folder elmo-folder)
-                                                 numbers
-                                                 &optional is-local)
-  (when (elmo-folder-msgdb-internal folder)
-    (dolist (number numbers)
-      (elmo-msgdb-set-flag (elmo-folder-msgdb folder)
-                          number
-                          'answered))))
+                            flag))))
 
 (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder))
   ;; Do nothing.
@@ -1368,11 +1348,11 @@ FIELD is a symbol of the field.")
               ;; Let duplicates be a temporary killed message.
               (elmo-folder-kill-messages folder duplicates)
               ;; Should be flag as read.
-              (elmo-folder-flag-as-read folder duplicates))
+              (elmo-folder-unset-flag folder duplicates 'unread))
              ((eq (elmo-folder-process-duplicates-internal folder)
                   'read)
               ;; Flag as read duplicates.
-              (elmo-folder-flag-as-read folder duplicates))
+              (elmo-folder-unset-flag folder duplicates 'unread))
              (t
               ;; Do nothing.
               (setq duplicates nil)))
@@ -1406,6 +1386,10 @@ FIELD is a symbol of the field.")
          (nthcdr (max (- len elmo-folder-update-threshold) 0) appends)
        appends))))
 
+(luna-define-method elmo-message-fetch-bodystructure ((folder elmo-folder)
+                                                     number strategy)
+  nil)
+
 (luna-define-method elmo-message-fetch ((folder elmo-folder)
                                        number strategy
                                        &optional
@@ -1449,7 +1433,6 @@ FIELD is a symbol of the field.")
                                                           unread)
                            (error (setq err error) nil))
                          (> (buffer-size) 0))
-                (elmo-delete-cr-buffer)
                 (when (and (elmo-fetch-strategy-save-cache strategy)
                            cache-path)
                   (elmo-file-cache-save cache-path section))
@@ -1458,18 +1441,21 @@ FIELD is a symbol of the field.")
     (or result
        (and err (signal (car err) (cdr err))))))
 
-(defun elmo-folder-kill-messages-before (folder msg)
+(defun elmo-folder-kill-messages-range (folder beg end)
   (elmo-folder-set-killed-list-internal
    folder
-   (list (cons 1 msg))))
+   (nconc
+    (elmo-folder-killed-list-internal folder)
+    (list (cons beg end)))))
 
 (defun elmo-folder-kill-messages (folder numbers)
   "Kill(hide) messages in the FOLDER with NUMBERS."
   (elmo-folder-set-killed-list-internal
    folder
-   (elmo-number-set-append-list (elmo-folder-killed-list-internal
-                                folder) numbers)))
-
+   (elmo-number-set-append-list
+    (elmo-folder-killed-list-internal folder)
+    numbers))
+  (elmo-folder-unset-flag folder numbers 'all 'local-only))
 
 (luna-define-method elmo-folder-clear ((folder elmo-folder)
                                       &optional keep-killed)
@@ -1485,7 +1471,8 @@ FIELD is a symbol of the field.")
                                              &optional
                                              disable-killed
                                              ignore-msgdb
-                                             no-check)
+                                             no-check
+                                             mask)
   "Synchronize the folder data to the newest status.
 FOLDER is the ELMO folder structure.
 
@@ -1493,53 +1480,56 @@ If optional DISABLE-KILLED is non-nil, killed messages are also synchronized.
 If optional IGNORE-MSGDB is non-nil, current msgdb is thrown away except
 flag status.
 If NO-CHECK is non-nil, rechecking folder is skipped.
-Return a list of a cross-posted message number.
+If optional argument MASK is specified and is a list of message numbers,
+synchronize messages only which are contained the list.
+Return amount of cross-posted messages.
 If update process is interrupted, return nil.")
 
 (luna-define-method elmo-folder-synchronize ((folder elmo-folder)
                                             &optional
                                             disable-killed
                                             ignore-msgdb
-                                            no-check)
-  (let ((killed-list (elmo-folder-killed-list-internal folder))
-       (before-append t)
-       old-msgdb diff diff-2 delete-list new-list new-msgdb flag
-       flag-table crossed after-append)
-    (setq old-msgdb (elmo-folder-msgdb folder))
-    (setq flag-table (elmo-flag-table-load (elmo-folder-msgdb-path folder)))
+                                            no-check
+                                            mask)
+  (let ((old-msgdb (elmo-folder-msgdb folder))
+       (killed-list (elmo-folder-killed-list-internal folder))
+       (flag-table (elmo-flag-table-load (elmo-folder-msgdb-path folder)))
+       (before-append t))
     (when ignore-msgdb
       (elmo-msgdb-flag-table (elmo-folder-msgdb folder) flag-table)
       (elmo-folder-clear folder (not disable-killed)))
     (unless no-check (elmo-folder-check folder))
     (condition-case nil
-       (progn
+       (let ((killed-list (elmo-folder-killed-list-internal folder))
+             diff-new diff-del
+             delete-list new-list new-msgdb crossed)
          (message "Checking folder diff...")
-         (setq diff (elmo-list-diff (elmo-folder-list-messages
-                                     folder
-                                     (not disable-killed))
-                                    (elmo-folder-list-messages
-                                     folder
-                                     (not disable-killed)
-                                     'in-msgdb)))
+         (elmo-set-list
+          '(diff-new diff-del)
+          (elmo-list-diff (elmo-folder-list-messages folder)
+                          (elmo-folder-list-messages folder nil 'in-msgdb)))
+         (when diff-new
+           (unless disable-killed
+             (setq diff-new (elmo-living-messages diff-new killed-list)))
+           (when mask
+             (setq diff-new (elmo-list-filter mask diff-new))))
          (message "Checking folder diff...done")
-         (setq new-list (elmo-folder-confirm-appends (car diff)))
-         ;; Set killed list as ((1 . MAX-OF-DISAPPEARED))
-         (when (and (not (eq (length (car diff))
-                             (length new-list)))
-                    (setq diff-2 (elmo-list-diff (car diff) new-list)))
-           (elmo-folder-kill-messages-before folder
-                                             (nth (- (length (car diff-2)) 1)
-                                                  (car diff-2))))
-         (setq delete-list (cadr diff))
-         (if (or (equal diff '(nil nil))
-                 (equal diff '(nil))
-                 (and (eq (length (car diff)) 0)
-                      (eq (length (cadr diff)) 0)))
+         (setq new-list (elmo-folder-confirm-appends diff-new))
+         ;; Append to killed list as (MIN-OF-DISAPPEARED . MAX-OF-DISAPPEARED)
+         (when (not (eq (length diff-new)
+                        (length new-list)))
+           (let* ((diff (elmo-list-diff diff-new new-list))
+                  (disappeared (car diff)))
+             (when disappeared
+               (elmo-folder-kill-messages-range folder
+                                                (car disappeared)
+                                                (elmo-last disappeared)))))
+         (setq delete-list diff-del)
+         (if (and (null diff-new) (null diff-del))
              (progn
                (elmo-folder-update-number folder)
                (elmo-folder-process-crosspost folder)
-               0 ; no updates.
-               )
+               0)                      ; `0' means no updates.
            (when delete-list
              (elmo-folder-detach-messages folder delete-list))
            (when new-list
@@ -1568,7 +1558,14 @@ If update process is interrupted, return nil.")
 
 (luna-define-method elmo-folder-detach-messages ((folder elmo-folder)
                                                 numbers)
-  (elmo-msgdb-delete-messages (elmo-folder-msgdb folder) numbers))
+  (when (elmo-msgdb-delete-messages (elmo-folder-msgdb folder) numbers)
+    ;; Remove NUMBERS from killed message list.
+    (elmo-folder-set-killed-list-internal
+     folder
+     (elmo-number-set-delete-list
+      (elmo-folder-killed-list-internal folder)
+      numbers))
+    t))
 
 (luna-define-generic elmo-folder-length (folder)
   "Return number of messages in the FOLDER.")
@@ -1657,8 +1654,8 @@ Return a hashtable for newsgroups."
   "Initialize ELMO module."
   (elmo-crosspost-message-alist-load)
   (elmo-resque-obsolete-variables)
-  (elmo-global-flag-initialize)
-  (elmo-dop-queue-load))
+  (elmo-dop-queue-load)
+  (run-hooks 'elmo-init-hook))
 
 (defun elmo-quit ()
   "Quit and cleanup ELMO."
@@ -1733,6 +1730,8 @@ Return a hashtable for newsgroups."
                               'elmo-cache-directory)
 (elmo-define-obsolete-variable 'elmo-msgdb-dir
                               'elmo-msgdb-directory)
+(elmo-define-obsolete-variable 'elmo-global-flag-list
+                              'elmo-global-flags)
 
 ;; Obsolete functions.
 ;; 2001-12-11: *-dir -> *-directory
@@ -1740,16 +1739,6 @@ Return a hashtable for newsgroups."
 (make-obsolete 'elmo-folder-make-temp-dir
               'elmo-folder-make-temporary-directory)
 
-
-;; autoloads
-(autoload 'elmo-dop-queue-flush "elmo-dop")
-(autoload 'elmo-nntp-post "elmo-nntp")
-(autoload 'elmo-global-flag-initialize "elmo-flag")
-(autoload 'elmo-global-flag-p "elmo-flag")
-(autoload 'elmo-global-flag-detach "elmo-flag")
-(autoload 'elmo-global-flag-detach-messages "elmo-flag")
-(autoload 'elmo-global-flag-set "elmo-flag")
-
 (require 'product)
 (product-provide (provide 'elmo) (require 'elmo-version))