Allow the user to say whether to split old messages or not in nnimap.
[gnus] / lisp / nnimap.el
index 21c0222..5c4476f 100644 (file)
@@ -78,6 +78,9 @@ Uses the same syntax as nnmail-split-methods")
 (defvoo nnimap-split-fancy nil
   "Uses the same syntax as nnmail-split-fancy.")
 
+(defvoo nnimap-unsplittable-articles '(%Deleted %Seen)
+  "Articles with the flags in the list will not be considered when splitting.")
+
 (make-obsolete-variable 'nnimap-split-rule "see `nnimap-split-methods'"
                        "Emacs 24.1")
 
@@ -136,6 +139,16 @@ textual parts.")
 (defun nnimap-buffer ()
   (nnimap-find-process-buffer nntp-server-buffer))
 
+(defun nnimap-header-parameters ()
+  (format "(UID RFC822.SIZE BODYSTRUCTURE %s)"
+         (format
+          (if (nnimap-ver4-p)
+              "BODY.PEEK[HEADER.FIELDS %s]"
+            "RFC822.HEADER.LINES %s")
+          (append '(Subject From Date Message-Id
+                            References In-Reply-To Xref)
+                  nnmail-extra-headers))))
+
 (deffoo nnimap-retrieve-headers (articles &optional group server fetch-old)
   (with-current-buffer nntp-server-buffer
     (erase-buffer)
@@ -146,14 +159,7 @@ textual parts.")
         (nnimap-send-command
          "UID FETCH %s %s"
          (nnimap-article-ranges (gnus-compress-sequence articles))
-         (format "(UID RFC822.SIZE BODYSTRUCTURE %s)"
-                 (format
-                  (if (nnimap-ver4-p)
-                      "BODY.PEEK[HEADER.FIELDS %s]"
-                    "RFC822.HEADER.LINES %s")
-                  (append '(Subject From Date Message-Id
-                                    References In-Reply-To Xref)
-                          nnmail-extra-headers))))
+         (nnimap-header-parameters))
         t)
        (nnimap-transform-headers))
       (insert-buffer-substring
@@ -171,7 +177,7 @@ textual parts.")
            (return)))
        (setq article (match-string 1))
        ;; Unfold quoted {number} strings.
-       (while (re-search-forward "[^]] {\\([0-9]+\\)}\r\n"
+       (while (re-search-forward "[^]][ (]{\\([0-9]+\\)}\r\n"
                                  (1+ (line-end-position)) t)
          (setq size (string-to-number (match-string 1)))
          (delete-region (+ (match-beginning 0) 2) (point))
@@ -200,7 +206,8 @@ textual parts.")
          (insert (format "Chars: %s\n" size)))
        (when lines
          (insert (format "Lines: %s\n" lines)))
-       (re-search-forward "^\r$")
+       (unless (re-search-forward "^\r$" nil t)
+         (goto-char (point-max)))
        (delete-region (line-beginning-position) (line-end-position))
        (insert ".")
        (forward-line 1)))))
@@ -408,9 +415,18 @@ textual parts.")
                                ;; physical address.
                                (nnimap-credentials nnimap-address ports)))))
                  (setq nnimap-object nil)
-               (setq login-result (nnimap-command "LOGIN %S %S"
-                                                  (car credentials)
-                                                  (cadr credentials)))
+               (setq login-result
+                     (if (member "AUTH=PLAIN"
+                                 (nnimap-capabilities nnimap-object))
+                         (nnimap-command
+                          "AUTHENTICATE PLAIN %s"
+                          (base64-encode-string
+                           (format "\000%s\000%s"
+                                   (nnimap-quote-specials (car credentials))
+                                   (nnimap-quote-specials (cadr credentials)))))
+                       (nnimap-command "LOGIN %S %S"
+                                       (car credentials)
+                                       (cadr credentials))))
                (unless (car login-result)
                  ;; If the login failed, then forget the credentials
                  ;; that are now possibly cached.
@@ -427,6 +443,16 @@ textual parts.")
                (nnimap-command "ENABLE QRESYNC"))
              (nnimap-process nnimap-object))))))))
 
+(defun nnimap-quote-specials (string)
+  (with-temp-buffer
+    (insert string)
+    (goto-char (point-min))
+    (while (re-search-forward "[\\\"]" nil t)
+      (forward-char -1)
+      (insert "\\")
+      (forward-char 1))
+    (buffer-string)))
+
 (defun nnimap-find-parameter (parameter elems)
   (let (result)
     (dolist (elem elems)
@@ -477,7 +503,7 @@ textual parts.")
                                (let ((start (point)))
                                  (forward-sexp 1)
                                  (downcase-region start (point))
-                                 (goto-char (point))
+                                 (goto-char start)
                                  (read (current-buffer))))
                    parts (nnimap-find-wanted-parts structure))))
          (when (if parts
@@ -490,12 +516,28 @@ textual parts.")
                (nnheader-ms-strip-cr)
                (cons group article)))))))))
 
