Use "[]" as the parameter instead of "".
[gnus] / lisp / nnimap.el
index 7d2c9a7..fddb406 100644 (file)
 (eval-when-compile
   (require 'cl))
 
 (eval-when-compile
   (require 'cl))
 
+(require 'nnheader)
+(require 'gnus-util)
+(require 'gnus)
+(require 'nnoo)
 (require 'netrc)
 
 (nnoo-declare nnimap)
 (require 'netrc)
 
 (nnoo-declare nnimap)
@@ -62,7 +66,21 @@ Values are `ssl' and `network'.")
 This is always done if the server supports UID EXPUNGE, but it's
 not done by default on servers that doesn't support that command.")
 
 This is always done if the server supports UID EXPUNGE, but it's
 not done by default on servers that doesn't support that command.")
 
+(defvoo nnimap-authenticator nil
+  "How nnimap authenticate itself to the server.
+Possible choices are nil (use default methods) or `anonymous'.")
+
+(defvoo nnimap-fetch-partial-articles nil
+  "If non-nil, nnimap will fetch partial articles.
+If t, nnimap will fetch only the first part.  If a string, it
+will fetch all parts that have types that match that string.  A
+likely value would be \"text/\" to automatically fetch all
+textual parts.")
+
 (defvoo nnimap-connection-alist nil)
 (defvoo nnimap-connection-alist nil)
+
+(defvoo nnimap-current-infos nil)
+
 (defvar nnimap-process nil)
 
 (defvar nnimap-status-string "")
 (defvar nnimap-process nil)
 
 (defvar nnimap-status-string "")
@@ -71,7 +89,7 @@ not done by default on servers that doesn't support that command.")
   "Internal variable with default value for `nnimap-split-download-body'.")
 
 (defstruct nnimap
   "Internal variable with default value for `nnimap-split-download-body'.")
 
 (defstruct nnimap
-  group process commands capabilities)
+  group process commands capabilities select-result newlinep)
 
 (defvar nnimap-object nil)
 
 
 (defvar nnimap-object nil)
 
@@ -91,7 +109,7 @@ not done by default on servers that doesn't support that command.")
 (defun nnimap-buffer ()
   (nnimap-find-process-buffer nntp-server-buffer))
 
 (defun nnimap-buffer ()
   (nnimap-find-process-buffer nntp-server-buffer))
 
-(defun nnimap-retrieve-headers (articles &optional group server fetch-old)
+(deffoo nnimap-retrieve-headers (articles &optional group server fetch-old)
   (with-current-buffer nntp-server-buffer
     (erase-buffer)
     (when (nnimap-possibly-change-group group server)
   (with-current-buffer nntp-server-buffer
     (erase-buffer)
     (when (nnimap-possibly-change-group group server)
@@ -139,7 +157,7 @@ not done by default on servers that doesn't support that command.")
        (delete-region (line-beginning-position) (line-end-position))
        (insert (format "211 %s Article retrieved." article))
        (forward-line 1)
        (delete-region (line-beginning-position) (line-end-position))
        (insert (format "211 %s Article retrieved." article))
        (forward-line 1)
-       (insert (format "Bytes: %d\n" bytes))
+       (insert (format "Chars: %d\n" bytes))
        (when lines
          (insert (format "Lines: %s\n" lines)))
        (re-search-forward "^\r$")
        (when lines
          (insert (format "Lines: %s\n" lines)))
        (re-search-forward "^\r$")
@@ -167,7 +185,7 @@ not done by default on servers that doesn't support that command.")
         result))
       (mapconcat #'identity (nreverse result) ",")))))
 
         result))
       (mapconcat #'identity (nreverse result) ",")))))
 
-(defun nnimap-open-server (server &optional defs)
+(deffoo nnimap-open-server (server &optional defs)
   (if (nnimap-server-opened server)
       t
     (unless (assq 'nnimap-address defs)
   (if (nnimap-server-opened server)
       t
     (unless (assq 'nnimap-address defs)
@@ -190,64 +208,83 @@ not done by default on servers that doesn't support that command.")
     (current-buffer)))
 
 (defun nnimap-open-shell-stream (name buffer host port)
     (current-buffer)))
 
 (defun nnimap-open-shell-stream (name buffer host port)
-  (let ((process (start-process name buffer shell-file-name
-                               shell-command-switch
-                               (format-spec
-                                nnimap-shell-program
-                                (format-spec-make
-                                 ?s host
-                                 ?p port)))))
-    process))
+  (let ((process-connection-type nil))
+    (start-process name buffer shell-file-name
+                  shell-command-switch
+                  (format-spec
+                   nnimap-shell-program
+                   (format-spec-make
+                    ?s host
+                    ?p port)))))
+
+(defun nnimap-credentials (address ports)
+  (let (port credentials)
+    ;; Request the credentials from all ports, but only query on the
+    ;; last port if all the previous ones have failed.
+    (while (and (null credentials)
+               (setq port (pop ports)))
+      (setq credentials
+           (auth-source-user-or-password
+            '("login" "password") address port nil (null ports))))
+    credentials))
 
 (defun nnimap-open-connection (buffer)
   (with-current-buffer (nnimap-make-process-buffer buffer)
     (let* ((coding-system-for-read 'binary)
           (coding-system-for-write 'binary)
 
 (defun nnimap-open-connection (buffer)
   (with-current-buffer (nnimap-make-process-buffer buffer)
     (let* ((coding-system-for-read 'binary)
           (coding-system-for-write 'binary)
-          (credentials
+          (ports
            (cond
             ((eq nnimap-stream 'network)
            (cond
             ((eq nnimap-stream 'network)
-             (open-network-stream "*nnimap*" (current-buffer) nnimap-address
-                                  (or nnimap-server-port
-                                      (if (netrc-find-service-number "imap")
-                                          "imap"
-                                        "143")))
-             (auth-source-user-or-password
-              '("login" "password") nnimap-address "imap" nil t))
-            ((eq nnimap-stream 'stream)
+             (open-network-stream
+              "*nnimap*" (current-buffer) nnimap-address
+              (or nnimap-server-port
+                  (if (netrc-find-service-number "imap")
+                      "imap"
+                    "143")))
+             '("143" "imap"))
+            ((eq nnimap-stream 'shell)
              (nnimap-open-shell-stream
               "*nnimap*" (current-buffer) nnimap-address
               (or nnimap-server-port "imap"))
              (nnimap-open-shell-stream
               "*nnimap*" (current-buffer) nnimap-address
               (or nnimap-server-port "imap"))
-             (auth-source-user-or-password
-              '("login" "password") nnimap-address "imap" nil t))
+             '("imap"))
             ((eq nnimap-stream 'ssl)
             ((eq nnimap-stream 'ssl)
-             (open-tls-stream "*nnimap*" (current-buffer) nnimap-address
-                              (or nnimap-server-port
-                                  (if (netrc-find-service-number "imaps")
-                                      "imaps"
-                                    "993")))
-             (or
-              (auth-source-user-or-password
-               '("login" "password") nnimap-address "imap")
-              (auth-source-user-or-password
-               '("login" "password") nnimap-address "imaps" nil t))))))
+             (open-tls-stream
+              "*nnimap*" (current-buffer) nnimap-address
+              (or nnimap-server-port
+                  (if (netrc-find-service-number "imaps")
+                      "imaps"
+                    "993")))
+             '("143" "993" "imap" "imaps"))))
+          connection-result login-result credentials)
       (setf (nnimap-process nnimap-object)
            (get-buffer-process (current-buffer)))
       (setf (nnimap-process nnimap-object)
            (get-buffer-process (current-buffer)))
-      (unless credentials
-       (delete-process (nnimap-process nnimap-object)))
       (when (and (nnimap-process nnimap-object)
                 (memq (process-status (nnimap-process nnimap-object))
                       '(open run)))
        (gnus-set-process-query-on-exit-flag (nnimap-process nnimap-object) nil)
       (when (and (nnimap-process nnimap-object)
                 (memq (process-status (nnimap-process nnimap-object))
                       '(open run)))
        (gnus-set-process-query-on-exit-flag (nnimap-process nnimap-object) nil)
-       (let ((result (nnimap-command "LOGIN %S %S"
-                                     (car credentials) (cadr credentials))))
-         (if (not (car result))
-             (progn
+       (when (setq connection-result (nnimap-wait-for-connection))
+         (unless (equal connection-result "PREAUTH")
+           (if (not (setq credentials
+                          (if (eq nnimap-authenticator 'anonymous)
+                              (list "anonymous"
+                                    (message-make-address))
+                            (nnimap-credentials
+                             nnimap-address
+                             (if nnimap-server-port
+                                 (cons (format "%s" nnimap-server-port) ports)
+                               ports)))))
+               (setq nnimap-object nil)
+             (setq login-result (nnimap-command "LOGIN %S %S"
+                                                (car credentials)
+                                                (cadr credentials)))
+             (unless (car login-result)
                (delete-process (nnimap-process nnimap-object))
                (delete-process (nnimap-process nnimap-object))
-               nil)
+               (setq nnimap-object nil))))
+         (when nnimap-object
            (setf (nnimap-capabilities nnimap-object)
                  (mapcar
                   #'upcase
            (setf (nnimap-capabilities nnimap-object)
                  (mapcar
                   #'upcase
-                  (or (nnimap-find-parameter "CAPABILITY" (cdr result))
+                  (or (nnimap-find-parameter "CAPABILITY" (cdr login-result))
                       (nnimap-find-parameter
                        "CAPABILITY" (cdr (nnimap-command "CAPABILITY"))))))
            (when (member "QRESYNC" (nnimap-capabilities nnimap-object))
                       (nnimap-find-parameter
                        "CAPABILITY" (cdr (nnimap-command "CAPABILITY"))))))
            (when (member "QRESYNC" (nnimap-capabilities nnimap-object))
@@ -266,24 +303,25 @@ not done by default on servers that doesn't support that command.")
        (setq result (cdr (cadr elem))))))
     result))
 
        (setq result (cdr (cadr elem))))))
     result))
 
-(defun nnimap-close-server (&optional server)
+(deffoo nnimap-close-server (&optional server)
   t)
 
   t)
 
-(defun nnimap-request-close ()
+(deffoo nnimap-request-close ()
   t)
 
   t)
 
-(defun nnimap-server-opened (&optional server)
+(deffoo nnimap-server-opened (&optional server)
   (and (nnoo-current-server-p 'nnimap server)
        nntp-server-buffer
        (gnus-buffer-live-p nntp-server-buffer)
        (nnimap-find-connection nntp-server-buffer)))
 
   (and (nnoo-current-server-p 'nnimap server)
        nntp-server-buffer
        (gnus-buffer-live-p nntp-server-buffer)
        (nnimap-find-connection nntp-server-buffer)))
 
-(defun nnimap-status-message (&optional server)
+(deffoo nnimap-status-message (&optional server)
   nnimap-status-string)
 
   nnimap-status-string)
 
-(defun nnimap-request-article (article &optional group server to-buffer)
+(deffoo nnimap-request-article (article &optional group server to-buffer)
   (with-current-buffer nntp-server-buffer
   (with-current-buffer nntp-server-buffer
-    (let ((result (nnimap-possibly-change-group group server)))
+    (let ((result (nnimap-possibly-change-group group server))
+         parts)
       (when (stringp article)
        (setq article (nnimap-find-article-by-message-id group article)))
       (when (and result
       (when (stringp article)
        (setq article (nnimap-find-article-by-message-id group article)))
       (when (and result
@@ -291,15 +329,27 @@ not done by default on servers that doesn't support that command.")
        (erase-buffer)
        (with-current-buffer (nnimap-buffer)
          (erase-buffer)
        (erase-buffer)
        (with-current-buffer (nnimap-buffer)
          (erase-buffer)
+         (when nnimap-fetch-partial-articles
+           (if (eq nnimap-fetch-partial-articles t)
+               (setq parts '(1))
+             (nnimap-command "UID FETCH %d (BODYSTRUCTURE)" article)
+             (goto-char (point-min))
+             (when (re-search-forward "FETCH.*BODYSTRUCTURE" nil t)
+               (let ((structure (ignore-errors (read (current-buffer)))))
+                 (setq parts (nnimap-find-wanted-parts structure))))))
          (setq result
                (nnimap-command
                 (if (member "IMAP4REV1" (nnimap-capabilities nnimap-object))
                     "UID FETCH %d BODY.PEEK[]"
                   "UID FETCH %d RFC822.PEEK")
          (setq result
                (nnimap-command
                 (if (member "IMAP4REV1" (nnimap-capabilities nnimap-object))
                     "UID FETCH %d BODY.PEEK[]"
                   "UID FETCH %d RFC822.PEEK")
-                article)))
+                article))
+         ;; Check that we really got an article.
+         (goto-char (point-min))
+         (unless (looking-at "\\* [0-9]+ FETCH")
+           (setq result nil)))
        (let ((buffer (nnimap-find-process-buffer (current-buffer))))
          (when (car result)
        (let ((buffer (nnimap-find-process-buffer (current-buffer))))
          (when (car result)
-           (with-current-buffer to-buffer
+           (with-current-buffer (or to-buffer nntp-server-buffer)
              (insert-buffer-substring buffer)
              (goto-char (point-min))
              (let ((bytes (nnimap-get-length)))
              (insert-buffer-substring buffer)
              (goto-char (point-min))
              (let ((bytes (nnimap-get-length)))
@@ -308,23 +358,71 @@ not done by default on servers that doesn't support that command.")
                (goto-char (+ (point) bytes))
                (delete-region (point) (point-max))
                (nnheader-ms-strip-cr))
                (goto-char (+ (point) bytes))
                (delete-region (point) (point-max))
                (nnheader-ms-strip-cr))
-             t)))))))
-
-(defun nnimap-request-group (group &optional server dont-check)
+             (cons group article))))))))
+
+(defun nnimap-find-wanted-parts (structure)
+  (message-flatten-list (nnimap-find-wanted-parts-1 structure "")))
+
+(defun nnimap-find-wanted-parts-1 (structure prefix)
+  (let ((num 1)
+       parts)
+    (while (consp (car structure))
+      (let ((sub (pop structure)))
+       (if (consp (car sub))
+           (push (nnimap-find-wanted-parts-1
+                  sub (if (string= prefix "")
+                          (number-to-string num)
+                        (format "%s.%s" prefix num)))
+                 parts)
+         (let ((type (format "%s/%s" (nth 0 sub) (nth 1 sub))))
+           (when (string-match nnimap-fetch-partial-articles type)
+             (push (if (string= prefix "")
+                       (number-to-string num)
+                     (format "%s.%s" prefix num))
+                   parts)))
+         (incf num))))
+    (nreverse parts)))
+
+(deffoo nnimap-request-group (group &optional server dont-check info)
   (with-current-buffer nntp-server-buffer
     (let ((result (nnimap-possibly-change-group group server))
   (with-current-buffer nntp-server-buffer
     (let ((result (nnimap-possibly-change-group group server))
-         articles)
+         articles active marks high low)
       (when result
       (when result
-       (setq articles (nnimap-get-flags "1:*"))
-       (erase-buffer)
-       (insert
-        (format
-         "211 %d %d %d %S\n"
-         (length articles)
-         (or (caar articles) 0)
-         (or (caar (last articles)) 0)
-         group))
-       t))))
+       (if (and dont-check
+                (setq active (nth 2 (assoc group nnimap-current-infos))))
+           (insert (format "211 %d %d %d %S\n"
+                           (- (cdr active) (car active))
+                           (car active)
+                           (cdr active)
+                           group))
+         (with-current-buffer (nnimap-buffer)
+           (erase-buffer)
+           (let ((group-sequence
+                  (nnimap-send-command "SELECT %S" (utf7-encode group)))
+                 (flag-sequence
+                  (nnimap-send-command "UID FETCH 1:* FLAGS")))
+             (nnimap-wait-for-response flag-sequence)
+             (setq marks
+                   (nnimap-flags-to-marks
+                    (nnimap-parse-flags
+                     (list (list group-sequence flag-sequence 1 group)))))
+             (when info
+               (nnimap-update-infos marks (list info)))
+             (goto-char (point-max))
+             (cond
+              (marks
+               (setq high (nth 3 (car marks))
+                     low (nth 4 (car marks))))
+              ((re-search-backward "UIDNEXT \\([0-9]+\\)" nil t)
+               (setq high (string-to-number (match-string 1))
+                     low 1)))))
+         (erase-buffer)
+         (insert
+          (format
+           "211 %d %d %d %S\n"
+           (1+ (- high low))
+           low high group))))
+      t)))
 
 (defun nnimap-get-flags (spec)
   (let ((articles nil)
 
 (defun nnimap-get-flags (spec)
   (let ((articles nil)
@@ -341,7 +439,7 @@ not done by default on servers that doesn't support that command.")
              articles)))
     (nreverse articles)))
 
              articles)))
     (nreverse articles)))
 
-(defun nnimap-close-group (group &optional server)
+(deffoo nnimap-close-group (group &optional server)
   t)
 
 (deffoo nnimap-request-move-article (article group server accept-form
   t)
 
 (deffoo nnimap-request-move-article (article group server accept-form
@@ -413,7 +511,7 @@ not done by default on servers that doesn't support that command.")
        (push flag flags)))
     flags))
 
        (push flag flags)))
     flags))
 
-(defun nnimap-request-set-mark (group actions &optional server)
+(deffoo nnimap-request-set-mark (group actions &optional server)
   (when (nnimap-possibly-change-group group server)
     (let (sequence)
       (with-current-buffer (nnimap-buffer)
   (when (nnimap-possibly-change-group group server)
     (let (sequence)
       (with-current-buffer (nnimap-buffer)
@@ -445,7 +543,10 @@ not done by default on servers that doesn't support that command.")
                        "APPEND %S {%d}" (utf7-encode group t)
                        (length message)))
        (process-send-string (get-buffer-process (current-buffer)) message)
                        "APPEND %S {%d}" (utf7-encode group t)
                        (length message)))
        (process-send-string (get-buffer-process (current-buffer)) message)
-       (process-send-string (get-buffer-process (current-buffer)) "\r\n")
+       (process-send-string (get-buffer-process (current-buffer))
+                            (if (nnimap-newlinep nnimap-object)
+                                "\n"
+                              "\r\n"))
        (let ((result (nnimap-get-response sequence)))
          (when result
            (cons group
        (let ((result (nnimap-get-response sequence)))
          (when result
            (cons group
@@ -467,7 +568,7 @@ not done by default on servers that doesn't support that command.")
          (push (car (last line)) groups)))
       (nreverse groups))))
 
          (push (car (last line)) groups)))
       (nreverse groups))))
 
-(defun nnimap-request-list (&optional server)
+(deffoo nnimap-request-list (&optional server)
   (nnimap-possibly-change-group nil server)
   (with-current-buffer nntp-server-buffer
     (erase-buffer)
   (nnimap-possibly-change-group nil server)
   (with-current-buffer nntp-server-buffer
     (erase-buffer)
@@ -510,7 +611,7 @@ not done by default on servers that doesn't support that command.")
                                  (or highest exists)))))))))
        t))))
 
                                  (or highest exists)))))))))
        t))))
 
-(defun nnimap-retrieve-group-data-early (server infos)
+(deffoo nnimap-retrieve-group-data-early (server infos)
   (when (nnimap-possibly-change-group nil server)
     (with-current-buffer (nnimap-buffer)
       ;; QRESYNC handling isn't implemented.
   (when (nnimap-possibly-change-group nil server)
     (with-current-buffer (nnimap-buffer)
       ;; QRESYNC handling isn't implemented.
@@ -550,8 +651,9 @@ not done by default on servers that doesn't support that command.")
                    sequences))))
        sequences))))
 
                    sequences))))
        sequences))))
 
-(defun nnimap-finish-retrieve-group-infos (server infos sequences)
-  (when (nnimap-possibly-change-group nil server)
+(deffoo nnimap-finish-retrieve-group-infos (server infos sequences)
+  (when (and sequences
+            (nnimap-possibly-change-group nil server))
     (with-current-buffer (nnimap-buffer)
       ;; Wait for the final data to trickle in.
       (nnimap-wait-for-response (cadar sequences))
     (with-current-buffer (nnimap-buffer)
       ;; Wait for the final data to trickle in.
       (nnimap-wait-for-response (cadar sequences))
@@ -561,7 +663,19 @@ not done by default on servers that doesn't support that command.")
       (nnimap-update-infos (nnimap-flags-to-marks
                            (nnimap-parse-flags
                             (nreverse sequences)))
       (nnimap-update-infos (nnimap-flags-to-marks
                            (nnimap-parse-flags
                             (nreverse sequences)))
-                          infos))))
+                          infos)
+      ;; Finally, just return something resembling an active file in
+      ;; the nntp buffer, so that the agent can save the info, too.
+      (with-current-buffer nntp-server-buffer
+       (erase-buffer)
+       (dolist (info infos)
+         (let* ((group (gnus-info-group info))
+                (active (gnus-active group)))
+           (when active
+             (insert (format "%S %d %d y\n"
+                             (gnus-group-real-name group)
+                             (cdr active)
+                             (car active))))))))))
 
 (defun nnimap-update-infos (flags infos)
   (dolist (info infos)
 
 (defun nnimap-update-infos (flags infos)
   (dolist (info infos)
@@ -596,9 +710,11 @@ not done by default on servers that doesn't support that command.")
          (when (> start-article 1)
            (setq read
                  (gnus-range-nconcat
          (when (> start-article 1)
            (setq read
                  (gnus-range-nconcat
-                  (gnus-sorted-range-intersection
-                   (cons 1 start-article)
-                   (gnus-info-read info))
+                  (if (> start-article 1)
+                      (gnus-sorted-range-intersection
+                       (cons 1 (1- start-article))
+                       (gnus-info-read info))
+                    (gnus-info-read info))
                   read)))
          (gnus-info-set-read info read)
          ;; Update the marks.
                   read)))
          (gnus-info-set-read info read)
          ;; Update the marks.
@@ -617,12 +733,20 @@ not done by default on servers that doesn't support that command.")
              (when (and old-marks
                         (> start-article 1))
                (setq old-marks (gnus-range-difference
              (when (and old-marks
                         (> start-article 1))
                (setq old-marks (gnus-range-difference
-                                (cons start-article high)
-                                old-marks))
+                                old-marks
+                                (cons start-article high)))
                (setq new-marks (gnus-range-nconcat old-marks new-marks)))
              (when new-marks
                (push (cons (car type) new-marks) marks)))
                (setq new-marks (gnus-range-nconcat old-marks new-marks)))
              (when new-marks
                (push (cons (car type) new-marks) marks)))
-           (gnus-info-set-marks info marks)))))))
+           (gnus-info-set-marks info marks t)
+           (nnimap-store-info info (gnus-active group))))))))
+
+(defun nnimap-store-info (info active)
+  (let* ((group (gnus-group-real-name (gnus-info-group info)))
+        (entry (assoc group nnimap-current-infos)))
+    (if entry
+       (setcdr entry (list info active))
+      (push (list group info active) nnimap-current-infos))))
 
 (defun nnimap-flags-to-marks (groups)
   (let (data group totalp uidnext articles start-article mark)
 
 (defun nnimap-flags-to-marks (groups)
   (let (data group totalp uidnext articles start-article mark)
@@ -676,23 +800,29 @@ not done by default on servers that doesn't support that command.")
 (defun nnimap-find-process-buffer (buffer)
   (cadr (assoc buffer nnimap-connection-alist)))
 
 (defun nnimap-find-process-buffer (buffer)
   (cadr (assoc buffer nnimap-connection-alist)))
 
-(defun nnimap-request-post (&optional server)
+(deffoo nnimap-request-post (&optional server)
   (setq nnimap-status-string "Read-only server")
   nil)
 
 (defun nnimap-possibly-change-group (group server)
   (setq nnimap-status-string "Read-only server")
   nil)
 
 (defun nnimap-possibly-change-group (group server)
-  (when (and server
-            (not (nnimap-server-opened server)))
-    (nnimap-open-server server))
-  (if (not group)
-      t
-    (with-current-buffer (nnimap-buffer)
-      (if (equal group (nnimap-group nnimap-object))
-         t
-       (let ((result (nnimap-command "SELECT %S" (utf7-encode group t))))
-         (when (car result)
-           (setf (nnimap-group nnimap-object) group)
-           result))))))
+  (let ((open-result t))
+    (when (and server
+              (not (nnimap-server-opened server)))
+      (setq open-result (nnimap-open-server server)))
+    (cond
+     ((not open-result)
+      nil)
+     ((not group)
+      t)
+     (t
+      (with-current-buffer (nnimap-buffer)
+       (if (equal group (nnimap-group nnimap-object))
+           t
+         (let ((result (nnimap-command "SELECT %S" (utf7-encode group t))))
+           (when (car result)
+             (setf (nnimap-group nnimap-object) group
+                   (nnimap-select-result nnimap-object) result)
+             result))))))))
 
 (defun nnimap-find-connection (buffer)
   "Find the connection delivering to BUFFER."
 
 (defun nnimap-find-connection (buffer)
   "Find the connection delivering to BUFFER."
@@ -712,9 +842,12 @@ not done by default on servers that doesn't support that command.")
   (process-send-string
    (get-buffer-process (current-buffer))
    (nnimap-log-command
   (process-send-string
    (get-buffer-process (current-buffer))
    (nnimap-log-command
-    (format "%d %s\r\n"
+    (format "%d %s%s\n"
            (incf nnimap-sequence)
            (incf nnimap-sequence)
-           (apply #'format args))))
+           (apply #'format args)
+           (if (nnimap-newlinep nnimap-object)
+               ""
+             "\r"))))
   nnimap-sequence)
 
 (defun nnimap-log-command (command)
   nnimap-sequence)
 
 (defun nnimap-log-command (command)
@@ -737,16 +870,30 @@ not done by default on servers that doesn't support that command.")
   (nnimap-wait-for-response sequence)
   (nnimap-parse-response))
 
   (nnimap-wait-for-response sequence)
   (nnimap-parse-response))
 
+(defun nnimap-wait-for-connection ()
+  (let ((process (get-buffer-process (current-buffer))))
+    (goto-char (point-min))
+    (while (and (memq (process-status process)
+                     '(open run))
+               (not (re-search-forward "^\\* .*\n" nil t)))
+      (nnheader-accept-process-output process)
+      (goto-char (point-min)))
+    (forward-line -1)
+    (and (looking-at "\\* \\([A-Z0-9]+\\)")
+        (match-string 1))))
+
 (defun nnimap-wait-for-response (sequence &optional messagep)
 (defun nnimap-wait-for-response (sequence &optional messagep)
-  (goto-char (point-max))
-  (while (or (bobp)
-            (progn
-              (forward-line -1)
-              (not (looking-at (format "^%d .*\n" sequence)))))
-    (when messagep
-      (message "Read %dKB" (/ (buffer-size) 1000)))
-    (nnheader-accept-process-output (get-buffer-process (current-buffer)))
-    (goto-char (point-max))))
+  (let ((process (get-buffer-process (current-buffer))))
+    (goto-char (point-max))
+    (while (and (memq (process-status process)
+                     '(open run))
+               (not (re-search-backward (format "^%d .*\n" sequence)
+                                        (max (point-min) (- (point) 500))
+                                        t)))
+      (when messagep
+       (message "Read %dKB" (/ (buffer-size) 1000)))
+      (nnheader-accept-process-output process)
+      (goto-char (point-max)))))
 
 (defun nnimap-parse-response ()
   (let ((lines (split-string (nnimap-last-response-string) "\r\n" t))
 
 (defun nnimap-parse-response ()
   (let ((lines (split-string (nnimap-last-response-string) "\r\n" t))
@@ -825,7 +972,7 @@ not done by default on servers that doesn't support that command.")
                 "BODY.PEEK[HEADER] BODY.PEEK"
               "RFC822.PEEK"))
            (if nnimap-split-download-body-default
                 "BODY.PEEK[HEADER] BODY.PEEK"
               "RFC822.PEEK"))
            (if nnimap-split-download-body-default
-               ""
+               "[]"
              "[1]")))
    t))
 
              "[1]")))
    t))