diff -r d440b7cad709 src/lib-storage/mail-search-build.c
--- a/src/lib-storage/mail-search-build.c	Mon Jun 09 22:02:02 2008 +0300
+++ b/src/lib-storage/mail-search-build.c	Mon Jun 09 22:24:19 2008 +0300
@@ -15,6 +15,105 @@
 	const char *error;
 };
 
+struct mail_search_arg_sub {
+	struct mail_search_arg arg;
+	struct mail_search_arg *subargs;
+};
+
+struct mail_search_arg_seqset {
+	struct mail_search_arg arg;
+	ARRAY_TYPE(seq_range) seqset;
+	/* SEARCHRES extension: $ used */
+	bool use_last_saved_search;
+};
+
+static struct mail_search_arg *
+search_arg_build(struct mail_search_args *args,
+		 const struct imap_arg **imap_args, const char **error_r);
+
+static void search_args_link(struct mail_search_arg **args,
+			     struct mail_search_arg *subarg)
+{
+	i_assert(subarg->next == NULL);
+
+	subarg->next = *args;
+	*args = subarg;
+}
+
+static const char *
+arg_next_str(const struct imap_arg **imap_args, const char **error_r)
+{
+	const char *value;
+
+	if ((*imap_args)->type == IMAP_ARG_EOL) {
+		*error_r = "Missing parameter for argument";
+		return NULL;
+	}
+	if ((*imap_args)->type != IMAP_ARG_ATOM &&
+	    (*imap_args)->type != IMAP_ARG_STRING) {
+		*error_r = "Invalid parameter for argument";
+		return NULL;
+	}
+
+	value = IMAP_ARG_STR_NONULL(*imap_args);
+	*imap_args += 1;
+	return value;
+}
+
+static struct mail_search_arg *
+search_seqset_build(struct mail_search_args *args,
+		    const struct imap_arg **imap_args, const char **error_r)
+{
+	struct mail_search_arg_seqset *arg;
+	const char *str;
+
+	str = arg_next_str(imap_args, error_r);
+	if (str == NULL)
+		return NULL;
+
+	arg = p_new(args->pool, struct mail_search_arg_seqset, 1);
+	p_array_init(&arg->seqset, args->pool, 16);
+	if (strcmp(str, "$") == 0) {
+		/* SEARCHRES: delay initialization */
+		arg->use_last_saved_search = TRUE;
+	} else if (imap_seq_set_parse(str, &arg->seqset) < 0) {
+		*error_r = "Invalid seq-set";
+		return NULL;
+	}
+	return &arg->arg;
+}
+
+static struct mail_search_arg *
+search_sub_build(struct mail_search_args *args,
+		 const struct imap_arg **imap_args, const char **error_r)
+{
+	struct mail_search_arg_sub *arg;
+	struct mail_search_arg *subarg;
+
+	arg = p_new(args->pool, struct mail_search_arg_sub, 1);
+	while ((*imap_args)->type != IMAP_ARG_EOL) {
+		subarg = search_arg_build(args, imap_args, error_r);
+		if (subarg == NULL)
+			return NULL;
+
+		search_args_link(&arg->subargs, subarg);
+	}
+	return &arg->arg;
+}
+
+static const struct mail_search_handler search_handlers[] = {
+	{ "UID", SEARCH_ARG_TYPE_STATIC_METADATA,
+	  search_seqset_build, NULL, NULL }
+};
+static const struct mail_search_handler search_seqset_handler = {
+	NULL, SEARCH_ARG_TYPE_STATIC_METADATA,
+	search_seqset_build, NULL, NULL
+};
+static const struct mail_search_handler search_sub_handler = {
+	NULL, SEARCH_ARG_TYPE_SUB, search_sub_build, NULL, NULL
+};
+
+#if 0
 static struct mail_search_arg *
 search_arg_new(pool_t pool, enum mail_search_arg_type type)
 {
@@ -24,25 +123,6 @@
 	arg->type = type;
 
 	return arg;
-}
-
-static bool
-arg_get_next(struct search_build_data *data, const struct imap_arg **args,
-	     const char **value_r)
-{
-	if ((*args)->type == IMAP_ARG_EOL) {
-		data->error = "Missing parameter for argument";
-		return FALSE;
-	}
-	if ((*args)->type != IMAP_ARG_ATOM &&
-	    (*args)->type != IMAP_ARG_STRING) {
-		data->error = "Invalid parameter for argument";
-		return FALSE;
-	}
-
-	*value_r = IMAP_ARG_STR(*args);
-	*args += 1;
-	return TRUE;
 }
 
 #define ARG_NEW_SINGLE(type) \
