(elmo-flatten): Use `append' and `listp' instead of
[elisp/wanderlust.git] / elmo / elmo-imap4.el
index 097edad..ce4b708 100644 (file)
@@ -52,8 +52,7 @@
 (eval-when-compile (require 'cl))
 
 (defvar elmo-imap4-disuse-server-flag-mailbox-regexp "^#mh" ; UW imapd
-  "Regexp to match IMAP4 mailbox names whose message flags on server should be ignored.
-\(Except `\\Deleted' flag\).")
+  "Regexp to match IMAP4 mailbox names whose message flags on server should be ignored (For STATUS command).")
 
 (defvar elmo-imap4-overview-fetch-chop-length 200
   "*Number of overviews to fetch in one request.")
@@ -165,6 +164,21 @@ REGEXP should have a grouping for namespace prefix.")
 (defconst elmo-imap4-literal-threshold 1024
  "Limitation of characters that can be used in a quoted string.")
 
+(defconst elmo-imap4-flag-specs '((important "\\Flagged")
+                                 (read "\\Seen")
+                                 (unread "\\Seen" 'remove)
+                                 (answered "\\Answered")
+                                 ;; draft-melnikov-imap-keywords-03.txt
+                                 (forwarded "$Forwarded")
+                                 (work "$Work")
+                                 (personal "$Personal")
+                                 (shouldreply "$ShouldReply")))
+
+(defconst elmo-imap4-folder-name-syntax
+  `(mailbox
+    (?: [user "^[A-Za-z]"] (?/ [auth ".+"]))
+    ,@elmo-net-folder-name-syntax))
+
 ;; For debugging.
 (defvar elmo-imap4-debug nil
   "Non-nil forces IMAP4 folder as debug mode.
@@ -181,7 +195,7 @@ Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"")
 ;;; Session
 (eval-and-compile
   (luna-define-class elmo-imap4-session (elmo-network-session)
-                    (capability current-mailbox read-only))
+                    (capability current-mailbox read-only flags))
   (luna-define-internal-accessors 'elmo-imap4-session))
 
 ;;; MIME-ELMO-IMAP Location
@@ -205,12 +219,12 @@ Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"")
 
 (defsubst elmo-imap4-decode-folder-string (string)
   (if elmo-imap4-use-modified-utf7
-      (utf7-decode-string string 'imap)
+      (utf7-decode string 'imap)
     string))
 
 (defsubst elmo-imap4-encode-folder-string (string)
   (if elmo-imap4-use-modified-utf7
-      (utf7-encode-string string 'imap)
+      (utf7-encode string 'imap)
     string))
 
 ;;; Response
@@ -310,12 +324,22 @@ Returns a TAG string which is assigned to the COMMAND."
                                    cmdstr
                                    (elmo-imap4-format-quoted (nth 1 token)))))
                     ((eq kind 'literal)
-                     (setq cmdstr (concat cmdstr
-                                          (format "{%d}" (nth 2 token))))
-                     (process-send-string process cmdstr)
-                     (process-send-string process "\r\n")
-                     (setq cmdstr nil)
-                     (elmo-imap4-accept-continue-req session)
+                     (if (memq 'literal+
+                               (elmo-imap4-session-capability-internal
+                                session))
+                         ;; rfc2088
+                         (progn
+                           (setq cmdstr (concat cmdstr
+                                                (format "{%d+}" (nth 2 token))))
+                           (process-send-string process cmdstr)
+                           (process-send-string process "\r\n")
+                           (setq cmdstr nil))
+                       (setq cmdstr (concat cmdstr
+                                            (format "{%d}" (nth 2 token))))
+                       (process-send-string process cmdstr)
+                       (process-send-string process "\r\n")
+                       (setq cmdstr nil)
+                       (elmo-imap4-accept-continue-req session))
                      (cond ((stringp (nth 1 token))
                             (setq cmdstr (nth 1 token)))
                            ((bufferp (nth 1 token))
@@ -439,21 +463,20 @@ If response is not `OK' response, causes error with IMAP response text."
         (mime-elmo-imap-location-folder-internal location)
         (mime-elmo-imap-location-number-internal location)
         (mime-elmo-imap-location-strategy-internal location)
-        section
-        (current-buffer)
-        'unseen)
+        'unseen
+        section)
        (buffer-string))
-    (elmo-message-fetch
+    (elmo-message-fetch-string
      (mime-elmo-imap-location-folder-internal location)
      (mime-elmo-imap-location-number-internal location)
      (mime-elmo-imap-location-strategy-internal location)
-     section
-     nil 'unseen)))
+     'unseen
+     section)))
 
 
 (luna-define-method mime-imap-location-bodystructure
   ((location mime-elmo-imap-location))
-  (elmo-imap4-fetch-bodystructure
+  (elmo-message-fetch-bodystructure
    (mime-elmo-imap-location-folder-internal location)
    (mime-elmo-imap-location-number-internal location)
    (mime-elmo-imap-location-strategy-internal location)))
@@ -614,8 +637,9 @@ BUFFER must be a single-byte buffer."
               (car (nth 1 entry))))
         response)))
 
-(defun elmo-imap4-fetch-bodystructure (folder number strategy)
-  "Fetch BODYSTRUCTURE for the message in the FOLDER with NUMBER using STRATEGY."
+(luna-define-method elmo-message-fetch-bodystructure ((folder
+                                                      elmo-imap4-folder)
+                                                     number strategy)
   (if (elmo-fetch-strategy-use-cache strategy)
       (elmo-object-load
        (elmo-file-cache-expand-path
@@ -693,7 +717,11 @@ Returns response value if selecting folder succeed. "
              (elmo-imap4-session-set-current-mailbox-internal session mailbox)
              (elmo-imap4-session-set-read-only-internal
               session
-              (nth 1 (assq 'read-only (assq 'ok response)))))
+              (nth 1 (assq 'read-only (assq 'ok response))))
+             (elmo-imap4-session-set-flags-internal
+              session
+              (nth 1 (or (assq 'permanentflags response)
+                         (assq 'flags response)))))
          (elmo-imap4-session-set-current-mailbox-internal session nil)
          (if (and (eq no-error 'notify-bye)
                   (elmo-imap4-response-bye-p response))
@@ -730,6 +758,83 @@ Returns response value if selecting folder succeed. "
                "search %s") flag))
      'search)))
 
+(defun elmo-imap4-session-flag-available-p (session flag)
+  (case flag
+    ((read unread) (elmo-string-member-ignore-case
+                   "\\seen" (elmo-imap4-session-flags-internal session)))
+    (important
+     (elmo-string-member-ignore-case
+      "\\flagged" (elmo-imap4-session-flags-internal session)))
+    (digest
+     (or (elmo-string-member-ignore-case
+         "\\seen" (elmo-imap4-session-flags-internal session))
+        (elmo-string-member-ignore-case
+         "\\flagged" (elmo-imap4-session-flags-internal session))))
+    (answered
+     (elmo-string-member-ignore-case
+      (concat "\\" (symbol-name flag))
+      (elmo-imap4-session-flags-internal session)))
+    (t
+     (member "\\*" (elmo-imap4-session-flags-internal session)))))
+
+(defun elmo-imap4-flag-to-imap-search-key (flag)
+  (case flag
+    (read "seen")
+    (unread "unseen")
+    (important "flagged")
+    (answered "answered")
+    (new "new")
+    (t (concat
+       "keyword "
+       (or (car (cdr (assq flag elmo-imap4-flag-specs)))
+           (symbol-name flag))))))
+
+(defun elmo-imap4-flag-to-imap-criteria (flag)
+  (case flag
+    ((any digest)
+     (let ((criteria "flagged")
+          (global-flags (delq 'important (elmo-get-global-flags t t))))
+       (dolist (flag (delete 'new
+                            (delete 'cached
+                                    (copy-sequence
+                                     (case flag
+                                       (any
+                                        elmo-preserved-flags)
+                                       (digest
+                                        elmo-digest-flags))))))
+        (setq criteria (concat "or "
+                               (elmo-imap4-flag-to-imap-search-key flag)
+                               " "
+                               criteria)))
+       (while global-flags
+        (setq criteria (concat "or keyword "
+                               (symbol-name (car global-flags))
+                               " "
+                               criteria))
+        (setq global-flags (cdr global-flags)))
+       criteria))
+    (t
+     (elmo-imap4-flag-to-imap-search-key flag))))
+
+(defun elmo-imap4-folder-list-flagged (folder flag)
+  "List flagged message numbers in the FOLDER.
+FLAG is one of the `unread', `read', `important', `answered', `any'."
+  (let ((session (elmo-imap4-get-session folder))
+       (criteria (elmo-imap4-flag-to-imap-criteria flag)))
+    (if (elmo-imap4-session-flag-available-p session flag)
+       (progn
+         (elmo-imap4-session-select-mailbox
+          session
+          (elmo-imap4-folder-mailbox-internal folder))
+         (elmo-imap4-response-value
+          (elmo-imap4-send-command-wait
+           session
+           (format (if elmo-imap4-use-uid "uid search %s"
+                     "search %s") criteria))
+          'search))
+      ;; List flagged messages in the msgdb.
+      (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag))))
+
 (defvar elmo-imap4-rfc822-size "RFC822\.SIZE")
 (defvar elmo-imap4-rfc822-text "RFC822\.TEXT")
 (defvar elmo-imap4-rfc822-header "RFC822\.HEADER")
@@ -786,7 +891,8 @@ If CHOP-LENGTH is not specified, message set is not chopped."
          flag-list
          (if use-flag
              (append
-              (and (elmo-string-member-ignore-case "\\Recent" flags)
+              (and (memq 'new saved-flags)
+                   (not (elmo-string-member-ignore-case "\\Seen" flags))
                    '(new))
               (and (elmo-string-member-ignore-case "\\Flagged" flags)
                    '(important))
@@ -809,20 +915,19 @@ If CHOP-LENGTH is not specified, message set is not chopped."
 
 ;; Current buffer is process buffer.
 (defun elmo-imap4-fetch-callback-1 (element app-data)
-  (elmo-imap4-fetch-callback-1-subr
-   (with-temp-buffer
-     (insert (or (elmo-imap4-response-bodydetail-text element)
-                ""))
-     ;; Delete CR.
-     (goto-char (point-min))
-     (while (search-forward "\r\n" nil t)
-       (replace-match "\n"))
-     (elmo-msgdb-create-message-entity-from-buffer
-      (elmo-folder-msgdb-internal (cdr app-data))
-      (elmo-imap4-response-value element 'uid)
-      :size (elmo-imap4-response-value element 'rfc822size)))
-   (elmo-imap4-response-value element 'flags)
-   app-data))
+  (let ((handler (elmo-msgdb-message-entity-handler elmo-imap4-current-msgdb)))
+    (elmo-imap4-fetch-callback-1-subr
+     (with-temp-buffer
+       (insert (or (elmo-imap4-response-bodydetail-text element)
+                  ""))
+       ;; Replace all CRLF with LF.
+       (elmo-delete-cr-buffer)
+       (elmo-msgdb-create-message-entity-from-buffer
+       handler
+       (elmo-imap4-response-value element 'uid)
+       :size (elmo-imap4-response-value element 'rfc822size)))
+     (elmo-imap4-response-value element 'flags)
+     app-data)))
 
 (defun elmo-imap4-parse-capability (string)
   (if (string-match "^\\*\\(.*\\)$" string)
@@ -879,6 +984,7 @@ If CHOP-LENGTH is not specified, message set is not chopped."
                  (forward-line -1)
                  (not (elmo-imap4-parse-greeting)))
        (accept-process-output process 1))
+      (erase-buffer)
       (set-process-filter process 'elmo-imap4-arrival-filter)
       (set-process-sentinel process 'elmo-imap4-sentinel)
 ;;;   (while (and (memq (process-status process) '(open run))
@@ -1029,7 +1135,7 @@ If CHOP-LENGTH is not specified, message set is not chopped."
       (save-match-data
        (set-buffer send-buf)
        (erase-buffer)
-       (elmo-set-buffer-multibyte nil)
+       (set-buffer-multibyte nil)
        (if string
            (insert string)
          (with-current-buffer source-buf
@@ -1745,9 +1851,7 @@ Return nil if no complete line has arrived."
        (elmo-imap4-forward)
        (nreverse body)))))
 
-(luna-define-method elmo-folder-initialize :around ((folder
-                                                    elmo-imap4-folder)
-                                                   name)
+(luna-define-method elmo-folder-initialize ((folder elmo-imap4-folder) name)
   (let ((default-user  elmo-imap4-default-user)
        (default-server elmo-imap4-default-server)
        (default-port   elmo-imap4-default-port)
@@ -1756,39 +1860,38 @@ Return nil if no complete line has arrived."
             (append elmo-imap4-stream-type-alist
                     elmo-network-stream-type-alist)
           elmo-network-stream-type-alist))
-       parse)
+       tokens)
     (when (string-match "\\(.*\\)@\\(.*\\)" default-server)
       ;; case: imap4-default-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 name (luna-call-next-method))
+    (setq tokens (car (elmo-parse-separated-tokens
+                      name
+                      elmo-imap4-folder-name-syntax)))
     ;; mailbox
-    (setq parse (elmo-parse-token name ":"))
     (elmo-imap4-folder-set-mailbox-internal folder
                                            (elmo-imap4-encode-folder-string
-                                            (car parse)))
+                                            (cdr (assq 'mailbox tokens))))
     ;; user
-    (setq parse (elmo-parse-prefixed-element ?: (cdr parse) "/"))
     (elmo-net-folder-set-user-internal folder
-                                      (if (eq (length (car parse)) 0)
-                                          default-user
-                                        (car parse)))
+                                      (or (cdr (assq 'user tokens))
+                                          default-user))
     ;; auth
-    (setq parse (elmo-parse-prefixed-element ?/ (cdr parse)))
     (elmo-net-folder-set-auth-internal
      folder
-     (if (eq (length (car parse)) 0)
-        (or elmo-imap4-default-authenticate-type 'clear)
-       (intern (car parse))))
-    (unless (elmo-net-folder-server-internal folder)
-      (elmo-net-folder-set-server-internal folder default-server))
-    (unless (elmo-net-folder-port-internal folder)
-      (elmo-net-folder-set-port-internal folder default-port))
-    (unless (elmo-net-folder-stream-type-internal folder)
-      (elmo-net-folder-set-stream-type-internal
-       folder
-       (elmo-get-network-stream-type elmo-imap4-default-stream-type)))
+     (let ((auth (cdr (assq 'auth tokens))))
+       (or (and auth (intern auth))
+          elmo-imap4-default-authenticate-type
+          'clear)))
+    ;; network
+    (elmo-net-folder-set-parameters
+     folder
+     tokens
+     (list :server     default-server
+          :port        default-port
+          :stream-type
+          (elmo-get-network-stream-type elmo-imap4-default-stream-type)))
     folder))
 
 ;;; ELMO IMAP4 folder
@@ -1854,23 +1957,9 @@ Return nil if no complete line has arrived."
                         (format "uid %d:*" (cdr (car killed)))
                       "all"))))
 
-(luna-define-method elmo-folder-list-unreads-plugged
-  ((folder elmo-imap4-folder))
-  (elmo-imap4-list folder "unseen"))
-
-(luna-define-method elmo-folder-list-importants-plugged
-  ((folder elmo-imap4-folder))
-  (elmo-imap4-list folder "flagged"))
-
-(luna-define-method elmo-folder-list-answereds-plugged
-  ((folder elmo-imap4-folder))
-  (elmo-imap4-list folder "answered"))
-
-(defun elmo-imap4-folder-list-any-plugged (folder)
-  (elmo-imap4-list folder "or answered or unseen flagged"))
-
-(defun elmo-imap4-folder-list-digest-plugged (folder)
-  (elmo-imap4-list folder "or unseen flagged"))
+(luna-define-method elmo-folder-list-flagged-plugged
+  ((folder elmo-imap4-folder) flag)
+  (elmo-imap4-folder-list-flagged folder flag))
 
 (luna-define-method elmo-folder-use-flag-p ((folder elmo-imap4-folder))
   (not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
@@ -1889,6 +1978,7 @@ Return nil if no complete line has arrived."
         (delim (or (cdr namespace-assoc)
                 elmo-imap4-default-hierarchy-delimiter))
         ;; Append delimiter when root with namespace.
+        (root-nodelim root)
         (root (if (and namespace-assoc
                        (match-end 1)
                        (string= (substring root (match-end 1))
@@ -1900,11 +1990,23 @@ Return nil if no complete line has arrived."
                  (elmo-imap4-send-command-wait
                   session
                   (list "list " (elmo-imap4-mailbox root) " *"))))
+    ;; The response of Courier-imap doesn't contain a specified folder itself.
+    (unless (member root result)
+      (setq result
+           (append result
+                   (elmo-imap4-response-get-selectable-mailbox-list
+                    (elmo-imap4-send-command-wait
+                     session
+                     (list "list \"\" " (elmo-imap4-mailbox
+                                         root-nodelim)))))))
     (when (or (not (string= (elmo-net-folder-user-internal folder)
                            elmo-imap4-default-user))
              (not (eq (elmo-net-folder-auth-internal folder)
                       (or elmo-imap4-default-authenticate-type 'clear))))
-      (setq append-serv (concat ":" (elmo-net-folder-user-internal folder))))
+      (setq append-serv (concat ":"
+                               (elmo-quote-syntactical-element
+                                (elmo-net-folder-user-internal folder)
+                                'user elmo-imap4-folder-name-syntax))))
     (unless (eq (elmo-net-folder-auth-internal folder)
                (or elmo-imap4-default-authenticate-type 'clear))
       (setq append-serv
@@ -1954,7 +2056,9 @@ Return nil if no complete line has arrived."
                                      fld))
                                  (cdr result)))
                  folder (concat prefix
-                                (elmo-imap4-decode-folder-string folder)
+                                (elmo-quote-syntactical-element
+                                 (elmo-imap4-decode-folder-string folder)
+                                 'mailbox elmo-imap4-folder-name-syntax)
                                 (and append-serv
                                      (eval append-serv)))
                  ret (append ret (if has-child-p
@@ -1962,7 +2066,10 @@ Return nil if no complete line has arrived."
                                    (list folder)))))
          ret)
       (mapcar (lambda (fld)
-               (concat prefix (elmo-imap4-decode-folder-string fld)
+               (concat prefix
+                       (elmo-quote-syntactical-element
+                        (elmo-imap4-decode-folder-string fld)
+                        'mailbox elmo-imap4-folder-name-syntax)
                        (and append-serv
                             (eval append-serv))))
              result))))
@@ -1994,13 +2101,14 @@ Return nil if no complete line has arrived."
                               (elmo-folder-name-internal folder)))
       (let ((session (elmo-imap4-get-session folder)))
        (when (elmo-imap4-folder-mailbox-internal folder)
-         (when msgs (elmo-folder-delete-messages folder msgs))
+         (when msgs (elmo-folder-delete-messages-internal folder msgs))
          (elmo-imap4-send-command-wait session "close")
          (elmo-imap4-send-command-wait
           session
           (list "delete "
                 (elmo-imap4-mailbox
-                 (elmo-imap4-folder-mailbox-internal folder))))))
+                 (elmo-imap4-folder-mailbox-internal folder)))))
+       (elmo-imap4-session-set-current-mailbox-internal session nil))
       (elmo-msgdb-delete-path folder)
       t)))
 
@@ -2058,26 +2166,31 @@ If optional argument REMOVE is non-nil, remove FLAG."
     (elmo-imap4-session-select-mailbox session
                                       (elmo-imap4-folder-mailbox-internal
                                        folder))
-    (setq set-list (elmo-imap4-make-number-set-list
-                   numbers
-                   elmo-imap4-number-set-chop-length))
-    (while set-list
-      (with-current-buffer (elmo-network-session-buffer session)
-       (setq elmo-imap4-fetch-callback nil)
-       (setq elmo-imap4-fetch-callback-data nil))
-      (unless (elmo-imap4-response-ok-p
-              (elmo-imap4-send-command-wait
-               session
-               (format
-                (if elmo-imap4-use-uid
-                    "uid store %s %sflags.silent (%s)"
-                  "store %s %sflags.silent (%s)")
-                (cdr (car set-list))
-                (if remove "-" "+")
-                flag)))
-       (setq response 'fail))
-      (setq set-list (cdr set-list)))
-    (not (eq response 'fail))))
+    (when (or (elmo-string-member-ignore-case
+              flag
+              (elmo-imap4-session-flags-internal session))
+             (member "\\*" (elmo-imap4-session-flags-internal session))
+             (string= flag "\\Deleted")) ; XXX Humm..
+      (setq set-list (elmo-imap4-make-number-set-list
+                     numbers
+                     elmo-imap4-number-set-chop-length))
+      (while set-list
+       (with-current-buffer (elmo-network-session-buffer session)
+         (setq elmo-imap4-fetch-callback nil)
+         (setq elmo-imap4-fetch-callback-data nil))
+       (unless (elmo-imap4-response-ok-p
+                (elmo-imap4-send-command-wait
+                 session
+                 (format
+                  (if elmo-imap4-use-uid
+                      "uid store %s %sflags.silent (%s)"
+                    "store %s %sflags.silent (%s)")
+                  (cdr (car set-list))
+                  (if remove "-" "+")
+                  flag)))
+         (setq response 'fail))
+       (setq set-list (cdr set-list)))
+      (not (eq response 'fail)))))
 
 (luna-define-method elmo-folder-delete-messages-plugged
   ((folder elmo-imap4-folder) numbers)
@@ -2116,17 +2229,8 @@ If optional argument REMOVE is non-nil, remove FLAG."
        (mapcar '(lambda (x) (delete x numbers)) rest)
        numbers))
      ((string= "flag" search-key)
-      (cond
-       ((string= "unread" (elmo-filter-value filter))
-       (elmo-folder-list-unreads folder))
-       ((string= "important" (elmo-filter-value filter))
-       (elmo-folder-list-importants folder))
-       ((string= "answered" (elmo-filter-value filter))
-       (elmo-folder-list-answereds folder))
-       ((string= "digest" (elmo-filter-value filter))
-       (elmo-imap4-folder-list-digest-plugged folder))
-       ((string= "any" (elmo-filter-value filter))
-       (elmo-imap4-folder-list-any-plugged folder))))
+      (elmo-imap4-folder-list-flagged
+       folder (intern (elmo-filter-value filter))))
      ((or (string= "since" search-key)
          (string= "before" search-key))
       (setq search-key (concat "sent" search-key)
@@ -2258,10 +2362,11 @@ If optional argument REMOVE is non-nil, remove FLAG."
   (when numbers
     (let ((session (elmo-imap4-get-session folder))
          (headers
-          (append
-           '("Subject" "From" "To" "Cc" "Date"
-             "Message-Id" "References" "In-Reply-To")
-           elmo-msgdb-extra-fields))
+          (elmo-uniq-list
+           (append
+            '("Subject" "From" "To" "Cc" "Date"
+              "Message-Id" "References" "In-Reply-To")
+            (mapcar #'capitalize (elmo-msgdb-extra-fields 'non-virtual)))))
          (total 0)
          (length (length numbers))
          print-length print-depth
@@ -2311,29 +2416,19 @@ If optional argument REMOVE is non-nil, remove FLAG."
                                  'message-id)))
        elmo-imap4-current-msgdb))))
 
-(luna-define-method elmo-folder-unflag-important-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Flagged" 'remove))
-
-(luna-define-method elmo-folder-flag-as-important-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Flagged"))
-
-(luna-define-method elmo-folder-unflag-read-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Seen" 'remove))
-
-(luna-define-method elmo-folder-flag-as-read-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Seen"))
+(luna-define-method elmo-folder-set-flag-plugged ((folder elmo-imap4-folder)
+                                                 numbers flag)
+  (let ((spec (cdr (assq flag elmo-imap4-flag-specs))))
+    (elmo-imap4-set-flag folder numbers (or (car spec)
+                                           (capitalize (symbol-name flag)))
+                        (nth 1 spec))))
 
-(luna-define-method elmo-folder-unflag-answered-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Answered" 'remove))
-
-(luna-define-method elmo-folder-flag-as-answered-plugged
-  ((folder elmo-imap4-folder) numbers)
-  (elmo-imap4-set-flag folder numbers "\\Answered"))
+(luna-define-method elmo-folder-unset-flag-plugged ((folder elmo-imap4-folder)
+                                                   numbers flag)
+  (let ((spec (cdr (assq flag elmo-imap4-flag-specs))))
+    (elmo-imap4-set-flag folder numbers (or (car spec)
+                                           (capitalize (symbol-name flag)))
+                        (not (nth 1 spec)))))
 
 (luna-define-method elmo-message-use-cache-p ((folder elmo-imap4-folder)
                                              number)
@@ -2381,7 +2476,7 @@ If optional argument REMOVE is non-nil, remove FLAG."
     (setq messages (elmo-imap4-response-value response 'messages))
     (setq uidnext (elmo-imap4-response-value response 'uidnext))
     (setq killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
-    ;; 
+    ;;
     (when killed
       (when (and (consp (car killed))
                 (eq (car (car killed)) 1))
@@ -2430,7 +2525,11 @@ If optional argument REMOVE is non-nil, remove FLAG."
                     session mailbox)
                    (elmo-imap4-session-set-read-only-internal
                     session
-                    (nth 1 (assq 'read-only (assq 'ok response)))))
+                    (nth 1 (assq 'read-only (assq 'ok response))))
+                   (elmo-imap4-session-set-flags-internal
+                    session
+                    (nth 1 (or (assq 'permanentflags response)
+                               (assq 'flags response)))))
                (elmo-imap4-session-set-current-mailbox-internal session nil)
                (if (elmo-imap4-response-bye-p response)
                    (elmo-imap4-process-bye session)
@@ -2459,52 +2558,41 @@ If optional argument REMOVE is non-nil, remove FLAG."
 
 ;; elmo-folder-open-internal: do nothing.
 
-(luna-define-method elmo-find-fetch-strategy
-  ((folder elmo-imap4-folder) entity &optional ignore-cache)
-  (let ((number (elmo-message-entity-number entity))
-       cache-file size message-id)
-    (setq size (elmo-message-entity-field entity 'size))
-    (setq message-id (elmo-message-entity-field entity 'message-id))
-    (setq cache-file (elmo-file-cache-get message-id))
-    (if (or ignore-cache
-           (null (elmo-file-cache-status cache-file)))
-       (if (and elmo-message-fetch-threshold
-                (integerp size)
-                (>= size elmo-message-fetch-threshold)
-                (or (not elmo-message-fetch-confirm)
-                    (not (prog1 (y-or-n-p
+(luna-define-method elmo-find-fetch-strategy ((folder elmo-imap4-folder) number
+                                             &optional
+                                             ignore-cache
+                                             require-entireness)
+  (let ((entity (elmo-message-entity folder number)))
+    (if (null entity)
+       (elmo-make-fetch-strategy 'entire)
+      (let* ((size (elmo-message-entity-field entity 'size))
+            (message-id (elmo-message-entity-field entity 'message-id))
+            (cache-file (elmo-file-cache-get message-id))
+            (use-cache (and (not ignore-cache)
+                            (elmo-message-use-cache-p folder number)
+                            (if require-entireness
+                                (eq (elmo-file-cache-status cache-file)
+                                    'entire)
+                              (elmo-file-cache-status cache-file)))))
+       (elmo-make-fetch-strategy
+        (if use-cache
+            (elmo-file-cache-status cache-file)
+          (if (and (not require-entireness)
+                   elmo-message-fetch-threshold
+                   (integerp size)
+                   (>= size elmo-message-fetch-threshold)
+                   (or (not elmo-message-fetch-confirm)
+                       (not (prog1
+                                (y-or-n-p
                                  (format
                                   "Fetch entire message at once? (%dbytes)"
                                   size))
-                           (message "")))))
-           ;; Fetch message as imap message.
-           (elmo-make-fetch-strategy 'section
-                                     nil
-                                     (elmo-message-use-cache-p
-                                      folder number)
-                                     (elmo-file-cache-path
-                                      cache-file))
-         ;; Don't use existing cache and fetch entire message at once.
-         (elmo-make-fetch-strategy 'entire nil
-                                   (elmo-message-use-cache-p
-                                    folder number)
-                                   (elmo-file-cache-path cache-file)))
-      ;; Cache found and use it.
-      (if (not ignore-cache)
-         (if (eq (elmo-file-cache-status cache-file) 'section)
-             ;; Fetch message with imap message.
-             (elmo-make-fetch-strategy 'section
-                                       t
-                                       (elmo-message-use-cache-p
-                                        folder number)
-                                       (elmo-file-cache-path
-                                        cache-file))
-           (elmo-make-fetch-strategy 'entire
-                                     t
-                                     (elmo-message-use-cache-p
-                                      folder number)
-                                     (elmo-file-cache-path
-                                      cache-file)))))))
+                              (message "")))))
+              'section
+            'entire))
+        use-cache
+        (elmo-message-use-cache-p folder number)
+        (elmo-file-cache-path cache-file))))))
 
 (luna-define-method elmo-folder-create-plugged ((folder elmo-imap4-folder))
   (elmo-imap4-send-command-wait
@@ -2513,6 +2601,18 @@ If optional argument REMOVE is non-nil, remove FLAG."
         (elmo-imap4-mailbox
          (elmo-imap4-folder-mailbox-internal folder)))))
 
+(defun elmo-imap4-flags-to-imap (flags)
+  "Convert FLAGS to the IMAP flag string."
+  (let ((imap-flag (if (not (memq 'unread flags)) "\\Seen")))
+    (dolist (flag flags)
+      (unless (memq flag '(new read unread cached))
+       (setq imap-flag
+             (concat imap-flag
+                     (if imap-flag " ")
+                     (or (car (cdr (assq flag elmo-imap4-flag-specs)))
+                         (capitalize (symbol-name flag)))))))
+    imap-flag))
+
 (luna-define-method elmo-folder-append-buffer
   ((folder elmo-imap4-folder) &optional flags number)
   (if (elmo-folder-plugged-p folder)
@@ -2531,21 +2631,13 @@ If optional argument REMOVE is non-nil, remove FLAG."
                    (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal
                                         folder))
                    (if (and flags (elmo-folder-use-flag-p folder))
-                       (concat " ("
-                               (mapconcat
-                                'identity
-                                (append
-                                 (and (memq 'important flags)
-                                      '("\\Flagged"))
-                                 (and (not (memq 'unread flags))
-                                      '("\\Seen"))
-                                 (and (memq 'answered flags)
-                                      '("\\Answered")))
-                                " ")
-                               ") ")
+                       (concat " (" (elmo-imap4-flags-to-imap flags) ") ")
                      " () ")
                    (elmo-imap4-buffer-literal send-buffer))))
          (kill-buffer send-buffer))
+       (when result
+         (elmo-folder-preserve-flags
+          folder (elmo-msgdb-get-message-id-from-buffer) flags))
        result)
     ;; Unplugged
     (if elmo-enable-disconnected-operation
@@ -2562,6 +2654,28 @@ If optional argument REMOVE is non-nil, remove FLAG."
            (string= (elmo-net-folder-user-internal (, folder1))
                     (elmo-net-folder-user-internal (, folder2)))))))
 
+(luna-define-method elmo-folder-next-message-number-plugged
+  ((folder elmo-imap4-folder))
+  (let ((session (elmo-imap4-get-session folder))
+       messages new unread response killed uidnext)
+    (with-current-buffer (elmo-network-session-buffer session)
+      (setq elmo-imap4-status-callback nil)
+      (setq elmo-imap4-status-callback-data nil))
+    (if elmo-imap4-use-select-to-update-status
+       (elmo-imap4-session-select-mailbox
+        session
+        (elmo-imap4-folder-mailbox-internal folder)))
+    (setq response
+         (elmo-imap4-send-command-wait session
+                                       (list
+                                        "status "
+                                        (elmo-imap4-mailbox
+                                         (elmo-imap4-folder-mailbox-internal
+                                          folder))
+                                        " (uidnext)"))
+         response (elmo-imap4-response-value response 'status))
+    (elmo-imap4-response-value response 'uidnext)))
+
 (luna-define-method elmo-folder-append-messages :around
   ((folder elmo-imap4-folder) src-folder numbers &optional same-number)
   (if (and (eq (elmo-folder-type-internal src-folder) 'imap4)
@@ -2621,6 +2735,7 @@ If optional argument REMOVE is non-nil, remove FLAG."
        (with-current-buffer outbuf
          (erase-buffer)
          (insert response)
+         (elmo-delete-cr-buffer)
          t))))
 
 (luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder)
@@ -2660,6 +2775,7 @@ If optional argument REMOVE is non-nil, remove FLAG."
   nil)
 
 (autoload 'elmo-global-flags-set "elmo-flag")
+(autoload 'elmo-get-global-flags "elmo-flag")
 
 (require 'product)
 (product-provide (provide 'elmo-imap4) (require 'elmo-version))