]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap: Fix SEARCH PARTIAL handling with other options
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 16 Mar 2020 11:33:55 +0000 (13:33 +0200)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Fri, 20 Mar 2020 08:03:57 +0000 (08:03 +0000)
Especially SAVE and RELEVANCY should use only the partial results, not all
results.

src/imap/imap-search.c

index 25b16551bf7723ebead872fbf656fee70a2f866a..9344695a3c4b63ef3b954126ea5b390321cfc486 100644 (file)
@@ -228,46 +228,7 @@ static void imap_search_send_result_standard(struct imap_search_context *ctx)
 static void
 imap_search_send_partial(struct imap_search_context *ctx, string_t *str)
 {
-       struct seq_range *range;
-       uint32_t n, diff;
-       unsigned int i, count, delete_count;
-
        str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2);
-       ctx->partial1--;
-       ctx->partial2--;
-
-       /* we need to be able to handle non-sorted seq ranges (for SORT
-          replies), so do this ourself instead of using seq_range_array_*()
-          functions. */
-       range = array_get_modifiable(&ctx->result, &count);
-       /* delete everything up to partial1 */
-       delete_count = 0;
-       for (i = n = 0; i < count; i++) {
-               diff = range[i].seq2 - range[i].seq1;
-               if (n + diff >= ctx->partial1) {
-                       range[i].seq1 += ctx->partial1 - n;
-                       delete_count = i;
-                       break;
-               }
-               n += diff + 1;
-       }
-       if (i == count) {
-               /* partial1 points past the result */
-               array_clear(&ctx->result);
-       } else {
-               /* delete everything after partial2 */
-               for (n = ctx->partial1; i < count; i++) {
-                       diff = range[i].seq2 - range[i].seq1;
-                       if (n + diff >= ctx->partial2) {
-                               range[i].seq2 = range[i].seq1 + (ctx->partial2 - n);
-                               array_delete(&ctx->result, i + 1, count-(i+1));
-                               break;
-                       }
-                       n += diff + 1;
-               }
-               array_delete(&ctx->result, 0, delete_count);
-       }
-
        if (array_count(&ctx->result) == 0) {
                /* no results (in range) */
                str_append(str, "NIL");
@@ -442,9 +403,29 @@ static bool cmd_search_more(struct client_command_context *cmd)
                        ctx->min_id = id;
                        search_update_mail(ctx, mail);
                }
-               if (HAS_ANY_BITS(opts, SEARCH_RETURN_ALL))
+               if (HAS_ANY_BITS(opts, SEARCH_RETURN_ALL)) {
+                       /* ALL and PARTIAL are mutually exclusive */
+                       i_assert(HAS_NO_BITS(opts, SEARCH_RETURN_PARTIAL));
                        search_add_result_id(ctx, id);
-               else if (HAS_ANY_BITS(opts, SEARCH_RETURN_COUNT)) {
+               } else if ((opts & SEARCH_RETURN_PARTIAL) != 0) {
+                       /* only update if it's within range */
+                       i_assert(HAS_NO_BITS(opts, SEARCH_RETURN_ALL));
+                       if (ctx->partial1 <= ctx->result_count &&
+                           ctx->partial2 >= ctx->result_count)
+                               search_add_result_id(ctx, id);
+                       else if (HAS_ALL_BITS(opts, SEARCH_RETURN_COUNT |
+                                            SEARCH_RETURN_SAVE)) {
+                               /* (SAVE COUNT PARTIAL n:m) must include all
+                                  results in SAVE, but not include mails
+                                  outside the PARTIAL range in MODSEQ or
+                                  RELEVANCY */
+                               seq_range_array_add(&cmd->client->search_saved_uidset,
+                                                   mail->uid);
+                               continue;
+                       } else {
+                               continue;
+                       }
+               } else if (HAS_ANY_BITS(opts, SEARCH_RETURN_COUNT)) {
                        /* with COUNT don't add it to results, but handle
                           SAVE and MODSEQ */
                } else if (HAS_ANY_BITS(opts, SEARCH_RETURN_MIN |