changeset 14228:d8a88e53f1e6

imap: Changed internal FETCH command handling API.
author Timo Sirainen <tss@iki.fi>
date Thu, 01 Mar 2012 09:31:40 +0200
parents df631445f150
children 96e3b3ef59dc
files src/imap/cmd-fetch.c src/imap/cmd-select.c src/imap/imap-fetch-body.c src/imap/imap-fetch.c src/imap/imap-fetch.h
diffstat 5 files changed, 253 insertions(+), 237 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/cmd-fetch.c	Tue Feb 28 05:27:03 2012 +0200
+++ b/src/imap/cmd-fetch.c	Thu Mar 01 09:31:40 2012 +0200
@@ -21,13 +21,14 @@
 };
 
 static bool
-fetch_parse_args(struct imap_fetch_context *ctx, const struct imap_arg *arg,
-		 const struct imap_arg **next_arg_r)
+fetch_parse_args(struct imap_fetch_context *ctx,
+		 struct client_command_context *cmd,
+		 const struct imap_arg *arg, const struct imap_arg **next_arg_r)
 {
 	const char *str, *const *macro;
 
-	if (ctx->cmd->uid) {
-		if (!imap_fetch_init_handler(ctx, "UID", &arg))
+	if (cmd->uid) {
+		if (!imap_fetch_cmd_init_handler(ctx, cmd, "UID", &arg))
 			return FALSE;
 	}
 	if (imap_arg_get_atom(arg, &str)) {
@@ -43,12 +44,12 @@
 			macro = full_macro;
 		else {
 			macro = NULL;
-			if (!imap_fetch_init_handler(ctx, str, &arg))
+			if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg))
 				return FALSE;
 		}
 		if (macro != NULL) {
 			while (*macro != NULL) {
-				if (!imap_fetch_init_handler(ctx, *macro, &arg))
+				if (!imap_fetch_cmd_init_handler(ctx, cmd, *macro, &arg))
 					return FALSE;
 				macro++;
 			}
@@ -60,11 +61,11 @@
 		while (imap_arg_get_atom(arg, &str)) {
 			str = t_str_ucase(str);
 			arg++;
-			if (!imap_fetch_init_handler(ctx, str, &arg))
+			if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg))
 				return FALSE;
 		}
 		if (!IMAP_ARG_IS_EOL(arg)) {
-			client_send_command_error(ctx->cmd,
+			client_send_command_error(cmd,
 				"FETCH list contains non-atoms.");
 			return FALSE;
 		}
@@ -74,6 +75,7 @@
 
 static bool
 fetch_parse_modifier(struct imap_fetch_context *ctx,
+		     struct client_command_context *cmd,
 		     const char *name, const struct imap_arg **args)
 {
 	const char *str;
@@ -82,58 +84,59 @@
 	if (strcmp(name, "CHANGEDSINCE") == 0) {
 		if (!imap_arg_get_atom(*args, &str) ||
 		    str_to_uint64(str, &modseq) < 0) {
-			client_send_command_error(ctx->cmd,
+			client_send_command_error(cmd,
 				"Invalid CHANGEDSINCE modseq.");
 			return FALSE;
 		}
 		*args += 1;
-		return imap_fetch_add_changed_since(ctx, modseq);
+		imap_fetch_add_changed_since(ctx, modseq);
+		return TRUE;
 	}
-	if (strcmp(name, "VANISHED") == 0 && ctx->cmd->uid) {
+	if (strcmp(name, "VANISHED") == 0 && cmd->uid) {
 		if ((ctx->client->enabled_features &
 		     MAILBOX_FEATURE_QRESYNC) == 0) {
-			client_send_command_error(ctx->cmd,
-						  "QRESYNC not enabled");
+			client_send_command_error(cmd, "QRESYNC not enabled");
 			return FALSE;
 		}
 		ctx->send_vanished = TRUE;
 		return TRUE;
 	}
 
-	client_send_command_error(ctx->cmd, "Unknown FETCH modifier");
+	client_send_command_error(cmd, "Unknown FETCH modifier");
 	return FALSE;
 }
 
 static bool
 fetch_parse_modifiers(struct imap_fetch_context *ctx,
+		      struct client_command_context *cmd,
 		      const struct imap_arg *args)
 {
 	const char *name;
 
 	while (!IMAP_ARG_IS_EOL(args)) {
 		if (!imap_arg_get_atom(args, &name)) {
-			client_send_command_error(ctx->cmd,
+			client_send_command_error(cmd,
 				"FETCH modifiers contain non-atoms.");
 			return FALSE;
 		}
 		args++;
-		if (!fetch_parse_modifier(ctx, t_str_ucase(name), &args))
+		if (!fetch_parse_modifier(ctx, cmd, t_str_ucase(name), &args))
 			return FALSE;
 	}
 	if (ctx->send_vanished &&
 	    (ctx->search_args->args->next == NULL ||
 	     ctx->search_args->args->next->type != SEARCH_MODSEQ)) {
-		client_send_command_error(ctx->cmd,
+		client_send_command_error(cmd,
 			"VANISHED used without CHANGEDSINCE");
 		return FALSE;
 	}
 	return TRUE;
 }
 
-static bool cmd_fetch_finish(struct imap_fetch_context *ctx)
+static bool cmd_fetch_finish(struct imap_fetch_context *ctx,
+			     struct client_command_context *cmd)
 {
 	static const char *ok_message = "OK Fetch completed.";
-	struct client_command_context *cmd = ctx->cmd;
 	const char *tagged_reply = ok_message;
 
 	if (ctx->partial_fetch) {
@@ -170,11 +173,11 @@
 {
         struct imap_fetch_context *ctx = cmd->context;
 
-	if (imap_fetch_more(ctx) == 0) {
+	if (imap_fetch_more(ctx, cmd) == 0) {
 		/* unfinished */
 		return FALSE;
 	}
-	return cmd_fetch_finish(ctx);
+	return cmd_fetch_finish(ctx, cmd);
 }
 
 bool cmd_fetch(struct client_command_context *cmd)
@@ -213,15 +216,15 @@
 	}
 	ctx->search_args = search_args;
 
-	if (!fetch_parse_args(ctx, &args[1], &next_arg) ||
+	if (!fetch_parse_args(ctx, cmd, &args[1], &next_arg) ||
 	    (imap_arg_get_list(next_arg, &list_arg) &&
-	     !fetch_parse_modifiers(ctx, list_arg))) {
+	     !fetch_parse_modifiers(ctx, cmd, list_arg))) {
 		imap_fetch_deinit(ctx);
 		return TRUE;
 	}
 
 	if (imap_fetch_begin(ctx) == 0) {
-		if (imap_fetch_more(ctx) == 0) {
+		if (imap_fetch_more(ctx, cmd) == 0) {
 			/* unfinished */
 			cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
 
@@ -230,5 +233,5 @@
 			return FALSE;
 		}
 	}
-	return cmd_fetch_finish(ctx);
+	return cmd_fetch_finish(ctx, cmd);
 }
--- a/src/imap/cmd-select.c	Tue Feb 28 05:27:03 2012 +0200
+++ b/src/imap/cmd-select.c	Thu Mar 01 09:31:40 2012 +0200
@@ -214,7 +214,7 @@
         struct imap_select_context *ctx = cmd->context;
 	int ret;
 
-	if (imap_fetch_more(ctx->fetch_ctx) == 0) {
+	if (imap_fetch_more(ctx->fetch_ctx, cmd) == 0) {
 		/* unfinished */
 		return FALSE;
 	}
@@ -247,16 +247,13 @@
 	fetch_ctx->qresync_sample_seqset = &ctx->qresync_sample_seqset;
 	fetch_ctx->qresync_sample_uidset = &ctx->qresync_sample_uidset;
 
-	if (!imap_fetch_add_changed_since(fetch_ctx, ctx->qresync_modseq) ||
-	    !imap_fetch_init_handler(fetch_ctx, "UID", NULL) ||
-	    !imap_fetch_init_handler(fetch_ctx, "FLAGS", NULL) ||
-	    !imap_fetch_init_handler(fetch_ctx, "MODSEQ", NULL)) {
-		(void)imap_fetch_deinit(fetch_ctx);
-		return -1;
-	}
+	imap_fetch_add_changed_since(fetch_ctx, ctx->qresync_modseq);
+	imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init);
+	imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init);
+	imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init);
 
 	if (imap_fetch_begin(fetch_ctx) == 0) {
-		if (imap_fetch_more(fetch_ctx) == 0) {
+		if (imap_fetch_more(fetch_ctx, ctx->cmd) == 0) {
 			/* unfinished */
 			ctx->fetch_ctx = fetch_ctx;
 			ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
--- a/src/imap/imap-fetch-body.c	Tue Feb 28 05:27:03 2012 +0200
+++ b/src/imap/imap-fetch-body.c	Thu Mar 01 09:31:40 2012 +0200
@@ -305,7 +305,7 @@
 {
 	string_t *str;
 
-	ctx->cur_name = p_strconcat(ctx->cmd->pool,
+	ctx->cur_name = p_strconcat(ctx->pool,
 				    "[", body->section, "]", NULL);
 	ctx->cur_size = get_send_size(body, size->virtual_size);
 
@@ -607,63 +607,60 @@
 	return TRUE;
 }
 
-static bool fetch_body_header_fields_init(struct imap_fetch_context *ctx,
+static bool fetch_body_header_fields_init(struct imap_fetch_init_context *ctx,
 					  struct imap_fetch_body_data *body,
 					  const char *section)
 {
-	const char *const *arr, *name;
+	const char *const *arr;
 
 	if (!fetch_body_header_fields_check(section))
 		return FALSE;
 
-	name = get_body_name(body);
-	if ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
-				MAIL_FETCH_STREAM_BODY)) != 0) {
+	if ((ctx->fetch_ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
+					   MAIL_FETCH_STREAM_BODY)) != 0) {
 		/* we'll need to open the file anyway, don't try to get the
 		   headers from cache. */
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
+		imap_fetch_add_handler(ctx, 0, "NIL",
 				       fetch_body_header_partial, body);
 		return TRUE;
 	}
 
 	for (arr = body->fields; *arr != NULL; arr++) {
-		const char *hdr = p_strdup(ctx->cmd->pool, *arr);
-		array_append(&ctx->all_headers, &hdr, 1);
+		const char *hdr = p_strdup(ctx->fetch_ctx->pool, *arr);
+		array_append(&ctx->fetch_ctx->all_headers, &hdr, 1);
 	}
 
-	body->header_ctx = mailbox_header_lookup_init(ctx->box, body->fields);
-	imap_fetch_add_handler(ctx, FALSE, TRUE, name, "NIL",
+	body->header_ctx = mailbox_header_lookup_init(ctx->fetch_ctx->box,
+						      body->fields);
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "NIL",
 			       fetch_body_header_fields, body);
 	return TRUE;
 }
 
-static bool fetch_body_section_name_init(struct imap_fetch_context *ctx,
-					 struct imap_fetch_body_data *body)
+static bool
+fetch_body_section_name_init(struct imap_fetch_init_context *ctx,
+			     struct imap_fetch_body_data *body)
 {
-	const char *name, *section = body->section;
+	const char *section = body->section;
 
-	name = get_body_name(body);
 	if (*section == '\0') {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
 			MAIL_FETCH_STREAM_BODY;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
-				       fetch_body, body);
+		imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body);
 		return TRUE;
 	}
 
 	if (strcmp(section, "TEXT") == 0) {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
-				       fetch_body, body);
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
+		imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body);
 		return TRUE;
 	}
 
 	if (strncmp(section, "HEADER", 6) == 0) {
 		/* exact header matches could be cached */
 		if (section[6] == '\0') {
-			ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
-			imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
-					       fetch_body, body);
+			ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
+			imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body);
 			return TRUE;
 		}
 
@@ -673,12 +670,12 @@
 
 		if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 &&
 		    fetch_body_header_fields_check(section+18)) {
-			imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
+			imap_fetch_add_handler(ctx, 0, "NIL",
 					       fetch_body_header_partial, body);
 			return TRUE;
 		}
 	} else if (*section >= '0' && *section <= '9') {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_BODY |
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY |
 			MAIL_FETCH_MESSAGE_PARTS;
 
 		while ((*section >= '0' && *section <= '9') ||
@@ -692,14 +689,13 @@
 		     fetch_body_header_fields_check(section+14)) ||
 		    (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 &&
 		     fetch_body_header_fields_check(section+18))) {
-			imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
+			imap_fetch_add_handler(ctx, 0, "NIL",
 					       fetch_body_mime, body);
 			return TRUE;
 		}
 	}
 
