dovecot-2.2: imapc: Added imapc_features=search support for send...

dovecot at dovecot.org dovecot at dovecot.org
Tue Feb 3 16:33:40 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/405959839f48
changeset: 18215:405959839f48
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Feb 03 18:33:12 2015 +0200
description:
imapc: Added imapc_features=search support for sending SEARCH commands.
Currently requires the remote server to support ESEARCH (but this would be
easy to avoid). This is only minimally tested for now, so bugs may exist
(especially related to sub-queries).

diffstat:

 src/lib-imap-client/imapc-client.c           |    1 +
 src/lib-imap-client/imapc-client.h           |    1 +
 src/lib-storage/index/imapc/Makefile.am      |    2 +
 src/lib-storage/index/imapc/imapc-mailbox.c  |    7 +-
 src/lib-storage/index/imapc/imapc-search.c   |  357 +++++++++++++++++++++++++++
 src/lib-storage/index/imapc/imapc-search.h   |   16 +
 src/lib-storage/index/imapc/imapc-settings.c |    1 +
 src/lib-storage/index/imapc/imapc-settings.h |    3 +-
 src/lib-storage/index/imapc/imapc-storage.c  |    7 +-
 src/lib-storage/index/imapc/imapc-storage.h  |    1 +
 10 files changed, 391 insertions(+), 5 deletions(-)

diffs (truncated from 513 to 300 lines):

diff -r 6c2ea1d6ab58 -r 405959839f48 src/lib-imap-client/imapc-client.c
--- a/src/lib-imap-client/imapc-client.c	Tue Feb 03 10:15:38 2015 +0200
+++ b/src/lib-imap-client/imapc-client.c	Tue Feb 03 18:33:12 2015 +0200
@@ -24,6 +24,7 @@
 	{ "CONDSTORE", IMAPC_CAPABILITY_CONDSTORE },
 	{ "NAMESPACE", IMAPC_CAPABILITY_NAMESPACE },
 	{ "UNSELECT", IMAPC_CAPABILITY_UNSELECT },
+	{ "ESEARCH", IMAPC_CAPABILITY_ESEARCH },
 
 	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
 	{ NULL, 0 }
diff -r 6c2ea1d6ab58 -r 405959839f48 src/lib-imap-client/imapc-client.h
--- a/src/lib-imap-client/imapc-client.h	Tue Feb 03 10:15:38 2015 +0200
+++ b/src/lib-imap-client/imapc-client.h	Tue Feb 03 18:33:12 2015 +0200
@@ -23,6 +23,7 @@
 	IMAPC_CAPABILITY_CONDSTORE	= 0x100,
 	IMAPC_CAPABILITY_NAMESPACE	= 0x200,
 	IMAPC_CAPABILITY_UNSELECT	= 0x400,
+	IMAPC_CAPABILITY_ESEARCH	= 0x800,
 
 	IMAPC_CAPABILITY_IMAP4REV1	= 0x40000000
 };
diff -r 6c2ea1d6ab58 -r 405959839f48 src/lib-storage/index/imapc/Makefile.am
--- a/src/lib-storage/index/imapc/Makefile.am	Tue Feb 03 10:15:38 2015 +0200
+++ b/src/lib-storage/index/imapc/Makefile.am	Tue Feb 03 18:33:12 2015 +0200
@@ -18,6 +18,7 @@
 	imapc-mail-fetch.c \
 	imapc-mailbox.c \
 	imapc-save.c \
+	imapc-search.c \
 	imapc-settings.c \
 	imapc-sync.c \
 	imapc-storage.c
@@ -25,6 +26,7 @@
 headers = \
 	imapc-list.h \
 	imapc-mail.h \
+	imapc-search.h \
 	imapc-settings.h \
 	imapc-storage.h \
 	imapc-sync.h
diff -r 6c2ea1d6ab58 -r 405959839f48 src/lib-storage/index/imapc/imapc-mailbox.c
--- a/src/lib-storage/index/imapc/imapc-mailbox.c	Tue Feb 03 10:15:38 2015 +0200
+++ b/src/lib-storage/index/imapc/imapc-mailbox.c	Tue Feb 03 18:33:12 2015 +0200
@@ -8,6 +8,7 @@
 #include "imapc-client.h"
 #include "imapc-mail.h"
 #include "imapc-msgmap.h"
+#include "imapc-search.h"
 #include "imapc-sync.h"
 #include "imapc-storage.h"
 
@@ -438,7 +439,9 @@
 
 	i_free_and_null(mbox->sync_gmail_pop3_search_tag);
 
