changeset 919:5ebec31b49e0 HEAD

Login process now uses the same imap-parser as the imap process itself. This fixes the problem of literals not working before logging in.
author Timo Sirainen <tss@iki.fi>
date Tue, 07 Jan 2003 19:45:38 +0200
parents 23b42ffe8756
children 30babf122f97
files src/login/Makefile.am src/login/client-authenticate.c src/login/client-authenticate.h src/login/client.c src/login/client.h
diffstat 5 files changed, 138 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/src/login/Makefile.am	Tue Jan 07 19:44:23 2003 +0200
+++ b/src/login/Makefile.am	Tue Jan 07 19:45:38 2003 +0200
@@ -3,10 +3,12 @@
 pkglibexec_PROGRAMS = imap-login
 
 INCLUDES = \
-	-I$(top_srcdir)/src/lib
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-imap
 
 imap_login_LDADD = \
 	../lib/liblib.a \
+	../lib-imap/imap-parser.o \
 	$(SSL_LIBS)
 
 imap_login_SOURCES = \
--- a/src/login/client-authenticate.c	Tue Jan 07 19:44:23 2003 +0200
+++ b/src/login/client-authenticate.c	Tue Jan 07 19:45:38 2003 +0200
@@ -8,6 +8,7 @@
 #include "ostream.h"
 #include "safe-memset.h"
 #include "str.h"
+#include "imap-parser.h"
 #include "auth-connection.h"
 #include "../auth/auth-mech-desc.h"
 #include "client.h"
@@ -128,7 +129,7 @@
 	case AUTH_RESULT_SUCCESS:
 		client->auth_request = NULL;
 
-		master_request_imap(client->fd, auth_process, client->tag,
+		master_request_imap(client->fd, auth_process, client->cmd_tag,
 				    request->cookie, &client->ip,
 				    master_callback, client);
 
@@ -180,9 +181,20 @@
 	}
 }
 