@@ -260,56 +340,79 @@
 	sarg->value.modseq->modseq = strtoull(value, NULL, 10);
 	return TRUE;
 }
+#endif
 
-static bool search_arg_build(struct search_build_data *data,
-			     const struct imap_arg **args,
-			     struct mail_search_arg **next_sarg)
+static int search_handler_cmp(const void *key, const void *data)
 {
-	struct mail_search_arg **subargs, *sarg;
-	const struct imap_arg *arg;
+	const char *name = key;
+	const struct mail_search_handler *handler = data;
+
+	return strcmp(name, handler->name);
+}
+
+static const struct mail_search_handler *search_handler_find(const char *name)
+{
+	return bsearch(name, search_handlers, N_ELEMENTS(search_handlers),
+		       sizeof(search_handlers[0]), search_handler_cmp);
+}
+
+static struct mail_search_arg *
+search_arg_build(struct mail_search_args *args,
+		 const struct imap_arg **imap_args, const char **error_r)
+{
+	const struct imap_arg *imap_arg = *imap_args;
+	const struct mail_search_handler *handler;
+	struct mail_search_arg *arg;
 	const char *str;
 
-	if ((*args)->type == IMAP_ARG_EOL) {
-		data->error = "Missing argument";
-		return FALSE;
+	if (imap_arg->type == IMAP_ARG_EOL) {
+		*error_r = "Missing argument";
+		return NULL;
+	}
+	*imap_args += 1;
+
+	if (imap_arg->type == IMAP_ARG_NIL) {
+		/* NIL not allowed */
+		*error_r = "NIL not allowed";
+		return NULL;
 	}
 
-	arg = *args;
-
-	if (arg->type == IMAP_ARG_NIL) {
-		/* NIL not allowed */
-		data->error = "NIL not allowed";
-		return FALSE;
-	}
-
-	if (arg->type == IMAP_ARG_LIST) {
-		const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(arg);
+	if (imap_arg->type == IMAP_ARG_LIST) {
+		const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(imap_arg);
 
 		if (listargs->type == IMAP_ARG_EOL) {
-			data->error = "Empty list not allowed";
-			return FALSE;
+			*error_r = "Empty list not allowed";
+			return NULL;
 		}
 
-		*next_sarg = search_arg_new(data->pool, SEARCH_SUB);
-		subargs = &(*next_sarg)->value.subargs;
-		while (listargs->type != IMAP_ARG_EOL) {
-			if (!search_arg_build(data, &listargs, subargs))
-				return FALSE;
-			subargs = &(*subargs)->next;
+		handler = &search_sub_handler;
+		arg = handler->build(args, &listargs, error_r);
+	} else {
+		i_assert(imap_arg->type == IMAP_ARG_ATOM ||
+			 imap_arg->type == IMAP_ARG_STRING);
+
+		str = t_str_ucase(IMAP_ARG_STR_NONULL(imap_arg));
+		if (*str >= '0' && *str <= '9') {
+			/* sequence-set most likely */
+			*imap_args -= 1;
+			handler = &search_seqset_handler;
+		} else {
+			handler = search_handler_find(str);
+			if (handler == NULL) {
+				*error_r = t_strconcat("Unknown search key ",
+						       str, NULL);
+				return NULL;
+			}
 		}
+		arg = handler->build(args, imap_args, error_r);
+	}
+	if (arg == NULL)
+		return NULL;
 
-		*args += 1;
-		return TRUE;
-	}
-
-	i_assert(arg->type == IMAP_ARG_ATOM ||
-		 arg->type == IMAP_ARG_STRING);
-
-	/* string argument - get the name and jump to next */
-	str = IMAP_ARG_STR(arg);
-	*args += 1;
-	str = t_str_ucase(str);
-
+	arg->handler = handler;
+	search_args_link(&args->args, arg);
+	return arg;
+#if 0
 	switch (*str) {
 	case 'A':
 		if (strcmp(str, "ANSWERED") == 0)
@@ -320,9 +423,9 @@
 	case 'B':
 		if (strcmp(str, "BODY") == 0) {
 			/* <string> */
-			if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
-			    *IMAP_ARG_STR(*args) == '\0') {
-				*args += 1;
+			if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) &&
+			    *IMAP_ARG_STR(*imap_args) == '\0') {
+				*imap_args += 1;
 				return ARG_NEW_SINGLE(SEARCH_ALL);
 			}
 			return ARG_NEW_STR(SEARCH_BODY);
@@ -359,18 +462,18 @@
 			/* <field-name> <string> */
 			const char *key;
 
-			if ((*args)->type == IMAP_ARG_EOL) {
-				data->error = "Missing parameter for HEADER";
+			if ((*imap_args)->type == IMAP_ARG_EOL) {
+				*error_r = "Missing parameter for HEADER";
 				return FALSE;
 			}
-			if ((*args)->type != IMAP_ARG_ATOM &&
-			    (*args)->type != IMAP_ARG_STRING) {
-				data->error = "Invalid parameter for HEADER";
+			if ((*imap_args)->type != IMAP_ARG_ATOM &&
+			    (*imap_args)->type != IMAP_ARG_STRING) {
+				*error_r = "Invalid parameter for HEADER";
 				return FALSE;
 			}
 
-			key = t_str_ucase(IMAP_ARG_STR(*args));
-			*args += 1;
+			key = t_str_ucase(IMAP_ARG_STR(*imap_args));
+			*imap_args += 1;
 			return ARG_NEW_HEADER(SEARCH_HEADER, key);
 		}
 		break;
@@ -393,7 +496,7 @@
   		break;
 	case 'N':
 		if (strcmp(str, "NOT") == 0) {
-			if (!search_arg_build(data, args, next_sarg))
+			if (!search_arg_build(data, imap_args, next_sarg))
 				return FALSE;
 			(*next_sarg)->not = !(*next_sarg)->not;
 			return TRUE;
@@ -418,25 +521,25 @@
 
 			subargs = &(*next_sarg)->value.subargs;
 			for (;;) {
-				if (!search_arg_build(data, args, subargs))
+				if (!search_arg_build(data, imap_args, subargs))
 					return FALSE;
 
 				subargs = &(*subargs)->next;
 
 				/* <key> OR <key> OR ... <key> - put them all
 				   under one SEARCH_OR list. */
-				if ((*args)->type == IMAP_ARG_EOL)
+				if ((*imap_args)->type == IMAP_ARG_EOL)
 					break;
 
-				if ((*args)->type != IMAP_ARG_ATOM ||
-				    strcasecmp(IMAP_ARG_STR_NONULL(*args),
+				if ((*imap_args)->type != IMAP_ARG_ATOM ||
+				    strcasecmp(IMAP_ARG_STR_NONULL(*imap_args),
 					       "OR") != 0)
 					break;
 
-				*args += 1;
+				*imap_args += 1;
 			}
 
-			if (!search_arg_build(data, args, subargs))
+			if (!search_arg_build(data, imap_args, subargs))
 				return FALSE;
 			return TRUE;
 		} if (strcmp(str, "ON") == 0) {
@@ -490,9 +593,9 @@
 	case 'T':
 		if (strcmp(str, "TEXT") == 0) {
 			/* <string> */
-			if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
-			    *IMAP_ARG_STR(*args) == '\0') {
-				*args += 1;
+			if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) &&
+			    *IMAP_ARG_STR(*imap_args) == '\0') {
+				*imap_args += 1;
 				return ARG_NEW_SINGLE(SEARCH_ALL);
 			}
 			return ARG_NEW_STR(SEARCH_TEXT);
@@ -515,7 +618,7 @@
 			}
 			if (imap_seq_set_parse(sarg->value.str,
 					       &sarg->value.seqset) < 0) {
-				data->error = "Invalid UID messageset";
+				*error_r = "Invalid UID messageset";
 				return FALSE;
 			}
 			return TRUE;
@@ -560,17 +663,17 @@
 	case 'X':
 		if (strcmp(str, "X-BODY-FAST") == 0) {
 			/* <string> */
-			if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
-			    *IMAP_ARG_STR(*args) == '\0') {
-				*args += 1;
+			if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) &&
+			    *IMAP_ARG_STR(*imap_args) == '\0') {
+				*imap_args += 1;
 				return ARG_NEW_SINGLE(SEARCH_ALL);
 			}
 			return ARG_NEW_STR(SEARCH_BODY_FAST);
 		} else if (strcmp(str, "X-TEXT-FAST") == 0) {
 			/* <string> */
-			if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
-			    *IMAP_ARG_STR(*args) == '\0') {
-				*args += 1;
+			if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) &&
+			    *IMAP_ARG_STR(*imap_args) == '\0') {
+				*imap_args += 1;
 				return ARG_NEW_SINGLE(SEARCH_ALL);
 			}
 			return ARG_NEW_STR(SEARCH_TEXT_FAST);
@@ -585,7 +688,7 @@
 			p_array_init(&(*next_sarg)->value.seqset,
 				     data->pool, 16);
 			if (imap_seq_set_parse(str, &(*next_sarg)->value.seqset) < 0) {
-				data->error = "Invalid messageset";
+				*error_r = "Invalid messageset";
 				return FALSE;
 			}
 			return TRUE;
@@ -601,9 +704,7 @@
 		}
 		break;
 	}