-	client_send_command_error(ctx->cmd,
-		"Invalid BODY[..] parameter: Unknown or broken section");
+	ctx->error = "Invalid BODY[..] parameter: Unknown or broken section";
 	return FALSE;
 }
 
@@ -723,34 +719,32 @@
 	return TRUE;
 }
 
-static bool body_section_build(struct imap_fetch_context *ctx,
-			       struct imap_fetch_body_data *body,
-			       const char *prefix,
-			       const struct imap_arg *args,
-			       unsigned int args_count)
+static bool
+body_section_build(struct imap_fetch_init_context *ctx,
+		   struct imap_fetch_body_data *body, const char *prefix,
+		   const struct imap_arg *args, unsigned int args_count)
 {
 	string_t *str;
 	const char **arr, *value;
 	size_t i;
 
-	str = str_new(ctx->cmd->pool, 128);
+	str = str_new(ctx->fetch_ctx->pool, 128);
 	str_append(str, prefix);
 	str_append(str, " (");
 
 	/* @UNSAFE: NULL-terminated list of headers */
-	arr = p_new(ctx->cmd->pool, const char *, args_count + 1);
+	arr = p_new(ctx->fetch_ctx->pool, const char *, args_count + 1);
 
 	for (i = 0; i < args_count; i++) {
 		if (!imap_arg_get_astring(&args[i], &value)) {
-			client_send_command_error(ctx->cmd,
-				"Invalid BODY[..] parameter: "
-				"Header list contains non-strings");
+			ctx->error = "Invalid BODY[..] parameter: "
+				"Header list contains non-strings";
 			return FALSE;
 		}
 
 		if (i != 0)
 			str_append_c(str, ' ');
-		arr[i] = p_strdup(ctx->cmd->pool, t_str_ucase(value));
+		arr[i] = p_strdup(ctx->fetch_ctx->pool, t_str_ucase(value));
 
 		if (args[i].type == IMAP_ARG_ATOM)
 			str_append(str, arr[i]);
@@ -769,53 +763,51 @@
 	return TRUE;
 }
   
-bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name,
-			     const struct imap_arg **args)
+bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx)
 {
 	struct imap_fetch_body_data *body;
 	const struct imap_arg *list_args;
 	unsigned int list_count;
-	const char *partial, *str;
-	const char *p = name + 4;
+	const char *partial, *str, *p;
 
-	body = p_new(ctx->cmd->pool, struct imap_fetch_body_data, 1);
+	i_assert(strncmp(ctx->name, "BODY", 4) == 0);
+	p = ctx->name + 4;
+
+	body = p_new(ctx->fetch_ctx->pool, struct imap_fetch_body_data, 1);
 	body->max_size = (uoff_t)-1;
 
 	if (strncmp(p, ".PEEK", 5) == 0) {
 		body->peek = TRUE;
 		p += 5;
 	} else {
-		ctx->flags_update_seen = TRUE;
+		ctx->fetch_ctx->flags_update_seen = TRUE;
 	}
 
 	if (*p != '[') {
-		client_send_command_error(ctx->cmd,
-			"Invalid BODY[..] parameter: Missing '['");
+		ctx->error = "Invalid BODY[..] parameter: Missing '['";
 		return FALSE;
 	}
 
-	if (imap_arg_get_list_full(&(*args)[0], &list_args, &list_count)) {
+	if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) {
 		/* BODY[HEADER.FIELDS.. (headers list)] */
-		if (!imap_arg_get_atom(&(*args)[1], &str) ||
+		if (!imap_arg_get_atom(&ctx->args[1], &str) ||
 		    str[0] != ']') {
-			client_send_command_error(ctx->cmd,
-				"Invalid BODY[..] parameter: Missing ']'");
+			ctx->error = "Invalid BODY[..] parameter: Missing ']'";
 			return FALSE;
 		}
 		if (!body_section_build(ctx, body, p+1, list_args, list_count))
 			return FALSE;
 		p = str;
-		*args += 2;
+		ctx->args += 2;
 	} else {
 		/* no headers list */
 		body->section = p+1;
 		p = strchr(body->section, ']');
 		if (p == NULL) {
-			client_send_command_error(ctx->cmd,
-				"Invalid BODY[..] parameter: Missing ']'");
+			ctx->error = "Invalid BODY[..] parameter: Missing ']'";
 			return FALSE;
 		}
-		body->section = p_strdup_until(ctx->cmd->pool,
+		body->section = p_strdup_until(ctx->fetch_ctx->pool,
 					       body->section, p);
 	}
 
@@ -827,9 +819,8 @@
 
 		if (!read_uoff_t(&p, &body->skip) || body->skip > OFF_T_MAX) {
 			/* wrapped */
-			client_send_command_error(ctx->cmd,
-				"Invalid BODY[..] parameter: "
-				"Too big partial start");
+			ctx->error = "Invalid BODY[..] parameter: "
+				"Too big partial start";
 			return FALSE;
 		}
 
@@ -838,22 +829,22 @@
 			if (!read_uoff_t(&p, &body->max_size) ||
 			    body->max_size > OFF_T_MAX) {
 				/* wrapped */
-				client_send_command_error(ctx->cmd,
-					"Invalid BODY[..] parameter: "
-					"Too big partial end");
+				ctx->error = "Invalid BODY[..] parameter: "
+					"Too big partial end";
 				return FALSE;
 			}
 		}
 
 		if (*p != '>') {
-			client_send_command_error(ctx->cmd,
-				t_strdup_printf("Invalid BODY[..] parameter: "
-						"Missing '>' in '%s'",
-						partial));
+			ctx->error = t_strdup_printf(
+				"Invalid BODY[..] parameter: "
+				"Missing '>' in '%s'", partial);
 			return FALSE;
 		}
 	}
 
+	/* sanitize the name */
+	ctx->name = get_body_name(body);
 	return fetch_body_section_name_init(ctx, body);
 }
 