-int cmd_login(struct client *client, const char *user, const char *pass)
+int cmd_login(struct client *client, struct imap_arg *args)
 {
-	const char *error;
+	const char *user, *pass, *error;
+
+	/* two arguments: username and password */
+	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
+		return FALSE;
+	if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING)
+		return FALSE;
+	if (args[2].type != IMAP_ARG_EOL)
+		return FALSE;
+
+	user = IMAP_ARG_STR(&args[0]);
+	pass = IMAP_ARG_STR(&args[1]);
 
 	if (!client->tls && disable_plaintext_auth) {
 		client_send_tagline(client,
@@ -239,6 +251,7 @@
 	if (!client_read(client))
 		return;
 
+	/* @UNSAFE */
 	line = i_stream_next_line(client->input);
 	if (line == NULL)
 		return;
@@ -270,11 +283,18 @@
 	safe_memset(buffer_free_without_data(buf), 0, bufsize);
 }
 
-int cmd_authenticate(struct client *client, const char *mech_name)
+int cmd_authenticate(struct client *client, struct imap_arg *args)
 {
 	struct auth_mech_desc *mech;
-	const char *error;
+	const char *mech_name, *error;
 
+	/* we want only one argument: authentication mechanism name */
+	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
+		return FALSE;
+	if (args[1].type != IMAP_ARG_EOL)
+		return FALSE;
+
+	mech_name = IMAP_ARG_STR(&args[0]);
 	if (*mech_name == '\0')
 		return FALSE;
 
--- a/src/login/client-authenticate.h	Tue Jan 07 19:44:23 2003 +0200
+++ b/src/login/client-authenticate.h	Tue Jan 07 19:45:38 2003 +0200
@@ -3,7 +3,7 @@
 
 const char *client_authenticate_get_capabilities(void);
 
-int cmd_login(struct client *client, const char *user, const char *pass);
-int cmd_authenticate(struct client *client, const char *method_name);
+int cmd_login(struct client *client, struct imap_arg *args);
+int cmd_authenticate(struct client *client, struct imap_arg *args);
 
 #endif
--- a/src/login/client.c	Tue Jan 07 19:44:23 2003 +0200
+++ b/src/login/client.c	Tue Jan 07 19:45:38 2003 +0200
@@ -9,12 +9,20 @@
 #include "process-title.h"
 #include "safe-memset.h"
 #include "strescape.h"
+#include "imap-parser.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "ssl-proxy.h"
 
 #include <syslog.h>
 
+/* max. size of one parameter in line */
+#define MAX_INBUF_SIZE 512
+
+/* max. number of IMAP argument elements to accept. The maximum memory usage
+   for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */
+#define MAX_IMAP_ARG_ELEMENTS 4
+
 /* Disconnect client after idling this many seconds */
 #define CLIENT_LOGIN_IDLE_TIMEOUT 60
 
@@ -118,6 +126,95 @@
 	return TRUE;
 }
 
+static int client_command_execute(struct client *client, const char *cmd,
+				  struct imap_arg *args)
+{
+	cmd = str_ucase(t_strdup_noconst(cmd));
+	if (strcmp(cmd, "LOGIN") == 0)
+		return cmd_login(client, args);
+	if (strcmp(cmd, "AUTHENTICATE") == 0)
+		return cmd_authenticate(client, args);
+	if (strcmp(cmd, "CAPABILITY") == 0)
+		return cmd_capability(client);
+	if (strcmp(cmd, "STARTTLS") == 0)
+		return cmd_starttls(client);
+	if (strcmp(cmd, "NOOP") == 0)
+		return cmd_noop(client);
+	if (strcmp(cmd, "LOGOUT") == 0)
+		return cmd_logout(client);
+
+	return FALSE;
+}
+
+/* Skip incoming data until newline is found,
+   returns TRUE if newline was found. */
+static int client_skip_line(struct client *client)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(client->input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == '\n') {
+			i_stream_skip(client->input, i+1);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void client_handle_input(struct client *client)
+{
+	struct imap_arg *args;
+
+	if (client->cmd_finished) {
+		/* clear the previous command from memory. don't do this
+		   immediately after handling command since we need the
+		   cmd_tag to stay some time after authentication commands. */
+		client->cmd_tag = NULL;
+		client->cmd_name = NULL;
+		imap_parser_reset(client->parser);
+
+		/* remove \r\n */
+		if (!client_skip_line(client))
+			return;
+
+		client->cmd_finished = FALSE;
+	}
+
+	if (client->cmd_tag == NULL) {
+                client->cmd_tag = imap_parser_read_word(client->parser);
+		if (client->cmd_tag == NULL)
+			return; /* need more data */
+	}
+
+	if (client->cmd_name == NULL) {
+                client->cmd_name = imap_parser_read_word(client->parser);
+		if (client->cmd_name == NULL)
+			return; /* need more data */
+	}
+
+	switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
+	case -1:
+		/* error */
+		client_destroy(client, NULL);
+		return;
+	case -2:
+		/* not enough data */
+		return;
+	}
+
+	if (*client->cmd_tag == '\0' ||
+	    !client_command_execute(client, client->cmd_name, args)) {
+		client_send_tagline(client,
+			"BAD Error in IMAP command received by server.");
+	}
+
+	client->cmd_finished = TRUE;
+}
+
 int client_read(struct client *client)
 {
 	switch (i_stream_read(client->input)) {
@@ -136,104 +233,20 @@
 	}
 }
 
-static char *get_next_arg(char **linep)
-{
-	char *line, *start;
-	int quoted;
-
-	line = *linep;
-	while (*line == ' ') line++;
-
-	/* @UNSAFE: get next argument, unescape arg if it's quoted */
-	if (*line == '"') {
-		quoted = TRUE;
-		line++;
-
-		start = line;
-		while (*line != '\0' && *line != '"') {
-			if (*line == '\\' && line[1] != '\0')
-				line++;
-			line++;
-		}
-
-		if (*line == '"')
-			*line++ = '\0';
-		str_unescape(start);
-	} else {
-		start = line;
-		while (*line != '\0' && *line != ' ')
-			line++;
-
-		if (*line == ' ')
-			*line++ = '\0';
-	}
-
-	*linep = line;
-	return start;
-}
-
-static int client_command_execute(struct client *client, char *line)
-{
-	char *cmd;
-	int ret;
-
-	cmd = get_next_arg(&line);
-	str_ucase(cmd);
-
-	if (strcmp(cmd, "LOGIN") == 0) {
-		char *user, *pass;
-
-		user = get_next_arg(&line);
-		pass = get_next_arg(&line);
-		ret = cmd_login(client, user, pass);
-
-		safe_memset(pass, 0, strlen(pass));
-		return ret;
-	}
-	if (strcmp(cmd, "AUTHENTICATE") == 0)
-		return cmd_authenticate(client, get_next_arg(&line));
-	if (strcmp(cmd, "CAPABILITY") == 0)
-		return cmd_capability(client);
-	if (strcmp(cmd, "STARTTLS") == 0)
-		return cmd_starttls(client);
-	if (strcmp(cmd, "NOOP") == 0)
-		return cmd_noop(client);
-	if (strcmp(cmd, "LOGOUT") == 0)
-		return cmd_logout(client);
-
-	return FALSE;
-}
-
 void client_input(void *context, int fd __attr_unused__,
 		  struct io *io __attr_unused__)
 {
 	struct client *client = context;
-	char *line;
 
 	client->last_input = ioloop_time;
 
-	i_free(client->tag);
-	client->tag = i_strdup("*");
-
 	if (!client_read(client))
 		return;
 
 	client_ref(client);
+
 	o_stream_cork(client->output);
-
-	while ((line = i_stream_next_line(client->input)) != NULL) {
-		/* split the arguments, make sure we have at
-		   least tag + command */
-		i_free(client->tag);
-		client->tag = i_strdup(get_next_arg(&line));
-
-		if (*client->tag == '\0' ||
-		    !client_command_execute(client, line)) {
-			/* error */
-			client_send_tagline(client, "BAD Error in IMAP command "
-					    "received by server.");
-		}
-	}
+	client_handle_input(client);
 
 	if (client_unref(client))
 		o_stream_flush(client->output);
@@ -306,7 +319,11 @@
 	client->input = i_stream_create_file(fd, default_pool, 8192, FALSE);
 	client->output = o_stream_create_file(fd, default_pool, 1024,
 					      IO_PRIORITY_DEFAULT, FALSE);
+	client->parser = imap_parser_create(client->input, client->output,
+					    MAX_INBUF_SIZE,
+					    MAX_IMAP_ARG_ELEMENTS);
 	client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
+
 	client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
@@ -324,6 +341,7 @@
 
 	hash_remove(clients, client);
 
+	imap_parser_destroy(client->parser);
 	i_stream_close(client->input);
 	o_stream_close(client->output);
 
@@ -351,7 +369,6 @@
 	i_stream_unref(client->input);
 	o_stream_unref(client->output);
 
-	i_free(client->tag);
 	buffer_free(client->plain_login);
 	i_free(client);
 
@@ -367,7 +384,7 @@
 
 void client_send_tagline(struct client *client, const char *line)
 {
-	client_send_line(client, t_strconcat(client->tag, " ", line, NULL));
+	client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
 }
 
 void client_syslog(struct client *client, const char *text)
--- a/src/login/client.h	Tue Jan 07 19:44:23 2003 +0200
+++ b/src/login/client.h	Tue Jan 07 19:45:38 2003 +0200
@@ -12,14 +12,16 @@
 	struct io *io;
 	struct istream *input;
 	struct ostream *output;
+	struct imap_parser *parser;
 
 	time_t last_input;
-	char *tag;
+	const char *cmd_tag, *cmd_name;
 
 	buffer_t *plain_login;
 	struct auth_request *auth_request;
 
 	unsigned int tls:1;
+	unsigned int cmd_finished:1;
 };
 
 struct client *client_create(int fd, struct ip_addr *ip, int imaps);