-(defun nnimap-get-whole-article (article)
+(deffoo nnimap-request-head (article &optional group server to-buffer)
+  (when (nnimap-possibly-change-group group server)
+    (with-current-buffer (nnimap-buffer)
+      (when (stringp article)
+       (setq article (nnimap-find-article-by-message-id group article)))
+      (nnimap-get-whole-article
+       article (format "UID FETCH %%d %s"
+                      (nnimap-header-parameters)))
+      (let ((buffer (current-buffer)))
+       (with-current-buffer (or to-buffer nntp-server-buffer)
+         (erase-buffer)
+         (insert-buffer-substring buffer)
+         (nnheader-ms-strip-cr)
+         (cons group article))))))
+
+(defun nnimap-get-whole-article (article &optional command)
   (let ((result
         (nnimap-command
-         (if (nnimap-ver4-p)
-             "UID FETCH %d BODY.PEEK[]"
-           "UID FETCH %d RFC822.PEEK")
+         (or command
+             (if (nnimap-ver4-p)
+                 "UID FETCH %d BODY.PEEK[]"
+               "UID FETCH %d RFC822.PEEK"))
          article)))
     ;; Check that we really got an article.
     (goto-char (point-min))
@@ -565,9 +607,9 @@ textual parts.")
        (pop bstruc))
       (setq type (car bstruc))
       (setq bstruc (car (cdr bstruc)))
-      (when (and (stringp (car bstruc))
-                (string= (downcase (car bstruc)) "boundary"))
-       (setq boundary (cadr bstruc))))
+      (let ((has-boundary (member "boundary" bstruc)))
+        (when has-boundary
+          (setq boundary (cadr has-boundary)))))
     (when subp
       (insert (format "Content-type: multipart/%s; boundary=%S\n\n"
                      (downcase type) boundary)))
@@ -715,7 +757,10 @@ textual parts.")
                                             &optional last internal-move-group)
   (with-temp-buffer
     (mm-disable-multibyte)
-    (when (nnimap-request-article article group server (current-buffer))
+    (when (funcall (if internal-move-group
+                      'nnimap-request-head
+                    'nnimap-request-article)
+                  article group server (current-buffer))
       ;; If the move is internal (on the same server), just do it the easy
       ;; way.
       (let ((message-id (message-field-value "message-id")))
@@ -881,7 +926,7 @@ textual parts.")
     (let ((message-id (message-field-value "message-id"))
          sequence message)
       (nnimap-add-cr)
-      (setq message (buffer-string))
+      (setq message (buffer-substring-no-properties (point-min) (point-max)))
       (with-current-buffer (nnimap-buffer)
        (setq sequence (nnimap-send-command
                        "APPEND %S {%d}" (utf7-encode group t)
@@ -899,6 +944,17 @@ textual parts.")
            (cons group
                  (nnimap-find-article-by-message-id group message-id))))))))
 
+(deffoo nnimap-request-replace-article (article group buffer)
+  (let (group-art)
+    (when (and (nnimap-possibly-change-group group nil)
+              ;; Put the article into the group.
+              (with-current-buffer buffer
+                (setq group-art
+                      (nnimap-request-accept-article group nil t))))
+      (nnimap-delete-article (list article))
+      ;; Return the new article number.
+      (cdr group-art))))
+
 (defun nnimap-add-cr ()
   (goto-char (point-min))
   (while (re-search-forward "\r?\n" nil t)
@@ -975,7 +1031,6 @@ textual parts.")
     (with-current-buffer (nnimap-buffer)
       (erase-buffer)
       (setf (nnimap-group nnimap-object) nil)
-      ;; QRESYNC handling isn't implemented.
       (let ((qresyncp (member "QRESYNC" (nnimap-capabilities nnimap-object)))
            params groups sequences active uidvalidity modseq group)
        ;; Go through the infos and gather the data needed to know
@@ -1014,12 +1069,7 @@ textual parts.")
                                               (utf7-encode group t))
                          (nnimap-send-command "UID FETCH %d:* FLAGS" start)
                          start group command)
-                   sequences)))
-         ;; Some servers apparently can't have many outstanding
-         ;; commands, so throttle them.
-         (when (and (not nnimap-streaming)
-                    (car sequences))
-           (nnimap-wait-for-response (caar sequences))))
+                   sequences))))
        sequences))))
 
 (deffoo nnimap-finish-retrieve-group-infos (server infos sequences)
@@ -1397,6 +1447,10 @@ textual parts.")
            (if (nnimap-newlinep nnimap-object)
                ""
              "\r"))))
+  ;; Some servers apparently can't have many outstanding
+  ;; commands, so throttle them.
+  (unless nnimap-streaming
+    (nnimap-wait-for-response nnimap-sequence))
   nnimap-sequence)
 
 (defun nnimap-log-command (command)
@@ -1561,6 +1615,7 @@ textual parts.")
          new-articles)
       (erase-buffer)
       (nnimap-command "SELECT %S" nnimap-inbox)
+      (setf (nnimap-group nnimap-object) group)
       (setq new-articles (nnimap-new-articles (nnimap-get-flags "1:*")))
       (when new-articles
        (nnimap-fetch-inbox new-articles)
@@ -1633,9 +1688,8 @@ textual parts.")
 (defun nnimap-new-articles (flags)
   (let (new)
     (dolist (elem flags)
-      (when (or (null (cdr elem))
-               (and (not (memq '%Deleted (cdr elem)))
-                    (not (memq '%Seen (cdr elem)))))
+      (unless (gnus-list-memq-of-list nnimap-unsplittable-articles
+                                     (cdr elem))
        (push (car elem) new)))
     (gnus-compress-sequence (nreverse new))))