* elmo-imap4.el: Remove Nemacs hack, replace `elmo-read' with `read'.
[elisp/wanderlust.git] / elmo / elmo-util.el
index 9c6de8c..2c59103 100644 (file)
@@ -1,10 +1,9 @@
-;;; elmo-util.el -- Utilities for Elmo.
+;;; elmo-util.el --- Utilities for ELMO.
 
-;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
+;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
 
 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
 ;; Keywords: mail, net news
-;; Time-stamp: <2000-03-29 09:42:41 teranisi>
 
 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
 
 ;;
 
 ;;; Commentary:
-;; 
+;;
 
 ;;; Code:
-;; 
+;;
 
+(eval-when-compile (require 'cl))
 (require 'elmo-vars)
 (require 'elmo-date)
-(eval-when-compile (require 'cl))
+(require 'mcharset)
+(require 'pces)
 (require 'std11)
 (require 'eword-decode)
 (require 'utf7)
-
-(eval-when-compile 
-  (condition-case nil 
-      (progn
-       (require 'ssl)
-       (require 'starttls))
-    (error))
-  (defun-maybe starttls-negotiate (a))
-  (defun-maybe starttls-open-stream (a b c d))
-  (defun-maybe open-ssl-stream (a b c d)))
+(require 'poem)
 
 (defmacro elmo-set-buffer-multibyte (flag)
   "Set the multibyte flag of the current buffer to FLAG."
   (cond ((boundp 'MULE)
-         (list 'setq 'mc-flag flag))
-        ((featurep 'xemacs)
-         flag)
-        ((and (boundp 'emacs-major-version) (>= emacs-major-version 20))
-         (list 'set-buffer-multibyte flag))
-        (t
-         flag)))
+        (list 'setq 'mc-flag flag))
+       ((featurep 'xemacs)
+        flag)
+       ((and (boundp 'emacs-major-version) (>= emacs-major-version 20))
+        (list 'set-buffer-multibyte flag))
+       (t
+        flag)))
 
 (defvar elmo-work-buf-name " *elmo work*")
 (defvar elmo-temp-buf-name " *elmo temp*")
@@ -70,7 +62,7 @@
 
 ;; base64 encoding/decoding
 (require 'mel)
-(fset 'elmo-base64-encode-string 
+(fset 'elmo-base64-encode-string
       (mel-find-function 'mime-encode-string "base64"))
 (fset 'elmo-base64-decode-string
       (mel-find-function 'mime-decode-string "base64"))
 ;; Check make-symbolic-link() instead.  -- 981002 by Fuji
 (if (fboundp 'make-symbolic-link)  ;; xxx
     (defalias 'elmo-add-name-to-file 'add-name-to-file)
-  (defun elmo-add-name-to-file 
+  (defun elmo-add-name-to-file
     (filename newname &optional ok-if-already-exists)
     (copy-file filename newname ok-if-already-exists t)))
 
-(require 'broken)
-(broken-facility timezone-y2k
-  "timezone.el does not clear Y2K."
-  (or (not (featurep 'timezone))
-      (string= (aref (timezone-parse-date "Sat, 1 Jan 00 07:00:00 JST") 0) 
-              "2000")))
-
-(when-broken timezone-y2k
-  (defun timezone-parse-date (date)
-    "Parse DATE and return a vector [YEAR MONTH DAY TIME TIMEZONE].
-19 is prepended to year if necessary.  Timezone may be nil if nothing.
-Understands the following styles:
- (1) 14 Apr 89 03:20[:12] [GMT]
- (2) Fri, 17 Mar 89 4:01[:33] [GMT]
- (3) Mon Jan 16 16:12[:37] [GMT] 1989
- (4) 6 May 1992 1641-JST (Wednesday)
- (5) 22-AUG-1993 10:59:12.82
- (6) Thu, 11 Apr 16:17:12 91 [MET]
- (7) Mon, 6  Jul 16:47:20 T 1992 [MET]"
-    (condition-case nil
-       (progn
-         ;; Get rid of any text properties.
-         (and (stringp date)
-              (or (text-properties-at 0 date)
-                  (next-property-change 0 date))
-              (setq date (copy-sequence date))
-              (set-text-properties 0 (length date) nil date))
-         (let ((date (or date ""))
-               (year nil)
-               (month nil)
-               (day nil)
-               (time nil)
-               (zone nil))                     ;This may be nil.
-           (cond ((string-match
-                   "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\'" date)
-                  ;; Styles: (6) and (7) without timezone
-                  (setq year 6 month 3 day 2 time 4 zone nil))
-                 ((string-match
-                   "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
-                  ;; Styles: (6) and (7) with timezone and buggy timezone
-                  (setq year 6 month 3 day 2 time 4 zone 7))
-                 ((string-match
-                   "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\'" date)
-                  ;; Styles: (1) and (2) without timezone
-                  (setq year 3 month 2 day 1 time 4 zone nil))
-                 ((string-match
-                   "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
-                  ;; Styles: (1) and (2) with timezone and buggy timezone
-                  (setq year 3 month 2 day 1 time 4 zone 5))
-                 ((string-match
-                   "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([0-9]+\\)" date)
-                  ;; Styles: (3) without timezone
-                  (setq year 4 month 1 day 2 time 3 zone nil))
-                 ((string-match
-                   "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)[ \t]+\\([0-9]+\\)" date)
-                  ;; Styles: (3) with timezone
-                  (setq year 5 month 1 day 2 time 3 zone 4))
-                 ((string-match
-                   "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
-                  ;; Styles: (4) with timezone
-                  (setq year 3 month 2 day 1 time 4 zone 5))
-                 ((string-match
-                   "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\.[0-9]+" date)
-                  ;; Styles: (5) without timezone.
-                  (setq year 3 month 2 day 1 time 4 zone nil))
-                 )
-           (if year
-               (progn
-                 (setq year
-                       (substring date (match-beginning year) 
-                                  (match-end year)))
-                 (if (< (length year) 4)
-                     (let ((yr (string-to-int year)))
-                       (when (>= yr 100)
-                         (setq yr (- yr 100)))
-                       (setq year (format "%d%02d"
-                                          (if (< yr 70)
-                                              20
-                                            19)
-                                          yr))))
-                 (let ((string (substring date
-                                          (match-beginning month)
-                                          (+ (match-beginning month) 3))))
-                   (setq month
-                         (int-to-string
-                          (cdr (assoc (upcase string) 
-                                      timezone-months-assoc)))))
-                 (setq day
-                       (substring date (match-beginning day) (match-end day)))
-                 (setq time
-                       (substring date (match-beginning time) 
-                                  (match-end time)))))
-           (if zone
-               (setq zone
-                     (substring date (match-beginning zone) 
-                                (match-end zone))))
-           (if year
-               (vector year month day time zone)
-             (vector "0" "0" "0" "0" nil))
-           )
-         )
-      (t (signal 'invalid-date (list date))))))
-
-(defsubst elmo-call-func (folder func-name &rest args)
-  (let* ((spec (if (stringp folder)
-                  (elmo-folder-get-spec folder)
-                folder))
-        (type (symbol-name (car spec)))
-        (backend-str (concat "elmo-" type))
-        (backend-sym (intern backend-str)))
-    (unless (featurep backend-sym)
-      (require backend-sym))
-    (apply (intern (format "%s-%s" backend-str func-name))
-          spec
-          args)))
+(defalias 'elmo-read 'read)
 
 (defmacro elmo-set-work-buf (&rest body)
-  "Execute BODY on work buffer. Work buffer remains."
+  "Execute BODY on work buffer.  Work buffer remains."
   (` (save-excursion
        (set-buffer (get-buffer-create elmo-work-buf-name))
        (elmo-set-buffer-multibyte default-enable-multibyte-characters)
        (erase-buffer)
        (,@ body))))
 
-(defmacro elmo-match-substring (pos string from)
-  "Substring of POSth matched string of STRING. "
-  (` (substring (, string) 
-               (+ (match-beginning (, pos)) (, from))
-               (match-end (, pos)))))
-
-(defmacro elmo-match-string (pos string)
-  "Substring POSth matched string."
-  (` (substring (, string) (match-beginning (, pos)) (match-end (, pos)))))
-
-(defmacro elmo-match-buffer (pos)
-  "Substring POSth matched from the current buffer."
-  (` (buffer-substring-no-properties
-      (match-beginning (, pos)) (match-end (, pos)))))
-
 (defmacro elmo-bind-directory (dir &rest body)
   "Set current directory DIR and execute BODY."
   (` (let ((default-directory (file-name-as-directory (, dir))))
        (,@ body))))
 
-(defmacro elmo-folder-get-type (folder)
-  "Get type of FOLDER."
-  (` (and (stringp (, folder))
-         (cdr (assoc (string-to-char (, folder)) elmo-spec-alist)))))
-
 (defun elmo-object-load (filename &optional mime-charset no-err)
   "Load OBJECT from the file specified by FILENAME.
 File content is decoded with MIME-CHARSET."
@@ -244,7 +102,7 @@ File content is decoded with MIME-CHARSET."
         (elmo-set-buffer-multibyte default-enable-multibyte-characters)
         (decode-mime-charset-region (point-min) (point-max) mime-charset))
        (condition-case nil
-          (read (current-buffer)) 
+          (read (current-buffer))
         (error (unless no-err
                  (message "Warning: Loading object from %s failed."
                           filename)
@@ -263,373 +121,168 @@ File content is encoded with MIME-CHARSET."
     (if (file-writable-p filename)
        (progn
          (when mime-charset
-           ;;(elmo-set-buffer-multibyte default-enable-multibyte-characters)
+;;;        (elmo-set-buffer-multibyte default-enable-multibyte-characters)
            (encode-mime-charset-region (point-min) (point-max) mime-charset))
          (as-binary-output-file
           (write-region (point-min) (point-max) filename nil 'no-msg)))
-      (message (format "%s is not writable." filename)))))
+      (message "%s is not writable." filename))))
 
 (defun elmo-object-save (filename object &optional mime-charset)
   "Save OBJECT to the file specified by FILENAME.
 Directory of the file is created if it doesn't exist.
 File content is encoded with MIME-CHARSET."
   (elmo-set-work-buf
-   (prin1 object (current-buffer))
-   ;;(princ "\n" (current-buffer))
+   (let (print-length print-level)
+     (prin1 object (current-buffer)))
+;;;(princ "\n" (current-buffer))
    (elmo-save-buffer filename mime-charset)))
 
-(defsubst elmo-imap4-decode-folder-string (string)
-  (if elmo-imap4-use-modified-utf7
-      (utf7-decode-string string 'imap)
-    string))
-
-(defsubst elmo-imap4-encode-folder-string (string)
-  (if elmo-imap4-use-modified-utf7
-      (utf7-encode-string string 'imap)
-    string))
-
-(defun elmo-network-get-spec (folder default-server default-port default-tls)
-  (let (server port tls)
-    (if (string-match "\\(@[^@:/!]+\\)?\\(:[0-9]+\\)?\\(!*\\)$" folder)
-       (progn
-         (if (match-beginning 1)
-             (setq server (elmo-match-substring 1 folder 1))
-           (setq server default-server))
-         (if (match-beginning 2)
-             (setq port 
-                   (string-to-int (elmo-match-substring 2 folder 1)))
-           (setq port default-port))
-         (setq tls (elmo-match-string 3 folder))
-         (if (and (match-beginning 3)
-                  (> (length tls) 0))
-             (setq tls (if (= 2 (length tls)) 'starttls
-                         (string= tls "!")))
-           (setq tls default-tls))
-         (setq folder (substring folder 0 (match-beginning 0))))
-      (setq server default-server
-           port   default-port
-           tls    default-tls))
-    (cons folder (list server port tls))))
-
-(defun elmo-imap4-get-spec (folder)
-  (let ((default-user    elmo-default-imap4-user)
-       (default-server  elmo-default-imap4-server)
-       (default-port    elmo-default-imap4-port)
-       (default-tls     elmo-default-imap4-ssl)
-       spec mailbox user auth)
-    (when (string-match "\\(.*\\)@\\(.*\\)" default-server)
-      ;; case: default-imap4-server is specified like 
-      ;; "hoge%imap.server@gateway".
-      (setq default-user (elmo-match-string 1 default-server))
-      (setq default-server (elmo-match-string 2 default-server)))
-    (setq spec (elmo-network-get-spec 
-               folder default-server default-port default-tls))
-    (setq folder (car spec))
-    (when (string-match
-          "^\\(%\\)\\([^:@!]*\\)\\(:[^/!]+\\)?\\(/[^/:@!]+\\)?"
-          folder)
-      (progn
-       (setq mailbox (if (match-beginning 2) 
-                         (elmo-match-string 2 folder)
-                       elmo-default-imap4-mailbox))
-       (setq user (if (match-beginning 3)
-                      (elmo-match-substring 3 folder 1)
-                    default-user))
-       (setq auth (if (match-beginning 4)
-                      (elmo-match-substring 4 folder 1)
-                    elmo-default-imap4-authenticate-type))
-       (append (list 'imap4 
-                     (elmo-imap4-encode-folder-string mailbox)
-                     user auth)
-               (cdr spec))))))
-
-(defsubst elmo-imap4-spec-mailbox (spec)
-  (nth 1 spec))
-
-(defsubst elmo-imap4-spec-username (spec)
-  (nth 2 spec))
-
-(defsubst elmo-imap4-spec-auth (spec)
-  (nth 3 spec))
-
-(defsubst elmo-imap4-spec-hostname (spec)
-  (nth 4 spec))
-
-(defsubst elmo-imap4-spec-port (spec)
-  (nth 5 spec))
-
-(defsubst elmo-imap4-spec-ssl (spec)
-  (nth 6 spec))
-
-(defsubst elmo-imap4-spec-folder (spec) ;; obsolete
-  (nth 1 spec))
-
-(defsubst elmo-imap4-connection-get-process (conn)
-  (nth 1 conn))
-
-(defsubst elmo-imap4-connection-get-buffer (conn)
-  (nth 0 conn))
-
-(defsubst elmo-imap4-connection-get-cwf (conn)
-  (nth 2 conn))
-
-(defun elmo-nntp-get-spec (folder)
-  (let (spec group user)
-    (setq spec (elmo-network-get-spec folder
-                                     elmo-default-nntp-server
-                                     elmo-default-nntp-port
-                                     elmo-default-nntp-ssl))
-    (setq folder (car spec))
-    (when (string-match
-          "^\\(-\\)\\([^:@!]*\\)\\(:[^/!]+\\)?\\(/[^/:@!]+\\)?"
-          folder)
-      (setq group 
-           (if (match-beginning 2)
-               (elmo-match-string 2 folder)))
-      (setq user 
-           (if (match-beginning 3) 
-               (elmo-match-substring 3 folder 1)
-             elmo-default-nntp-user))
-      (append (list 'nntp group user)
-             (cdr spec)))))
-
-(defsubst elmo-nntp-spec-group (spec)
-  (nth 1 spec))
-
-(defsubst elmo-nntp-spec-username (spec)  
-  (nth 2 spec))
-
-;; future use?
-;; (defsubst elmo-nntp-spec-auth (spec))
-
-(defsubst elmo-nntp-spec-hostname (spec)
-  (nth 3 spec))
-
-(defsubst elmo-nntp-spec-port (spec)
-  (nth 4 spec))
-
-(defsubst elmo-nntp-spec-ssl (spec)
-  (nth 5 spec))
-
-(defun elmo-localdir-get-spec (folder)
-  (let (fld-name path)
-    (when (string-match
-          "^\\(\\+\\)\\(.*\\)$"
-          folder)
-      (if (eq (length (setq fld-name
-                           (elmo-match-string 2 folder))) 0)
-         (setq fld-name "")
-       )
-      (if (file-name-absolute-p fld-name)
-         (setq path (expand-file-name fld-name))
-       (setq path fld-name))
-       ;(setq path (expand-file-name fld-name
-       ;elmo-localdir-folder-path)))
-      (list (if (elmo-folder-maildir-p folder)
-               'maildir
-             'localdir) path))))
-
-(defun elmo-maildir-get-spec (folder)
-  (let (fld-name path)
-    (when (string-match
-          "^\\(\\.\\)\\(.*\\)$"
-          folder)
-      (if (eq (length (setq fld-name
-                           (elmo-match-string 2 folder))) 0)
-         (setq fld-name ""))
-      (if (file-name-absolute-p fld-name)
-         (setq path (expand-file-name fld-name))
-       (setq path fld-name))
-      (list 'maildir path))))
-
-(defun elmo-folder-maildir-p (folder)
-  (catch 'found
-    (let ((li elmo-maildir-list))
-      (while li
-       (if (string-match (car li) folder)
-           (throw 'found t))
-       (setq li (cdr li))))))
-
-(defun elmo-localnews-get-spec (folder)
-  (let (fld-name)
-    (when (string-match
-        "^\\(=\\)\\(.*\\)$"
-        folder)
-      (if (eq (length (setq fld-name
-                           (elmo-match-string 2 folder))) 0)
-         (setq fld-name "")
-       )
-      (list 'localnews 
-           (elmo-replace-in-string fld-name "\\." "/")))))
-
-(defun elmo-cache-get-spec (folder)
-  (let (fld-name)
-    (when (string-match
-        "^\\(!\\)\\(.*\\)$"
-        folder)
-      (if (eq (length (setq fld-name
-                           (elmo-match-string 2 folder))) 0)
-         (setq fld-name "")
-       )
-      (list 'cache
-           (elmo-replace-in-string fld-name "\\." "/")))))
-
-;; Archive interface by OKUNISHI Fujikazu <fuji0924@mbox.kyoto-inet.or.jp>
-(defun elmo-archive-get-spec (folder)
-  (require 'elmo-archive)
-  (let (fld-name type prefix)
-    (when (string-match
-          "^\\(\\$\\)\\([^;]*\\);?\\([^;]*\\);?\\([^;]*\\)$"
-          folder)
-      ;; Drive letter is OK!
-      (if (eq (length (setq fld-name
-                           (elmo-match-string 2 folder))) 0)
-         (setq fld-name "")
-       )
-      (if (eq (length (setq type
-                           (elmo-match-string 3 folder))) 0)
-         (setq type (symbol-name elmo-archive-default-type)))
-      (if (eq (length (setq prefix
-                           (elmo-match-string 4 folder))) 0)
-         (setq prefix ""))
-      (list 'archive fld-name (intern-soft type) prefix))))
-
-(defun elmo-pop3-get-spec (folder)
-  (let (spec user auth)
-    (setq spec (elmo-network-get-spec folder
-                                     elmo-default-pop3-server
-                                     elmo-default-pop3-port
-                                     elmo-default-pop3-ssl))
-    (setq folder (car spec))
-    (when (string-match
-          "^\\(&\\)\\([^:/!]*\\)\\(/[^/:@!]+\\)?"
-          folder)
-      (setq user (if (match-beginning 2)
-                    (elmo-match-string 2 folder)))
-      (if (eq (length user) 0)
-         (setq user elmo-default-pop3-user))
-      (setq auth (if (match-beginning 3)
-                    (elmo-match-substring 3 folder 1)
-                  elmo-default-pop3-authenticate-type))
-      (append (list 'pop3 user auth)
-             (cdr spec)))))
-
-(defsubst elmo-pop3-spec-username (spec)
-  (nth 1 spec))
-
-(defsubst elmo-pop3-spec-auth (spec)
-  (nth 2 spec))
-
-(defsubst elmo-pop3-spec-hostname (spec)
-  (nth 3 spec))
-
-(defsubst elmo-pop3-spec-port (spec)
-  (nth 4 spec))
-
-(defsubst elmo-pop3-spec-ssl (spec)
-  (nth 5 spec))
-
-(defun elmo-internal-get-spec (folder)
-  (if (string-match "\\('\\)\\([^/]*\\)/?\\(.*\\)$" folder)
-      (let* ((item (downcase (elmo-match-string 2 folder)))
-            (sym (and (> (length item) 0) (intern item))))
-       (cond ((or (null sym)
-                  (eq sym 'mark))
-              (list 'internal sym (elmo-match-string 3 folder)))
-             ((eq sym 'cache)
-              (list 'cache (elmo-match-string 3 folder)))
-             (t (error "Invalid internal folder spec"))))))
-
-(defun elmo-multi-get-spec (folder)
-  (save-match-data
-    (when (string-match
-          "^\\(\\*\\)\\(.*\\)$"
-          folder)
-      (append (list 'multi)
-             (split-string
-              (elmo-match-string 2 folder)
-              ",")))))
-
-(defun elmo-filter-get-spec (folder)
-  (save-match-data
-    (when (string-match
-          "^\\(/\\)\\(.*\\)$"
-          folder)
-      (let ((spec (elmo-match-string 2 folder))
-           filter)
-       (when (string-match "\\([^/]+\\)/" spec)
-         (setq filter (elmo-match-string 1 spec))
-         (setq spec (substring spec (match-end 0))))
-       (cond
-        ((string-match "^\\(last\\|first\\):\\(.*\\)$" filter) ; partial
-         (setq filter (vector 'partial
-                              (elmo-match-string 1 filter)
-                              (elmo-match-string 2 filter))))
-        (t
-         (setq filter (elmo-parse-search-condition filter))))
-       (list 'filter filter spec)))))
-
-(defun elmo-pipe-get-spec (folder)
-  (when (string-match "^\\(|\\)\\([^|]*\\)|\\(.*\\)$" folder)
-    (list 'pipe
-         (elmo-match-string 2 folder)
-         (elmo-match-string 3 folder))))
-
-(defun elmo-folder-get-spec (folder)
-  "return spec of folder"
-  (let ((type (elmo-folder-get-type folder)))
-    (if type
-       (funcall (intern (concat "elmo-" (symbol-name type) "-get-spec"))
-                folder)
-      (error "%s is not supported folder type" folder))))
+;;; Search Condition
+
+(defconst elmo-condition-atom-regexp "[^/ \")|&]*")
+
+(defun elmo-read-search-condition (default)
+  "Read search condition string interactively."
+  (elmo-read-search-condition-internal "Search by" default))
+
+(defun elmo-read-search-condition-internal (prompt default)
+  (let* ((completion-ignore-case t)
+        (field (completing-read
+                (format "%s (%s): " prompt default)
+                (mapcar 'list
+                        (append '("AND" "OR"
+                                  "Last" "First"
+                                  "From" "Subject" "To" "Cc" "Body"
+                                  "Since" "Before" "ToCc"
+                                  "!From" "!Subject" "!To" "!Cc" "!Body"
+                                  "!Since" "!Before" "!ToCc")
+                                elmo-msgdb-extra-fields))))
+        value)
+    (setq field (if (string= field "")
+                   (setq field default)
+                 field))
+    (cond
+     ((or (string= field "AND") (string= field "OR"))
+      (concat "("
+             (elmo-read-search-condition-internal
+              (concat field "(1) Search by") default)
+             (if (string= field "AND") "&" "|")
+             (elmo-read-search-condition-internal
+              (concat field "(2) Search by") default)
+             ")"))
+     ((string-match "Since\\|Before" field)
+      (let ((default (format-time-string "%Y-%m-%d")))
+       (setq value (completing-read
+                    (format "Value for '%s' [%s]: " field default)
+                    (mapcar (function
+                             (lambda (x)
+                               (list (format "%s" (car x)))))
+                            elmo-date-descriptions)))
+       (concat (downcase field) ":"
+               (if (equal value "") default value))))
+     (t
+      (setq value (read-from-minibuffer (format "Value for '%s': " field)))
+      (unless (string-match (concat "^" elmo-condition-atom-regexp "$")
+                           value)
+       (setq value (prin1-to-string value)))
+      (concat (downcase field) ":" value)))))
+
+(defsubst elmo-condition-parse-error ()
+  (error "Syntax error in '%s'" (buffer-string)))
 
 (defun elmo-parse-search-condition (condition)
-  (let ((terms (split-string condition "|")) ; split by OR
-       term ret-val)
-    (while terms
-      (setq term (car terms))
-      (cond 
-       ((string-match "^\\([a-zA-Z\\-]+\\)=\\(.*\\)$" term)
-       (if (save-match-data
-             (string-match "tocc" (elmo-match-string 1 term))) ;; syntax sugar
-           (setq ret-val (nconc
-                          ret-val
-                          (list (vector 'match "to"
-                                        (elmo-match-string 2 term))
-                                (vector 'match "cc"
-                                        (elmo-match-string 2 term)))))
-         (setq ret-val (cons (vector 'match 
-                                     (elmo-match-string 1 term)
-                                     (elmo-match-string 2 term))
-                             ret-val))))
-       ((string-match "^\\([a-zA-Z\\-]+\\)!=\\(.*\\)$" term)
-       (if (save-match-data
-             (string-match "tocc" (elmo-match-string 1 term))) ;; syntax sugar
-           (setq ret-val (nconc
-                          ret-val
-                          (list (vector 'unmatch "to"
-                                        (elmo-match-string 2 term))
-                                (vector 'unmatch "cc"
-                                        (elmo-match-string 2 term)))))
-         (setq ret-val (cons (vector 'unmatch 
-                                     (elmo-match-string 1 term)
-                                     (elmo-match-string 2 term))
-                             ret-val))))
-       ((string-match "^\\(since\\|before\\):\\(.*\\)$" term)
-       (setq ret-val (cons (vector 'date
-                                   (elmo-match-string 1 term)
-                                   (elmo-match-string 2 term))
-                           ret-val))))
-      (setq terms (cdr terms)))
-    ret-val))
-
-(defun elmo-multi-get-real-folder-number (folder number)
-  (let* ((spec (elmo-folder-get-spec folder))
-        (flds (cdr spec))
-        (num number)
-        (fld (nth (- (/ num elmo-multi-divide-number) 1) flds)))
-    (cons fld (% num elmo-multi-divide-number))))
-
+  "Parse CONDITION.
+Return value is a cons cell of (STRUCTURE . REST)"
+  (with-temp-buffer
+    (insert condition)
+    (goto-char (point-min))
+    (cons (elmo-condition-parse) (buffer-substring (point) (point-max)))))
+
+;; condition    ::= or-expr
+(defun elmo-condition-parse ()
+  (or (elmo-condition-parse-or-expr)
+      (elmo-condition-parse-error)))
+
+;; or-expr      ::= and-expr /
+;;                 and-expr "|" or-expr
+(defun elmo-condition-parse-or-expr ()
+  (let ((left (elmo-condition-parse-and-expr)))
+    (if (looking-at "| *")
+       (progn
+         (goto-char (match-end 0))
+         (list 'or left (elmo-condition-parse-or-expr)))
+      left)))
+
+;; and-expr     ::= primitive /
+;;                  primitive "&" and-expr
+(defun elmo-condition-parse-and-expr ()
+  (let ((left (elmo-condition-parse-primitive)))
+    (if (looking-at "& *")
+       (progn
+         (goto-char (match-end 0))
+         (list 'and left (elmo-condition-parse-and-expr)))
+      left)))
+
+;; primitive    ::= "(" expr ")" /
+;;                  ["!"] search-key SPACE* ":" SPACE* search-value
+(defun elmo-condition-parse-primitive ()
+  (cond
+   ((looking-at "( *")
+    (goto-char (match-end 0))
+    (prog1 (elmo-condition-parse)
+      (unless (looking-at ") *")
+       (elmo-condition-parse-error))
+      (goto-char (match-end 0))))
+;; search-key   ::= [A-Za-z-]+
+;;                 ;; "since" / "before" / "last" / "first" /
+;;                 ;; "body" / field-name
+   ((looking-at "\\(!\\)? *\\([A-Za-z-]+\\) *: *")
+    (goto-char (match-end 0))
+    (let ((search-key (vector
+                      (if (match-beginning 1) 'unmatch 'match)
+                      (elmo-match-buffer 2)
+                      (elmo-condition-parse-search-value))))
+      ;; syntax sugar.
+      (if (string= (aref search-key 1) "tocc")
+         (if (eq (aref search-key 0) 'match)
+             (list 'or
+                   (vector 'match "to" (aref search-key 2))
+                   (vector 'match "cc" (aref search-key 2)))
+           (list 'and
+                 (vector 'unmatch "to" (aref search-key 2))
+                 (vector 'unmatch "cc" (aref search-key 2))))
+       search-key)))))
+
+;; search-value ::= quoted / time / number / atom
+;; quoted       ::= <elisp string expression>
+;; time         ::= "yesterday" / "lastweek" / "lastmonth" / "lastyear" /
+;;                   number SPACE* "daysago" /
+;;                   number "-" month "-" number  ; ex. 10-May-2000
+;;                   number "-" number "-" number  ; ex. 2000-05-10
+;; number       ::= [0-9]+
+;; month        ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
+;;                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
+;; atom         ::= ATOM_CHARS*
+;; SPACE        ::= <ascii space character, 0x20>
+;; ATOM_CHARS   ::= <any character except specials>
+;; specials     ::= SPACE / <"> / </> / <)> / <|> / <&>
+;;                  ;; These characters should be quoted.
+(defun elmo-condition-parse-search-value ()
+  (cond
+   ((looking-at "\"")
+    (read (current-buffer)))
+   ((or (looking-at "yesterday") (looking-at "lastweek")
+       (looking-at "lastmonth") (looking-at "lastyear")
+       (looking-at "[0-9]+ *daysago")
+       (looking-at "[0-9]+-[A-Za-z]+-[0-9]+")
+       (looking-at "[0-9]+-[0-9]+-[0-9]+")
+       (looking-at "[0-9]+")
+       (looking-at elmo-condition-atom-regexp))
+    (prog1 (elmo-match-buffer 0)
+      (goto-char (match-end 0))))
+   (t (error "Syntax error '%s'" (buffer-string)))))
+
+;;;
 (defsubst elmo-buffer-replace (regexp &optional newtext)
   (goto-char (point-min))
   (while (re-search-forward regexp nil t)
@@ -647,6 +300,13 @@ File content is encoded with MIME-CHARSET."
         (replace-match ""))
        (buffer-string)))))
 
+(defsubst elmo-delete-cr-buffer ()
+  "Delete CR from buffer."
+  (save-excursion
+    (goto-char (point-min))
+    (while (search-forward "\r\n" nil t)
+      (replace-match "\n")) ))
+
 (defsubst elmo-delete-cr-get-content-type ()
   (save-excursion
     (goto-char (point-min))
@@ -668,9 +328,22 @@ File content is encoded with MIME-CHARSET."
 (defun elmo-uniq-list (lst)
   "Distractively uniqfy elements of LST."
   (let ((tmp lst))
-    (while tmp (setq tmp (setcdr tmp (and (cdr tmp) (delete (car tmp) (cdr tmp)))))))
+    (while tmp (setq tmp
+                    (setcdr tmp
+                            (and (cdr tmp)
+                                 (delete (car tmp)
+                                         (cdr tmp)))))))
   lst)
 
+(defun elmo-list-insert (list element after)
+  (let* ((match (memq after list))
+        (rest (and match (cdr (memq after list)))))
+    (if match
+       (progn
+         (setcdr match (list element))
+         (nconc list rest))
+      (nconc list (list element)))))
+
 (defun elmo-string-partial-p (string)
   (and (stringp string) (string-match "message/partial" string)))
 
@@ -693,12 +366,12 @@ File content is encoded with MIME-CHARSET."
       (elmo-set-work-buf
        (as-binary-output-file
        (insert string)
-       (write-region (point-min) (point-max) 
+       (write-region (point-min) (point-max)
                      filename nil 'no-msg))
        )))
 
 (defun elmo-max-of-list (nlist)
-  (let ((l nlist) 
+  (let ((l nlist)
        (max-num 0))
     (while l
       (if (< max-num (car l))
@@ -718,82 +391,94 @@ File content is encoded with MIME-CHARSET."
 (defun elmo-passwd-alist-load ()
   (save-excursion
     (let ((filename (expand-file-name elmo-passwd-alist-file-name
-                                      elmo-msgdb-dir))
-          (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*"))
-         insert-file-contents-pre-hook   ; To avoid autoconv-xmas...
-          insert-file-contents-post-hook 
-          ret-val)
+                                     elmo-msgdb-directory))
+         (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*"))
+         insert-file-contents-pre-hook ; To avoid autoconv-xmas...
+         insert-file-contents-post-hook
+         ret-val)
       (if (not (file-readable-p filename))
-          ()
-        (set-buffer tmp-buffer)
-        (insert-file-contents filename)
-        (setq ret-val
-              (condition-case nil
-                  (read (current-buffer)) 
-                (error nil nil))))
+         ()
+       (set-buffer tmp-buffer)
+       (insert-file-contents filename)
+       (setq ret-val
+             (condition-case nil
+                 (read (current-buffer))
+               (error nil nil))))
       (kill-buffer tmp-buffer)
       ret-val)))
 
+(defun elmo-passwd-alist-clear ()
+  "Clear password cache."
+  (interactive)
+  (setq elmo-passwd-alist nil))
+
 (defun elmo-passwd-alist-save ()
   "Save password into file."
   (interactive)
   (save-excursion
     (let ((filename (expand-file-name elmo-passwd-alist-file-name
-                                      elmo-msgdb-dir))
-          (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*")))
+                                     elmo-msgdb-directory))
+         (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*"))
+         print-length print-level)
       (set-buffer tmp-buffer)
       (erase-buffer)
       (prin1 elmo-passwd-alist tmp-buffer)
       (princ "\n" tmp-buffer)
-;      (if (and (file-exists-p filename)
-;             (not (equal 384 (file-modes filename))))
-;        (error "%s is not safe.chmod 600 %s!" filename filename))
+;;;   (if (and (file-exists-p filename)
+;;;           (not (equal 384 (file-modes filename))))
+;;;      (error "%s is not safe.chmod 600 %s!" filename filename))
       (if (file-writable-p filename)
-         (progn
-           (write-region (point-min) (point-max) 
-                         filename nil 'no-msg)
-           (set-file-modes filename 384))
-        (message (format "%s is not writable." filename)))
+         (progn
+           (write-region (point-min) (point-max)
+                         filename nil 'no-msg)
+           (set-file-modes filename 384))
+       (message "%s is not writable." filename))
       (kill-buffer tmp-buffer))))
 
-(defun elmo-get-passwd (user-at-host)
+(defun elmo-get-passwd (key)
   "Get password from password pool."
-  (let (data pass)
+  (let (pair pass)
     (if (not elmo-passwd-alist)
        (setq elmo-passwd-alist (elmo-passwd-alist-load)))
-    (setq data (assoc user-at-host elmo-passwd-alist))
-    (if data
-       (elmo-base64-decode-string (cdr data))
-      (setq pass (elmo-read-passwd (format "Password for %s: " 
-                                          user-at-host) t))
+    (setq pair (assoc key elmo-passwd-alist))
+    (if pair
+       (elmo-base64-decode-string (cdr pair))
+      (setq pass (elmo-read-passwd (format "Password for %s: "
+                                          key) t))
       (setq elmo-passwd-alist
            (append elmo-passwd-alist
-                   (list (cons user-at-host 
+                   (list (cons key
                                (elmo-base64-encode-string pass)))))
+      (if elmo-passwd-life-time
+         (run-with-timer elmo-passwd-life-time nil
+                         (` (lambda () (elmo-remove-passwd (, key))))))
       pass)))
 
-(defun elmo-remove-passwd (user-at-host)
+(defun elmo-remove-passwd (key)
   "Remove password from password pool (for failure)."
-  (setq elmo-passwd-alist
-       (delete (assoc user-at-host elmo-passwd-alist)
-               elmo-passwd-alist
-               )))
+  (let (pass-cons)
+    (if (setq pass-cons (assoc key elmo-passwd-alist))
+       (progn
+         (unwind-protect
+             (fillarray (cdr pass-cons) 0))
+         (setq elmo-passwd-alist
+               (delete pass-cons elmo-passwd-alist))))))
 
 (defmacro elmo-read-char-exclusive ()
   (cond ((featurep 'xemacs)
-         '(let ((table (quote ((backspace . ?\C-h) (delete . ?\C-?)
-                               (left . ?\C-h))))
-                event key)
-            (while (not
-                    (and
-                     (key-press-event-p (setq event (next-command-event)))
-                     (setq key (or (event-to-character event)
-                                   (cdr (assq (event-key event) table)))))))
-            key))
-        ((fboundp 'read-char-exclusive)
-         '(read-char-exclusive))
-        (t
-         '(read-char))))
+        '(let ((table (quote ((backspace . ?\C-h) (delete . ?\C-?)
+                              (left . ?\C-h))))
+               event key)
+           (while (not
+                   (and
+                    (key-press-event-p (setq event (next-command-event)))
+                    (setq key (or (event-to-character event)
+                                  (cdr (assq (event-key event) table)))))))
+           key))
+       ((fboundp 'read-char-exclusive)
+        '(read-char-exclusive))
+       (t
+        '(read-char))))
 
 (defun elmo-read-passwd (prompt &optional stars)
   "Read a single line of text from user without echoing, and return it."
@@ -832,49 +517,6 @@ File content is encoded with MIME-CHARSET."
       (message "")
       ans)))
 
-;; from subr.el
-(defun elmo-replace-in-string (str regexp newtext &optional literal)
-  "Replaces all matches in STR for REGEXP with NEWTEXT string,
- and returns the new string.
-Optional LITERAL non-nil means do a literal replacement.
-Otherwise treat \\ in NEWTEXT string as special:
-  \\& means substitute original matched text,
-  \\N means substitute match for \(...\) number N,
-  \\\\ means insert one \\."
-  (let ((rtn-str "")
-       (start 0)
-       (special)
-       match prev-start)
-    (while (setq match (string-match regexp str start))
-      (setq prev-start start
-           start (match-end 0)
-           rtn-str
-           (concat
-             rtn-str
-             (substring str prev-start match)
-             (cond (literal newtext)
-                   (t (mapconcat
-                       (function
-                        (lambda (c)
-                          (if special
-                              (progn
-                                (setq special nil)
-                                (cond ((eq c ?\\) "\\")
-                                      ((eq c ?&)
-                                       (elmo-match-string 0 str))
-                                      ((and (>= c ?0) (<= c ?9))
-                                       (if (> c (+ ?0 (length
-                                                       (match-data))))
-                                       ; Invalid match num
-                                           (error "Invalid match num: %c" c)
-                                         (setq c (- c ?0))
-                                         (elmo-match-string c str)))
-                                      (t (char-to-string c))))
-                            (if (eq c ?\\) (progn (setq special t) nil)
-                              (char-to-string c)))))
-                       newtext ""))))))
-    (concat rtn-str (substring str start))))
-
 (defun elmo-string-to-list (string)
   (elmo-set-work-buf
    (insert string)
@@ -885,6 +527,31 @@ Otherwise treat \\ in NEWTEXT string as special:
    (goto-char (point-min))
    (read (current-buffer))))
 
+(defun elmo-list-to-string (list)
+  (let ((tlist list)
+       str)
+    (if (listp tlist)
+       (progn
+         (setq str "(")
+         (while (car tlist)
+           (setq str
+                 (concat str
+                         (if (symbolp (car tlist))
+                             (symbol-name (car tlist))
+                           (car tlist))))
+           (if (cdr tlist)
+               (setq str
+                     (concat str " ")))
+           (setq tlist (cdr tlist)))
+         (setq str
+               (concat str ")")))
+      (setq str
+           (if (symbolp tlist)
+               (symbol-name tlist)
+             tlist)))
+    str))
+
+
 (defun elmo-plug-on-by-servers (alist &optional servers)
   (let ((server-list (or servers elmo-plug-on-servers)))
     (catch 'plugged
@@ -903,23 +570,27 @@ Otherwise treat \\ in NEWTEXT string as special:
       (setq alist (cdr alist)))
     (elmo-plug-on-by-servers alist other-servers)))
 
-(defun elmo-plugged-p (&optional server port alist label-exp)
+(defun elmo-plugged-p (&optional server port stream-type alist label-exp)
   (let ((alist (or alist elmo-plugged-alist))
        plugged-info)
     (cond ((and (not port) (not server))
           (cond ((eq elmo-plugged-condition 'one)
-                 (catch 'plugged
-                   (while alist
-                     (if (nth 2 (car alist))
-                         (throw 'plugged t))
-                     (setq alist (cdr alist)))))
+                 (if alist
+                     (catch 'plugged
+                       (while alist
+                         (if (nth 2 (car alist))
+                             (throw 'plugged t))
+                         (setq alist (cdr alist))))
+                   elmo-plugged))
                 ((eq elmo-plugged-condition 'all)
-                 (catch 'plugged
-                   (while alist
-                     (if (not (nth 2 (car alist)))
-                         (throw 'plugged nil))
-                     (setq alist (cdr alist)))
-                   t))
+                 (if alist
+                     (catch 'plugged
+                       (while alist
+                         (if (not (nth 2 (car alist)))
+                             (throw 'plugged nil))
+                         (setq alist (cdr alist)))
+                       t)
+                   elmo-plugged))
                 ((functionp elmo-plugged-condition)
                  (funcall elmo-plugged-condition alist))
                 (t ;; independent
@@ -932,11 +603,12 @@ Otherwise treat \\ in NEWTEXT string as special:
                     (throw 'plugged t)))
               (setq alist (cdr alist)))))
          (t
-          (setq plugged-info (assoc (cons server port) alist))
+          (setq plugged-info (assoc (list server port stream-type) alist))
           (if (not plugged-info)
               ;; add elmo-plugged-alist automatically
               (progn
-                (elmo-set-plugged elmo-plugged server port nil nil label-exp)
+                (elmo-set-plugged elmo-plugged server port stream-type
+                                  nil nil nil label-exp)
                 elmo-plugged)
             (if (and elmo-auto-change-plugged
                      (> elmo-auto-change-plugged 0)
@@ -946,7 +618,7 @@ Otherwise treat \\ in NEWTEXT string as special:
                 t
               (nth 2 plugged-info)))))))
 
-(defun elmo-set-plugged (plugged &optional server port time
+(defun elmo-set-plugged (plugged &optional server port stream-type time
                                 alist label-exp add)
   (let ((alist (or alist elmo-plugged-alist))
        label plugged-info)
@@ -964,7 +636,7 @@ Otherwise treat \\ in NEWTEXT string as special:
             (setq alist (cdr alist))))
          (t
           ;; set plugged one port of server
-          (setq plugged-info (assoc (cons server port) alist))
+          (setq plugged-info (assoc (list server port stream-type) alist))
           (setq label (if label-exp
                           (eval label-exp)
                         (nth 1 plugged-info)))
@@ -974,15 +646,17 @@ Otherwise treat \\ in NEWTEXT string as special:
                 (setcdr plugged-info (list label plugged time)))
             (setq alist
                   (setq elmo-plugged-alist
-                        (nconc elmo-plugged-alist
-                               (list
-                                (list (cons server port) label plugged time))))))))
+                        (nconc
+                         elmo-plugged-alist
+                         (list
+                          (list (list server port stream-type)
+                                label plugged time))))))))
     alist))
 
 (defun elmo-delete-plugged (&optional server port alist)
   (let* ((alist (or alist elmo-plugged-alist))
         (alist2 alist))
-    (cond ((and (not port) (not server))    
+    (cond ((and (not port) (not server))
           (setq alist nil))
          ((not port)
           ;; delete plugged all port of server
@@ -993,16 +667,16 @@ Otherwise treat \\ in NEWTEXT string as special:
          (t
           ;; delete plugged one port of server
           (setq alist
-                (delete (assoc (cons server port) alist)) alist)))
+                (delete (assoc (cons server port) alist) alist))))
     alist))
 
 (defun elmo-disk-usage (path)
   "Get disk usage (bytes) in PATH."
-  (let ((file-attr 
+  (let ((file-attr
         (condition-case () (file-attributes path) (error nil))))
     (if file-attr
        (if (nth 0 file-attr) ; directory
-           (let ((files (condition-case () 
+           (let ((files (condition-case ()
                             (directory-files path t "^[^\\.]")
                           (error nil)))
                  (result 0.0))
@@ -1011,10 +685,11 @@ Otherwise treat \\ in NEWTEXT string as special:
                (setq result (+ result (or (elmo-disk-usage (car files)) 0)))
                (setq files (cdr files)))
              result)
-         (float (nth 7 file-attr))))))
+         (float (nth 7 file-attr)))
+      0)))
 
 (defun elmo-get-last-accessed-time (path &optional dir)
-  "Returns last accessed time."
+  "Return the last accessed time of PATH."
   (let ((last-accessed (nth 4 (file-attributes (or (and dir
                                                        (expand-file-name
                                                         path dir))
@@ -1025,7 +700,7 @@ Otherwise treat \\ in NEWTEXT string as special:
       0)))
 
 (defun elmo-get-last-modification-time (path &optional dir)
-  "Returns last accessed time."
+  "Return the last accessed time of PATH."
   (let ((last-modified (nth 5 (file-attributes (or (and dir
                                                        (expand-file-name
                                                         path dir))
@@ -1033,18 +708,18 @@ Otherwise treat \\ in NEWTEXT string as special:
     (setq last-modified (+ (* (nth 0 last-modified)
                              (float 65536)) (nth 1 last-modified)))))
 
-(defun elmo-make-directory (path)
-  "create directory recursively."
+(defun elmo-make-directory (path &optional mode)
+  "Create directory recursively."
   (let ((parent (directory-file-name (file-name-directory path))))
     (if (null (file-directory-p parent))
        (elmo-make-directory parent))
     (make-directory path)
-    (if (string= path (expand-file-name elmo-msgdb-dir))
-       (set-file-modes path 448) ; 700
-      )))
+    (set-file-modes path (or mode
+                            (+ (* 64 7) (* 8 0) 0))))) ; chmod 0700
 
 (defun elmo-delete-directory (path &optional no-hierarchy)
-  "delete directory recursively."
+  "Delete directory recursively."
+  (if (stringp path) ; nil is not permitted.
   (let ((dirent (directory-files path))
        relpath abspath hierarchy)
     (while dirent
@@ -1058,7 +733,19 @@ Otherwise treat \\ in NEWTEXT string as special:
              (elmo-delete-directory abspath no-hierarchy))
          (delete-file abspath))))
     (unless hierarchy
-      (delete-directory path))))
+      (delete-directory path)))))
+
+(defun elmo-delete-match-files (path regexp &optional remove-if-empty)
+  "Delete directory files specified by PATH.
+If optional REMOVE-IF-EMPTY is non-nil, delete directory itself if
+the directory becomes empty after deletion."
+  (when (stringp path) ; nil is not permitted.
+    (dolist (file (directory-files path t regexp))
+      (delete-file file))
+    (if remove-if-empty
+       (ignore-errors 
+         (delete-directory path) ; should be removed if empty.
+         ))))
 
 (defun elmo-list-filter (l1 l2)
   "L1 is filter."
@@ -1070,38 +757,6 @@ Otherwise treat \\ in NEWTEXT string as special:
       ;; filter is nil
       l2)))
 
-(defun elmo-folder-local-p (folder)
-  "Return whether FOLDER is a local folder or not."
-  (let ((type (elmo-folder-get-type folder)))
-    (memq type '(localdir localnews archive maildir internal cache))))
-
-(defun elmo-folder-writable-p (folder)
-  (let ((type (elmo-folder-get-type folder)))
-    (memq type '(imap4 localdir archive))))
-
-(defun elmo-multi-get-intlist-list (numlist &optional as-is)
-  (let ((numbers (sort numlist '<))
-       (cur-number 0)
-       one-list int-list-list)
-    (while numbers
-      (setq cur-number (+ cur-number 1))
-      (setq one-list nil)
-      (while (and numbers 
-                 (eq 0
-                     (/ (- (car numbers)
-                           (* elmo-multi-divide-number cur-number))
-                        elmo-multi-divide-number)))
-       (setq one-list (nconc
-                       one-list 
-                       (list 
-                        (if as-is
-                            (car numbers)
-                          (% (car numbers)
-                             (* elmo-multi-divide-number cur-number))))))
-       (setq numbers (cdr numbers)))
-      (setq int-list-list (nconc int-list-list (list one-list))))
-    int-list-list))
-
 (defsubst elmo-list-delete-if-smaller (list number)
   (let ((ret-val (copy-sequence list)))
     (while list
@@ -1111,7 +766,7 @@ Otherwise treat \\ in NEWTEXT string as special:
     ret-val))
 
 (defun elmo-list-diff (list1 list2 &optional mes)
-  (if mes 
+  (if mes
       (message mes))
   (let ((clist1 (copy-sequence list1))
        (clist2 (copy-sequence list2)))
@@ -1122,11 +777,11 @@ Otherwise treat \\ in NEWTEXT string as special:
       (setq clist2 (delq (car list1) clist2))
       (setq list1 (cdr list1)))
     (if mes
-       (message (concat mes "done.")))
+       (message (concat mes "done")))
     (list clist1 clist2)))
 
 (defun elmo-list-bigger-diff (list1 list2 &optional mes)
-  "Returns a list (- +). + is bigger than max of LIST1, in LIST2"
+  "Returns a list (- +). + is bigger than max of LIST1, in LIST2."
   (if (null list2)
       (cons list1  nil)
     (let* ((l1 list1)
@@ -1153,57 +808,8 @@ Otherwise treat \\ in NEWTEXT string as special:
        (setq l1 (cdr l1)))
       (cons diff1 (list l2)))))
 
-(defun elmo-multi-list-bigger-diff (list1 list2 &optional mes)
-  (let ((list1-list (elmo-multi-get-intlist-list list1 t))
-       (list2-list (elmo-multi-get-intlist-list list2 t))
-       result
-       dels news)
-    (while (or list1-list list2-list)
-      (setq result (elmo-list-bigger-diff (car list1-list) (car list2-list) 
-                                         mes))
-      (setq dels (append dels (car result)))
-      (setq news (append news (cadr result)))
-      (setq list1-list (cdr list1-list))
-      (setq list2-list (cdr list2-list)))
-    (cons dels (list news))))
-
-(defvar elmo-imap4-name-space-regexp-list nil)
-(defun elmo-imap4-identical-name-space-p (fld1 fld2)
-  ;; only on UW?
-  (if (or (eq (string-to-char fld1) ?#)
-         (eq (string-to-char fld2) ?#))
-      (string= (car (split-string fld1 "/"))
-              (car (split-string fld2 "/")))
-    t))
-
-(defun elmo-folder-identical-system-p (folder1 folder2)
-  "folder1 and folder2 should be real folder (not virtual)."
-  (cond ((eq (elmo-folder-get-type folder1) 'imap4)
-        (let ((spec1 (elmo-folder-get-spec folder1))
-              (spec2 (elmo-folder-get-spec folder2)))
-          (and (elmo-imap4-identical-name-space-p
-                (nth 1 spec1) (nth 1 spec2))
-               (string= (elmo-imap4-spec-hostname spec1)
-                        (elmo-imap4-spec-hostname spec2))    ; hostname
-               (string= (elmo-imap4-spec-username spec1)
-                        (elmo-imap4-spec-username spec2))))) ; username
-       (t
-        (elmo-folder-direct-copy-p folder1 folder2))))
-
-(defconst elmo-folder-direct-copy-alist
-  '((localdir  . (localdir localnews archive))
-    (maildir   . (maildir  localdir localnews archive))
-    (localnews . (localdir localnews archive))
-    (archive   . (localdir localnews archive))
-    (cache     . (localdir localnews archive))))
-
-(defun elmo-folder-direct-copy-p (src-folder dst-folder)
-  (let ((src-type (car (elmo-folder-get-spec src-folder)))
-       (dst-type (car (elmo-folder-get-spec dst-folder)))
-       dst-copy-type)
-    (and (setq dst-copy-type
-              (cdr (assq src-type elmo-folder-direct-copy-alist)))
-        (memq dst-type dst-copy-type))))
+(defmacro elmo-filter-condition-p (filter)
+  `(or (vectorp ,filter) (consp ,filter)))
 
 (defmacro elmo-filter-type (filter)
   (` (aref (, filter) 0)))
@@ -1214,109 +820,184 @@ Otherwise treat \\ in NEWTEXT string as special:
 (defmacro elmo-filter-value (filter)
   (` (aref (, filter) 2)))
 
-(defsubst elmo-buffer-field-condition-match (condition)
-  (let (term)
-    (catch 'done
-      (while condition
-       (goto-char (point-min))
-       (setq term (car condition))
-       (cond 
-        ((and (eq (elmo-filter-type term) 'date)
-              (string= (elmo-filter-key term) "since"))
-         (let ((date (elmo-date-get-datevec (elmo-filter-value term))))
-           (if (string<
-                (timezone-make-sortable-date (aref date 0) 
-                                             (aref date 1)
-                                             (aref date 2)
-                                             (timezone-make-time-string
-                                              (aref date 3) 
-                                              (aref date 4) 
-                                              (aref date 5)))
-                (timezone-make-date-sortable (std11-field-body "date")))
-               (throw 'done t))))
-        ((and (eq (elmo-filter-type term) 'date)
-              (string= (elmo-filter-key term) "before"))
-         (let ((date (elmo-date-get-datevec (elmo-filter-value term))))
-           (if (string<
-                (timezone-make-date-sortable (std11-field-body "date"))
-                (timezone-make-sortable-date (aref date 0) 
-                                             (aref date 1)
-                                             (aref date 2)
-                                             (timezone-make-time-string
-                                              (aref date 3) 
-                                              (aref date 4) 
-                                              (aref date 5))))
-               (throw 'done t))))
-        ((eq (elmo-filter-type term) 'match)
-         (if (string= "body" (elmo-filter-key term))
-             (progn
-               (re-search-forward "^$" nil t)     ; goto body
-               (if (search-forward (elmo-filter-value term) nil t)
-                   (throw 'done t)))
-           (let ((fval (eword-decode-string
-                        (or (std11-field-body (elmo-filter-key term)) ""))))
-             (if (and fval (string-match (elmo-filter-value term)
-                                         fval))
-                 (throw 'done t)))))
-        ((eq (elmo-filter-type term) 'unmatch)
-         (if (string= "body" (elmo-filter-key term))
-             (progn
-               (re-search-forward "^$" nil t)     ; goto body
-               (if (not (search-forward (elmo-filter-value term) nil t))
-                   (throw 'done t)))
-           (let ((fval (eword-decode-string
-                        (or (std11-field-body (elmo-filter-key term)) ""))))
-             (if fval
-                 (if (not (string-match (elmo-filter-value term)
-                                        fval))
-                     (throw 'done t))
-               (throw 'done t)))))) ; OK?
-       (setq condition (cdr condition)))
-      (throw 'done nil))))
-
-(defsubst elmo-file-field-condition-match (file condition)
-  (elmo-set-work-buf
-   (as-binary-input-file
-    (insert-file-contents file))
-   (elmo-set-buffer-multibyte default-enable-multibyte-characters)
-   (decode-mime-charset-region (point-min)(point-max) elmo-mime-charset)
-   (elmo-buffer-field-condition-match condition)))
-
-(defun elmo-cross-device-link-error-p (err)
-  (let ((errobj err)
-       cur)
-    (catch 'done
-      (while errobj
-       (if (and (stringp (setq cur (car errobj)))
-                (or (string-match "cross-device" cur)
-                    (string-match "operation not supported" cur)))
-           (throw 'done t))
-       (setq errobj (cdr errobj)))
-      nil)))
+(defsubst elmo-buffer-field-primitive-condition-match (condition
+                                                      number
+                                                      number-list)
+  (let (result)
+    (goto-char (point-min))
+    (cond
+     ((string= (elmo-filter-key condition) "last")
+      (setq result (<= (length (memq number number-list))
+                      (string-to-int (elmo-filter-value condition)))))
+     ((string= (elmo-filter-key condition) "first")
+      (setq result (< (- (length number-list)
+                        (length (memq number number-list)))
+                     (string-to-int (elmo-filter-value condition)))))
+     ((string= (elmo-filter-key condition) "since")
+      (let ((field-date (elmo-date-make-sortable-string
+                        (timezone-fix-time
+                         (std11-field-body "date")
+                         (current-time-zone) nil)))
+           (specified-date (elmo-date-make-sortable-string
+                            (elmo-date-get-datevec
+                             (elmo-filter-value condition)))))
+       (setq result
+             (or (string= field-date specified-date)
+                 (string< specified-date field-date)))))
+     ((string= (elmo-filter-key condition) "before")
+      (setq result
+           (string<
+            (elmo-date-make-sortable-string
+             (timezone-fix-time
+              (std11-field-body "date")
+              (current-time-zone) nil))
+            (elmo-date-make-sortable-string
+             (elmo-date-get-datevec
+              (elmo-filter-value condition))))))
+     ((string= (elmo-filter-key condition) "body")
+      (and (re-search-forward "^$" nil t)         ; goto body
+          (setq result (search-forward (elmo-filter-value condition)
+                                       nil t))))
+     (t
+      (let ((fval (std11-field-body (elmo-filter-key condition))))
+       (if (eq (length fval) 0) (setq fval nil))
+       (if fval (setq fval (eword-decode-string fval)))
+       (setq result (and fval (string-match
+                               (elmo-filter-value condition) fval))))))
+    (if (eq (elmo-filter-type condition) 'unmatch)
+       (setq result (not result)))
+    result))
+
+(defun elmo-condition-in-msgdb-p-internal (condition fields)
+  (cond
+   ((vectorp condition)
+    (if (not (member (elmo-filter-key condition) fields))
+       (throw 'found t)))
+   ((or (eq (car condition) 'and)
+       (eq (car condition) 'or))
+    (elmo-condition-in-msgdb-p-internal (nth 1 condition) fields)
+    (elmo-condition-in-msgdb-p-internal (nth 2 condition) fields))))
+
+(defun elmo-condition-in-msgdb-p (condition)
+  (not (catch 'found
+        (elmo-condition-in-msgdb-p-internal condition
+                                            (append
+                                             elmo-msgdb-extra-fields
+                                             '("last" "first" "from"
+                                               "subject" "to" "cc" "since"
+                                               "before"))))))
+
+(defun elmo-buffer-field-condition-match (condition number number-list)
+  (cond
+   ((vectorp condition)
+    (elmo-buffer-field-primitive-condition-match
+     condition number number-list))
+   ((eq (car condition) 'and)
+    (and (elmo-buffer-field-condition-match
+         (nth 1 condition) number number-list)
+        (elmo-buffer-field-condition-match
+         (nth 2 condition) number number-list)))
+   ((eq (car condition) 'or)
+    (or (elmo-buffer-field-condition-match
+        (nth 1 condition) number number-list)
+       (elmo-buffer-field-condition-match
+        (nth 2 condition) number number-list)))))
+
+(defsubst elmo-file-field-primitive-condition-match (file
+                                                    condition
+                                                    number
+                                                    number-list)
+  (let (result)
+    (goto-char (point-min))
+    (cond
+     ((string= (elmo-filter-key condition) "last")
+      (setq result (<= (length (memq number number-list))
+                      (string-to-int (elmo-filter-value condition))))
+      (if (eq (elmo-filter-type condition) 'unmatch)
+         (setq result (not result))))
+     ((string= (elmo-filter-key condition) "first")
+      (setq result (< (- (length number-list)
+                        (length (memq number number-list)))
+                     (string-to-int (elmo-filter-value condition))))
+      (if (eq (elmo-filter-type condition) 'unmatch)
+         (setq result (not result))))
+     (t
+      (elmo-set-work-buf
+       (as-binary-input-file (insert-file-contents file))
+       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+       ;; Should consider charset?
+       (decode-mime-charset-region (point-min)(point-max) elmo-mime-charset)
+       (setq result
+            (elmo-buffer-field-primitive-condition-match
+             condition number number-list)))))
+    result))
+
+(defun elmo-file-field-condition-match (file condition number number-list)
+  (cond
+   ((vectorp condition)
+    (elmo-file-field-primitive-condition-match
+     file condition number number-list))
+   ((eq (car condition) 'and)
+    (and (elmo-file-field-condition-match
+         file (nth 1 condition) number number-list)
+        (elmo-file-field-condition-match
+         file (nth 2 condition) number number-list)))
+   ((eq (car condition) 'or)
+    (or (elmo-file-field-condition-match
+        file (nth 1 condition) number number-list)
+       (elmo-file-field-condition-match
+        file (nth 2 condition) number number-list)))))
 
 (defmacro elmo-get-hash-val (string hashtable)
-  (let ((sym (list 'intern-soft string hashtable)))
-    (list 'if (list 'boundp sym)
-       (list 'symbol-value sym))))
+  `(and (stringp ,string)
+       (let ((sym (intern-soft ,string ,hashtable)))
+         (if (boundp sym)
+             (symbol-value sym)))))
 
 (defmacro elmo-set-hash-val (string value hashtable)
   (list 'set (list 'intern string hashtable) value))
 
-;; Make a hash table (default and minimum size is 1024).
+(defmacro elmo-clear-hash-val (string hashtable)
+  (static-if (fboundp 'unintern)
+      (list 'unintern string hashtable)
+    (list 'makunbound (list 'intern string hashtable))))
+
+(defmacro elmo-unintern (string)
+  "`unintern' symbol named STRING,  When can use `unintern'.
+Emacs 19.28 or earlier does not have `unintern'."
+  (static-if (fboundp 'unintern)
+      (list 'unintern string)))
+
 (defun elmo-make-hash (&optional hashsize)
-  (make-vector (if hashsize (max (elmo-create-hash-size hashsize) 1024) 1024) 0))
+  "Make a new hash table which have HASHSIZE size."
+  (make-vector
+   (if hashsize
+       (max
+       ;; Prime numbers as lengths tend to result in good
+       ;; hashing; lengths one less than a power of two are
+       ;; also good.
+       (min
+        (let ((i 1))
+          (while (< (- i 1) hashsize)
+            (setq i (* 2 i)))
+          (- i 1))
+        elmo-hash-maximum-size)
+       elmo-hash-minimum-size)
+     elmo-hash-minimum-size)
+   0))
 
 (defsubst elmo-mime-string (string)
-  "Normalize MIME encoded string."
-    (and string
-        (let (str)
-          (elmo-set-work-buf
-           (elmo-set-buffer-multibyte default-enable-multibyte-characters)
-           (setq str (eword-decode-string
-                      (decode-mime-charset-string string elmo-mime-charset)))
-           (setq str (encode-mime-charset-string str elmo-mime-charset))
-           (elmo-set-buffer-multibyte nil)
-           str))))
+  "Normalize MIME encoded STRING."
+  (and string
+       (elmo-set-work-buf
+       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+       (setq string
+             (encode-mime-charset-string
+              (eword-decode-and-unfold-unstructured-field-body
+               string)
+              elmo-mime-charset))
+       (elmo-set-buffer-multibyte nil)
+       string)))
 
 (defsubst elmo-collect-field (beg end downcase-field-name)
   (save-excursion
@@ -1353,12 +1034,6 @@ Otherwise treat \\ in NEWTEXT string as special:
            (setq dest (cons (cons name body) dest))))
       dest)))
 
-(defun elmo-create-hash-size (min)
-  (let ((i 1))
-    (while (< i min)
-      (setq i (* 2 i)))
-    i))
-
 (defun elmo-safe-filename (folder)
   (elmo-replace-in-string
    (elmo-replace-in-string
@@ -1366,69 +1041,60 @@ Otherwise treat \\ in NEWTEXT string as special:
     ":" "__")
    "|" "_or_"))
 
-(defvar elmo-msgid-replace-chars nil)
+(defvar elmo-filename-replace-chars nil)
 
-(defsubst elmo-replace-msgid-as-filename (msgid)
-  "Replace message-id string as filename." 
+(defsubst elmo-replace-string-as-filename (msgid)
+  "Replace string as filename."
   (setq msgid (elmo-replace-in-string msgid " " "  "))
-  (if (null elmo-msgid-replace-chars)
-      (setq elmo-msgid-replace-chars 
-           (regexp-quote (mapconcat 
-                          'car elmo-msgid-replace-string-alist ""))))
-  (while (string-match (concat "[" elmo-msgid-replace-chars "]")
+  (if (null elmo-filename-replace-chars)
+      (setq elmo-filename-replace-chars
+           (regexp-quote (mapconcat
+                          'car elmo-filename-replace-string-alist ""))))
+  (while (string-match (concat "[" elmo-filename-replace-chars "]")
                       msgid)
-    (setq msgid (concat 
+    (setq msgid (concat
                 (substring msgid 0 (match-beginning 0))
-                (cdr (assoc 
-                      (substring msgid 
+                (cdr (assoc
+                      (substring msgid
                                  (match-beginning 0) (match-end 0))
-                      elmo-msgid-replace-string-alist))
+                      elmo-filename-replace-string-alist))
                 (substring msgid (match-end 0)))))
   msgid)
 
-(defsubst elmo-recover-msgid-from-filename (filename)
-  "Recover Message-ID from filename."
+(defsubst elmo-recover-string-from-filename (filename)
+  "Recover string from FILENAME."
   (let (tmp result)
     (while (string-match " " filename)
-      (setq tmp (substring filename 
+      (setq tmp (substring filename
                           (match-beginning 0)
                           (+ (match-end 0) 1)))
       (if (string= tmp "  ")
          (setq tmp " ")
-       (setq tmp (car (rassoc tmp 
-                              elmo-msgid-replace-string-alist))))
+       (setq tmp (car (rassoc tmp
+                              elmo-filename-replace-string-alist))))
       (setq result
-           (concat result 
+           (concat result
                    (substring filename 0 (match-beginning 0))
                    tmp))
       (setq filename (substring filename (+ (match-end 0) 1))))
     (concat result filename)))
 
-(defsubst elmo-copy-file (src dst)
+(defsubst elmo-copy-file (src dst &optional ok-if-already-exists)
   (condition-case err
-      (elmo-add-name-to-file src dst t)
-    (error (if (elmo-cross-device-link-error-p err)
-              (copy-file src dst t)
-            (error "copy file failed")))))
-
-(defmacro elmo-buffer-exists-p (buffer)
-  (` (when (, buffer)
-       (funcall (if (stringp (, buffer)) 'get-buffer 'buffer-name)
-               (, buffer)))))
-
-(defmacro elmo-kill-buffer (buffer)
-  (` (when (elmo-buffer-exists-p (, buffer))
-       (kill-buffer (, buffer)))))
-
-(defun elmo-delete-lists (keys list)
-  "Delete all entries in LIST that equal to KEYS."
-  (while keys
-    (setq list (delete (car keys) list))
-    (setq keys (cdr keys)))
-  list)
+      (elmo-add-name-to-file src dst ok-if-already-exists)
+    (error (copy-file src dst ok-if-already-exists t))))
+
+(defsubst elmo-buffer-exists-p (buffer)
+  (if (bufferp buffer)
+      (buffer-live-p buffer)
+    (get-buffer buffer)))
+
+(defsubst elmo-kill-buffer (buffer)
+  (when (elmo-buffer-exists-p buffer)
+    (kill-buffer buffer)))
 
 (defun elmo-delete-if (pred lst)
-  "Returns new list contains items which don't satisfy PRED in LST."
+  "Return new list contain items which don't satisfy PRED in LST."
   (let (result)
     (while lst
       (unless (funcall pred (car lst))
@@ -1436,15 +1102,20 @@ Otherwise treat \\ in NEWTEXT string as special:
       (setq lst (cdr lst)))
     result))
 
-(defun elmo-list-delete (list1 list2)
-  "Any element of list1 is deleted from list2."
+(defun elmo-list-delete (list1 list2 &optional delete-function)
+  "Delete by side effect any occurrences equal to elements of LIST1 from LIST2.
+Return the modified LIST2.  Deletion is done with `delete'.
+Write `(setq foo (elmo-list-delete bar foo))' to be sure of changing
+the value of `foo'.
+If optional DELETE-FUNCTION is speficied, it is used as delete procedure."
+  (setq delete-function (or delete-function 'delete))
   (while list1
-    (setq list2 (delete (car list1) list2))
-    (setq list1 (cdr list1)))  
+    (setq list2 (funcall delete-function (car list1) list2))
+    (setq list1 (cdr list1)))
   list2)
 
 (defun elmo-list-member (list1 list2)
-  "If any element of list1 is member of list2, returns t."
+  "If any element of LIST1 is member of LIST2, return t."
   (catch 'done
     (while list1
       (if (member (car list1) list2)
@@ -1462,30 +1133,92 @@ Otherwise treat \\ in NEWTEXT string as special:
 (if (fboundp 'display-error)
     (defalias 'elmo-display-error 'display-error)
   (defun elmo-display-error (error-object stream)
-    "a tiny function to display error-object to the stream."
+    "A tiny function to display ERROR-OBJECT to the STREAM."
     (let ((first t)
          (errobj error-object)
          err-mes)
       (while errobj
-       (setq err-mes (concat err-mes (format 
+       (setq err-mes (concat err-mes (format
                                       (if (stringp (car errobj))
                                           "%s"
-                                        (if (boundp 'nemacs-version)
-                                            "%s"
-                                          "%S")) (car errobj))))
+                                        "%S")
+                                      (car errobj))))
        (setq errobj (cdr errobj))
        (if errobj (setq err-mes (concat err-mes (if first ": " ", "))))
        (setq first nil))
       (princ err-mes stream))))
 
-(if (fboundp 'lprogress-display)
-    (defalias 'elmo-display-progress 'lprogress-display)
-  (defun elmo-display-progress (label format &optional value &rest args)
-    "Print a progress message."
-    (if (and (null format) (null args))
-       (message nil)
-      (apply (function message) (concat format " %d%%")
-            (nconc args (list value))))))
+(if (fboundp 'define-error)
+    (defalias 'elmo-define-error 'define-error)
+  (defun elmo-define-error (error doc &optional parents)
+    (or parents
+       (setq parents 'error))
+    (let ((conds (get parents 'error-conditions)))
+      (or conds
+         (error "Not an error symbol: %s" error))
+      (setplist error
+               (list 'error-message doc
+                     'error-conditions (cons error conds))))))
+
+(cond ((fboundp 'progress-feedback-with-label)
+       (defalias 'elmo-display-progress 'progress-feedback-with-label))
+      ((fboundp 'lprogress-display)
+       (defalias 'elmo-display-progress 'lprogress-display))
+      (t
+       (defun elmo-display-progress (label format &optional value &rest args)
+        "Print a progress message."
+        (if (and (null format) (null args))
+            (message nil)
+          (apply (function message) (concat format " %d%%")
+                 (nconc args (list value)))))))
+
+(defvar elmo-progress-counter-alist nil)
+
+(defmacro elmo-progress-counter-value (counter)
+  (` (aref (cdr (, counter)) 0)))
+
+(defmacro elmo-progress-counter-all-value (counter)
+  (` (aref (cdr (, counter)) 1)))
+
+(defmacro elmo-progress-counter-format (counter)
+  (` (aref (cdr (, counter)) 2)))
+
+(defmacro elmo-progress-counter-set-value (counter value)
+  (` (aset (cdr (, counter)) 0 (, value))))
+
+(defun elmo-progress-set (label all-value &optional format)
+  (unless (assq label elmo-progress-counter-alist)
+    (setq elmo-progress-counter-alist
+         (cons (cons label (vector 0 all-value (or format "")))
+               elmo-progress-counter-alist))))
+
+(defun elmo-progress-clear (label)
+  (let ((counter (assq label elmo-progress-counter-alist)))
+    (when counter
+      (elmo-display-progress label
+                            (elmo-progress-counter-format counter)
+                            100)
+      (setq elmo-progress-counter-alist
+           (delq counter elmo-progress-counter-alist)))))
+
+(defun elmo-progress-notify (label &optional value op &rest args)
+  (let ((counter (assq label elmo-progress-counter-alist)))
+    (when counter
+      (let* ((value (or value 1))
+            (cur-value (elmo-progress-counter-value counter))
+            (all-value (elmo-progress-counter-all-value counter))
+            (new-value (if (eq op 'set) value (+ cur-value value)))
+            (cur-rate (/ (* cur-value 100) all-value))
+            (new-rate (/ (* new-value 100) all-value)))
+       (elmo-progress-counter-set-value counter new-value)
+       (unless (= cur-rate new-rate)
+         (apply 'elmo-display-progress
+                label
+                (elmo-progress-counter-format counter)
+                new-rate
+                args))
+       (when (>= new-rate 100)
+         (elmo-progress-clear label))))))
 
 (defun elmo-time-expire (before-time diff-time)
   (let* ((current (current-time))
@@ -1498,43 +1231,33 @@ Otherwise treat \\ in NEWTEXT string as special:
     (and (eq (car diff) 0)
         (< diff-time (nth 1 diff)))))
 
-(defun elmo-open-network-stream (name buffer host service ssl)
-  (let ((auto-plugged (and elmo-auto-change-plugged
-                          (> elmo-auto-change-plugged 0)))
-       process)
-    (if (eq ssl 'starttls)
-       (require 'starttls)
-      (if ssl (require 'ssl)))
-    (condition-case err
-       (let (process-connection-type)
-         (setq process
-               (if (eq ssl 'starttls)
-                   (starttls-open-stream name buffer host service)
-                 (if ssl
-                     (open-ssl-stream name buffer host service)
-                   (open-network-stream name buffer host service)))))
-      (error
-       (when auto-plugged
-        (elmo-set-plugged nil host service (current-time))
-        (message "Auto plugged off at %s:%d" host service)
-        (sit-for 1))
-       (signal (car err) (cdr err))))
-    (when process
-      (process-kill-without-query process)
-      (when auto-plugged
-       (elmo-set-plugged t host service))
-      process)))
-
 (if (fboundp 'std11-fetch-field)
     (defalias 'elmo-field-body 'std11-fetch-field) ;;no narrow-to-region
   (defalias 'elmo-field-body 'std11-field-body))
 
+(defun elmo-address-quote-specials (word)
+  "Make quoted string of WORD if needed."
+  (let ((lal (std11-lexical-analyze word)))
+    (if (or (assq 'specials lal)
+           (assq 'domain-literal lal))
+       (prin1-to-string word)
+      word)))
+
 (defmacro elmo-string (string)
-  "String without text property"
+  "STRING without text property."
   (` (let ((obj (copy-sequence (, string))))
-       (set-text-properties 0 (length obj) nil obj)
+       (and obj (set-text-properties 0 (length obj) nil obj))
        obj)))
 
+(defun elmo-flatten (list-of-list)
+  "Flatten LIST-OF-LIST."
+  (unless (null list-of-list)
+    (append (if (and (car list-of-list)
+                    (listp (car list-of-list)))
+               (car list-of-list)
+             (list (car list-of-list)))
+           (elmo-flatten (cdr list-of-list)))))
+
 (defun elmo-y-or-n-p (prompt &optional auto default)
   "Same as `y-or-n-p'.
 But if optional argument AUTO is non-nil, DEFAULT is returned."
@@ -1543,7 +1266,7 @@ But if optional argument AUTO is non-nil, DEFAULT is returned."
     (y-or-n-p prompt)))
 
 (defun elmo-string-member (string slist)
-  "Returns t if STRING is a member of the SLIST."
+  "Return t if STRING is a member of the SLIST."
   (catch 'found
     (while slist
       (if (and (stringp (car slist))
@@ -1559,6 +1282,14 @@ But if optional argument AUTO is non-nil, DEFAULT is returned."
            (throw 'member (car list)))
        (setq list (cdr list))))))
 
+(defun elmo-string-matched-member (str list &optional case-ignore)
+  (let ((case-fold-search case-ignore))
+    (catch 'member
+      (while list
+       (if (string-match str (car list))
+           (throw 'member (car list)))
+       (setq list (cdr list))))))
+
 (defsubst elmo-string-delete-match (string pos)
   (concat (substring string
                     0 (match-beginning pos))
@@ -1601,6 +1332,16 @@ But if optional argument AUTO is non-nil, DEFAULT is returned."
            (throw 'loop a))
        (setq alist (cdr alist))))))
 
+(defun elmo-string-assoc-all (key alist)
+  (let (matches)
+    (while alist
+      (if (string= key (car (car alist)))
+         (setq matches
+               (cons (car alist)
+                     matches)))
+      (setq alist (cdr alist)))
+    matches))
+
 (defun elmo-string-rassoc (key alist)
   (let (a)
     (catch 'loop
@@ -1612,6 +1353,649 @@ But if optional argument AUTO is non-nil, DEFAULT is returned."
            (throw 'loop a))
        (setq alist (cdr alist))))))
 
-(provide 'elmo-util)
+(defun elmo-string-rassoc-all (key alist)
+  (let (matches)
+    (while alist
+      (if (string= key (cdr (car alist)))
+         (setq matches
+               (cons (car alist)
+                     matches)))
+      (setq alist (cdr alist)))
+    matches))
+
+(defun elmo-expand-newtext (newtext original)
+  (let ((len (length newtext))
+       (pos 0)
+       c expanded beg N did-expand)
+    (while (< pos len)
+      (setq beg pos)
+      (while (and (< pos len)
+                 (not (= (aref newtext pos) ?\\)))
+       (setq pos (1+ pos)))
+      (unless (= beg pos)
+       (push (substring newtext beg pos) expanded))
+      (when (< pos len)
+       ;; We hit a \; expand it.
+       (setq did-expand t
+             pos (1+ pos)
+             c (aref newtext pos))
+       (if (not (or (= c ?\&)
+                    (and (>= c ?1)
+                         (<= c ?9))))
+           ;; \ followed by some character we don't expand.
+           (push (char-to-string c) expanded)
+         ;; \& or \N
+         (if (= c ?\&)
+             (setq N 0)
+           (setq N (- c ?0)))
+         (when (match-beginning N)
+           (push (substring original (match-beginning N) (match-end N))
+                 expanded))))
+      (setq pos (1+ pos)))
+    (if did-expand
+       (apply (function concat) (nreverse expanded))
+      newtext)))
+
+;;; Folder parser utils.
+(defun elmo-parse-token (string &optional seps)
+  "Parse atom from STRING using SEPS as a string of separator char list."
+  (let ((len (length string))
+       (seps (and seps (string-to-char-list seps)))
+       (i 0)
+       (sep nil)
+       content c in)
+    (if (eq len 0)
+       (cons "" "")
+      (while (and (< i len) (or in (null sep)))
+       (setq c (aref string i))
+       (cond
+        ((and in (eq c ?\\))
+         (setq i (1+ i)
+               content (cons (aref string i) content)
+               i (1+ i)))
+        ((eq c ?\")
+         (setq in (not in)
+               i (1+ i)))
+        (in (setq content (cons c content)
+                  i (1+ i)))
+        ((memq c seps)
+         (setq sep c))
+        (t (setq content (cons c content)
+                 i (1+ i)))))
+      (if in (error "Parse error in quoted"))
+      (cons (if (null content) "" (char-list-to-string (nreverse content)))
+           (substring string i)))))
+
+(defun elmo-parse-prefixed-element (prefix string &optional seps)
+  (if (and (not (eq (length string) 0))
+          (eq (aref string 0) prefix))
+      (elmo-parse-token (substring string 1) seps)
+    (cons "" string)))
+
+;;; Number set defined by OKAZAKI Tetsurou <okazaki@be.to>
+;;
+;; number          ::= [0-9]+
+;; beg             ::= number
+;; end             ::= number
+;; number-range    ::= "(" beg " . " end ")"      ;; cons cell
+;; number-set-elem ::= number / number-range
+;; number-set      ::= "(" *number-set-elem ")"   ;; list
+
+(defun elmo-number-set-member (number number-set)
+  "Return non-nil if NUMBER is an element of NUMBER-SET.
+The value is actually the tail of NUMBER-RANGE whose car contains NUMBER."
+  (or (memq number number-set)
+      (let (found)
+       (while (and number-set (not found))
+         (if (and (consp (car number-set))
+                  (and (<= (car (car number-set)) number)
+                       (<= number (cdr (car number-set)))))
+             (setq found t)
+           (setq number-set (cdr number-set))))
+       number-set)))
+
+(defun elmo-number-set-append-list (number-set list)
+  "Append LIST of numbers to the NUMBER-SET.
+NUMBER-SET is altered."
+  (let ((appended number-set))
+    (while list
+      (setq appended (elmo-number-set-append appended (car list)))
+      (setq list (cdr list)))
+    appended))
+
+(defun elmo-number-set-append (number-set number)
+  "Append NUMBER to the NUMBER-SET.
+NUMBER-SET is altered."
+  (let ((number-set-1 number-set)
+       found elem)
+    (while (and number-set (not found))
+      (setq elem (car number-set))
+      (cond
+       ((and (consp elem)
+            (eq (+ 1 (cdr elem)) number))
+       (setcdr elem number)
+       (setq found t))
+       ((and (integerp elem)
+            (eq (+ 1 elem) number))
+       (setcar number-set (cons elem number))
+       (setq found t))
+       ((or (and (integerp elem) (eq elem number))
+           (and (consp elem)
+                (<= (car elem) number)
+                (<= number (cdr elem))))
+       (setq found t)))
+      (setq number-set (cdr number-set)))
+    (if (not found)
+       (setq number-set-1 (nconc number-set-1 (list number))))
+    number-set-1))
+
+(defun elmo-number-set-to-number-list (number-set)
+  "Return a number list which corresponds to NUMBER-SET."
+  (let (number-list elem i)
+    (while number-set
+      (setq elem (car number-set))
+      (cond
+       ((consp elem)
+       (setq i (car elem))
+       (while (<= i (cdr elem))
+         (setq number-list (cons i number-list))
+         (incf i)))
+       ((integerp elem)
+       (setq number-list (cons elem number-list))))
+      (setq number-set (cdr number-set)))
+    (nreverse number-list)))
+
+(defcustom elmo-list-subdirectories-ignore-regexp "^\\(\\.\\.?\\|[0-9]+\\)$"
+  "*Regexp to filter subfolders."
+  :type 'regexp
+  :group 'elmo)
+
+(defun elmo-list-subdirectories-1 (basedir curdir one-level)
+  (let ((root (zerop (length curdir)))
+       (w32-get-true-file-link-count t) ; for Meadow
+       attr dirs dir)
+    (catch 'done
+      (dolist (file (directory-files (setq dir (expand-file-name curdir basedir))))
+       (when (and (not (string-match
+                        elmo-list-subdirectories-ignore-regexp
+                        file))
+                  (car (setq attr (file-attributes
+                                   (expand-file-name file dir)))))
+         (when (eq one-level 'check) (throw 'done t))
+         (let ((relpath
+                (concat curdir (and (not root) elmo-path-sep) file))
+               subdirs)
+           (setq dirs (nconc dirs
+                             (if (if elmo-have-link-count (< 2 (nth 1 attr))
+                                   (setq subdirs
+                                         (elmo-list-subdirectories-1
+                                          basedir
+                                          relpath
+                                          (if one-level 'check))))
+                                 (if one-level
+                                     (list (list relpath))
+                                   (cons relpath
+                                         (or subdirs
+                                             (elmo-list-subdirectories-1
+                                              basedir
+                                              relpath
+                                              nil))))
+                               (list relpath)))))))
+      dirs)))
+
+(defun elmo-list-subdirectories (directory file one-level)
+  (let ((subdirs (elmo-list-subdirectories-1 directory file one-level)))
+    (if (zerop (length file))
+       subdirs
+      (cons file subdirs))))
+
+(defun elmo-mapcar-list-of-list (func list-of-list)
+  (mapcar
+   (lambda (x)
+     (cond ((listp x) (elmo-mapcar-list-of-list func x))
+          (t (funcall func x))))
+   list-of-list))
+
+(defun elmo-parse (string regexp &optional matchn)
+  (or matchn (setq matchn 1))
+  (let (list)
+    (store-match-data nil)
+    (while (string-match regexp string (match-end 0))
+      (setq list (cons (substring string (match-beginning matchn)
+                                 (match-end matchn)) list)))
+    (nreverse list)))
+
+;;;
+;; parsistent mark handling
+;; (for global!)
+;; (FIXME: this should be treated in the msgdb.)
+
+(defvar elmo-msgdb-global-mark-alist nil)
+
+(defun elmo-msgdb-global-mark-delete (msgid)
+  (let* ((path (expand-file-name
+               elmo-msgdb-global-mark-filename
+               elmo-msgdb-directory))
+        (malist (or elmo-msgdb-global-mark-alist
+                    (setq elmo-msgdb-global-mark-alist
+                          (elmo-object-load path))))
+        match)
+    (when (setq match (assoc msgid malist))
+      (setq elmo-msgdb-global-mark-alist
+           (delete match elmo-msgdb-global-mark-alist))
+      (elmo-object-save path elmo-msgdb-global-mark-alist))))
+
+(defun elmo-msgdb-global-mark-set (msgid mark)
+  (let* ((path (expand-file-name
+               elmo-msgdb-global-mark-filename
+               elmo-msgdb-directory))
+        (malist (or elmo-msgdb-global-mark-alist
+                    (setq elmo-msgdb-global-mark-alist
+                          (elmo-object-load path))))
+        match)
+    (if (setq match (assoc msgid malist))
+       (setcdr match mark)
+      (setq elmo-msgdb-global-mark-alist
+           (nconc elmo-msgdb-global-mark-alist
+                  (list (cons msgid mark)))))
+    (elmo-object-save path elmo-msgdb-global-mark-alist)))
+
+(defun elmo-msgdb-global-mark-get (msgid)
+  (cdr (assoc msgid (or elmo-msgdb-global-mark-alist
+                       (setq elmo-msgdb-global-mark-alist
+                             (elmo-object-load
+                              (expand-file-name
+                               elmo-msgdb-global-mark-filename
+                               elmo-msgdb-directory)))))))
+
+;;; File cache.
+(defmacro elmo-make-file-cache (path status)
+  "PATH is the cache file name.
+STATUS is one of 'section, 'entire or nil.
+ nil means no cache exists.
+'section means partial section cache exists.
+'entire means entire cache exists.
+If the cache is partial file-cache, TYPE is 'partial."
+  (` (cons (, path) (, status))))
+
+(defmacro elmo-file-cache-path (file-cache)
+  "Returns the file path of the FILE-CACHE."
+  (` (car (, file-cache))))
+
+(defmacro elmo-file-cache-status (file-cache)
+  "Returns the status of the FILE-CACHE."
+  (` (cdr (, file-cache))))
+
+(defsubst elmo-cache-to-msgid (filename)
+  (concat "<" (elmo-recover-string-from-filename filename) ">"))
+
+(defsubst elmo-cache-get-path-subr (msgid)
+  (let ((chars '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F))
+       (clist (string-to-char-list msgid))
+       (sum 0))
+    (while clist
+      (setq sum (+ sum (car clist)))
+      (setq clist (cdr clist)))
+    (format "%c%c"
+           (nth (% (/ sum 16) 2) chars)
+           (nth (% sum 16) chars))))
+
+(defun elmo-file-cache-get-path (msgid &optional section)
+  "Get cache path for MSGID.
+If optional argument SECTION is specified, partial cache path is returned."
+  (if (setq msgid (elmo-msgid-to-cache msgid))
+      (expand-file-name
+       (if section
+          (format "%s/%s/%s/%s"
+                  elmo-cache-directory
+                  (elmo-cache-get-path-subr msgid)
+                  msgid
+                  section)
+        (format "%s/%s/%s"
+                elmo-cache-directory
+                (elmo-cache-get-path-subr msgid)
+                msgid)))))
+
+(defmacro elmo-file-cache-expand-path (path section)
+  "Return file name for the file-cache corresponds to the section.
+PATH is the file-cache path.
+SECTION is the section string."
+  (` (expand-file-name (or (, section) "") (, path))))
+
+(defun elmo-file-cache-delete (path)
+  "Delete a cache on PATH."
+  (unless (elmo-msgdb-global-mark-get
+          (elmo-cache-to-msgid (file-name-nondirectory path)))
+    (when (file-exists-p path)
+      (if (file-directory-p path)
+         (progn
+           (dolist (file (directory-files path t "^[^\\.]"))
+             (delete-file file))
+           (delete-directory path))
+       (delete-file path))
+      t)))
+
+(defun elmo-file-cache-exists-p (msgid)
+  "Returns 'section or 'entire if a cache which corresponds to MSGID exists."
+  (elmo-file-cache-status (elmo-file-cache-get msgid)))
+
+(defun elmo-file-cache-save (cache-path section)
+  "Save current buffer as cache on PATH.
+Return t if cache is saved successfully."
+  (condition-case nil
+      (let ((path (if section (expand-file-name section cache-path)
+                   cache-path))
+           files dir)
+       (if (and (null section)
+                (file-directory-p path))
+           (progn
+             (setq files (directory-files path t "^[^\\.]"))
+             (while files
+               (delete-file (car files))
+               (setq files (cdr files)))
+             (delete-directory path))
+         (if (and section
+                  (not (file-directory-p cache-path)))
+             (delete-file cache-path)))
+       (when path
+         (setq dir (directory-file-name (file-name-directory path)))
+         (if (not (file-exists-p dir))
+             (elmo-make-directory dir))
+         (write-region-as-binary (point-min) (point-max)
+                                 path nil 'no-msg)
+         t))
+    ;; ignore error
+    (error)))
+
+(defun elmo-file-cache-load (cache-path section)
+  "Load cache on PATH into the current buffer.
+Return t if cache is loaded successfully."
+  (condition-case nil
+      (let (cache-file)
+       (when (and cache-path
+                  (if (elmo-cache-path-section-p cache-path)
+                      section
+                    (null section))
+                  (setq cache-file (elmo-file-cache-expand-path
+                                    cache-path
+                                    section))
+                  (file-exists-p cache-file))
+         (insert-file-contents-as-binary cache-file)
+         t))
+    ;; igore error
+    (error)))
+
+(defun elmo-cache-path-section-p (path)
+  "Return non-nil when PATH is `section' cache path."
+  (file-directory-p path))
+
+(defun elmo-file-cache-get (msgid &optional section)
+  "Returns the current file-cache object associated with MSGID.
+MSGID is the message-id of the message.
+If optional argument SECTION is specified, get partial file-cache object
+associated with SECTION."
+  (if msgid
+      (let ((path (elmo-cache-get-path msgid)))
+       (if (and path (file-exists-p path))
+           (if (elmo-cache-path-section-p path)
+               (if section
+                   (if (file-exists-p (setq path (expand-file-name
+                                                  section path)))
+                       (cons path 'section))
+                 ;; section is not specified but sectional.
+                 (cons path 'section))
+             ;; not directory.
+             (unless section
+               (cons path 'entire)))
+         ;; no cache.
+         (cons path nil)))))
+
+;;;
+;; Expire cache.
+
+(defun elmo-cache-expire ()
+  (interactive)
+  (let* ((completion-ignore-case t)
+        (method (completing-read (format "Expire by (%s): "
+                                         elmo-cache-expire-default-method)
+                                 '(("size" . "size")
+                                   ("age" . "age")))))
+    (if (string= method "")
+       (setq method elmo-cache-expire-default-method))
+    (funcall (intern (concat "elmo-cache-expire-by-" method)))))
+
+(defun elmo-read-float-value-from-minibuffer (prompt &optional initial)
+  (let ((str (read-from-minibuffer prompt initial)))
+    (cond
+     ((string-match "[0-9]*\\.[0-9]+" str)
+      (string-to-number str))
+     ((string-match "[0-9]+" str)
+      (string-to-number (concat str ".0")))
+     (t (error "%s is not number" str)))))
+
+(defun elmo-cache-expire-by-size (&optional kbytes)
+  "Expire cache file by size.
+If KBYTES is kilo bytes (This value must be float)."
+  (interactive)
+  (let ((size (or kbytes
+                 (and (interactive-p)
+                      (elmo-read-float-value-from-minibuffer
+                       "Enter cache disk size (Kbytes): "
+                       (number-to-string
+                        (if (integerp elmo-cache-expire-default-size)
+                            (float elmo-cache-expire-default-size)
+                          elmo-cache-expire-default-size))))
+                 (if (integerp elmo-cache-expire-default-size)
+                     (float elmo-cache-expire-default-size))))
+       (count 0)
+       (Kbytes 1024)
+       total beginning)
+    (message "Checking disk usage...")
+    (setq total (/ (elmo-disk-usage
+                   elmo-cache-directory) Kbytes))
+    (setq beginning total)
+    (message "Checking disk usage...done")
+    (let ((cfl (elmo-cache-get-sorted-cache-file-list))
+         (deleted 0)
+         oldest
+         cur-size cur-file)
+      (while (and (<= size total)
+                 (setq oldest (elmo-cache-get-oldest-cache-file-entity cfl)))
+       (setq cur-file (expand-file-name (car (cdr oldest)) (car oldest)))
+       (setq cur-size (/ (elmo-disk-usage cur-file) Kbytes))
+       (when (elmo-file-cache-delete cur-file)
+         (setq count (+ count 1))
+         (message "%d cache(s) are expired." count))
+       (setq deleted (+ deleted cur-size))
+       (setq total (- total cur-size)))
+      (message "%d cache(s) are expired from disk (%d Kbytes/%d Kbytes)."
+              count deleted beginning))))
+
+(defun elmo-cache-make-file-entity (filename path)
+  (cons filename (elmo-get-last-accessed-time filename path)))
+
+(defun elmo-cache-get-oldest-cache-file-entity (cache-file-list)
+  (let ((cfl cache-file-list)
+       flist firsts oldest-entity wonlist)
+    (while cfl
+      (setq flist (cdr (car cfl)))
+      (setq firsts (append firsts (list
+                                  (cons (car (car cfl))
+                                        (car flist)))))
+      (setq cfl (cdr cfl)))
+;;; (prin1 firsts)
+    (while firsts
+      (if (and (not oldest-entity)
+              (cdr (cdr (car firsts))))
+         (setq oldest-entity (car firsts)))
+      (if (and (cdr (cdr (car firsts)))
+              (cdr (cdr oldest-entity))
+              (> (cdr (cdr oldest-entity)) (cdr (cdr (car firsts)))))
+         (setq oldest-entity (car firsts)))
+      (setq firsts (cdr firsts)))
+    (setq wonlist (assoc (car oldest-entity) cache-file-list))
+    (and wonlist
+        (setcdr wonlist (delete (car (cdr wonlist)) (cdr wonlist))))
+    oldest-entity))
+
+(defun elmo-cache-get-sorted-cache-file-list ()
+  (let ((dirs (directory-files
+              elmo-cache-directory
+              t "^[^\\.]"))
+       (i 0) num
+       elist
+       ret-val)
+    (setq num (length dirs))
+    (message "Collecting cache info...")
+    (while dirs
+      (setq elist (mapcar (lambda (x)
+                           (elmo-cache-make-file-entity x (car dirs)))
+                         (directory-files (car dirs) nil "^[^\\.]")))
+      (setq ret-val (append ret-val
+                           (list (cons
+                                  (car dirs)
+                                  (sort
+                                   elist
+                                   (lambda (x y)
+                                     (< (cdr x)
+                                        (cdr y))))))))
+      (when (> num elmo-display-progress-threshold)
+       (setq i (+ i 1))
+       (elmo-display-progress
+        'elmo-cache-get-sorted-cache-file-list "Collecting cache info..."
+        (/ (* i 100) num)))
+      (setq dirs (cdr dirs)))
+    (message "Collecting cache info...done")
+    ret-val))
+
+(defun elmo-cache-expire-by-age (&optional days)
+  (let ((age (or (and days (int-to-string days))
+                (and (interactive-p)
+                     (read-from-minibuffer
+                      (format "Enter days (%s): "
+                              elmo-cache-expire-default-age)))
+                (int-to-string elmo-cache-expire-default-age)))
+       (dirs (directory-files
+              elmo-cache-directory
+              t "^[^\\.]"))
+       (count 0)
+       curtime)
+    (if (string= age "")
+       (setq age elmo-cache-expire-default-age)
+      (setq age (string-to-int age)))
+    (setq curtime (current-time))
+    (setq curtime (+ (* (nth 0 curtime)
+                       (float 65536)) (nth 1 curtime)))
+    (while dirs
+      (let ((files (directory-files (car dirs) t "^[^\\.]"))
+           (limit-age (* age 86400)))
+       (while files
+         (when (> (- curtime (elmo-get-last-accessed-time (car files)))
+                  limit-age)
+           (when (elmo-file-cache-delete (car files))
+             (setq count (+ 1 count))
+             (message "%d cache file(s) are expired." count)))
+         (setq files (cdr files))))
+      (setq dirs (cdr dirs)))))
+
+;;;
+;; msgid to path.
+(defun elmo-msgid-to-cache (msgid)
+  (save-match-data
+    (when (and msgid
+              (string-match "<\\(.+\\)>$" msgid))
+      (elmo-replace-string-as-filename (elmo-match-string 1 msgid)))))
+
+(defun elmo-cache-get-path (msgid &optional folder number)
+  "Get path for cache file associated with MSGID, FOLDER, and NUMBER."
+  (if (setq msgid (elmo-msgid-to-cache msgid))
+      (expand-file-name
+       (expand-file-name
+       (if folder
+           (format "%s/%s/%s@%s"
+                   (elmo-cache-get-path-subr msgid)
+                   msgid
+                   (or number "")
+                   (elmo-safe-filename folder))
+         (format "%s/%s"
+                 (elmo-cache-get-path-subr msgid)
+                 msgid))
+       elmo-cache-directory))))
+
+;;;
+;; Warnings.
+
+(static-if (fboundp 'display-warning)
+    (defmacro elmo-warning (&rest args)
+      "Display a warning with `elmo' group."
+      `(display-warning 'elmo (format ,@args)))
+  (defconst elmo-warning-buffer-name "*elmo warning*")
+  (defun elmo-warning (&rest args)
+    "Display a warning. ARGS are passed to `format'."
+    (with-current-buffer (get-buffer-create elmo-warning-buffer-name)
+      (goto-char (point-max))
+      (funcall 'insert (apply 'format (append args '("\n"))))
+      (ignore-errors (recenter 1))
+      (display-buffer elmo-warning-buffer-name))))
+
+(defvar elmo-obsolete-variable-alist nil)
+
+(defcustom elmo-obsolete-variable-show-warnings t
+  "Show warning window if obsolete variable is treated."
+  :type 'boolean
+  :group 'elmo)
+
+(defun elmo-define-obsolete-variable (obsolete var)
+  "Define obsolete variable.
+OBSOLETE is a symbol for obsolete variable.
+VAR is a symbol for new variable.
+Definition is stored in `elmo-obsolete-variable-alist'."
+  (let ((pair (assq var elmo-obsolete-variable-alist)))
+    (if pair
+       (setcdr pair obsolete)
+      (setq elmo-obsolete-variable-alist
+           (cons (cons var obsolete)
+                 elmo-obsolete-variable-alist)))))
+
+(defun elmo-resque-obsolete-variable (obsolete var)
+  "Resque obsolete variable OBSOLETE as VAR.
+If `elmo-obsolete-variable-show-warnings' is non-nil, show warning message."
+  (when (boundp obsolete)
+    (static-if (and (fboundp 'defvaralias)
+                   (subrp (symbol-function 'defvaralias)))
+       (defvaralias var obsolete)
+      (set var (symbol-value obsolete)))
+    (if elmo-obsolete-variable-show-warnings
+       (elmo-warning "%s is obsolete. Use %s instead."
+                     (symbol-name obsolete)
+                     (symbol-name var)))))
+
+(defun elmo-resque-obsolete-variables (&optional alist)
+  "Resque obsolete variables in ALIST.
+ALIST is a list of cons cell of
+\(OBSOLETE-VARIABLE-SYMBOL . NEW-VARIABLE-SYMBOL\).
+If ALIST is nil, `elmo-obsolete-variable-alist' is used."
+  (dolist (pair elmo-obsolete-variable-alist)
+    (elmo-resque-obsolete-variable (cdr pair)
+                                  (car pair))))
+
+;;; Queue.
+(defvar elmo-dop-queue-filename "queue"
+  "*Disconnected operation queue is saved in this file.")
+
+(defun elmo-dop-queue-load ()
+  (setq elmo-dop-queue
+       (elmo-object-load
+        (expand-file-name elmo-dop-queue-filename
+                          elmo-msgdb-directory))))
+
+(defun elmo-dop-queue-save ()
+  (elmo-object-save
+   (expand-file-name elmo-dop-queue-filename
+                    elmo-msgdb-directory)
+   elmo-dop-queue))
+
+(require 'product)
+(product-provide (provide 'elmo-util) (require 'elmo-version))
 
 ;;; elmo-util.el ends here