summaryrefslogtreecommitdiffstats
path: root/src/plugins/virtual/virtual-search.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/plugins/virtual/virtual-search.c
parentInitial commit. (diff)
downloaddovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz
dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/plugins/virtual/virtual-search.c')
-rw-r--r--src/plugins/virtual/virtual-search.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/plugins/virtual/virtual-search.c b/src/plugins/virtual/virtual-search.c
new file mode 100644
index 0000000..ee04a74
--- /dev/null
+++ b/src/plugins/virtual/virtual-search.c
@@ -0,0 +1,206 @@
+/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "mail-search.h"
+#include "index-search-private.h"
+#include "virtual-storage.h"
+
+
+enum virtual_search_state {
+ VIRTUAL_SEARCH_STATE_BUILD,
+ VIRTUAL_SEARCH_STATE_RETURN,
+ VIRTUAL_SEARCH_STATE_SORT,
+ VIRTUAL_SEARCH_STATE_SORT_DONE
+};
+
+struct virtual_search_record {
+ uint32_t mailbox_id;
+ uint32_t real_uid;
+ uint32_t virtual_seq;
+};
+
+struct virtual_search_context {
+ union mail_search_module_context module_ctx;
+
+ ARRAY_TYPE(seq_range) result;
+ struct seq_range_iter result_iter;
+ ARRAY(struct virtual_search_record) records;
+
+ enum virtual_search_state search_state;
+ unsigned int next_result_n;
+ unsigned int next_record_idx;
+};
+
+static int virtual_search_record_cmp(const struct virtual_search_record *r1,
+ const struct virtual_search_record *r2)
+{
+ if (r1->mailbox_id < r2->mailbox_id)
+ return -1;
+ if (r1->mailbox_id > r2->mailbox_id)
+ return 1;
+
+ if (r1->real_uid < r2->real_uid)
+ return -1;
+ if (r1->real_uid > r2->real_uid)
+ return 1;
+ return 0;
+}
+
+static int mail_search_get_result(struct mail_search_context *ctx)
+{
+ const struct mail_search_arg *arg;
+ int ret = 1;
+
+ for (arg = ctx->args->args; arg != NULL; arg = arg->next) {
+ if (arg->result < 0)
+ return -1;
+ if (arg->result == 0)
+ ret = 0;
+ }
+ return ret;
+}
+
+static void virtual_search_get_records(struct mail_search_context *ctx,
+ struct virtual_search_context *vctx)
+{
+ struct virtual_mailbox *mbox =
+ (struct virtual_mailbox *)ctx->transaction->box;
+ const struct virtual_mail_index_record *vrec;
+ struct virtual_search_record srec;
+ const void *data;
+ int result;
+
+ i_zero(&srec);
+ while (index_storage_search_next_update_seq(ctx)) {
+ result = mail_search_get_result(ctx);
+ i_assert(result != 0);
+ if (result > 0) {
+ /* full match, no need to check this any further */
+ seq_range_array_add(&vctx->result, ctx->seq);
+ } else {
+ /* possible match, save and check later */
+ mail_index_lookup_ext(mbox->box.view, ctx->seq,
+ mbox->virtual_ext_id,
+ &data, NULL);
+ vrec = data;
+
+ srec.mailbox_id = vrec->mailbox_id;
+ srec.real_uid = vrec->real_uid;
+ srec.virtual_seq = ctx->seq;
+ array_push_back(&vctx->records, &srec);
+ }
+ mail_search_args_reset(ctx->args->args, FALSE);
+ }
+ array_sort(&vctx->records, virtual_search_record_cmp);
+
+ ctx->progress_max = array_count(&vctx->records);
+}
+
+struct mail_search_context *
+virtual_search_init(struct mailbox_transaction_context *t,
+ struct mail_search_args *args,
+ const enum mail_sort_type *sort_program,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers)
+{
+ struct mail_search_context *ctx;
+ struct virtual_search_context *vctx;
+
+ ctx = index_storage_search_init(t, args, sort_program,
+ wanted_fields, wanted_headers);
+
+ vctx = i_new(struct virtual_search_context, 1);
+ vctx->search_state = VIRTUAL_SEARCH_STATE_BUILD;
+ i_array_init(&vctx->result, 64);
+ i_array_init(&vctx->records, 64);
+ MODULE_CONTEXT_SET(ctx, virtual_storage_module, vctx);
+
+ virtual_search_get_records(ctx, vctx);
+ seq_range_array_iter_init(&vctx->result_iter, &vctx->result);
+ return ctx;
+}
+
+int virtual_search_deinit(struct mail_search_context *ctx)
+{
+ struct virtual_search_context *vctx = VIRTUAL_CONTEXT_REQUIRE(ctx);
+
+ array_free(&vctx->result);
+ array_free(&vctx->records);
+ i_free(vctx);
+ return index_storage_search_deinit(ctx);
+}
+
+bool virtual_search_next_nonblock(struct mail_search_context *ctx,
+ struct mail **mail_r, bool *tryagain_r)
+{
+ struct virtual_search_context *vctx = VIRTUAL_CONTEXT_REQUIRE(ctx);
+ struct index_search_context *ictx = (struct index_search_context *)ctx;
+ uint32_t seq;
+
+ switch (vctx->search_state) {
+ case VIRTUAL_SEARCH_STATE_BUILD:
+ if (ctx->sort_program == NULL)
+ vctx->search_state = VIRTUAL_SEARCH_STATE_SORT;
+ else
+ vctx->search_state = VIRTUAL_SEARCH_STATE_RETURN;
+ return virtual_search_next_nonblock(ctx, mail_r, tryagain_r);
+ case VIRTUAL_SEARCH_STATE_RETURN:
+ return index_storage_search_next_nonblock(ctx, mail_r, tryagain_r);
+ case VIRTUAL_SEARCH_STATE_SORT:
+ /* the messages won't be returned sorted, so we'll have to
+ do it ourself */
+ while (index_storage_search_next_nonblock(ctx, mail_r, tryagain_r))
+ seq_range_array_add(&vctx->result, (*mail_r)->seq);
+ if (*tryagain_r)
+ return FALSE;
+
+ vctx->next_result_n = 0;
+ vctx->search_state = VIRTUAL_SEARCH_STATE_SORT_DONE;
+ /* fall through */
+ case VIRTUAL_SEARCH_STATE_SORT_DONE:
+ *tryagain_r = FALSE;
+ if (!seq_range_array_iter_nth(&vctx->result_iter,
+ vctx->next_result_n, &seq))
+ return FALSE;
+ vctx->next_result_n++;
+ *mail_r = index_search_get_mail(ictx);
+ i_assert(*mail_r != NULL);
+ mail_set_seq(*mail_r, seq);
+ return TRUE;
+ }
+ i_unreached();
+}
+
+static void search_args_set_full_match(struct mail_search_arg *args)
+{
+ for (; args != NULL; args = args->next)
+ args->result = 1;
+}
+
+bool virtual_search_next_update_seq(struct mail_search_context *ctx)
+{
+ struct virtual_search_context *vctx = VIRTUAL_CONTEXT_REQUIRE(ctx);
+ const struct virtual_search_record *recs;
+ unsigned int count;
+
+ recs = array_get(&vctx->records, &count);
+ if (vctx->next_record_idx < count) {
+ /* go through potential results first */
+ ctx->seq = recs[vctx->next_record_idx++].virtual_seq - 1;
+ if (!index_storage_search_next_update_seq(ctx))
+ i_unreached();
+ ctx->progress_cur = vctx->next_record_idx;
+ return TRUE;
+ }
+
+ if (ctx->sort_program != NULL &&
+ seq_range_array_iter_nth(&vctx->result_iter,
+ vctx->next_result_n, &ctx->seq)) {
+ /* this is known to match fully */
+ search_args_set_full_match(ctx->args->args);
+ vctx->next_result_n++;
+ return TRUE;
+ }
+ return FALSE;
+}