dovecot-2.2: imap: Allow longer ID command parameters without di...

dovecot at dovecot.org dovecot at dovecot.org
Fri May 2 12:43:58 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/3ecb2b831402
changeset: 17310:3ecb2b831402
user:      Timo Sirainen <tss at iki.fi>
date:      Fri May 02 15:43:30 2014 +0300
description:
imap: Allow longer ID command parameters without disconnecting the client.

diffstat:

 src/imap-login/client.c      |  223 +++++++++++++++++++++++++++++++++---------
 src/imap-login/client.h      |   18 +++
 src/lib-imap/imap-id.c       |   18 ++-
 src/lib-imap/imap-id.h       |    8 +
 src/lib-master/master-auth.h |    8 +-
 5 files changed, 218 insertions(+), 57 deletions(-)

diffs (truncated from 397 to 300 lines):

diff -r a1831c9896d4 -r 3ecb2b831402 src/imap-login/client.c
--- a/src/imap-login/client.c	Fri May 02 13:48:36 2014 +0300
+++ b/src/imap-login/client.c	Fri May 02 15:43:30 2014 +0300
@@ -48,6 +48,27 @@
 	return FALSE;
 }
 
+static bool client_handle_parser_error(struct imap_client *client,
+				       struct imap_parser *parser)
+{
+	const char *msg;
+	bool fatal;
+
+	msg = imap_parser_get_error(parser, &fatal);
+	if (fatal) {
+		client_send_reply(&client->common,
+				  IMAP_CMD_REPLY_BYE, msg);
+		client_destroy(&client->common,
+			       t_strconcat("Disconnected: ", msg, NULL));
+		return FALSE;
+	}
+
+	client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
+	client->cmd_finished = TRUE;
+	client->skip_line = TRUE;
+	return TRUE;
+}
+
 static bool is_login_cmd_disabled(struct client *client)
 {
 	if (client->secured) {
@@ -121,46 +142,91 @@
 }
 
 static void
-client_update_info(struct imap_client *client, const struct imap_arg *args)
+client_update_info(struct imap_client *client,
+		   const char *key, const char *value)
 {
-	const char *key, *value;
-
-	if (!imap_arg_get_list(args, &args))
-		return;
-
-	while (imap_arg_get_string(&args[0], &key) &&
-	       imap_arg_get_nstring(&args[1], &value)) {
-		if (strcasecmp(key, "x-originating-ip") == 0)
-			(void)net_addr2ip(value, &client->common.ip);
-		else if (strcasecmp(key, "x-originating-port") == 0)
-			client->common.remote_port = atoi(value);
-		else if (strcasecmp(key, "x-connected-ip") == 0)
-			(void)net_addr2ip(value, &client->common.local_ip);
-		else if (strcasecmp(key, "x-connected-port") == 0)
-			client->common.local_port = atoi(value);
-		else if (strcasecmp(key, "x-proxy-ttl") == 0)
-			client->common.proxy_ttl = atoi(value);
-		else if (strcasecmp(key, "x-session-id") == 0) {
-			client->common.session_id =
-				p_strdup(client->common.pool, value);
-		}
-		args += 2;
+	if (strcasecmp(key, "x-originating-ip") == 0)
+		(void)net_addr2ip(value, &client->common.ip);
+	else if (strcasecmp(key, "x-originating-port") == 0)
+		client->common.remote_port = atoi(value);
+	else if (strcasecmp(key, "x-connected-ip") == 0)
+		(void)net_addr2ip(value, &client->common.local_ip);
+	else if (strcasecmp(key, "x-connected-port") == 0)
+		client->common.local_port = atoi(value);
+	else if (strcasecmp(key, "x-proxy-ttl") == 0)
+		client->common.proxy_ttl = atoi(value);
+	else if (strcasecmp(key, "x-session-id") == 0) {
+		client->common.session_id =
+			p_strdup(client->common.pool, value);
 	}
 }
 
-static int cmd_id(struct imap_client *client, const struct imap_arg *args)
+static void cmd_id_handle_keyvalue(struct imap_client *client,
+				   const char *key, const char *value)
 {
-	const char *value;
+	if (client->common.trusted && !client->id_logged)
+		client_update_info(client, key, value);
 
+	if (client->cmd_id->log_reply != NULL &&
+	    (client->cmd_id->log_keys == NULL ||
+	     str_array_icase_find((void *)client->cmd_id->log_keys, key)))
+		imap_id_log_reply_append(client->cmd_id->log_reply, key, value);
+}
+
+static int cmd_id_handle_args(struct imap_client *client,
+			      const struct imap_arg *arg)
+{
+	struct imap_client_cmd_id *id = client->cmd_id;
+	const char *key, *value;
+
+	switch (id->state) {
+	case IMAP_CLIENT_ID_STATE_LIST:
+		if (arg->type == IMAP_ARG_NIL)
+			return 1;
+		if (arg->type != IMAP_ARG_LIST)
+			return -1;
+		if (client->set->imap_id_log[0] == '\0') {
+			/* no ID logging */
+		} else if (client->id_logged) {
+			/* already logged the ID reply */
+		} else {
+			id->log_reply = str_new(default_pool, 64);
+			if (strcmp(client->set->imap_id_log, "*") == 0) {
+				/* log all keys */
+			} else {
+				/* log only specified keys */
+				id->log_keys = p_strsplit_spaces(default_pool,
+					client->set->imap_id_log, " ");
+			}
+		}
+		id->state = IMAP_CLIENT_ID_STATE_KEY;
+		break;
+	case IMAP_CLIENT_ID_STATE_KEY:
+		if (!imap_arg_get_string(arg, &key))
+			return -1;
+		if (i_strocpy(id->key, key, sizeof(id->key)) < 0)
+			return -1;
+		id->state = IMAP_CLIENT_ID_STATE_VALUE;
+		break;
+	case IMAP_CLIENT_ID_STATE_VALUE:
+		if (!imap_arg_get_nstring(arg, &value))
+			return -1;
+		cmd_id_handle_keyvalue(client, id->key, value);
+		id->state = IMAP_CLIENT_ID_STATE_KEY;
+		break;
+	}
+	return 0;
+}
+
+static void cmd_id_finish(struct imap_client *client)
+{
+	/* finished handling the parameters */
 	if (!client->id_logged) {
 		client->id_logged = TRUE;
-		if (client->common.trusted)
-			client_update_info(client, args);
 
-		value = imap_id_args_get_log_reply(args, client->set->imap_id_log);
-		if (value != NULL) {
-			client_log(&client->common,
-				   t_strdup_printf("ID sent: %s", value));
+		if (client->cmd_id->log_reply != NULL) {
+			client_log(&client->common, t_strdup_printf(
+				"ID sent: %s", str_c(client->cmd_id->log_reply)));
 		}
 	}
 
@@ -168,7 +234,72 @@
 		t_strdup_printf("* ID %s\r\n",
 			imap_id_reply_generate(client->set->imap_id_send)));
 	client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed.");
-	return 1;
+}
+
+static void cmd_id_free(struct imap_client *client)
+{
+	struct imap_client_cmd_id *id = client->cmd_id;
+
+	if (id->log_reply != NULL)
+		str_free(&id->log_reply);
+	if (id->log_keys != NULL)
+		p_strsplit_free(default_pool, id->log_keys);
+	imap_parser_unref(&id->parser);
+
+	i_free_and_null(client->cmd_id);
+	client->skip_line = TRUE;
+}
+
+static int cmd_id(struct imap_client *client)
+{
+	struct imap_client_cmd_id *id;
+	enum imap_parser_flags parser_flags;
+	const struct imap_arg *args;
+	int ret;
+
+	if (client->cmd_id == NULL) {
+		client->cmd_id = id = i_new(struct imap_client_cmd_id, 1);
+		id->parser = imap_parser_create(client->common.input,
+						client->common.output,
+						MAX_IMAP_LINE);
+		parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST;
+	} else {
+		id = client->cmd_id;
+		parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST;
+	}
+
+	while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) {
+		i_assert(ret == 1);
+
+		if ((ret = cmd_id_handle_args(client, args)) < 0) {
+			client_send_reply(&client->common,
+					  IMAP_CMD_REPLY_BAD,
+					  "Invalid ID parameters");
+			cmd_id_free(client);
+			return -1;
+		}
+		if (ret > 0) {
+			/* NIL parameter */
+			ret = 0;
+			break;
+		}
+		imap_parser_reset(id->parser);
+		parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST;
+	}
+	if (ret == 0) {
+		/* finished the line */
+		cmd_id_finish(client);
+		cmd_id_free(client);
+		return 1;
+	} else if (ret == -1) {
+		if (!client_handle_parser_error(client, id->parser))
+			return 0;
+		cmd_id_free(client);
+		return -1;
+	} else {
+		i_assert(ret == -2);
+		return 0;
+	}
 }
 
 static int cmd_noop(struct imap_client *client)
@@ -205,8 +336,6 @@
 		return cmd_capability(client);
 	if (strcmp(cmd, "STARTTLS") == 0)
 		return cmd_starttls(client);
-	if (strcmp(cmd, "ID") == 0)
-		return cmd_id(client, args);
 	if (strcmp(cmd, "NOOP") == 0)
 		return cmd_noop(client);
 	if (strcmp(cmd, "LOGOUT") == 0)
@@ -247,24 +376,13 @@
 static int client_parse_command(struct imap_client *client,
 				const struct imap_arg **args_r)
 {
-	const char *msg;
-	bool fatal;
-
 	switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
 	case -1:
 		/* error */
-		msg = imap_parser_get_error(client->parser, &fatal);
-		if (fatal) {
-			client_send_reply(&client->common,
-					  IMAP_CMD_REPLY_BYE, msg);
-			client_destroy(&client->common,
-				t_strconcat("Disconnected: ", msg, NULL));
-			return FALSE;
+		if (!client_handle_parser_error(client, client->parser)) {
+			/* client destroyed */
+			return 0;
 		}
-
-		client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
-		client->cmd_finished = TRUE;
-		client->skip_line = TRUE;
 		return -1;
 	case -2:
 		/* not enough data */
@@ -329,6 +447,15 @@
 		ret = cmd_authenticate(client, &parsed);
 		if (ret == 0 && !parsed)
 			return FALSE;
+	} else if (strcasecmp(client->cmd_name, "ID") == 0) {
+		/* ID extensions allows max. 30 parameters,
+		   each max. 1024 bytes long. that brings us over the input
+		   buffer's size, so handle the parameters one at a time */
+		ret = cmd_id(client);
+		if (ret == 0)
+			return FALSE;
+		if (ret < 0)
+			ret = 1; /* don't send the error reply again */
 	} else {
 		ret = client_parse_command(client, &args);
 		if (ret < 0)
diff -r a1831c9896d4 -r 3ecb2b831402 src/imap-login/client.h
--- a/src/imap-login/client.h	Fri May 02 13:48:36 2014 +0300
+++ b/src/imap-login/client.h	Fri May 02 15:43:30 2014 +0300
@@ -2,11 +2,28 @@
 #define CLIENT_H
 
 #include "net.h"
+#include "imap-id.h"
 #include "client-common.h"
 
 /* Master prefix is: <1|0><imap tag><NUL> */
 #define IMAP_TAG_MAX_LEN (LOGIN_MAX_MASTER_PREFIX_LEN-2)
 
+enum imap_client_id_state {
+	IMAP_CLIENT_ID_STATE_LIST = 0,
+	IMAP_CLIENT_ID_STATE_KEY,
+	IMAP_CLIENT_ID_STATE_VALUE
+};
+


More information about the dovecot-cvs mailing list