X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=elmo%2Felmo-imap4.el;h=3f694d4aff03aacfc61242011523c0f9b9c3fee9;hb=ba3cbf9aa81cbbeaf6d7016121f364ae3784bf79;hp=edd27023f487e2ce1141d55b4979151d5cf370a4;hpb=539186207d6fd19a5c20c1ef97128ffa3dc6cbbf;p=elisp%2Fwanderlust.git diff --git a/elmo/elmo-imap4.el b/elmo/elmo-imap4.el index edd2702..3f694d4 100644 --- a/elmo/elmo-imap4.el +++ b/elmo/elmo-imap4.el @@ -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.") @@ -82,7 +81,7 @@ (defvar elmo-imap4-use-select-to-update-status nil "*Some imapd have to send select command to update status. -(ex. UW imapd 4.5-BETA?). For these imapd, you must set this variable t.") +\(ex. UW imapd 4.5-BETA?\). For these imapd, you must set this variable t.") (defvar elmo-imap4-use-modified-utf7 nil "*Use mofidied UTF-7 (rfc2060) encoding for IMAP4 folder name.") @@ -92,7 +91,7 @@ (defvar elmo-imap4-extra-namespace-alist '(("^\\({.*/nntp}\\).*$" . ".")) ; Default is for UW's remote nntp mailbox... - "Extra namespace alist. + "Extra namespace alist. A list of cons cell like: (REGEXP . DELIMITER). REGEXP should have a grouping for namespace prefix.") ;; @@ -165,6 +164,11 @@ 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"))) + ;; For debugging. (defvar elmo-imap4-debug nil "Non-nil forces IMAP4 folder as debug mode. @@ -181,7 +185,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 @@ -227,6 +231,10 @@ Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"") "Returns non-nil if RESPONSE is an 'BYE' response." (` (assq 'bye (, response)))) +(defmacro elmo-imap4-response-garbage-p (response) + "Returns non-nil if RESPONSE is an 'garbage' response." + (` (assq 'garbage (, response)))) + (defmacro elmo-imap4-response-value (response symbol) "Get value of the SYMBOL from RESPONSE." (` (nth 1 (assq (, symbol) (, response))))) @@ -287,8 +295,11 @@ Returns a TAG string which is assigned to the COMMAND." (when (elmo-imap4-response-bye-p elmo-imap4-current-response) (elmo-imap4-process-bye session)) (setq elmo-imap4-current-response nil) - (if elmo-imap4-parsing - (error "IMAP process is running. Please wait (or plug again)")) + (when elmo-imap4-parsing + (message "Waiting for IMAP response...") + (accept-process-output (elmo-network-session-process-internal + session)) + (message "Waiting for IMAP response...done")) (setq elmo-imap4-parsing t) (elmo-imap4-debug "<-(%s)- %s" tag command) (while (setq token (car command-args)) @@ -347,7 +358,14 @@ TAG is the tag of the command" (with-current-buffer (process-buffer (elmo-network-session-process-internal session)) (while (not (or (string= tag elmo-imap4-reached-tag) - (elmo-imap4-response-bye-p elmo-imap4-current-response))) + (elmo-imap4-response-bye-p elmo-imap4-current-response) + (when (elmo-imap4-response-garbage-p + elmo-imap4-current-response) + (message "Garbage response: %s" + (elmo-imap4-response-value + elmo-imap4-current-response + 'garbage)) + t))) (when (memq (process-status (elmo-network-session-process-internal session)) '(open run)) @@ -439,7 +457,7 @@ If response is not `OK' response, causes error with IMAP response text." (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))) @@ -463,7 +481,7 @@ If response is not `OK' response, causes error with IMAP response text." (with-current-buffer (elmo-network-session-buffer session) (setq elmo-imap4-fetch-callback nil) (setq elmo-imap4-fetch-callback-data nil)) - (elmo-imap4-send-command-wait session "check")) + (elmo-imap4-send-command session "check")) (defun elmo-imap4-atom-p (string) "Return t if STRING is an atom defined in rfc2060." @@ -596,12 +614,13 @@ BUFFER must be a single-byte buffer." (mapcar (lambda (entry) (if (and (eq 'list (car entry)) - (not (member "\\NoSelect" (nth 1 (nth 1 entry))))) + (not (elmo-string-member-ignore-case "\\Noselect" (nth 1 (nth 1 entry))))) (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 @@ -679,15 +698,19 @@ 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)) (elmo-imap4-process-bye session) (unless no-error - (error (or - (elmo-imap4-response-error-text response) - (format "Select %s failed" mailbox))))))) + (error "%s" + (or (elmo-imap4-response-error-text response) + (format "Select %s failed" mailbox))))))) (and result response)))) (defun elmo-imap4-check-validity (spec validity-file) @@ -716,43 +739,65 @@ Returns response value if selecting folder succeed. " "search %s") flag)) 'search))) -(static-cond - ((fboundp 'float) - ;; Emacs can parse dot symbol. - (defvar elmo-imap4-rfc822-size "RFC822\.SIZE") - (defvar elmo-imap4-rfc822-text "RFC822\.TEXT") - (defvar elmo-imap4-rfc822-header "RFC822\.HEADER") - (defvar elmo-imap4-rfc822-size "RFC822\.SIZE") - (defvar elmo-imap4-header-fields "HEADER\.FIELDS") - (defmacro elmo-imap4-replace-dot-symbols ()) ;; noop - (defalias 'elmo-imap4-fetch-read 'read) - ) - (t - ;;; For Nemacs. - ;; Cannot parse dot symbol. - (defvar elmo-imap4-rfc822-size "RFC822_SIZE") - (defvar elmo-imap4-header-fields "HEADER_FIELDS") - (defvar elmo-imap4-rfc822-size "RFC822_SIZE") - (defvar elmo-imap4-rfc822-text "RFC822_TEXT") - (defvar elmo-imap4-rfc822-header "RFC822_HEADER") - (defvar elmo-imap4-header-fields "HEADER_FIELDS") - (defun elmo-imap4-fetch-read (buffer) - (with-current-buffer buffer - (let ((beg (point)) - token) - (when (re-search-forward "[[ ]" nil t) - (goto-char (match-beginning 0)) - (setq token (buffer-substring beg (point))) - (cond ((string= token "RFC822.SIZE") - (intern elmo-imap4-rfc822-size)) - ((string= token "RFC822.HEADER") - (intern elmo-imap4-rfc822-header)) - ((string= token "RFC822.TEXT") - (intern elmo-imap4-rfc822-text)) - ((string= token "HEADER\.FIELDS") - (intern elmo-imap4-header-fields)) - (t (goto-char beg) - (elmo-read (current-buffer)))))))))) +(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-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 (case flag + (read "seen") + (unread "unseen") + (important "flagged") + (answered "answered") + (new "new") + (any "or answered or unseen flagged") + (digest "or unseen flagged") + (t (concat "keyword " (capitalize (symbol-name flag))))))) + ;; Add search keywords + (when (or (eq flag 'digest)(eq flag 'any)) + (let ((flags (delq 'important (elmo-get-global-flags t t)))) + (while flags + (setq criteria (concat "or keyword " + (symbol-name (car flags)) + " " + criteria)) + (setq flags (cdr flags))))) + (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") +(defvar elmo-imap4-header-fields "HEADER\.FIELDS") (defun elmo-imap4-make-number-set-list (msg-list &optional chop-length) "Make RFC2060's message set specifier from MSG-LIST. @@ -792,67 +837,60 @@ If CHOP-LENGTH is not specified, message set is not chopped." ;; ;; app-data: -;; cons of list -;; 0: new-mark 1: already-mark 2: seen-mark 3: important-mark -;; 4: seen-list -;; and result of use-flag-p. +;; cons of flag-table and folder structure (defsubst elmo-imap4-fetch-callback-1-subr (entity flags app-data) "A msgdb entity callback function." - (let* ((use-flag (cdr app-data)) - (app-data (car app-data)) - (seen (member (car entity) (nth 4 app-data))) - mark) - (if (member "\\Flagged" flags) - (elmo-msgdb-global-mark-set (car entity) (nth 3 app-data))) - (if (setq mark (elmo-msgdb-global-mark-get (car entity))) - (unless (member "\\Seen" flags) - (setq elmo-imap4-seen-messages - (cons - (elmo-msgdb-overview-entity-get-number entity) - elmo-imap4-seen-messages))) - (setq mark (or (if (elmo-file-cache-status - (elmo-file-cache-get (car entity))) - (if (or seen - (and use-flag - (member "\\Seen" flags))) - nil - (nth 1 app-data)) - (if (or seen - (and use-flag - (member "\\Seen" flags))) - (if elmo-imap4-use-cache - (nth 2 app-data)) - (nth 0 app-data)))))) - (setq elmo-imap4-current-msgdb - (elmo-msgdb-append - elmo-imap4-current-msgdb - (list (list entity) - (list (cons (elmo-msgdb-overview-entity-get-number entity) - (car entity))) - (if mark - (list - (list (elmo-msgdb-overview-entity-get-number entity) - mark)))))))) + (let ((use-flag (elmo-folder-use-flag-p (cdr app-data))) + (flag-table (car app-data)) + (msg-id (elmo-message-entity-field entity 'message-id)) + saved-flags flag-list) +;; (when (elmo-string-member-ignore-case "\\Flagged" flags) +;; (elmo-msgdb-global-mark-set msg-id elmo-msgdb-important-mark)) + (setq saved-flags (elmo-flag-table-get flag-table msg-id) + flag-list + (if use-flag + (append + (and (memq 'new saved-flags) + (not (elmo-string-member-ignore-case "\\Seen" flags)) + '(new)) + (and (elmo-string-member-ignore-case "\\Flagged" flags) + '(important)) + (and (not (elmo-string-member-ignore-case "\\Seen" flags)) + '(unread)) + (and (elmo-string-member-ignore-case "\\Answered" flags) + '(answered)) + (and (elmo-file-cache-exists-p msg-id) + '(cached))) + saved-flags)) + (when (and (or (memq 'important flag-list) + (memq 'answered flag-list)) + (memq 'unread flag-list)) + (setq elmo-imap4-seen-messages + (cons (elmo-message-entity-number entity) + elmo-imap4-seen-messages))) + (elmo-msgdb-append-entity elmo-imap4-current-msgdb + entity + flag-list))) ;; 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-overview-from-buffer - (elmo-imap4-response-value element 'uid) - (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) - (elmo-read + (read (concat "(" (downcase (elmo-match-string 1 string)) ")")))) (defun elmo-imap4-clear-login (session) @@ -1316,11 +1354,11 @@ Return nil if no complete line has arrived." (defun elmo-imap4-parse-response () "Parse a IMAP command response." (let (token) - (case (setq token (elmo-read (current-buffer))) + (case (setq token (read (current-buffer))) (+ (progn (skip-chars-forward " ") (list 'continue-req (buffer-substring (point) (point-max))))) - (* (case (prog1 (setq token (elmo-read (current-buffer))) + (* (case (prog1 (setq token (read (current-buffer))) (elmo-imap4-forward)) (OK (elmo-imap4-parse-resp-text-code)) (NO (elmo-imap4-parse-resp-text-code)) @@ -1332,19 +1370,19 @@ Return nil if no complete line has arrived." (LSUB (list 'lsub (elmo-imap4-parse-data-list))) (SEARCH (list 'search - (elmo-read (concat "(" + (read (concat "(" (buffer-substring (point) (point-max)) ")")))) (STATUS (elmo-imap4-parse-status)) ;; Added (NAMESPACE (elmo-imap4-parse-namespace)) (CAPABILITY (list 'capability - (elmo-read + (read (concat "(" (downcase (buffer-substring (point) (point-max))) ")")))) (ACL (elmo-imap4-parse-acl)) - (t (case (prog1 (elmo-read (current-buffer)) + (t (case (prog1 (read (current-buffer)) (elmo-imap4-forward)) (EXISTS (list 'exists token)) (RECENT (list 'recent token)) @@ -1353,7 +1391,7 @@ Return nil if no complete line has arrived." (t (list 'garbage (buffer-string))))))) (t (if (not (string-match elmo-imap4-seq-prefix (symbol-name token))) (list 'garbage (buffer-string)) - (case (prog1 (elmo-read (current-buffer)) + (case (prog1 (read (current-buffer)) (elmo-imap4-forward)) (OK (progn (setq elmo-imap4-parsing nil) @@ -1412,9 +1450,9 @@ Return nil if no complete line has arrived." (cond ((search-forward "PERMANENTFLAGS " nil t) (list 'permanentflags (elmo-imap4-parse-flag-list))) ((search-forward "UIDNEXT " nil t) - (list 'uidnext (elmo-read (current-buffer)))) + (list 'uidnext (read (current-buffer)))) ((search-forward "UNSEEN " nil t) - (list 'unseen (elmo-read (current-buffer)))) + (list 'unseen (read (current-buffer)))) ((looking-at "UIDVALIDITY \\([0-9]+\\)") (list 'uidvalidity (match-string 1))) ((search-forward "READ-ONLY" nil t) @@ -1478,12 +1516,12 @@ Return nil if no complete line has arrived." (let (element list) (while (not (eq (char-after (point)) ?\))) (elmo-imap4-forward) - (let ((token (elmo-imap4-fetch-read (current-buffer)))) + (let ((token (read (current-buffer)))) (elmo-imap4-forward) (setq element (cond ((eq token 'UID) (list 'uid (condition-case nil - (elmo-read (current-buffer)) + (read (current-buffer)) (error nil)))) ((eq token 'FLAGS) (list 'flags (elmo-imap4-parse-flag-list))) @@ -1498,7 +1536,7 @@ Return nil if no complete line has arrived." ((eq token (intern elmo-imap4-rfc822-text)) (list 'rfc822text (elmo-imap4-parse-nstring))) ((eq token (intern elmo-imap4-rfc822-size)) - (list 'rfc822size (elmo-read (current-buffer)))) + (list 'rfc822size (read (current-buffer)))) ((eq token 'BODY) (if (eq (char-before) ?\[) (list @@ -1528,24 +1566,25 @@ Return nil if no complete line has arrived." (while (not (eq (char-after (point)) ?\))) (setq status (cons - (let ((token (elmo-read (current-buffer)))) + (let ((token (read (current-buffer)))) (cond ((eq token 'MESSAGES) - (list 'messages (elmo-read (current-buffer)))) + (list 'messages (read (current-buffer)))) ((eq token 'RECENT) - (list 'recent (elmo-read (current-buffer)))) + (list 'recent (read (current-buffer)))) ((eq token 'UIDNEXT) - (list 'uidnext (elmo-read (current-buffer)))) + (list 'uidnext (read (current-buffer)))) ((eq token 'UIDVALIDITY) (and (looking-at " \\([0-9]+\\)") (prog1 (list 'uidvalidity (match-string 1)) (goto-char (match-end 1))))) ((eq token 'UNSEEN) - (list 'unseen (elmo-read (current-buffer)))) + (list 'unseen (read (current-buffer)))) (t (message "Unknown status data %s in mailbox %s ignored" token mailbox)))) - status)))) + status)) + (skip-chars-forward " "))) (and elmo-imap4-status-callback (funcall elmo-imap4-status-callback status @@ -1566,9 +1605,9 @@ Return nil if no complete line has arrived." (nconc (copy-sequence elmo-imap4-extra-namespace-alist) (elmo-imap4-parse-namespace-subr - (elmo-read (concat "(" (buffer-substring - (point) (point-max)) - ")")))))) + (read (concat "(" (buffer-substring + (point) (point-max)) + ")")))))) (defun elmo-imap4-parse-namespace-subr (ns) (let (prefix delim namespace-alist default-delim) @@ -1825,15 +1864,16 @@ Return nil if no complete line has arrived." (setq mailbox "inbox")) (if (eq (string-to-char mailbox) ?/) (setq mailbox (substring mailbox 1 (length mailbox)))) - (expand-file-name - mailbox + ;; don't use expand-file-name (e.g. %~/something) + (concat (expand-file-name (or (elmo-net-folder-user-internal folder) "nobody") (expand-file-name (or (elmo-net-folder-server-internal folder) "nowhere") (expand-file-name "imap" - elmo-msgdb-directory))))))) + elmo-msgdb-directory))) + "/" mailbox)))) (luna-define-method elmo-folder-status-plugged ((folder elmo-imap4-folder)) @@ -1865,22 +1905,22 @@ Return nil if no complete line has arrived." (luna-define-method elmo-folder-list-messages-plugged ((folder elmo-imap4-folder) - &optional nohide) + &optional + enable-killed) (elmo-imap4-list folder - (let ((max (elmo-msgdb-max-of-killed - (elmo-folder-killed-list-internal folder)))) - (if (or nohide - (null (eq max 0))) - (format "uid %d:*" (1+ max)) + (let ((killed + (elmo-folder-killed-list-internal + folder))) + (if (and killed + (eq (length killed) 1) + (consp (car killed)) + (eq (car (car killed)) 1)) + (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-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 @@ -1891,15 +1931,16 @@ Return nil if no complete line has arrived." (let* ((root (elmo-imap4-folder-mailbox-internal folder)) (session (elmo-imap4-get-session folder)) (prefix (elmo-folder-prefix-internal folder)) - (delim (or - (cdr + (namespace-assoc (elmo-string-matched-assoc root (with-current-buffer (elmo-network-session-buffer session) elmo-imap4-server-namespace))) + (delim (or (cdr namespace-assoc) elmo-imap4-default-hierarchy-delimiter)) ;; Append delimiter when root with namespace. - (root (if (and (match-end 1) + (root (if (and namespace-assoc + (match-end 1) (string= (substring root (match-end 1)) "")) (concat root delim) @@ -1909,6 +1950,14 @@ 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))))))) (when (or (not (string= (elmo-net-folder-user-internal folder) elmo-imap4-default-user)) (not (eq (elmo-net-folder-auth-internal folder) @@ -1993,18 +2042,25 @@ Return nil if no complete line has arrived." (luna-define-method elmo-folder-writable-p ((folder elmo-imap4-folder)) t) -(luna-define-method elmo-folder-delete :before ((folder elmo-imap4-folder)) - (let ((session (elmo-imap4-get-session folder)) - msgs) - (when (elmo-imap4-folder-mailbox-internal folder) - (when (setq msgs (elmo-folder-list-messages folder)) - (elmo-folder-delete-messages 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))))))) +(luna-define-method elmo-folder-delete ((folder elmo-imap4-folder)) + (let ((msgs (and (elmo-folder-exists-p folder) + (elmo-folder-list-messages folder)))) + (when (yes-or-no-p (format "%sDelete msgdb and substance of \"%s\"? " + (if (> (length msgs) 0) + (format "%d msg(s) exists. " (length msgs)) + "") + (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)) + (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-msgdb-delete-path folder) + t))) (luna-define-method elmo-folder-rename-internal ((folder elmo-imap4-folder) new-folder) @@ -2060,32 +2116,41 @@ 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) (let ((session (elmo-imap4-get-session folder))) - (elmo-imap4-set-flag folder numbers "\\Deleted") - (elmo-imap4-send-command-wait session "expunge"))) + (elmo-imap4-session-select-mailbox + session + (elmo-imap4-folder-mailbox-internal folder)) + (unless (elmo-imap4-set-flag folder numbers "\\Deleted") + (error "Failed to set deleted flag")) + (elmo-imap4-send-command session "expunge"))) (defmacro elmo-imap4-detect-search-charset (string) (` (with-temp-buffer @@ -2095,7 +2160,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (defun elmo-imap4-search-internal-primitive (folder session filter from-msgs) (let ((search-key (elmo-filter-key filter)) (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to" - "larger" "smaller")) + "larger" "smaller" "flag")) (total 0) (length (length from-msgs)) charset set-list end results) @@ -2113,6 +2178,9 @@ If optional argument REMOVE is non-nil, remove FLAG." numbers))) (mapcar '(lambda (x) (delete x numbers)) rest) numbers)) + ((string= "flag" search-key) + (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) @@ -2240,7 +2308,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (luna-call-next-method))) (luna-define-method elmo-folder-msgdb-create-plugged - ((folder elmo-imap4-folder) numbers &rest args) + ((folder elmo-imap4-folder) numbers flag-table) (when numbers (let ((session (elmo-imap4-get-session folder)) (headers @@ -2250,6 +2318,7 @@ If optional argument REMOVE is non-nil, remove FLAG." elmo-msgdb-extra-fields)) (total 0) (length (length numbers)) + print-length print-depth rfc2060 set-list) (setq rfc2060 (memq 'imap4rev1 (elmo-imap4-session-capability-internal @@ -2262,12 +2331,10 @@ If optional argument REMOVE is non-nil, remove FLAG." elmo-imap4-overview-fetch-chop-length)) ;; Setup callback. (with-current-buffer (elmo-network-session-buffer session) - (setq elmo-imap4-current-msgdb nil + (setq elmo-imap4-current-msgdb (elmo-make-msgdb) elmo-imap4-seen-messages nil elmo-imap4-fetch-callback 'elmo-imap4-fetch-callback-1 - elmo-imap4-fetch-callback-data (cons args - (elmo-folder-use-flag-p - folder))) + elmo-imap4-fetch-callback-data (cons flag-table folder)) (while set-list (elmo-imap4-send-command-wait session @@ -2287,23 +2354,30 @@ If optional argument REMOVE is non-nil, remove FLAG." (message "Getting overview...done") (when elmo-imap4-seen-messages (elmo-imap4-set-flag folder elmo-imap4-seen-messages "\\Seen")) + ;; cannot setup the global flag while retrieval. + (dolist (number (elmo-msgdb-list-messages elmo-imap4-current-msgdb)) + (elmo-global-flags-set (elmo-msgdb-flags elmo-imap4-current-msgdb + number) + folder number + (elmo-message-entity-field + (elmo-msgdb-message-entity + elmo-imap4-current-msgdb number) + 'message-id))) elmo-imap4-current-msgdb)))) -(luna-define-method elmo-folder-unmark-important-plugged - ((folder elmo-imap4-folder) numbers) - (elmo-imap4-set-flag folder numbers "\\Flagged" 'remove)) - -(luna-define-method elmo-folder-mark-as-important-plugged - ((folder elmo-imap4-folder) numbers) - (elmo-imap4-set-flag folder numbers "\\Flagged")) +(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-unmark-read-plugged - ((folder elmo-imap4-folder) numbers) - (elmo-imap4-set-flag folder numbers "\\Seen" 'remove)) - -(luna-define-method elmo-folder-mark-as-read-plugged - ((folder elmo-imap4-folder) numbers) - (elmo-imap4-set-flag folder numbers "\\Seen")) +(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) @@ -2330,11 +2404,15 @@ If optional argument REMOVE is non-nil, remove FLAG." (defsubst elmo-imap4-folder-diff-plugged (folder) (let ((session (elmo-imap4-get-session folder)) - messages new unread response killed) + messages new unread response killed uidnext) ;;; (elmo-imap4-commit spec) (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 @@ -2342,14 +2420,18 @@ If optional argument REMOVE is non-nil, remove FLAG." (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal folder)) - " (recent unseen messages)"))) + " (recent unseen messages uidnext)"))) (setq response (elmo-imap4-response-value response 'status)) (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))) - (if killed - (setq messages (- messages - (elmo-msgdb-killed-list-length - killed)))) + ;; + (when killed + (when (and (consp (car killed)) + (eq (car (car killed)) 1)) + (setq messages (- uidnext (cdr (car killed)) 1))) + (setq messages (- messages + (elmo-msgdb-killed-list-length (cdr killed))))) (setq new (elmo-imap4-response-value response 'recent) unread (elmo-imap4-response-value response 'unseen)) (if (< unread new) (setq new unread)) @@ -2358,8 +2440,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (luna-define-method elmo-folder-diff-plugged ((folder elmo-imap4-folder)) (elmo-imap4-folder-diff-plugged folder)) -(luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder) - &optional number-alist) +(luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder)) (setq elmo-imap4-server-diff-async-callback elmo-folder-diff-async-callback) (setq elmo-imap4-server-diff-async-callback-data @@ -2381,7 +2462,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (message "Selecting %s..." (elmo-folder-name-internal folder)) (if load-msgdb - (setq msgdb (elmo-msgdb-load folder 'silent))) + (setq msgdb (elmo-folder-msgdb-load folder 'silent))) (elmo-folder-set-killed-list-internal folder (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))) @@ -2393,13 +2474,17 @@ 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) - (error (or - (elmo-imap4-response-error-text response) - (format "Select %s failed" mailbox))))) + (error "%s" + (or (elmo-imap4-response-error-text response) + (format "Select %s failed" mailbox))))) (message "Selecting %s...done" (elmo-folder-name-internal folder)) (elmo-folder-set-msgdb-internal @@ -2424,10 +2509,10 @@ If optional argument REMOVE is non-nil, remove FLAG." (luna-define-method elmo-find-fetch-strategy ((folder elmo-imap4-folder) entity &optional ignore-cache) - (let ((number (elmo-msgdb-overview-entity-get-number entity)) + (let ((number (elmo-message-entity-number entity)) cache-file size message-id) - (setq size (elmo-msgdb-overview-entity-get-size entity)) - (setq message-id (elmo-msgdb-overview-entity-get-id entity)) + (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))) @@ -2477,7 +2562,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (elmo-imap4-folder-mailbox-internal folder))))) (luna-define-method elmo-folder-append-buffer - ((folder elmo-imap4-folder) unread &optional number) + ((folder elmo-imap4-folder) &optional flags number) (if (elmo-folder-plugged-p folder) (let ((session (elmo-imap4-get-session folder)) send-buffer result) @@ -2493,13 +2578,27 @@ If optional argument REMOVE is non-nil, remove FLAG." "append " (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal folder)) - (if unread " () " " (\\Seen) ") + (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"))) + " ") + ;; XX KEYWORD flags + ") ") + " () ") (elmo-imap4-buffer-literal send-buffer)))) (kill-buffer send-buffer)) result) ;; Unplugged (if elmo-enable-disconnected-operation - (elmo-folder-append-buffer-dop folder unread number) + (elmo-folder-append-buffer-dop folder flags number) (error "Unplugged")))) (eval-when-compile @@ -2512,9 +2611,30 @@ 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 unread-marks - &optional same-number) + ((folder elmo-imap4-folder) src-folder numbers &optional same-number) (if (and (eq (elmo-folder-type-internal src-folder) 'imap4) (elmo-imap4-identical-system-p folder src-folder) (elmo-folder-plugged-p folder)) @@ -2565,13 +2685,14 @@ If optional argument REMOVE is non-nil, remove FLAG." (unless elmo-inhibit-display-retrieval-progress (elmo-display-progress 'elmo-imap4-display-literal-progress "Retrieving..." 100) ; remove progress bar. - (message "Retrieving...done.")) + (message "Retrieving...done")) (if (setq response (elmo-imap4-response-bodydetail-text (elmo-imap4-response-value-all response 'fetch))) (with-current-buffer outbuf (erase-buffer) (insert response) + (elmo-delete-cr-buffer) t)))) (luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder) @@ -2605,7 +2726,13 @@ If optional argument REMOVE is non-nil, remove FLAG." (goto-char (point-min)) (std11-field-body (symbol-name field))))) +(luna-define-method elmo-folder-search-requires-msgdb-p ((folder + elmo-imap4-folder) + condition) + 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))