-	/* It should contain ALL <seqset>  */
+	/* It should contain ALL <seqset> or nonexistent if nothing matched */
+	if (args[0].type == IMAP_ARG_EOL)
+		return;
 	t_array_init(&rseqs, 64);
 	if (!imap_arg_atom_equals(&args[0], "ALL") ||
 	    !imap_arg_get_atom(&args[1], &atom) ||
@@ -493,6 +496,8 @@
 	if (mbox->sync_gmail_pop3_search_tag != NULL &&
 	    strcmp(mbox->sync_gmail_pop3_search_tag, str) == 0)
 		imapc_untagged_esearch_gmail_pop3(reply->args+1, mbox);
+	else
+		imapc_search_reply(reply->args+1, mbox);
 }
 
 static void
diff -r 6c2ea1d6ab58 -r 405959839f48 src/lib-storage/index/imapc/imapc-search.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/imapc/imapc-search.c	Tue Feb 03 18:33:12 2015 +0200
@@ -0,0 +1,357 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "imap-arg.h"
+#include "imap-date.h"
+#include "imap-quote.h"
+#include "imap-seqset.h"
+#include "imap-util.h"
+#include "mail-search.h"
+#include "imapc-client.h"
+#include "imapc-storage.h"
+#include "imapc-search.h"
+
+#define IMAPC_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, imapc_storage_module)
+
+struct imapc_search_context {
+	union mail_search_module_context module_ctx;
+
+	ARRAY_TYPE(seq_range) rseqs;
+	struct seq_range_iter iter;
+	unsigned int n;
+	bool finished;
+	bool success;
+};
+
+static MODULE_CONTEXT_DEFINE_INIT(imapc_storage_module,
+				  &mail_storage_module_register);
+
+static bool
+imapc_build_search_query_args(struct imapc_mailbox *mbox,
+			      const struct mail_search_arg *args,
+			      bool parent_or, string_t *str);
+
+static bool imapc_search_is_fast_local(const struct mail_search_arg *args)
+{
+	const struct mail_search_arg *arg;
+
+	for (arg = args; arg != NULL; arg = arg->next) {
+		switch (arg->type) {
+		case SEARCH_OR:
+		case SEARCH_SUB:
+			if (!imapc_search_is_fast_local(arg->value.subargs))
+				return FALSE;
+			break;
+		case SEARCH_ALL:
+		case SEARCH_SEQSET:
+		case SEARCH_UIDSET:
+		case SEARCH_FLAGS:
+		case SEARCH_KEYWORDS:
+		case SEARCH_MODSEQ:
+		case SEARCH_MAILBOX:
+		case SEARCH_MAILBOX_GUID:
+		case SEARCH_MAILBOX_GLOB:
+		case SEARCH_REAL_UID:
+			break;
+		default:
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+static bool
+imapc_build_search_query_arg(struct imapc_mailbox *mbox,
+			     const struct mail_search_arg *arg,
+			     string_t *str)
+{
+	enum imapc_capability capa =
+		imapc_client_get_capabilities(mbox->storage->client->client);
+
+	if (arg->match_not)
+		str_append(str, "NOT ");
+	switch (arg->type) {
+	case SEARCH_OR:
+		str_append_c(str, '(');
+		imapc_build_search_query_args(mbox, arg->value.subargs, TRUE, str);
+		str_append_c(str, ')');
+		break;
+	case SEARCH_SUB:
+		str_append_c(str, '(');
+		imapc_build_search_query_args(mbox, arg->value.subargs, FALSE, str);
+		str_append_c(str, ')');
+		break;
+
+	case SEARCH_ALL:
+		str_append(str, "ALL");
+		break;
+	case SEARCH_SEQSET:
+		/* translate to UIDs */
+		T_BEGIN {
+			ARRAY_TYPE(seq_range) uids;
+
+			t_array_init(&uids, 64);
+			mailbox_get_uid_range(&mbox->box, &arg->value.seqset,
+					      &uids);
+			str_append(str, "UID ");
+			imap_write_seq_range(str, &uids);
+		} T_END;
+		break;
+	case SEARCH_UIDSET:
+		str_append(str, "UID ");
+		imap_write_seq_range(str, &arg->value.seqset);
+		break;
+	case SEARCH_FLAGS:
+		i_assert((arg->value.flags & MAIL_FLAGS_MASK) != 0);
+		str_append_c(str, '(');
+		if ((arg->value.flags & MAIL_ANSWERED) != 0)
+			str_append(str, "ANSWERED ");
+		if ((arg->value.flags & MAIL_FLAGGED) != 0)
+			str_append(str, "FLAGGED ");
+		if ((arg->value.flags & MAIL_DELETED) != 0)
+			str_append(str, "DELETED ");
+		if ((arg->value.flags & MAIL_SEEN) != 0)
+			str_append(str, "SEEN ");
+		if ((arg->value.flags & MAIL_DRAFT) != 0)
+			str_append(str, "DRAFT ");
+		if ((arg->value.flags & MAIL_RECENT) != 0)
+			str_append(str, "RECENT ");
+		str_truncate(str, str_len(str)-1);
+		str_append_c(str, ')');
+		break;
+	case SEARCH_KEYWORDS: {
+		const struct mail_keywords *kw = arg->value.keywords;
+		const ARRAY_TYPE(keywords) *names_arr;
+		const char *const *namep;
+		unsigned int i;
+
+		names_arr = mail_index_get_keywords(kw->index);
+
+		str_append_c(str, '(');
+		for (i = 0; i < kw->count; i++) {
+			namep = array_idx(names_arr, kw->idx[i]);
+			if (i > 0)
+				str_append_c(str, ' ');
+			str_printfa(str, "KEYWORD %s", *namep);
+		}
+		str_append_c(str, ')');
+		break;
+	}
+
+	case SEARCH_BEFORE:
+		str_printfa(str, "BEFORE \"%s\"", imap_to_datetime(arg->value.time));
+		break;
+	case SEARCH_ON:
+		str_printfa(str, "ON \"%s\"", imap_to_datetime(arg->value.time));
+		break;
+	case SEARCH_SINCE:
+		str_printfa(str, "SINCE \"%s\"", imap_to_datetime(arg->value.time));
+		break;
+	case SEARCH_SMALLER:
+		str_printfa(str, "SMALLER %llu", (unsigned long long)arg->value.size);
+		break;
+	case SEARCH_LARGER:
+		str_printfa(str, "LARGER %llu", (unsigned long long)arg->value.size);
+		break;
+	case SEARCH_HEADER:
+	case SEARCH_HEADER_ADDRESS:
+	case SEARCH_HEADER_COMPRESS_LWSP:
+		if (strcasecmp(arg->hdr_field_name, "From") == 0 ||
+		    strcasecmp(arg->hdr_field_name, "To") == 0 ||
+		    strcasecmp(arg->hdr_field_name, "Cc") == 0 ||
+		    strcasecmp(arg->hdr_field_name, "Bcc") == 0 ||
+		    strcasecmp(arg->hdr_field_name, "Subject") == 0)
+			str_append(str, arg->hdr_field_name);
+		else {
+			str_append(str, "HEADER ");
+			imap_append_astring(str, arg->hdr_field_name);
+		}
+		str_append_c(str, ' ');
+		imap_append_astring(str, arg->value.str);
+		break;
+
+	case SEARCH_BODY:
+		str_append(str, "BODY ");
+		imap_append_astring(str, arg->value.str);
+		break;
+	case SEARCH_TEXT:
+		str_append(str, "TEXT ");
+		imap_append_astring(str, arg->value.str);
+		break;
+
+	/* extensions */
+	case SEARCH_MODSEQ:
+		if ((capa & IMAPC_CAPABILITY_CONDSTORE) == 0)
+			return FALSE;
+		str_printfa(str, "MODSEQ %llu", (unsigned long long)arg->value.modseq);
+		break;
+	case SEARCH_INTHREAD:
+	case SEARCH_GUID:
+	case SEARCH_MAILBOX:
+	case SEARCH_MAILBOX_GUID:
+	case SEARCH_MAILBOX_GLOB:
+	case SEARCH_REAL_UID:
+		return FALSE;
+	default:
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static bool
+imapc_build_search_query_args(struct imapc_mailbox *mbox,
+			      const struct mail_search_arg *args,
+			      bool parent_or, string_t *str)
+{
+	const struct mail_search_arg *arg;
+
+	for (arg = args; arg != NULL; arg = arg->next) {
+		if (parent_or && arg->next != NULL)
+			str_append(str, "OR ");
+		if (!imapc_build_search_query_arg(mbox, arg, str))
+			return FALSE;
+		str_append_c(str, ' ');
+	}
+	str_truncate(str, str_len(str)-1);
+	return TRUE;
+}
+
+static bool imapc_build_search_query(struct imapc_mailbox *mbox,
+				     const struct mail_search_args *args,
+				     const char **query_r)
+{


More information about the dovecot-cvs mailing list