X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=elmo%2Felmo-imap4.el;h=8c8221ff51028b2cc7153224013d5273bf26d0a2;hb=db0c79dbf84aba8cd087793299d734bdbc381051;hp=f59cddb3800c5125abcff2b44a29163d790a3957;hpb=9e39553b80115a949a7f04ddced4459a7797f8bd;p=elisp%2Fwanderlust.git diff --git a/elmo/elmo-imap4.el b/elmo/elmo-imap4.el index f59cddb..8c8221f 100644 --- a/elmo/elmo-imap4.el +++ b/elmo/elmo-imap4.el @@ -56,7 +56,26 @@ (Except `\\Deleted' flag).") (defvar elmo-imap4-overview-fetch-chop-length 200 - "*Number of overviews to fetch in one request in imap4.") + "*Number of overviews to fetch in one request.") + +;; c.f. rfc2683 3.2.1.5 Long Command Lines +;; +;; "A client should limit the length of the command lines it generates +;; to approximately 1000 octets (including all quoted strings but not +;; including literals). If the client is unable to group things into +;; ranges so that the command line is within that length, it should +;; split the request into multiple commands. The client should use +;; literals instead of long quoted strings, in order to keep the command +;; length down. +;; For its part, a server should allow for a command line of at least +;; 8000 octets. This provides plenty of leeway for accepting reasonable +;; length commands from clients. The server should send a BAD response +;; to a command that does not end within the server's maximum accepted +;; command length. " + +;; To limit command line length, chop number set. +(defvar elmo-imap4-number-set-chop-length 1000 + "*Number of messages to specify as a number-set argument for one request.") (defvar elmo-imap4-force-login nil "*Non-nil forces to try 'login' if there is no 'auth' capability in imapd.") @@ -72,8 +91,10 @@ "Use cache in imap4 folder.") (defvar elmo-imap4-extra-namespace-alist - '(("^{.*/nntp}.*$" . ".")) ; Default is for UW's remote nntp mailbox... - "Extra namespace alist. A list of cons cell like: (REGEXP . DELIMITER).") + '(("^\\({.*/nntp}\\).*$" . ".")) ; Default is for UW's remote nntp mailbox... + "Extra namespace alist. +A list of cons cell like: (REGEXP . DELIMITER). +REGEXP should have a grouping for namespace prefix.") ;; ;;; internal variables ;; @@ -179,7 +200,6 @@ Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"") (insert "NO LOGGING\n") (insert (apply 'format message args) "\n"))))) - (defsubst elmo-imap4-decode-folder-string (string) (if elmo-imap4-use-modified-utf7 (utf7-decode-string string 'imap) @@ -233,9 +253,9 @@ Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"") ; "Send COMMAND to the SESSION and wait for response. ; Returns RESPONSE (parsed lisp object) of IMAP session." ; (elmo-imap4-read-response session -; (elmo-imap4-send-command -; session -; command))) +; (elmo-imap4-send-command +; session +; command))) (defun elmo-imap4-send-command-wait (session command) "Send COMMAND to the SESSION. @@ -265,7 +285,7 @@ Returns a TAG string which is assigned to the COMMAND." (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.)")) + (error "IMAP process is running. Please wait (or plug again)")) (setq elmo-imap4-parsing t) (elmo-imap4-debug "<-(%s)- %s" tag command) (while (setq token (car command-args)) @@ -621,7 +641,12 @@ BUFFER must be a single-byte buffer." (elmo-imap4-folder-mailbox-internal folder))))) (defun elmo-imap4-get-session (folder &optional if-exists) - (elmo-network-get-session 'elmo-imap4-session "IMAP" folder if-exists)) + (elmo-network-get-session 'elmo-imap4-session + (concat + (if (elmo-folder-biff-internal folder) + "BIFF-") + "IMAP") + folder if-exists)) (defun elmo-imap4-session-select-mailbox (session mailbox &optional force no-error) @@ -1557,11 +1582,11 @@ Return nil if no complete line has arrived." (if (eq (length prefix) 0) (progn (setq default-delim delim) nil) (cons - (concat "^" + (concat "^\\(" (if (string= (downcase prefix) "inbox") "[Ii][Nn][Bb][Oo][Xx]" (regexp-quote prefix)) - ".*$") + "\\).*$") delim))) (elmo-imap4-nth i ns)))))) (if default-delim @@ -1867,13 +1892,21 @@ Return nil if no complete line has arrived." (with-current-buffer (elmo-network-session-buffer session) elmo-imap4-server-namespace))) elmo-imap4-default-hierarchy-delimiter)) + ;; Append delimiter when root with namespace. + (root (if (and (match-end 1) + (string= (substring root (match-end 1)) + "")) + (concat root delim) + root)) result append-serv type) (setq result (elmo-imap4-response-get-selectable-mailbox-list (elmo-imap4-send-command-wait session (list "list " (elmo-imap4-mailbox root) " *")))) - (unless (string= (elmo-net-folder-user-internal folder) - elmo-imap4-default-user) + (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)))) (unless (eq (elmo-net-folder-auth-internal folder) (or elmo-imap4-default-authenticate-type 'clear)) @@ -1897,6 +1930,7 @@ Return nil if no complete line has arrived." type))))) (if one-level (let ((re-delim (regexp-quote delim)) + (case-fold-search nil) folder ret has-child-p) ;; Append delimiter (when (and root @@ -1947,10 +1981,13 @@ Return nil if no complete line has arrived." (elmo-imap4-folder-mailbox-internal folder) 'force 'notify-bye)))) +(luna-define-method elmo-folder-creatable-p ((folder elmo-imap4-folder)) + t) + (luna-define-method elmo-folder-writable-p ((folder elmo-imap4-folder)) t) -(luna-define-method elmo-folder-delete ((folder elmo-imap4-folder)) +(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) @@ -1978,15 +2015,20 @@ Return nil if no complete line has arrived." (elmo-imap4-folder-mailbox-internal folder)) " " (elmo-imap4-mailbox - (elmo-imap4-folder-mailbox-internal new-folder)))))) + (elmo-imap4-folder-mailbox-internal new-folder)))) + (elmo-imap4-session-set-current-mailbox-internal + session (elmo-imap4-folder-mailbox-internal new-folder)))) (defun elmo-imap4-copy-messages (src-folder dst-folder numbers) (let ((session (elmo-imap4-get-session src-folder)) - (set-list (elmo-imap4-make-number-set-list numbers))) + (set-list (elmo-imap4-make-number-set-list + numbers + elmo-imap4-number-set-chop-length)) + succeeds) (elmo-imap4-session-select-mailbox session (elmo-imap4-folder-mailbox-internal src-folder)) - (when set-list + (while set-list (if (elmo-imap4-send-command-wait session (list (format @@ -1997,7 +2039,9 @@ Return nil if no complete line has arrived." (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal dst-folder)))) - numbers)))) + (setq succeeds (append succeeds numbers))) + (setq set-list (cdr set-list))) + succeeds)) (defun elmo-imap4-set-flag (folder numbers flag &optional remove) "Set flag on messages. @@ -2006,24 +2050,30 @@ NUMBERS is the message numbers to be flagged. FLAG is the flag name. If optional argument REMOVE is non-nil, remove FLAG." (let ((session (elmo-imap4-get-session folder)) - set-list) + response set-list) (elmo-imap4-session-select-mailbox session (elmo-imap4-folder-mailbox-internal folder)) - (setq set-list (elmo-imap4-make-number-set-list numbers)) - (when set-list + (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)) - (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))))) + (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) @@ -2038,8 +2088,12 @@ 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")) - charset) + (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to" + "larger" "smaller")) + (total 0) + (length (length from-msgs)) + charset set-list end results) + (message "Searching...") (cond ((string= "last" search-key) (let ((numbers (or from-msgs (elmo-folder-list-messages folder)))) @@ -2055,68 +2109,96 @@ If optional argument REMOVE is non-nil, remove FLAG." numbers)) ((or (string= "since" search-key) (string= "before" search-key)) - (setq search-key (concat "sent" search-key)) - (elmo-imap4-response-value - (elmo-imap4-send-command-wait session - (format - (if elmo-imap4-use-uid - "uid search %s%s%s %s" - "search %s%s%s %s") - (if from-msgs - (concat - (if elmo-imap4-use-uid "uid ") - (cdr - (car - (elmo-imap4-make-number-set-list - from-msgs))) - " ") - "") - (if (eq (elmo-filter-type filter) - 'unmatch) - "not " "") - search-key - (elmo-date-get-description - (elmo-date-get-datevec - (elmo-filter-value filter))))) - 'search)) + (setq search-key (concat "sent" search-key) + set-list (elmo-imap4-make-number-set-list + from-msgs + elmo-imap4-number-set-chop-length) + end nil) + (while (not end) + (setq results + (append + results + (elmo-imap4-response-value + (elmo-imap4-send-command-wait + session + (format + (if elmo-imap4-use-uid + "uid search %s%s%s %s" + "search %s%s%s %s") + (if from-msgs + (concat + (if elmo-imap4-use-uid "uid ") + (cdr (car set-list)) + " ") + "") + (if (eq (elmo-filter-type filter) + 'unmatch) + "not " "") + search-key + (elmo-date-get-description + (elmo-date-get-datevec + (elmo-filter-value filter))))) + 'search))) + (when (> length elmo-display-progress-threshold) + (setq total (+ total (car (car set-list)))) + (elmo-display-progress + 'elmo-imap4-search "Searching..." + (/ (* total 100) length))) + (setq set-list (cdr set-list) + end (null set-list))) + results) (t (setq charset (if (eq (length (elmo-filter-value filter)) 0) (setq charset 'us-ascii) (elmo-imap4-detect-search-charset - (elmo-filter-value filter)))) - (elmo-imap4-response-value - (elmo-imap4-send-command-wait session - (list - (if elmo-imap4-use-uid "uid ") - "search " - "CHARSET " - (elmo-imap4-astring - (symbol-name charset)) - " " - (if from-msgs - (concat - (if elmo-imap4-use-uid "uid ") - (cdr - (car - (elmo-imap4-make-number-set-list - from-msgs))) - " ") - "") - (if (eq (elmo-filter-type filter) - 'unmatch) - "not " "") - (format "%s%s " - (if (member - (elmo-filter-key filter) - imap-search-keys) - "" - "header ") - (elmo-filter-key filter)) - (elmo-imap4-astring - (encode-mime-charset-string - (elmo-filter-value filter) charset)))) - 'search))))) + (elmo-filter-value filter))) + set-list (elmo-imap4-make-number-set-list + from-msgs + elmo-imap4-number-set-chop-length) + end nil) + (while (not end) + (setq results + (append + results + (elmo-imap4-response-value + (elmo-imap4-send-command-wait + session + (list + (if elmo-imap4-use-uid "uid ") + "search " + "CHARSET " + (elmo-imap4-astring + (symbol-name charset)) + " " + (if from-msgs + (concat + (if elmo-imap4-use-uid "uid ") + (cdr (car set-list)) + " ") + "") + (if (eq (elmo-filter-type filter) + 'unmatch) + "not " "") + (format "%s%s " + (if (member + (elmo-filter-key filter) + imap-search-keys) + "" + "header ") + (elmo-filter-key filter)) + (elmo-imap4-astring + (encode-mime-charset-string + (elmo-filter-value filter) charset)))) + 'search))) + (when (> length elmo-display-progress-threshold) + (setq total (+ total (car (car set-list)))) + (elmo-display-progress + 'elmo-imap4-search "Searching..." + (/ (* total 100) length))) + (setq set-list (cdr set-list) + end (null set-list))) + results)))) (defun elmo-imap4-search-internal (folder session condition from-msgs) (let (result) @@ -2140,14 +2222,16 @@ If optional argument REMOVE is non-nil, remove FLAG." folder session (nth 2 condition) from-msgs))) result (sort result '<)))))) -(luna-define-method elmo-folder-search ((folder elmo-imap4-folder) - condition &optional numbers) - (save-excursion - (let ((session (elmo-imap4-get-session folder))) - (elmo-imap4-session-select-mailbox - session - (elmo-imap4-folder-mailbox-internal folder)) - (elmo-imap4-search-internal folder session condition numbers)))) +(luna-define-method elmo-folder-search :around ((folder elmo-imap4-folder) + condition &optional numbers) + (if (elmo-folder-plugged-p folder) + (save-excursion + (let ((session (elmo-imap4-get-session folder))) + (elmo-imap4-session-select-mailbox + session + (elmo-imap4-folder-mailbox-internal folder)) + (elmo-imap4-search-internal folder session condition numbers))) + (luna-call-next-method))) (luna-define-method elmo-folder-msgdb-create-plugged ((folder elmo-imap4-folder) numbers &rest args) @@ -2240,8 +2324,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (defsubst elmo-imap4-folder-diff-plugged (folder) (let ((session (elmo-imap4-get-session folder)) - messages - response killed) + messages new unread response killed) ;;; (elmo-imap4-commit spec) (with-current-buffer (elmo-network-session-buffer session) (setq elmo-imap4-status-callback nil) @@ -2261,9 +2344,10 @@ If optional argument REMOVE is non-nil, remove FLAG." (setq messages (- messages (elmo-msgdb-killed-list-length killed)))) - (list (elmo-imap4-response-value response 'recent) - (elmo-imap4-response-value response 'unseen) - messages))) + (setq new (elmo-imap4-response-value response 'recent) + unread (elmo-imap4-response-value response 'unseen)) + (if (< unread new) (setq new unread)) + (list new unread messages))) (luna-define-method elmo-folder-diff-plugged ((folder elmo-imap4-folder)) (elmo-imap4-folder-diff-plugged folder)) @@ -2279,7 +2363,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (luna-define-method elmo-folder-open :around ((folder elmo-imap4-folder) &optional load-msgdb) (if (elmo-folder-plugged-p folder) - (let (session mailbox msgdb response tag) + (let (session mailbox msgdb result response tag) (condition-case err (progn (setq session (elmo-imap4-get-session folder) @@ -2288,12 +2372,32 @@ If optional argument REMOVE is non-nil, remove FLAG." (list "select " (elmo-imap4-mailbox mailbox)))) + (message "Selecting %s..." + (elmo-folder-name-internal folder)) (if load-msgdb - (setq msgdb (elmo-msgdb-load folder))) + (setq msgdb (elmo-msgdb-load folder 'silent))) (elmo-folder-set-killed-list-internal folder (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))) - (setq response (elmo-imap4-read-response session tag))) + (if (setq result (elmo-imap4-response-ok-p + (setq response + (elmo-imap4-read-response session tag)))) + (progn + (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))))) + (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))))) + (message "Selecting %s...done" + (elmo-folder-name-internal folder)) + (elmo-folder-set-msgdb-internal + folder msgdb)) (quit (if response (elmo-imap4-session-set-current-mailbox-internal @@ -2307,11 +2411,7 @@ If optional argument REMOVE is non-nil, remove FLAG." session mailbox) (and session (elmo-imap4-session-set-current-mailbox-internal - session nil))))) - (if load-msgdb - (elmo-folder-set-msgdb-internal - folder - (or msgdb (elmo-msgdb-load folder))))) + session nil)))))) (luna-call-next-method))) ;; elmo-folder-open-internal: do nothing. @@ -2363,7 +2463,7 @@ If optional argument REMOVE is non-nil, remove FLAG." (elmo-file-cache-path cache-file))))))) -(luna-define-method elmo-folder-create ((folder elmo-imap4-folder)) +(luna-define-method elmo-folder-create-plugged ((folder elmo-imap4-folder)) (elmo-imap4-send-command-wait (elmo-imap4-get-session folder) (list "create " @@ -2458,14 +2558,15 @@ If optional argument REMOVE is non-nil, remove FLAG." (setq elmo-imap4-display-literal-progress nil)) (unless elmo-inhibit-display-retrieval-progress (elmo-display-progress 'elmo-imap4-display-literal-progress - "" 100) ; remove progress bar. + "Retrieving..." 100) ; remove progress bar. (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))))) + (insert response) + t)))) (luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder) number strategy