@@ -954,39 +945,37 @@
 	return fetch_stream(ctx, &body_size);
 }
 
-bool fetch_rfc822_init(struct imap_fetch_context *ctx, const char *name,
-		       const struct imap_arg **args ATTR_UNUSED)
+bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx)
 {
+	const char *name = ctx->name;
+
 	if (name[6] == '\0') {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
 			MAIL_FETCH_STREAM_BODY;
-		ctx->flags_update_seen = TRUE;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
-				       fetch_rfc822, NULL);
+		ctx->fetch_ctx->flags_update_seen = TRUE;
+		imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822, NULL);
 		return TRUE;
 	}
 
 	if (strcmp(name+6, ".SIZE") == 0) {
-		ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE;
-		imap_fetch_add_handler(ctx, TRUE, FALSE, name, "0",
-				       fetch_rfc822_size, NULL);
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE;
+		imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+				       "0", fetch_rfc822_size, NULL);
 		return TRUE;
 	}
 	if (strcmp(name+6, ".HEADER") == 0) {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
+		imap_fetch_add_handler(ctx, 0, "NIL",
 				       fetch_rfc822_header, NULL);
 		return TRUE;
 	}
 	if (strcmp(name+6, ".TEXT") == 0) {
-		ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
-		ctx->flags_update_seen = TRUE;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
-				       fetch_rfc822_text, NULL);
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
+		ctx->fetch_ctx->flags_update_seen = TRUE;
+		imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822_text, NULL);
 		return TRUE;
 	}
 