-
-	data->error = t_strconcat("Unknown argument ", str, NULL);
-	return FALSE;
+#endif
 }
 
 int mail_search_build_from_imap_args(const struct imap_arg *imap_args,
@@ -611,9 +712,8 @@
 				     struct mail_search_args **args_r,
 				     const char **error_r)
 {
-        struct search_build_data data;
 	struct mail_search_args *args;
-	struct mail_search_arg **sargs;
+	struct mail_search_arg *arg;
 
 	*args_r = NULL;
 	*error_r = NULL;
@@ -621,17 +721,14 @@
 	args = mail_search_build_init();
 	args->charset = p_strdup(args->pool, charset);
 
-	data.pool = args->pool;
-	data.error = NULL;
-
-	sargs = &args->args;
 	while (imap_args->type != IMAP_ARG_EOL) {
-		if (!search_arg_build(&data, &imap_args, sargs)) {
+		arg = search_arg_build(args, &imap_args, error_r);
+		if (arg == NULL) {
 			pool_unref(&args->pool);
-			*error_r = data.error;
 			return -1;
 		}
-		sargs = &(*sargs)->next;
+
+		search_args_link(&args->args, arg);
 	}
 
 	*args_r = args;
diff -r d440b7cad709 src/lib-storage/mail-search.h
--- a/src/lib-storage/mail-search.h	Mon Jun 09 22:02:02 2008 +0300
+++ b/src/lib-storage/mail-search.h	Mon Jun 09 22:24:19 2008 +0300
@@ -4,44 +4,17 @@
 #include "seq-range-array.h"
 #include "mail-types.h"
 
+struct imap_arg;
+struct mail_search_args;
+
 enum mail_search_arg_type {
-	SEARCH_OR,
-	SEARCH_SUB,
-
-	/* sequence sets */
-	SEARCH_ALL,
-	SEARCH_SEQSET,
-	SEARCH_UIDSET,
-
-	/* flags */
-	SEARCH_FLAGS,
-	SEARCH_KEYWORDS,
-
-	/* dates */
-	SEARCH_BEFORE,
-	SEARCH_ON, /* time must point to beginning of the day */
-	SEARCH_SINCE,
-	SEARCH_SENTBEFORE,
-	SEARCH_SENTON, /* time must point to beginning of the day */
-	SEARCH_SENTSINCE,
-
-	/* sizes */
-	SEARCH_SMALLER,
-	SEARCH_LARGER,
-
-	/* headers */
-	SEARCH_HEADER,
-	SEARCH_HEADER_ADDRESS,
-	SEARCH_HEADER_COMPRESS_LWSP,
-
-	/* body */
-	SEARCH_BODY,
-	SEARCH_TEXT,
-	SEARCH_BODY_FAST,
-	SEARCH_TEXT_FAST,
-
-	/* extensions */
-	SEARCH_MODSEQ
+	SEARCH_ARG_TYPE_SUB,
+	SEARCH_ARG_TYPE_DYNAMIC,
+	SEARCH_ARG_TYPE_STATIC_METADATA,
+	SEARCH_ARG_TYPE_STATIC_HEADER,
+	SEARCH_ARG_TYPE_STATIC_HEADER_ADDRESS,
+	SEARCH_ARG_TYPE_STATIC_HEADER_COMPRESS_LWSP,
+	SEARCH_ARG_TYPE_STATIC_BODY
 };
 
 enum mail_search_arg_flag {
@@ -61,9 +34,29 @@
 	enum mail_search_modseq_type type;
 };
 
+struct mail_search_handler {
+	const char *name;
+	enum mail_search_arg_type type;
+
+	/* If imap_args is invalid, return NULL and set error_r. Mailbox isn't
+	   known at this point. */
+	struct mail_search_arg *
+		(*build)(struct mail_search_args *args,
+			 const struct imap_arg **imap_args,
+			 const char **error_r);
+	/* Initialize arguments before searching. Mailbox is known now.
+	   May be NULL. */
+	void (*init)(struct mail_search_args *args,
+		     struct mail_search_arg *arg);
+	/* Free any resourced used by the arg. May be NULL. */
+	void (*free)(struct mail_search_args *args,
+		     struct mail_search_arg *arg);
+};
+
 struct mail_search_arg {
 	struct mail_search_arg *next;
 
+	const struct mail_search_handler *handler;
 	enum mail_search_arg_type type;
 	struct {
 		struct mail_search_arg *subargs;
