* elmo.el (elmo-file-tag): New class.
[elisp/wanderlust.git] / elmo / elmo.el
index 6cedb36..e52ae66 100644 (file)
@@ -35,6 +35,7 @@
 (require 'elmo-vars)
 (require 'elmo-util)
 (require 'elmo-msgdb)
 (require 'elmo-vars)
 (require 'elmo-util)
 (require 'elmo-msgdb)
+(require 'elmo-signal)
 
 (eval-when-compile (require 'cl))
 
 
 (eval-when-compile (require 'cl))
 
@@ -59,7 +60,8 @@ Otherwise, entire fetching of the message is aborted without confirmation."
 
 (defcustom elmo-folder-update-threshold 500
   "Update threshold."
 
 (defcustom elmo-folder-update-threshold 500
   "Update threshold."
-  :type 'integer
+  :type '(choice (integer :tag "Number of messages")
+                (const :tag "No limitation" nil))
   :group 'elmo)
 
 (defcustom elmo-folder-update-confirm t
   :group 'elmo)
 
 (defcustom elmo-folder-update-confirm t
@@ -67,6 +69,12 @@ Otherwise, entire fetching of the message is aborted without confirmation."
   :type 'boolean
   :group 'elmo)
 
   :type 'boolean
   :group 'elmo)
 
+(defcustom elmo-msgdb-path-encode-threshold nil
+  "*Encode msgdb path if its length is longer than this value."
+  :type '(choice (const :tag "No encode" nil)
+                number)
+  :group 'elmo)
+
 (defvar elmo-message-displaying nil
   "A global switch to indicate message is displaying or not.")
 
 (defvar elmo-message-displaying nil
   "A global switch to indicate message is displaying or not.")
 
@@ -80,8 +88,25 @@ 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)
 
 (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)
 
+;; Event declarations
+(elmo-define-signal flag-changing (number old-flags new-flags)
+  "Notify the changing flag of the messages with NUMBER.")
+
+(elmo-define-signal flag-changed (numbers)
+  "Notify the change flag of the messages with NUMBERS.")
+
+(elmo-define-signal status-changed (numbers)
+  "Notify the change status of the message with NUMBERS.")
+
+(elmo-define-signal update-overview (number)
+  "Notify update overview of the message with NUMBER.")
+
+(elmo-define-signal message-number-changed (old-number new-number)
+  "Notify change of message number within the folder.")
+
 ;; autoloads
 (eval-and-compile
 ;; autoloads
 (eval-and-compile
+  (autoload 'md5 "md5")
   (autoload 'elmo-dop-queue-flush "elmo-dop")
   (autoload 'elmo-nntp-post "elmo-nntp")
   (autoload 'elmo-global-flag-p "elmo-flag")
   (autoload 'elmo-dop-queue-flush "elmo-dop")
   (autoload 'elmo-nntp-post "elmo-nntp")
   (autoload 'elmo-global-flag-p "elmo-flag")
@@ -89,9 +114,12 @@ Otherwise, entire fetching of the message is aborted without confirmation."
   (autoload 'elmo-global-flag-detach "elmo-flag")
   (autoload 'elmo-global-flag-detach-messages "elmo-flag")
   (autoload 'elmo-global-flag-set "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-global-flag-replace-referrer "elmo-flag")
   (autoload 'elmo-get-global-flags "elmo-flag")
   (autoload 'elmo-get-global-flags "elmo-flag")
+  (autoload 'elmo-global-flags-initialize "elmo-flag")
   (autoload 'elmo-global-mark-migrate "elmo-flag")
   (autoload 'elmo-global-mark-migrate "elmo-flag")
-  (autoload 'elmo-folder-list-global-flag-messages "elmo-flag"))
+  (autoload 'elmo-folder-list-global-flag-messages "elmo-flag")
+  (autoload 'elmo-search-register-engine "elmo-search"))
 
 (defun elmo-define-folder (prefix backend)
   "Define a folder.
 
 (defun elmo-define-folder (prefix backend)
   "Define a folder.
@@ -123,6 +151,7 @@ If a folder name begins with PREFIX, use BACKEND."
                                     persistent   ; non-nil if persistent.
                                     process-duplicates  ; read or hide
                                     biff   ; folder for biff
                                     persistent   ; non-nil if persistent.
                                     process-duplicates  ; read or hide
                                     biff   ; folder for biff
+                                    mime-charset ; charset for encode & decode
                                     ))
   (luna-define-internal-accessors 'elmo-folder))
 
                                     ))
   (luna-define-internal-accessors 'elmo-folder))
 
@@ -135,9 +164,11 @@ If a folder name begins with PREFIX, use BACKEND."
   (` (luna-send (, folder) (, message) (, folder) (,@ args))))
 
 ;;;###autoload
   (` (luna-send (, folder) (, message) (, folder) (,@ args))))
 
 ;;;###autoload
-(defun elmo-make-folder (name &optional non-persistent)
+(defun elmo-make-folder (name &optional non-persistent mime-charset)
   "Make an ELMO folder structure specified by NAME.
   "Make an ELMO folder structure specified by NAME.
-If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved."
+If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved.
+If optional argument MIME-CHARSET is specified, it is used for
+encode and decode a multibyte string."
   (let ((type (elmo-folder-type name))
        prefix split class folder original)
     (setq original (elmo-string name))
   (let ((type (elmo-folder-type name))
        prefix split class folder original)
     (setq original (elmo-string name))
@@ -156,10 +187,18 @@ If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved."
                                   :type type
                                   :prefix prefix
                                   :name original
                                   :type type
                                   :prefix prefix
                                   :name original
-                                  :persistent (not non-persistent)))
+                                  :persistent (not non-persistent)
+                                  :mime-charset mime-charset))
     (save-match-data
       (elmo-folder-send folder 'elmo-folder-initialize name))))
 
     (save-match-data
       (elmo-folder-send folder 'elmo-folder-initialize name))))
 
+(defvar elmo-get-folder-function nil)
+
+(defun elmo-get-folder (name)
+  (or (and elmo-get-folder-function
+          (funcall elmo-get-folder-function name))
+      (elmo-make-folder name)))
+
 ;; Note that this function is for internal use only.
 (luna-define-generic elmo-folder-msgdb (folder)
   "Return the msgdb of FOLDER (on-demand loading).
 ;; Note that this function is for internal use only.
 (luna-define-generic elmo-folder-msgdb (folder)
   "Return the msgdb of FOLDER (on-demand loading).
@@ -395,21 +434,6 @@ If optional argument NUMBER is specified, the new message number is set
 \(if possible\).
 Return nil on failure.")
 
 \(if possible\).
 Return nil on failure.")
 
-(luna-define-generic elmo-folder-append-messages (folder
-                                                 src-folder
-                                                 numbers
-                                                 &optional
-                                                 same-number)
-  "Append messages from folder.
-FOLDER is the ELMO folder structure.
-Caller should make sure FOLDER is `writable'.
-\(Can be checked with `elmo-folder-writable-p'\).
-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\).
-Returns a list of message numbers successfully appended.")
-
 (luna-define-generic elmo-folder-pack-numbers (folder)
   "Pack message numbers of FOLDER.")
 
 (luna-define-generic elmo-folder-pack-numbers (folder)
   "Pack message numbers of FOLDER.")
 
@@ -471,6 +495,19 @@ Return newly created temporary directory name which contains temporary files.")
 FOLDER is a ELMO folder structure.
 NUMBER is a number of the message.")
 
 FOLDER is a ELMO folder structure.
 NUMBER is a number of the message.")
 
+(defun elmo-message-flags-for-append (folder number &optional message-id)
+  "Return a list of flags for `elmo-folder-append-buffer'.
+FOLDER is a ELMO folder structure.
+NUMBER is a number of the message.
+If optional argument MESSAGES-ID is not specified, get it from current buffer."
+  (let ((this-id (elmo-message-field folder number 'message-id)))
+    (and this-id
+        (string= this-id (or message-id
+                             (elmo-msgdb-get-message-id-from-buffer)))
+        (or (elmo-message-flags folder number)
+            ;; message exists, but no flag.
+            '(read)))))
+
 (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-flag-available-p ((folder elmo-folder) number
                                                   flag)
   (elmo-msgdb-flag-available-p (elmo-folder-msgdb folder) flag))
@@ -489,97 +526,94 @@ NUMBER is a message number to test."
       (t
        (memq flag 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.
-;; FOLDER is the ELMO folder structure.
-;; ENTITY is the overview entity of the message in the folder.
-;; If optional argument IGNORE-CACHE is non-nil, cache is ignored.
-;; Returned value is a elmo-fetch-strategy object.
-;; If return value is nil, message should not be nil.
-  )
+(luna-define-generic elmo-find-fetch-strategy (folder number
+                                                     &optional
+                                                     ignore-cache
+                                                     require-entireness)
+  "Return the message fetching strategy suitable for the message with NUMBER.
+FOLDER is the ELMO folder structure.
+If optional argument IGNORE-CACHE is non-nil, existing cache is ignored.
+If second optional argument REQUIRE-ENTIRENESS is non-nil,
+ensure that entireness of the returned strategy is entire.
+Returned value is a elmo-fetch-strategy object.
+If return value is nil, message should not be nil.")
 
 (defmacro elmo-make-fetch-strategy (entireness
                                    &optional
                                    use-cache
                                    save-cache
                                    cache-path)
 
 (defmacro elmo-make-fetch-strategy (entireness
                                    &optional
                                    use-cache
                                    save-cache
                                    cache-path)
-;; Make elmo-message-fetching strategy.
-;; ENTIRENESS is 'entire or 'section.
-;; 'entire means fetch message entirely at once.
-;; 'section means fetch message section by section.
-;; If optional USE-CACHE is non-nil, existing cache is used and otherwise,
-;; existing cache is thrown away.
-;; If SAVE-CACHE is non-nil, fetched message is saved.
-;; CACHE-PATH is the cache path to be used as a message cache file.
-  (` (vector (, entireness)
-            (, use-cache) (, save-cache) (, cache-path))))
+  "Make elmo-message-fetching strategy.
+ENTIRENESS is 'entire or 'section.
+'entire means fetch message entirely at once.
+'section means fetch message section by section.
+If optional USE-CACHE is non-nil, existing cache is used and otherwise,
+existing cache is thrown away.
+If SAVE-CACHE is non-nil, fetched message is saved.
+CACHE-PATH is the cache path to be used as a message cache file."
+  `(vector ,entireness ,use-cache ,save-cache ,cache-path))
 
 (defmacro elmo-fetch-strategy-entireness (strategy)
 
 (defmacro elmo-fetch-strategy-entireness (strategy)
-  ;; Return entireness of STRATEGY.
-  (` (aref (, strategy) 0)))
+  "Return entireness of STRATEGY."
+  `(aref ,strategy 0))
 
 (defmacro elmo-fetch-strategy-use-cache (strategy)
 
 (defmacro elmo-fetch-strategy-use-cache (strategy)
-  ;; Return use-cache of STRATEGY.
-  (` (aref (, strategy) 1)))
+  "Return use-cache of STRATEGY."
+  `(aref ,strategy 1))
 
 (defmacro elmo-fetch-strategy-save-cache (strategy)
 
 (defmacro elmo-fetch-strategy-save-cache (strategy)
-  ;; Return save-cache of STRATEGY.
-  (` (aref (, strategy) 2)))
+  "Return save-cache of STRATEGY."
+  `(aref ,strategy 2))
 
 (defmacro elmo-fetch-strategy-cache-path (strategy)
 
 (defmacro elmo-fetch-strategy-cache-path (strategy)
-  ;;  Return cache-path of STRATEGY.
-  (` (aref (, strategy) 3)))
-
-(luna-define-method elmo-find-fetch-strategy
-  ((folder elmo-folder) entity &optional ignore-cache)
-  (let (cache-file size message-id number)
-    (setq size (elmo-message-entity-field entity 'size))
-    (setq message-id (elmo-message-entity-field entity 'message-id))
-    (setq number (elmo-message-entity-number entity))
-    (setq cache-file (elmo-file-cache-get message-id))
-    (setq ignore-cache (or ignore-cache
-                          (null (elmo-message-use-cache-p folder number))))
-    (if (or ignore-cache
-           (null (elmo-file-cache-status cache-file)))
-       ;; No cache or ignore-cache.
-       (if (and (not (elmo-folder-local-p folder))
-                elmo-message-fetch-threshold
-                (integerp size)
-                (>= size elmo-message-fetch-threshold)
-                (or (not elmo-message-fetch-confirm)
-                    (not (prog1 (y-or-n-p
-                                 (format "Fetch entire message(%dbytes)? "
-                                         size))
-                           (message "")))))
-           ;; Don't fetch message at all.
-           nil
-         ;; Don't use existing cache and fetch entire message at once.
-         (elmo-make-fetch-strategy
-          'entire nil
-          (elmo-message-use-cache-p folder number)
-          (elmo-file-cache-path cache-file)))
-      ;; Cache exists.
-      (if (not ignore-cache)
-         (elmo-make-fetch-strategy
-          'entire
-          ;; ...But ignore current section cache and re-fetch
-          ;; if section cache.
-          (not (eq (elmo-file-cache-status cache-file) 'section))
-          ;; Save cache.
-          (elmo-message-use-cache-p folder number)
-          (elmo-file-cache-path cache-file))))))
+  "Return cache-path of STRATEGY."
+  `(aref ,strategy 3))
+
+(luna-define-method elmo-find-fetch-strategy ((folder elmo-folder) number
+                                             &optional
+                                             ignore-cache
+                                             require-entireness)
+  (let ((entity (elmo-message-entity folder number)))
+    (if (null entity)
+       (elmo-make-fetch-strategy 'entire)
+      (let* ((size (elmo-message-entity-field entity 'size))
+            (message-id (elmo-message-entity-field entity 'message-id))
+            (cache-file (elmo-file-cache-get message-id))
+            (use-cache (elmo-message-use-cache-p folder number)))
+       (if (and (not ignore-cache)
+                use-cache
+                (eq (elmo-file-cache-status cache-file) 'entire))
+           ;; Cache exists and use it.
+           (elmo-make-fetch-strategy
+            'entire
+            t                          ; Use cache.
+            use-cache                  ; Save cache.
+            (elmo-file-cache-path cache-file))
+         ;; No cache or ignore-cache.
+         (if (and (not (elmo-folder-local-p folder))
+                  (not require-entireness)
+                  elmo-message-fetch-threshold
+                  (integerp size)
+                  (>= size elmo-message-fetch-threshold)
+                  (or (not elmo-message-fetch-confirm)
+                      (not (prog1
+                               (y-or-n-p
+                                (format "Fetch entire message(%dbytes)? "
+                                        size))
+                             (message "")))))
+             ;; Don't fetch message at all.
+             nil
+           ;; Don't use existing cache and fetch entire message at once.
+           (elmo-make-fetch-strategy
+            'entire
+            nil                        ; Don't use cache.
+            use-cache                  ; Save cache.
+            (elmo-file-cache-path cache-file))))))))
 
 (luna-define-method elmo-folder-list-messages-internal
   ((folder elmo-folder) &optional visible-only)
   t)
 
 
 (luna-define-method elmo-folder-list-messages-internal
   ((folder elmo-folder) &optional visible-only)
   t)
 
-(defun elmo-folder-encache (folder numbers &optional unread)
-  "Encache messages in the FOLDER with NUMBERS.
-If UNREAD is non-nil, messages are not flaged as read."
-  (dolist (number numbers)
-    (elmo-message-encache folder number unread)))
-
 (luna-define-generic elmo-message-encache (folder number &optional read)
   "Encache message in the FOLDER with NUMBER.
 If READ is non-nil, message is flaged as read.")
 (luna-define-generic elmo-message-encache (folder number &optional read)
   "Encache message in the FOLDER with NUMBER.
 If READ is non-nil, message is flaged as read.")
@@ -587,15 +621,16 @@ If READ is non-nil, message is flaged as read.")
 (luna-define-method elmo-message-encache ((folder elmo-folder) number
                                          &optional read)
   (let (path)
 (luna-define-method elmo-message-encache ((folder elmo-folder) number
                                          &optional read)
   (let (path)
-    (elmo-message-fetch
-     folder number
-     (elmo-make-fetch-strategy 'entire
-                              nil ;use-cache
-                              t   ;save-cache
-                              (setq path (elmo-file-cache-get-path
-                                          (elmo-message-field
-                                           folder number 'message-id))))
-     nil nil (not read))
+    (with-temp-buffer
+      (elmo-message-fetch
+       folder number
+       (elmo-make-fetch-strategy 'entire
+                                nil ;use-cache
+                                t   ;save-cache
+                                (setq path (elmo-file-cache-get-path
+                                            (elmo-message-field
+                                             folder number 'message-id))))
+       (not read)))
     path))
 
 (luna-define-generic elmo-message-fetch-bodystructure (folder number strategy)
     path))
 
 (luna-define-generic elmo-message-fetch-bodystructure (folder number strategy)
@@ -603,32 +638,15 @@ If READ is non-nil, message is flaged as read.")
 
 (luna-define-generic elmo-message-fetch (folder number strategy
                                                &optional
 
 (luna-define-generic elmo-message-fetch (folder number strategy
                                                &optional
-                                               section
-                                               outbuf
-                                               unread)
-  "Fetch a message and return as a string.
-FOLDER is the ELMO folder structure.
-NUMBER is the number of the message in the FOLDER.
-STRATEGY is the message fetching strategy.
-If optional argument SECTION is specified, only the SECTION of the message
-is fetched (if possible).
-If second optional argument OUTBUF is specified, fetched message is
-inserted to the buffer and returns t if fetch was ended successfully.
-If third optional argument UNREAD is non-nil, message is not flaged as read.
-Returns non-nil if fetching was succeed.")
-
-(luna-define-generic elmo-message-fetch-with-cache-process (folder
-                                                           number strategy
-                                                           &optional
-                                                           section
-                                                           unread)
-  "Fetch a message into current buffer with cache process.
+                                               unread
+                                               section)
+  "Fetch a message into current buffer.
 FOLDER is the ELMO folder structure.
 NUMBER is the number of the message in the FOLDER.
 STRATEGY is the message fetching strategy.
 FOLDER is the ELMO folder structure.
 NUMBER is the number of the message in the FOLDER.
 STRATEGY is the message fetching strategy.
-If optional argument SECTION is specified, only the SECTION of the message
-is fetched (if possible).
-If second optional argument UNREAD is non-nil, message is not flaged as read.
+If optional argument UNREAD is non-nil, message is not flaged as read.
+If second optional argument SECTION is specified, only the
+SECTION of the message is fetched (if possible).
 Returns non-nil if fetching was succeed.")
 
 (luna-define-generic elmo-message-fetch-internal (folder number strategy
 Returns non-nil if fetching was succeed.")
 
 (luna-define-generic elmo-message-fetch-internal (folder number strategy
@@ -753,14 +771,19 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
     t))
 
 (luna-define-method elmo-folder-rename ((folder elmo-folder) new-name)
     t))
 
 (luna-define-method elmo-folder-rename ((folder elmo-folder) new-name)
-  (let* ((new-folder (elmo-make-folder new-name)))
+  (let ((new-folder (elmo-make-folder
+                    new-name
+                    nil
+                    (elmo-folder-mime-charset-internal folder))))
     (unless (eq (elmo-folder-type-internal folder)
                (elmo-folder-type-internal new-folder))
       (error "Not same folder type"))
     (unless (eq (elmo-folder-type-internal folder)
                (elmo-folder-type-internal new-folder))
       (error "Not same folder type"))
-    (if (or (file-exists-p (elmo-folder-msgdb-path new-folder))
-           (elmo-folder-exists-p new-folder))
-       (error "Already exists folder: %s" new-name))
+    (when (or (file-exists-p (elmo-folder-msgdb-path new-folder))
+             (elmo-folder-exists-p new-folder))
+      (error "Already exists folder: %s" new-name))
     (elmo-folder-send folder 'elmo-folder-rename-internal new-folder)
     (elmo-folder-send folder 'elmo-folder-rename-internal new-folder)
+    (elmo-global-flag-replace-referrer (elmo-folder-name-internal folder)
+                                      new-name)
     (elmo-msgdb-rename-path folder new-folder)))
 
 (luna-define-method elmo-folder-delete-messages ((folder elmo-folder)
     (elmo-msgdb-rename-path folder new-folder)))
 
 (luna-define-method elmo-folder-delete-messages ((folder elmo-folder)
@@ -777,6 +800,14 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
     (setq results (elmo-msgdb-search msgdb condition numbers))
     (if (listp results)
        results
     (setq results (elmo-msgdb-search msgdb condition numbers))
     (if (listp results)
        results
+      (elmo-condition-optimize condition)
+      (when (and (consp condition)
+                (eq (car condition) 'and)
+                (listp (setq results (elmo-msgdb-search msgdb
+                                                        (nth 1 condition)
+                                                        numbers))))
+       (setq numbers results
+             condition (nth 2 condition)))
       (let ((len (length numbers))
            matched)
        (elmo-with-progress-display (> len elmo-display-progress-threshold)
       (let ((len (length numbers))
            matched)
        (elmo-with-progress-display (> len elmo-display-progress-threshold)
@@ -798,6 +829,22 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
        (message "Searching...done")
        (nreverse matched)))))
 
        (message "Searching...done")
        (nreverse matched)))))
 
+(defun elmo-message-buffer-match-condition (condition number)
+  (let* ((handler (luna-make-entity 'modb-buffer-entity-handler))
+        (result (elmo-condition-match
+                 condition
+                 (lambda (condition handler entity)
+                   (elmo-msgdb-message-match-condition handler
+                                                       condition
+                                                       entity))
+                 (list
+                  handler
+                  (elmo-msgdb-make-message-entity
+                   handler
+                   :number number
+                   :buffer (current-buffer))))))
+    (and result (not (elmo-filter-condition-p result)))))
+
 (luna-define-method elmo-message-match-condition ((folder elmo-folder)
                                                  number condition
                                                  numbers)
 (luna-define-method elmo-message-match-condition ((folder elmo-folder)
                                                  number condition
                                                  numbers)
@@ -821,11 +868,11 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).")
                                                      (and cache t)
                                                      nil
                                                      cache-path)
                                                      (and cache t)
                                                      nil
                                                      cache-path)
-                           nil (current-buffer) t)
+                           'unread)
        (set-buffer-multibyte default-enable-multibyte-characters)
        (decode-coding-region (point-min) (point-max)
                              elmo-mime-display-as-is-coding-system)
        (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)))))
+       (elmo-message-buffer-match-condition condition number)))))
 
 (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder))
   nil) ; default is noop.
 
 (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder))
   nil) ; default is noop.
@@ -960,7 +1007,7 @@ If optional argument IF-EXISTS is nil, load on demand.
        append-list delete-list diff)
     (cons (if (equal in-folder in-db)
              0
        append-list delete-list diff)
     (cons (if (equal in-folder in-db)
              0
-           (setq diff (elmo-list-diff in-folder in-db nil))
+           (setq diff (elmo-list-diff in-folder in-db))
            (setq append-list (car diff))
            (setq delete-list (cadr diff))
            (if append-list
            (setq append-list (car diff))
            (setq delete-list (cadr diff))
            (if append-list
@@ -1019,13 +1066,66 @@ If optional argument IF-EXISTS is nil, load on demand.
   (+ 1 (elmo-max-of-list (or (elmo-folder-list-messages folder)
                             '(0)))))
 
   (+ 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
-                                                &optional
-                                                same-number)
-  (elmo-generic-folder-append-messages folder src-folder numbers
-                                      same-number))
+(eval-and-compile
+  (luna-define-class elmo-file-tag))
+
+(defconst elmo-append-messages-disptch-table
+  '(((nil      . null)         . elmo-folder-append-messages-*-null)
+    ((filter   . nil)          . elmo-folder-append-messages-filter-*)
+    ((nil      . filter)       . elmo-folder-append-messages-*-filter)
+    ((pipe     . nil)          . elmo-folder-append-messages-pipe-*)
+    ((nil      . pipe)         . elmo-folder-append-messages-*-pipe)
+    ((multi    . nil)          . elmo-folder-append-messages-multi-*)
+    ((nil      . flag)         . elmo-folder-append-messages-*-flag)
+    ((imap4    . imap4)        . elmo-folder-append-messages-imap4-imap4)
+    ((elmo-file-tag . localdir)        . elmo-folder-append-messages-*-localdir)
+    ((elmo-file-tag . maildir) . elmo-folder-append-messages-*-maildir)
+    ((nil      . archive)      . elmo-folder-append-messages-*-archive)
+    ((nil      . nil)          . elmo-generic-folder-append-messages)))
+
+(defun elmo-folder-type-p (folder type)
+  (or (null type)
+      (eq (elmo-folder-type-internal folder) type)
+      (labels ((member-if (predicate list)
+                         (and list
+                              (or (funcall predicate (car list))
+                                  (member-if predicate (cdr list)))))
+              (subtypep (name type)
+                        (or (eq name type)
+                            (let ((class (luna-find-class name)))
+                              (and class
+                                   (member-if (lambda (name)
+                                                (subtypep name type))
+                                              (luna-class-parents class)))))))
+       (subtypep (luna-class-name folder)
+                 (or (intern-soft (format "elmo-%s-folder" type))
+                     type)))))
+
+(defun elmo-folder-append-messages (dst-folder src-folder numbers
+                                              &optional same-number caller)
+  "Append messages from folder.
+DST-FOLDER is the ELMO folder structure.
+Caller should make sure DST-FOLDER is `writable'.
+\(Can be checked with `elmo-folder-writable-p'\).
+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\).
+Returns a list of message numbers successfully appended."
+  (let ((rest (if caller
+                 (cdr (memq (rassq caller elmo-append-messages-disptch-table)
+                            elmo-append-messages-disptch-table))
+               elmo-append-messages-disptch-table))
+       result)
+    (while rest
+      (let ((types (car (car rest))))
+       (if (and (elmo-folder-type-p src-folder (car types))
+                (elmo-folder-type-p dst-folder (cdr types)))
+           (setq result (funcall (cdr (car rest))
+                                 dst-folder src-folder numbers same-number)
+                 rest nil)
+         (setq rest (cdr rest)))))
+    result))
 
 (defun elmo-generic-folder-append-messages (folder src-folder numbers
                                                   same-number)
 
 (defun elmo-generic-folder-append-messages (folder src-folder numbers
                                                   same-number)
@@ -1058,18 +1158,11 @@ If optional argument IF-EXISTS is nil, load on demand.
                                 'entire t nil
                                 (elmo-file-cache-path cache)))
                           (error "Unplugged")))
                                 'entire t nil
                                 (elmo-file-cache-path cache)))
                           (error "Unplugged")))
-                    nil (current-buffer)
                     'unread)
                    (> (buffer-size) 0)
                    (elmo-folder-append-buffer
                     folder
                     'unread)
                    (> (buffer-size) 0)
                    (elmo-folder-append-buffer
                     folder
-                    (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))))
+                    (elmo-message-flags-for-append src-folder (car numbers))
                     (if same-number (car numbers))))))
          (error (setq failure t)))
        ;; FETCH & APPEND finished
                     (if same-number (car numbers))))))
          (error (setq failure t)))
        ;; FETCH & APPEND finished
@@ -1132,7 +1225,19 @@ If optional argument IF-EXISTS is nil, load on demand.
   (or (elmo-folder-path-internal folder)
       (elmo-folder-set-path-internal
        folder
   (or (elmo-folder-path-internal folder)
       (elmo-folder-set-path-internal
        folder
-       (elmo-folder-expand-msgdb-path folder))))
+       (if (null elmo-msgdb-path-encode-threshold)
+          (elmo-folder-expand-msgdb-path folder)
+        (let* ((path (directory-file-name
+                      (elmo-folder-expand-msgdb-path folder)))
+               (dirname (file-name-nondirectory path)))
+          (if (<= (length dirname) elmo-msgdb-path-encode-threshold)
+              path
+            (require 'md5)
+            (setq dirname (md5 dirname))
+            (when (> (length dirname) elmo-msgdb-path-encode-threshold)
+              (error "Cannot shrink msgdb path for `%s'"
+                     (elmo-folder-name-internal folder)))
+            (expand-file-name dirname (file-name-directory path))))))))
 
 (luna-define-generic elmo-message-cached-p (folder number)
   "Return non-nil if the message is cached.")
 
 (luna-define-generic elmo-message-cached-p (folder number)
   "Return non-nil if the message is cached.")
@@ -1140,11 +1245,20 @@ If optional argument IF-EXISTS is nil, load on demand.
 (luna-define-method elmo-message-cached-p ((folder elmo-folder) number)
   (elmo-message-flagged-p folder number 'cached))
 
 (luna-define-method elmo-message-cached-p ((folder elmo-folder) number)
   (elmo-message-flagged-p folder number 'cached))
 
+(luna-define-generic elmo-message-killed-p (folder number)
+  "Return non-nil if the message is killed.")
+
+(luna-define-method elmo-message-killed-p ((folder elmo-folder) number)
+  (let ((killed-list (elmo-folder-killed-list-internal folder)))
+    (and killed-list
+        (elmo-number-set-member number killed-list))))
+
 (defun elmo-message-accessible-p (folder number)
   "Get accessibility of the message.
 Return non-nil when message is accessible."
   (or (elmo-folder-plugged-p folder)
       (elmo-folder-local-p folder)
 (defun elmo-message-accessible-p (folder number)
   "Get accessibility of the message.
 Return non-nil when message is accessible."
   (or (elmo-folder-plugged-p folder)
       (elmo-folder-local-p folder)
+      (< number 0) ; in dop spool
       (elmo-message-cached-p folder number)))
 
 (luna-define-generic elmo-message-set-cached (folder number cached)
       (elmo-message-cached-p folder number)))
 
 (luna-define-generic elmo-message-set-cached (folder number cached)
@@ -1157,7 +1271,8 @@ If CACHED is t, message is set as cached.")
                                             number cached)
   (if cached
       (elmo-msgdb-set-flag (elmo-folder-msgdb folder) number 'cached)
                                             number cached)
   (if cached
       (elmo-msgdb-set-flag (elmo-folder-msgdb folder) number 'cached)
-    (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number 'cached)))
+    (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number 'cached))
+  (elmo-emit-signal 'status-changed folder (list number)))
 
 (defun elmo-message-copy-entity (entity)
   (elmo-msgdb-copy-message-entity (elmo-message-entity-handler entity)
 
 (defun elmo-message-copy-entity (entity)
   (elmo-msgdb-copy-message-entity (elmo-message-entity-handler entity)
@@ -1244,14 +1359,16 @@ If optional IS-LOCAL is non-nil, update only local (not server) status."
   ;; XXX Transitional implementation.
   (elmo-folder-unset-flag folder (list number) flag is-local))
 
   ;; XXX Transitional implementation.
   (elmo-folder-unset-flag folder (list number) flag is-local))
 
-(luna-define-generic elmo-message-field (folder number field)
+(luna-define-generic elmo-message-field (folder number field &optional type)
   "Get message field value in the msgdb.
 FOLDER is the ELMO folder structure.
 NUMBER is a number of the message.
   "Get 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.")
+FIELD is a symbol of the field.
+If optional argument TYPE is specified, return converted value.")
 
 
-(luna-define-method elmo-message-field ((folder elmo-folder) number field)
-  (elmo-msgdb-message-field (elmo-folder-msgdb folder) number field))
+(luna-define-method elmo-message-field ((folder elmo-folder)
+                                       number field &optional type)
+  (elmo-msgdb-message-field (elmo-folder-msgdb folder) number field type))
 
 (luna-define-generic elmo-message-set-field (folder number field value)
   "Set message field value in the msgdb.
 
 (luna-define-generic elmo-message-set-field (folder number field value)
   "Set message field value in the msgdb.
@@ -1277,12 +1394,17 @@ VALUE is a value to set.")
                                          &optional is-local)
   (when (elmo-folder-msgdb-internal folder)
     (dolist (number numbers)
                                          &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
-                          flag))))
+      (let ((old-flags (elmo-message-flags folder number)))
+       (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 flag)
+       (elmo-emit-signal 'flag-changing
+                         folder
+                         number
+                         old-flags
+                         (elmo-message-flags folder number))))
+    (elmo-emit-signal 'flag-changed folder numbers)))
 
 (defun elmo-message-has-global-flag-p (folder number)
   "Return non-nil when the message in the FOLDER with NUMBER has global flag."
 
 (defun elmo-message-has-global-flag-p (folder number)
   "Return non-nil when the message in the FOLDER with NUMBER has global flag."
@@ -1316,11 +1438,16 @@ If Optional LOCAL is non-nil, don't update server flag."
                                            &optional is-local)
   (when (elmo-folder-msgdb-internal folder)
     (dolist (number numbers)
                                            &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
-                            flag))))
+      (let ((old-flags (elmo-message-flags folder number)))
+       (when (elmo-global-flag-p flag)
+         (elmo-global-flag-detach flag folder number 'always))
+       (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number flag)
+       (elmo-emit-signal 'flag-changing
+                         folder
+                         number
+                         old-flags
+                         (elmo-message-flags folder number))))
+    (elmo-emit-signal 'flag-changed folder numbers)))
 
 (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder))
   ;; Do nothing.
 
 (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder))
   ;; Do nothing.
@@ -1359,13 +1486,15 @@ If Optional LOCAL is non-nil, don't update server flag."
        (length duplicates))
     0))
 
        (length duplicates))
     0))
 
-(defun elmo-folder-confirm-appends (appends)
+(defun elmo-folder-confirm-appends (folder appends)
   (let ((len (length appends))
        in)
     (if (and elmo-folder-update-threshold
             (> len elmo-folder-update-threshold)
             elmo-folder-update-confirm)
   (let ((len (length appends))
        in)
     (if (and elmo-folder-update-threshold
             (> len elmo-folder-update-threshold)
             elmo-folder-update-confirm)
-       (if (y-or-n-p (format "Too many messages(%d).  Update all? " len))
+       (if (y-or-n-p (format
+                      "Too many messages(%d) in %s.  Update all? "
+                      len (elmo-folder-name-internal folder)))
            appends
          (setq in elmo-folder-update-threshold)
          (catch 'end
            appends
          (setq in elmo-folder-update-threshold)
          (catch 'end
@@ -1390,26 +1519,21 @@ If Optional LOCAL is non-nil, don't update server flag."
                                                      number strategy)
   nil)
 
                                                      number strategy)
   nil)
 
+(defun elmo-message-fetch-string (folder number strategy
+                                        &optional
+                                        unread
+                                        section)
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (when (elmo-message-fetch folder number strategy unread section)
+      (buffer-string))))
+
 (luna-define-method elmo-message-fetch ((folder elmo-folder)
                                        number strategy
                                        &optional
 (luna-define-method elmo-message-fetch ((folder elmo-folder)
                                        number strategy
                                        &optional
-                                       section
-                                       outbuf
-                                       unread)
-  (if outbuf
-      (with-current-buffer outbuf
-       (erase-buffer)
-       (elmo-message-fetch-with-cache-process folder number
-                                              strategy section unread))
-    (with-temp-buffer
-      (elmo-message-fetch-with-cache-process folder number
-                                            strategy section unread)
-      (buffer-string))))
-
-(luna-define-method elmo-message-fetch-with-cache-process ((folder elmo-folder)
-                                                          number strategy
-                                                          &optional
-                                                          section unread)
+                                       unread
+                                       section)
+  (erase-buffer)
   (let ((cache-path (elmo-fetch-strategy-cache-path strategy))
        (method-priorities
         (cond ((eq (elmo-fetch-strategy-use-cache strategy) 'maybe)
   (let ((cache-path (elmo-fetch-strategy-cache-path strategy))
        (method-priorities
         (cond ((eq (elmo-fetch-strategy-use-cache strategy) 'maybe)
@@ -1418,28 +1542,33 @@ If Optional LOCAL is non-nil, don't update server flag."
                '(cache entity))
               (t
                '(entity))))
                '(cache entity))
               (t
                '(entity))))
-       result err)
+       result err updated-server-flag)
     (while (and method-priorities
     (while (and method-priorities
-               (null result))
+               (not result))
       (setq result
            (case (car method-priorities)
              (cache
               (elmo-file-cache-load cache-path section))
              (entity
       (setq result
            (case (car method-priorities)
              (cache
               (elmo-file-cache-load cache-path section))
              (entity
-              (when (and (condition-case error
-                             (elmo-message-fetch-internal folder number
-                                                          strategy
-                                                          section
-                                                          unread)
-                           (error (setq err error) nil))
-                         (> (buffer-size) 0))
+              (when (condition-case error
+                        (elmo-message-fetch-internal folder number
+                                                     strategy
+                                                     section
+                                                     unread)
+                      (error (setq err error) nil))
+                (setq updated-server-flag t)
                 (when (and (elmo-fetch-strategy-save-cache strategy)
                            cache-path)
                   (elmo-file-cache-save cache-path section))
                 t)))
            method-priorities (cdr method-priorities)))
                 (when (and (elmo-fetch-strategy-save-cache strategy)
                            cache-path)
                   (elmo-file-cache-save cache-path section))
                 t)))
            method-priorities (cdr method-priorities)))
-    (or result
-       (and err (signal (car err) (cdr err))))))
+    (if result
+       (when (and (not unread)
+                  (elmo-message-flagged-p folder number 'unread))
+         (elmo-message-unset-flag folder number 'unread updated-server-flag))
+      (when err
+       (signal (car err) (cdr err))))
+    result))
 
 (defun elmo-folder-kill-messages-range (folder beg end)
   (elmo-folder-set-killed-list-internal
 
 (defun elmo-folder-kill-messages-range (folder beg end)
   (elmo-folder-set-killed-list-internal
@@ -1457,6 +1586,23 @@ If Optional LOCAL is non-nil, don't update server flag."
     numbers))
   (elmo-folder-unset-flag folder numbers 'all 'local-only))
 
     numbers))
   (elmo-folder-unset-flag folder numbers 'all 'local-only))
 
+(luna-define-generic elmo-folder-recover-messages (folder numbers)
+  "Recover killed messages in the FOLDER with NUMBERS.")
+
+(luna-define-method elmo-folder-recover-messages ((folder elmo-folder) numbers)
+  (let ((msgdb (elmo-folder-msgdb folder)))
+    (elmo-folder-set-killed-list-internal
+     folder
+     (elmo-number-set-delete-list
+      (elmo-folder-killed-list-internal folder)
+      numbers))
+    (dolist (number numbers)
+      (if (elmo-file-cache-exists-p
+          (elmo-message-field folder number 'message-id))
+         (elmo-msgdb-set-flag msgdb number 'cached)
+       (elmo-msgdb-unset-flag msgdb number 'cached)))
+    (elmo-emit-signal 'status-changed folder numbers)))
+
 (luna-define-method elmo-folder-clear ((folder elmo-folder)
                                       &optional keep-killed)
   (unless keep-killed
 (luna-define-method elmo-folder-clear ((folder elmo-folder)
                                       &optional keep-killed)
   (unless keep-killed
@@ -1509,17 +1655,18 @@ If update process is interrupted, return nil.")
           (elmo-list-diff (elmo-folder-list-messages folder)
                           (elmo-folder-list-messages folder nil 'in-msgdb)))
          (when diff-new
           (elmo-list-diff (elmo-folder-list-messages folder)
                           (elmo-folder-list-messages folder nil 'in-msgdb)))
          (when diff-new
+           (setq diff-new (sort diff-new #'<))
            (unless disable-killed
              (setq diff-new (elmo-living-messages diff-new killed-list)))
            (unless disable-killed
              (setq diff-new (elmo-living-messages diff-new killed-list)))
-           (when mask
+           (when (and mask (not ignore-msgdb))
              (setq diff-new (elmo-list-filter mask diff-new))))
          (message "Checking folder diff...done")
              (setq diff-new (elmo-list-filter mask diff-new))))
          (message "Checking folder diff...done")
-         (setq new-list (elmo-folder-confirm-appends diff-new))
+         (setq new-list (elmo-folder-confirm-appends folder 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))
          ;; 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)))
+                  (disappeared (sort (car diff) #'<)))
              (when disappeared
                (elmo-folder-kill-messages-range folder
                                                 (car disappeared)
              (when disappeared
                (elmo-folder-kill-messages-range folder
                                                 (car disappeared)
@@ -1578,7 +1725,8 @@ If update process is interrupted, return nil.")
 (defun elmo-folder-msgdb-load (folder &optional silent)
   (unless silent
     (message "Loading msgdb for %s..." (elmo-folder-name-internal folder)))
 (defun elmo-folder-msgdb-load (folder &optional silent)
   (unless silent
     (message "Loading msgdb for %s..." (elmo-folder-name-internal folder)))
-  (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder))))
+  (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder)
+                               (elmo-folder-mime-charset-internal folder))))
     (elmo-folder-set-info-max-by-numdb
      folder
      (elmo-msgdb-list-messages msgdb))
     (elmo-folder-set-info-max-by-numdb
      folder
      (elmo-msgdb-list-messages msgdb))
@@ -1650,6 +1798,43 @@ Return a hashtable for newsgroups."
     (elmo-make-directory temp-dir)
     temp-dir))
 
     (elmo-make-directory temp-dir)
     temp-dir))
 
+;; ELMO status structure.
+(defmacro elmo-message-status (folder number &optional flags killed)
+  "Make ELMO status structure from FOLDER and NUMBER.
+A value in this structure is cached at first access."
+  `(vector ,folder ,number ,flags ,killed))
+
+(defmacro elmo-message-status-folder (status)
+  `(aref ,status 0))
+
+(defmacro elmo-message-status-number (status)
+  `(aref ,status 1))
+
+(defmacro elmo-message-status-set-flags (status flags)
+  `(aset ,status 2 (or ,flags '(read))))
+
+(defsubst elmo-message-status-flags (status)
+  (or (aref status 2)
+      (elmo-message-status-set-flags
+       status
+       (elmo-message-flags (elmo-message-status-folder status)
+                          (elmo-message-status-number status)))))
+
+(defsubst elmo-message-status-cached-p (status)
+  (memq 'cached (elmo-message-status-flags status)))
+
+(defmacro elmo-message-status-set-killed (status killed)
+  `(aset ,status 3 (if ,killed 'killed 'living)))
+
+(defsubst elmo-message-status-killed-p (status)
+  (eq 'killed
+      (or (aref status 3)
+         (elmo-message-status-set-killed
+          status
+          (elmo-message-killed-p (elmo-message-status-folder status)
+                                 (elmo-message-status-number status))))))
+
+;;;
 (defun elmo-init ()
   "Initialize ELMO module."
   (elmo-crosspost-message-alist-load)
 (defun elmo-init ()
   "Initialize ELMO module."
   (elmo-crosspost-message-alist-load)
@@ -1692,7 +1877,7 @@ Return a hashtable for newsgroups."
 (elmo-define-folder ?|  'pipe)
 (elmo-define-folder ?.  'maildir)
 (elmo-define-folder ?'  'internal)
 (elmo-define-folder ?|  'pipe)
 (elmo-define-folder ?.  'maildir)
 (elmo-define-folder ?'  'internal)
-(elmo-define-folder ?\[  'nmz)
+(elmo-define-folder ?\[  'search)
 (elmo-define-folder ?@  'shimbun)
 
 ;;; Obsolete variables.
 (elmo-define-folder ?@  'shimbun)
 
 ;;; Obsolete variables.
@@ -1732,6 +1917,13 @@ Return a hashtable for newsgroups."
                               'elmo-msgdb-directory)
 (elmo-define-obsolete-variable 'elmo-global-flag-list
                               'elmo-global-flags)
                               'elmo-msgdb-directory)
 (elmo-define-obsolete-variable 'elmo-global-flag-list
                               'elmo-global-flags)
+(elmo-define-obsolete-variable 'elmo-nmz-default-index-path
+                              'elmo-search-namazu-default-index-path)
+(elmo-define-obsolete-variable 'elmo-nmz-index-alias-alist
+                              'elmo-search-namazu-index-alias-alist)
+(elmo-define-obsolete-variable 'elmo-nmz-use-drive-letter
+                              'elmo-search-use-drive-letter)
+
 
 ;; Obsolete functions.
 ;; 2001-12-11: *-dir -> *-directory
 
 ;; Obsolete functions.
 ;; 2001-12-11: *-dir -> *-directory