-	client_send_command_error(ctx->cmd, t_strconcat(
-		"Unknown parameter ", name, NULL));
+	ctx->error = t_strconcat("Unknown parameter ", name, NULL);
 	return FALSE;
 }
--- a/src/imap/imap-fetch.c	Tue Feb 28 05:27:03 2012 +0200
+++ b/src/imap/imap-fetch.c	Thu Mar 01 09:31:40 2012 +0200
@@ -45,24 +45,53 @@
 	return strcmp(name, h->name);
 }
 
-bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
-			     const struct imap_arg **args)
+bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx)
 {
 	const struct imap_fetch_handler *handler;
 	const char *lookup_name, *p;
 
-	for (p = name; i_isalnum(*p) || *p == '-'; p++) ;
-	lookup_name = t_strdup_until(name, p);
+	for (p = init_ctx->name; i_isalnum(*p) || *p == '-'; p++) ;
+	lookup_name = t_strdup_until(init_ctx->name, p);
 
 	handler = array_bsearch(&fetch_handlers, lookup_name,
 				imap_fetch_handler_bsearch);
 	if (handler == NULL) {
-		client_send_command_error(ctx->cmd,
-			t_strconcat("Unknown parameter ", name, NULL));
+		init_ctx->error = t_strdup_printf("Unknown parameter: %s",
+						  init_ctx->name);
 		return FALSE;
 	}
+	return handler->init(init_ctx);
+}
 
