Subject: Re: [PATCH] Use IMAP MOVE extension if available

From: Nikolaus Rath <[email-masked]>
Date: Wed, 08 Jul 2015 19:13:03 -0700

On Jul 06 2015, Eric Abrahamsen <[email-masked]> wrote:
> Nikolaus Rath <[email-masked]> writes:
>
>> Hello,
>>
>> Currently Gnus uses a combination of COPY, STORE + EXPUNGE to move
>> messages from one imap group into another.
>>
>> The attached patch enables the use of the MOVE command instead if the
>> server supports it.
>>
>> Thanks for considering.
>>
>> (Is this the right place to post patches, or should it go to
>> [email-masked]?)
>>
>> Best,
>> -Nikolaus
>
> and `nnimap-process-expiry-target', sorry...

Updated patch is attached. Note that the changes to
nnimap-split-incoming-mail are untested (I don't use that function).

Best,
-Nikolaus

-- 
GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F
Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

             »Time flies like an arrow, fruit flies like a Banana.«
>From 2cd9616461e3551e3e2e556879fb3e83846754b7 Mon Sep 17 00:00:00 2001
From: Nikolaus Rath <[email-masked]>
Date: Wed, 8 Jul 2015 19:10:46 -0700
Subject: [PATCH] Use IMAP MOVE extension if available

Currently Gnus uses a combination of COPY, STORE + EXPUNGE to move
messages from one IMAP group into another.

This patch enables the use of the MOVE command instead if the
server supports it.
---
 lisp/nnimap.el | 59 ++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 24 deletions(-)

diff --git a/lisp/nnimap.el b/lisp/nnimap.el
index cc1691b..db1a91a 100644
--- a/lisp/nnimap.el
+++ b/lisp/nnimap.el
@@ -960,17 +960,19 @@ If non-nil, articles flagged as deleted (using the IMAP
       ;; way.
       (let ((message-id (message-field-value "message-id")))
 	(if internal-move-group
-	    (let ((result
-		   (with-current-buffer (nnimap-buffer)
-		     (nnimap-command "UID COPY %d %S"
-				     article
-				     (utf7-encode internal-move-group t)))))
-	      (when (car result)
-		(nnimap-delete-article article)
-		(cons internal-move-group
-		      (or (nnimap-find-uid-response "COPYUID" (cadr result))
-			  (nnimap-find-article-by-message-id
-			   internal-move-group server message-id
+            (with-current-buffer (nnimap-buffer)
+              (let* ((can-move (nnimap-capability "MOVE"))
+                    (command (if can-move
+                                 "UID MOVE %d %S"
+                               "UID COPY %d %S"))
+                    (result (nnimap-command command article
+                                            (utf7-encode internal-move-group t))))
+                (when (and (car result) (not can-move))
+                  (nnimap-delete-article article))
+                (cons internal-move-group
+                      (or (nnimap-find-uid-response "COPYUID" (cadr result))
+                          (nnimap-find-article-by-message-id
+                           internal-move-group server message-id
                            nnimap-request-articles-find-limit)))))
 	  ;; Move the article to a different method.
 	  (let ((result (eval accept-form)))
@@ -1009,11 +1011,12 @@ If non-nil, articles flagged as deleted (using the IMAP
 	(gnus-sorted-complement articles deletable-articles))))))
 
 (defun nnimap-process-expiry-targets (articles group server)
-  (let ((deleted-articles nil))
+  (let ((deleted-articles nil)
+        (articles-to-delete nil))
     (cond
      ;; shortcut further processing if we're going to delete the articles
      ((eq nnmail-expiry-target 'delete)
-      (setq deleted-articles articles)
+      (setq articles-to-delete articles)
       t)
      ;; or just move them to another folder on the same IMAP server
      ((and (not (functionp nnmail-expiry-target))
@@ -1023,11 +1026,14 @@ If non-nil, articles flagged as deleted (using the IMAP
       (and (nnimap-change-group group server)
 	   (with-current-buffer (nnimap-buffer)
 	     (nnheader-message 7 "Expiring articles from %s: %s" group articles)
-	     (nnimap-command
-	      "UID COPY %s %S"
-	      (nnimap-article-ranges (gnus-compress-sequence articles))
-	      (utf7-encode (gnus-group-real-name nnmail-expiry-target) t))
-	     (setq deleted-articles articles)))
+             (let ((can-move (nnimap-capability "MOVE")))
+               (nnimap-command
+                (if can-move
+                    "UID MOVE %s %S"
+                  "UID COPY %s %S")
+                (nnimap-article-ranges (gnus-compress-sequence articles))
+                (utf7-encode (gnus-group-real-name nnmail-expiry-target) t))
+               (set (if can-move 'deleted-articles 'articles-to-delete) articles))))
       t)
      (t
       (dolist (article articles)
@@ -1048,11 +1054,13 @@ If non-nil, articles flagged as deleted (using the IMAP
 		    (setq target nil))
 		(nnheader-message 7 "Expiring article %s:%d" group article))
 	      (when target
-		(push article deleted-articles))))))
-      (setq deleted-articles (nreverse deleted-articles))))
+		(push article articles-to-delete))))))
+      (setq articles-to-delete (nreverse articles-to-delete))))
     ;; Change back to the current group again.
     (nnimap-change-group group server)
-    (nnimap-delete-article (gnus-compress-sequence deleted-articles))
+    (when articles-to-delete
+      (nnimap-delete-article (gnus-compress-sequence articles-to-delete))
+      (setq deleted-articles articles-to-delete))
     deleted-articles))
 
 (defun nnimap-find-expired-articles (group)
@@ -2103,6 +2111,7 @@ Return the server's response to the SELECT or EXAMINE command."
 				  nnmail-split-fancy))
 	  (nnmail-inhibit-default-split-group t)
 	  (groups (nnimap-get-groups))
+          (can-move (nnimap-capability "MOVE"))
 	  new-articles)
       (erase-buffer)
       (nnimap-command "SELECT %S" nnimap-inbox)
@@ -2137,14 +2146,16 @@ Return the server's response to the SELECT or EXAMINE command."
 		  ;; Don't copy if the message is already in its
 		  ;; target group.
 		  (unless (string= group nnimap-inbox)
-		   (push (list (nnimap-send-command
-				"UID COPY %s %S"
+                    (push (list (nnimap-send-command
+                                 (if can-move
+                                     "UID MOVE %d %S"
+                                     "UID COPY %s %S")
 				(nnimap-article-ranges ranges)
 				(utf7-encode group t))
 			       ranges)
 			 sequences)))))
 	    ;; Wait for the last COPY response...
-	    (when sequences
+	    (when (and (not can-move) sequences)
 	      (nnimap-wait-for-response (caar sequences))
 	      ;; And then mark the successful copy actions as deleted,
 	      ;; and possibly expunge them.
-- 
2.1.4