-	return handler->init(ctx, name, args);
+void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx,
+				    bool (*init)(struct imap_fetch_init_context *))
+{
+	struct imap_fetch_init_context init_ctx;
+
+	memset(&init_ctx, 0, sizeof(init_ctx));
+	init_ctx.fetch_ctx = ctx;
+	if (!init(&init_ctx))
+		i_unreached();
+}
+
+bool imap_fetch_cmd_init_handler(struct imap_fetch_context *ctx,
+				 struct client_command_context *cmd,
+				 const char *name, const struct imap_arg **args)
+{
+	struct imap_fetch_init_context init_ctx;
+
+	memset(&init_ctx, 0, sizeof(init_ctx));
+	init_ctx.fetch_ctx = ctx;
+	init_ctx.name = name;
+	init_ctx.args = *args;
+
+	if (!imap_fetch_init_handler(&init_ctx)) {
+		i_assert(init_ctx.error != NULL);
+		client_send_command_error(cmd, init_ctx.error);
+		return FALSE;
+	}
+	*args = init_ctx.args;
+	return TRUE;
 }
 
 struct imap_fetch_context *
@@ -73,7 +102,7 @@
 
 	ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
 	ctx->client = client;
-	ctx->cmd = cmd;
+	ctx->pool = cmd->pool;
 	ctx->box = box;
 
 	ctx->cur_str = str_new(default_pool, 8192);
@@ -85,7 +114,7 @@
 	return ctx;
 }
 
-bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
+void imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
 				  uint64_t modseq)
 {
 	struct mail_search_arg *search_arg;
@@ -93,26 +122,26 @@
 	search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1);
 	search_arg->type = SEARCH_MODSEQ;
 	search_arg->value.modseq =
-		p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
+		p_new(ctx->pool, struct mail_search_modseq, 1);
 	search_arg->value.modseq->modseq = modseq + 1;
 
 	search_arg->next = ctx->search_args->args->next;
 	ctx->search_args->args->next = search_arg;
 
-	return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
+	imap_fetch_init_nofail_handler(ctx, imap_fetch_modseq_init);
 }
 
 #undef imap_fetch_add_handler
-void imap_fetch_add_handler(struct imap_fetch_context *ctx,
-			    bool buffered, bool want_deinit,
-			    const char *name, const char *nil_reply,
+void imap_fetch_add_handler(struct imap_fetch_init_context *ctx,
+			    enum imap_fetch_handler_flags flags,
+			    const char *nil_reply,
 			    imap_fetch_handler_t *handler, void *context)
 {
 	/* partially because of broken clients, but also partially because
 	   it potentially can make client implementations faster, we have a
 	   buffered parameter which basically means that the handler promises
-	   to write the output in ctx->cur_str. The cur_str is then sent to
-	   client before calling any non-buffered handlers.
+	   to write the output in fetch_ctx->cur_str. The cur_str is then sent
+	   to client before calling any non-buffered handlers.
 
 	   We try to keep the handler registration order the same as the
 	   client requested them. This is especially useful to get UID
@@ -123,7 +152,7 @@
 
 	if (context == NULL) {
 		/* don't allow duplicate handlers */
-		array_foreach(&ctx->handlers, ctx_handler) {
+		array_foreach(&ctx->fetch_ctx->handlers, ctx_handler) {
 			if (ctx_handler->handler == handler &&
 			    ctx_handler->context == NULL)
 				return;
@@ -133,17 +162,17 @@
 	memset(&h, 0, sizeof(h));
 	h.handler = handler;
 	h.context = context;
-	h.buffered = buffered;
-	h.want_deinit = want_deinit;
-	h.name = p_strdup(ctx->cmd->pool, name);
-	h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);
+	h.buffered = (flags & IMAP_FETCH_HANDLER_FLAG_BUFFERED) != 0;
+	h.want_deinit = (flags & IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT) != 0;
+	h.name = p_strdup(ctx->fetch_ctx->pool, ctx->name);
+	h.nil_reply = p_strdup(ctx->fetch_ctx->pool, nil_reply);
 
-	if (!buffered)
-		array_append(&ctx->handlers, &h, 1);
+	if (!h.buffered)
+		array_append(&ctx->fetch_ctx->handlers, &h, 1);
 	else {
-		array_insert(&ctx->handlers, ctx->buffered_handlers_count,
-			     &h, 1);
-                ctx->buffered_handlers_count++;
+		array_insert(&ctx->fetch_ctx->handlers,
+			     ctx->fetch_ctx->buffered_handlers_count, &h, 1);
+                ctx->fetch_ctx->buffered_handlers_count++;
 	}
 }
 
@@ -312,7 +341,7 @@
 			ctx->flags_update_seen = FALSE;
 		else if (!ctx->flags_have_handler) {
 			ctx->flags_show_only_seen_changes = TRUE;
-			(void)imap_fetch_init_handler(ctx, "FLAGS", NULL);
+			imap_fetch_init_nofail_handler(ctx, imap_fetch_flags_init);
 		}
 	}
 
@@ -337,7 +366,7 @@
 
 	/* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */
 	mail_search_args_init(ctx->search_args, ctx->box, TRUE,
-			      &ctx->cmd->client->search_saved_uidset);
+			      &ctx->client->search_saved_uidset);
 	ctx->search_ctx =
 		mailbox_search_init(ctx->trans, ctx->search_args, NULL,
 				    ctx->fetch_data, ctx->all_headers_ctx);
@@ -387,7 +416,8 @@
 	return 0;
 }
 
-static int imap_fetch_more_int(struct imap_fetch_context *ctx)
+static int imap_fetch_more_int(struct imap_fetch_context *ctx,
+			       struct client_command_context *cmd)
 {
 	struct client *client = ctx->client;
 	const struct imap_fetch_context_handler *handlers;
@@ -430,7 +460,7 @@
 		}
 
 		if (ctx->cur_mail == NULL) {
-			if (ctx->cmd->cancel)
+			if (cmd->cancel)
 				return 1;
 
 			if (!mailbox_search_next(ctx->search_ctx,
@@ -503,19 +533,20 @@
 	return 1;
 }
 
-int imap_fetch_more(struct imap_fetch_context *ctx)
+int imap_fetch_more(struct imap_fetch_context *ctx,
+		    struct client_command_context *cmd)
 {
 	int ret;
 
 	i_assert(ctx->client->output_lock == NULL ||
-		 ctx->client->output_lock == ctx->cmd);
+		 ctx->client->output_lock == cmd);
 
-	ret = imap_fetch_more_int(ctx);
+	ret = imap_fetch_more_int(ctx, cmd);
 	if (ret < 0)
 		ctx->failed = TRUE;
 	if (ctx->line_partial) {
 		/* nothing can be sent until FETCH is finished */
-		ctx->client->output_lock = ctx->cmd;
+		ctx->client->output_lock = cmd;
 	}
 	return ret;
 }
@@ -580,16 +611,15 @@
 	return 1;
 }
 
-static bool fetch_body_init(struct imap_fetch_context *ctx, const char *name,
-			    const struct imap_arg **args)
+static bool fetch_body_init(struct imap_fetch_init_context *ctx)
 {
-	if (name[4] == '\0') {
-		ctx->fetch_data |= MAIL_FETCH_IMAP_BODY;
-		imap_fetch_add_handler(ctx, FALSE, FALSE, name,
-				       "("BODY_NIL_REPLY")", fetch_body, NULL);
+	if (ctx->name[4] == '\0') {
+		ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODY;
+		imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY")",
+				       fetch_body, NULL);
 		return TRUE;
 	}
-	return fetch_body_section_init(ctx, name, args);
+	return imap_fetch_body_section_init(ctx);
 }
 
 static int fetch_bodystructure(struct imap_fetch_context *ctx,
@@ -616,13 +646,10 @@
 	return 1;
 }
 
-static bool
-fetch_bodystructure_init(struct imap_fetch_context *ctx, const char *name,
-			 const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_bodystructure_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE;
-	imap_fetch_add_handler(ctx, FALSE, FALSE, name,
-			       "("BODY_NIL_REPLY" NIL NIL NIL NIL)",
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE;
+	imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY" NIL NIL NIL NIL)",
 			       fetch_bodystructure, NULL);
 	return TRUE;
 }
@@ -649,12 +676,10 @@
 	return 1;
 }
 
-static bool
-fetch_envelope_init(struct imap_fetch_context *ctx, const char *name,
-		    const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_envelope_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE;
-	imap_fetch_add_handler(ctx, FALSE, FALSE, name, ENVELOPE_NIL_REPLY,
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE;
+	imap_fetch_add_handler(ctx, 0, ENVELOPE_NIL_REPLY,
 			       fetch_envelope, NULL);
 	return TRUE;
 }
@@ -684,13 +709,12 @@
 	return 1;
 }
 
-static bool
-fetch_flags_init(struct imap_fetch_context *ctx, const char *name,
-		 const struct imap_arg **args ATTR_UNUSED)
+bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->flags_have_handler = TRUE;
-	ctx->fetch_data |= MAIL_FETCH_FLAGS;
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, "()", fetch_flags, NULL);
+	ctx->fetch_ctx->flags_have_handler = TRUE;
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_FLAGS;
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       "()", fetch_flags, NULL);
 	return TRUE;
 }
 
@@ -707,12 +731,10 @@
 	return 1;
 }
 
-static bool
-fetch_internaldate_init(struct imap_fetch_context *ctx, const char *name,
-			const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_internaldate_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE;
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name,
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE;
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
 			       "\"01-Jan-1970 00:00:00 +0000\"",
 			       fetch_internaldate, NULL);
 	return TRUE;
@@ -731,13 +753,11 @@
 	return 1;
 }
 
-static bool
-fetch_modseq_init(struct imap_fetch_context *ctx, const char *name,
-		  const struct imap_arg **args ATTR_UNUSED)
+bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx)
 {
-	(void)client_enable(ctx->client, MAILBOX_FEATURE_CONDSTORE);
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
-			       fetch_modseq, NULL);
+	(void)client_enable(ctx->fetch_ctx->client, MAILBOX_FEATURE_CONDSTORE);
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       NULL, fetch_modseq, NULL);
 	return TRUE;
 }
 
@@ -748,11 +768,10 @@
 	return 1;
 }
 
-static bool
-fetch_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
-	       const struct imap_arg **args ATTR_UNUSED)
+bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx)
 {
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, fetch_uid, NULL);
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       NULL, fetch_uid, NULL);
 	return TRUE;
 }
 
@@ -770,12 +789,11 @@
 	return 1;
 }
 
-static bool
-fetch_guid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
-		const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_guid_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->fetch_data |= MAIL_FETCH_GUID;
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, "", fetch_guid, NULL);
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_GUID;
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       "", fetch_guid, NULL);
 	return TRUE;
 }
 
@@ -798,13 +816,10 @@
 	return 1;
 }
 
-static bool
-fetch_x_mailbox_init(struct imap_fetch_context *ctx ATTR_UNUSED,
-		     const char *name,
-		     const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_x_mailbox_init(struct imap_fetch_init_context *ctx)
 {
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
-			       fetch_x_mailbox, NULL);
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       NULL, fetch_x_mailbox, NULL);
 	return TRUE;
 }
 
@@ -816,13 +831,10 @@
 	return 1;
 }
 
-static bool
-fetch_x_real_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED,
-		      const char *name,
-		      const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_x_real_uid_init(struct imap_fetch_init_context *ctx)
 {
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
-			       fetch_x_real_uid, NULL);
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
+			       NULL, fetch_x_real_uid, NULL);
 	return TRUE;
 }
 
@@ -839,12 +851,10 @@
 	return 1;
 }
 
-static bool
-fetch_x_savedate_init(struct imap_fetch_context *ctx, const char *name,
-		      const struct imap_arg **args ATTR_UNUSED)
+static bool fetch_x_savedate_init(struct imap_fetch_init_context *ctx)
 {
-	ctx->fetch_data |= MAIL_FETCH_SAVE_DATE;
-	imap_fetch_add_handler(ctx, TRUE, FALSE, name,
+	ctx->fetch_ctx->fetch_data |= MAIL_FETCH_SAVE_DATE;
+	imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED,
 			       "\"01-Jan-1970 00:00:00 +0000\"",
 			       fetch_x_savedate, NULL);
 	return TRUE;
@@ -855,11 +865,11 @@
 	{ "BODY", fetch_body_init },
 	{ "BODYSTRUCTURE", fetch_bodystructure_init },
 	{ "ENVELOPE", fetch_envelope_init },
-	{ "FLAGS", fetch_flags_init },
+	{ "FLAGS", imap_fetch_flags_init },
 	{ "INTERNALDATE", fetch_internaldate_init },
-	{ "MODSEQ", fetch_modseq_init },
-	{ "RFC822", fetch_rfc822_init },
-	{ "UID", fetch_uid_init },
+	{ "MODSEQ", imap_fetch_modseq_init },
+	{ "RFC822", imap_fetch_rfc822_init },
+	{ "UID", imap_fetch_uid_init },
 	{ "X-GUID", fetch_guid_init },
 	{ "X-MAILBOX", fetch_x_mailbox_init },
 	{ "X-REAL-UID", fetch_x_real_uid_init },
--- a/src/imap/imap-fetch.h	Tue Feb 28 05:27:03 2012 +0200
+++ b/src/imap/imap-fetch.h	Thu Mar 01 09:31:40 2012 +0200
@@ -3,17 +3,29 @@
 
 struct imap_fetch_context;
 
+enum imap_fetch_handler_flags {
+	IMAP_FETCH_HANDLER_FLAG_BUFFERED	= 0x01,
+	IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT	= 0x02
+};
+
 /* Returns 1 = ok, 0 = client output buffer full, call again, -1 = error.
    mail = NULL for deinit. */
 typedef int imap_fetch_handler_t(struct imap_fetch_context *ctx,
 				 struct mail *mail, void *context);
 
+struct imap_fetch_init_context {
+	struct imap_fetch_context *fetch_ctx;
+	const char *name;
+	const struct imap_arg *args;
+
+	const char *error;
+};
+
 struct imap_fetch_handler {
 	const char *name;
 
-	/* Returns FALSE if arg is invalid. */
-	bool (*init)(struct imap_fetch_context *ctx, const char *name,
-		     const struct imap_arg **args);
+	/* Returns FALSE and sets ctx->error if arg is invalid */
+	bool (*init)(struct imap_fetch_init_context *ctx);
 };
 
 struct imap_fetch_context_handler {
@@ -29,8 +41,8 @@
 
 struct imap_fetch_context {
 	struct client *client;
-	struct client_command_context *cmd;
 	struct mailbox *box;
+	pool_t pool;
 
 	struct mailbox_transaction_context *trans;
 	struct mail_search_args *search_args;
@@ -76,40 +88,45 @@
 void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
 				  size_t count);
 
-void imap_fetch_add_handler(struct imap_fetch_context *ctx,
-			    bool buffered, bool want_deinit,
-			    const char *name, const char *nil_reply,
+void imap_fetch_add_handler(struct imap_fetch_init_context *ctx,
+			    enum imap_fetch_handler_flags flags,
+			    const char *nil_reply,
 			    imap_fetch_handler_t *handler, void *context);
 #ifdef CONTEXT_TYPE_SAFETY
-#  define imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \
-				 handler, context) \
+#  define imap_fetch_add_handler(ctx, flags, nil_reply, handler, context) \
 	({(void)(1 ? 0 : handler((struct imap_fetch_context *)NULL, \
 				 (struct mail *)NULL, context)); \
-	  imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \
+	  imap_fetch_add_handler(ctx, flags, nil_reply, \
 		(imap_fetch_handler_t *)handler, context); })
 #else
-#  define imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \
-				 handler, context) \
-	  imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \
+#  define imap_fetch_add_handler(ctx, flags, nil_reply, handler, context) \
+	  imap_fetch_add_handler(ctx, flags, nil_reply, \
 		(imap_fetch_handler_t *)handler, context)
 #endif
 
 struct imap_fetch_context *
 imap_fetch_init(struct client_command_context *cmd, struct mailbox *box);
 int imap_fetch_deinit(struct imap_fetch_context *ctx);
-bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
-			     const struct imap_arg **args);
+bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx);
+void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx,
+				    bool (*init)(struct imap_fetch_init_context *));
+bool imap_fetch_cmd_init_handler(struct imap_fetch_context *ctx,
+				 struct client_command_context *cmd,
+				 const char *name, const struct imap_arg **args);
 
-bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
+void imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
 				  uint64_t modseq);
 
 int imap_fetch_begin(struct imap_fetch_context *ctx);
-int imap_fetch_more(struct imap_fetch_context *ctx);
+int imap_fetch_more(struct imap_fetch_context *ctx,
+		    struct client_command_context *cmd);
 
-bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name,
-			     const struct imap_arg **args);
-bool fetch_rfc822_init(struct imap_fetch_context *ctx, const char *name,
-		       const struct imap_arg **args);
+bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx);
+bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx);
+bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx);
+
+bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx);
+bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx);
 
 void imap_fetch_handlers_init(void);
 void imap_fetch_handlers_deinit(void);