changeset 19569:5bad98da9310

imap-login: Renamed client.[ch] to imap-login-client.[ch] This allows installing the imap-login-client.h without conflicting.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Fri, 15 Jan 2016 16:49:05 +0200
parents 828dd58e03a4
children da28bf026c2e
files src/imap-login/Makefile.am src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-login-client.c src/imap-login/imap-login-client.h src/imap-login/imap-proxy.c
diffstat 7 files changed, 784 insertions(+), 784 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/Makefile.am	Fri Jan 15 16:46:34 2016 +0200
+++ b/src/imap-login/Makefile.am	Fri Jan 15 16:49:05 2016 +0200
@@ -20,14 +20,14 @@
 	$(LIBDOVECOT_DEPS)
 
 imap_login_SOURCES = \
-	client.c \
+	imap-login-client.c \
 	client-authenticate.c \
 	imap-login-commands.c \
 	imap-login-settings.c \
 	imap-proxy.c
 
 noinst_HEADERS = \
-	client.h \
+	imap-login-client.h \
 	client-authenticate.h \
 	imap-login-commands.h \
 	imap-login-settings.h \
--- a/src/imap-login/client-authenticate.c	Fri Jan 15 16:46:34 2016 +0200
+++ b/src/imap-login/client-authenticate.c	Fri Jan 15 16:49:05 2016 +0200
@@ -14,7 +14,7 @@
 #include "imap-parser.h"
 #include "imap-url.h"
 #include "auth-client.h"
-#include "client.h"
+#include "imap-login-client.h"
 #include "client-authenticate.h"
 #include "imap-proxy.h"
 
--- a/src/imap-login/client.c	Fri Jan 15 16:46:34 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,717 +0,0 @@
-/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
-
-#include "login-common.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "ostream.h"
-#include "safe-memset.h"
-#include "str.h"
-#include "strescape.h"
-#include "imap-parser.h"
-#include "imap-id.h"
-#include "imap-resp-code.h"
-#include "master-service.h"
-#include "master-service-ssl-settings.h"
-#include "master-auth.h"
-#include "client.h"
-#include "client-authenticate.h"
-#include "auth-client.h"
-#include "ssl-proxy.h"
-#include "imap-proxy.h"
-#include "imap-login-commands.h"
-#include "imap-login-settings.h"
-
-#if LOGIN_MAX_INBUF_SIZE < 1024+2
-#  error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
-#endif
-
-/* maximum length for IMAP command line. */
-#define MAX_IMAP_LINE 8192
-
-/* Disconnect client when it sends too many bad commands */
-#define CLIENT_MAX_BAD_COMMANDS 3
-
-/* Skip incoming data until newline is found,
-   returns TRUE if newline was found. */
-bool client_skip_line(struct imap_client *client)
-{
-	const unsigned char *data;
-	size_t i, data_size;
-
-	data = i_stream_get_data(client->common.input, &data_size);
-
-	for (i = 0; i < data_size; i++) {
-		if (data[i] == '\n') {
-			i_stream_skip(client->common.input, i+1);
-			return TRUE;
-		}
-	}
-
-	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) {
-		if (auth_client_find_mech(auth_client, "PLAIN") == NULL) {
-			/* no PLAIN authentication, can't use LOGIN command */
-			return TRUE;
-		}
-		return FALSE;
-	}
-	if (client->set->disable_plaintext_auth)
-		return TRUE;
-	if (strcmp(client->ssl_set->ssl, "required") == 0)
-		return TRUE;
-	return FALSE;
-}
-
-static const char *get_capability(struct client *client)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-	string_t *cap_str = t_str_new(256);
-
-	if (*imap_client->set->imap_capability == '\0')
-		str_append(cap_str, CAPABILITY_BANNER_STRING);
-	else if (*imap_client->set->imap_capability != '+')
-		str_append(cap_str, imap_client->set->imap_capability);
-	else {
-		str_append(cap_str, CAPABILITY_BANNER_STRING);
-		str_append_c(cap_str, ' ');
-		str_append(cap_str, imap_client->set->imap_capability + 1);
-	}
-
-	if (client_is_tls_enabled(client) && !client->tls)
-		str_append(cap_str, " STARTTLS");
-	if (is_login_cmd_disabled(client))
-		str_append(cap_str, " LOGINDISABLED");
-
-	client_authenticate_get_capabilities(client, cap_str);
-	return str_c(cap_str);
-}
-
-static int cmd_capability(struct imap_client *imap_client,
-			  const struct imap_arg *args ATTR_UNUSED)
-{
-	struct client *client = &imap_client->common;
-
-	/* Client is required to send CAPABILITY after STARTTLS, so the
-	   capability resp-code workaround checks only pre-STARTTLS
-	   CAPABILITY commands. */
-	if (!client->starttls)
-		imap_client->client_ignores_capability_resp_code = TRUE;
-	client_send_raw(client, t_strconcat(
-		"* CAPABILITY ", get_capability(client), "\r\n", NULL));
-	client_send_reply(client, IMAP_CMD_REPLY_OK,
-		"Pre-login capabilities listed, post-login capabilities have more.");
-	return 1;
-}
-
-static int cmd_starttls(struct imap_client *client,
-			const struct imap_arg *args ATTR_UNUSED)
-{
-	client_cmd_starttls(&client->common);
-	return 1;
-}
-
-static void
-imap_client_notify_starttls(struct client *client,
-			    bool success, const char *text)
-{
-	if (success)
-		client_send_reply(client, IMAP_CMD_REPLY_OK, text);
-	else
-		client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
-}
-
-static void
-client_update_info(struct imap_client *client,
-		   const char *key, const char *value)
-{
-	if (strcasecmp(key, "x-originating-ip") == 0) {
-		(void)net_addr2ip(value, &client->common.ip);
-	} else if (strcasecmp(key, "x-originating-port") == 0) {
-		(void)net_str2port(value, &client->common.remote_port);
-	} else if (strcasecmp(key, "x-connected-ip") == 0) {
-		(void)net_addr2ip(value, &client->common.local_ip);
-	} else if (strcasecmp(key, "x-connected-port") == 0) {
-		(void)net_str2port(value, &client->common.local_port);
-	}	else if (strcasecmp(key, "x-proxy-ttl") == 0) {
-		if (str_to_uint(value, &client->common.proxy_ttl) < 0) {
-			/* nothing */
-		}
-	} else if (strcasecmp(key, "x-session-id") == 0 ||
-		 strcasecmp(key, "x-session-ext-id") == 0) {
-		if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
-			client->common.session_id =
-				p_strdup(client->common.pool, value);
-		}
-	}
-}
-
-static void cmd_id_handle_keyvalue(struct imap_client *client,
-				   const char *key, 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->cmd_id->log_reply != NULL) {
-			client_log(&client->common, t_strdup_printf(
-				"ID sent: %s", str_c(client->cmd_id->log_reply)));
-		}
-	}
-
-	client_send_raw(&client->common,
-		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.");
-}
-
-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,
-		    const struct imap_arg *args ATTR_UNUSED)
-{
-	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
-			  "NOOP completed.");
-	return 1;
-}
-
-static int cmd_logout(struct imap_client *client,
-		      const struct imap_arg *args ATTR_UNUSED)
-{
-	client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
-	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
-			  "Logout completed.");
-	client_destroy(&client->common, "Aborted login");
-	return 1;
-}
-
-static int cmd_enable(struct imap_client *client,
-		      const struct imap_arg *args ATTR_UNUSED)
-{
-	client_send_raw(&client->common, "* ENABLED\r\n");
-	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
-			  "ENABLE ignored in non-authenticated state.");
-	return 1;
-}
-
-static int client_command_execute(struct imap_client *client, const char *cmd,
-				  const struct imap_arg *args)
-{
-	struct imap_login_command *login_cmd;
-
-	login_cmd = imap_login_command_lookup(cmd);
-	if (login_cmd == NULL)
-		return -2;
-	return login_cmd->func(client, args);
-}
-
-static bool imap_is_valid_tag(const char *tag)
-{
-	for (; *tag != '\0'; tag++) {
-		switch (*tag) {
-		case '+':
-		/* atom-specials: */
-		case '(':
-		case ')':
-		case '{':
-		case '/':
-		case ' ':
-		/* list-wildcards: */
-		case '%':
-		case '*':
-		/* quoted-specials: */
-		case '"':
-		case '\\':
-			return FALSE;
-		default:
-			if (*tag < ' ') /* CTL */
-				return FALSE;
-			break;
-		}
-	}
-	return TRUE;
-}
-
-static int client_parse_command(struct imap_client *client,
-				const struct imap_arg **args_r)
-{
-	switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
-	case -1:
-		/* error */
-		if (!client_handle_parser_error(client, client->parser)) {
-			/* client destroyed */
-			return 0;
-		}
-		return -1;
-	case -2:
-		/* not enough data */
-		return 0;
-	default:
-		/* we read the entire line - skip over the CRLF */
-		if (!client_skip_line(client))
-			i_unreached();
-		return 1;
-	}
-}
-
-static bool client_handle_input(struct imap_client *client)
-{
-	const struct imap_arg *args;
-	bool parsed;
-	int ret;
-
-	i_assert(!client->common.authenticating);
-
-	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) {
-			if (!client_skip_line(client))
-				return FALSE;
-                        client->skip_line = FALSE;
-		}
-
-		client->cmd_finished = FALSE;
-	}
-
-	if (client->cmd_tag == NULL) {
-                client->cmd_tag = imap_parser_read_word(client->parser);
-		if (client->cmd_tag == NULL)
-			return FALSE; /* need more data */
-		if (!imap_is_valid_tag(client->cmd_tag) ||
-		    strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) {
-			/* the tag is invalid, don't allow it and don't
-			   send it back. this attempts to prevent any
-			   potentially dangerous replies in case someone tries
-			   to access us using HTTP protocol. */
-			client->cmd_tag = "";
-		}
-	}
-
-	if (client->cmd_name == NULL) {
-                client->cmd_name = imap_parser_read_word(client->parser);
-		if (client->cmd_name == NULL)
-			return FALSE; /* need more data */
-	}
-
-	if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
-		/* SASL-IR may need more space than input buffer's size,
-		   so we'll handle it as a special case. */
-		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)
-			return TRUE;
-		if (ret == 0)
-			return FALSE;
-		ret = *client->cmd_tag == '\0' ? -1 :
-			client_command_execute(client, client->cmd_name, args);
-	}
-
-	client->cmd_finished = TRUE;
-	if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
-		client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
-			"First parameter in line is IMAP's command tag, "
-			"not the command name. Add that before the command, "
-			"like: a login user pass");
-	} else if (ret < 0) {
-		if (*client->cmd_tag == '\0')
-			client->cmd_tag = "*";
-		if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
-			client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
-				"Too many invalid IMAP commands.");
-			client_destroy(&client->common,
-				"Disconnected: Too many invalid commands");
-			return FALSE;
-		}
-		client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
-			"Error in IMAP command received by server.");
-	}
-
-	return ret != 0 && !client->common.destroyed;
-}
-
-static void imap_client_input(struct client *client)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-
-	if (!client_read(client))
-		return;
-
-	client_ref(client);
-	o_stream_cork(imap_client->common.output);
-	for (;;) {
-		if (!auth_client_is_connected(auth_client)) {
-			/* we're not currently connected to auth process -
-			   don't allow any commands */
-			client_notify_status(client, FALSE,
-					     AUTH_SERVER_WAITING_MSG);
-			if (client->to_auth_waiting != NULL)
-				timeout_remove(&client->to_auth_waiting);
-
-			client->input_blocked = TRUE;
-			break;
-		} else {
-			if (!client_handle_input(imap_client))
-				break;
-		}
-	}
-	o_stream_uncork(imap_client->common.output);
-	client_unref(&client);
-}
-
-static struct client *imap_client_alloc(pool_t pool)
-{
-	struct imap_client *imap_client;
-
-	imap_client = p_new(pool, struct imap_client, 1);
-	return &imap_client->common;
-}
-
-static void imap_client_create(struct client *client, void **other_sets)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-
-	imap_client->set = other_sets[0];
-	imap_client->parser =
-		imap_parser_create(imap_client->common.input,
-				   imap_client->common.output, MAX_IMAP_LINE);
-	client->io = io_add(client->fd, IO_READ, client_input, client);
-}
-
-static void imap_client_destroy(struct client *client)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-
-	i_free_and_null(imap_client->proxy_backend_capability);
-	imap_parser_unref(&imap_client->parser);
-}
-
-static void imap_client_notify_auth_ready(struct client *client)
-{
-	string_t *greet;
-
-	greet = t_str_new(128);
-	str_append(greet, "* OK ");
-	str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
-	str_append(greet, client->set->login_greeting);
-	str_append(greet, "\r\n");
-
-	client_send_raw(client, str_c(greet));
-}
-
-static void imap_client_starttls(struct client *client)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-
-	imap_parser_unref(&imap_client->parser);
-	imap_client->parser =
-		imap_parser_create(imap_client->common.input,
-				   imap_client->common.output, MAX_IMAP_LINE);
-
-	/* CRLF is lost from buffer when streams are reopened. */
-	imap_client->skip_line = FALSE;
-}
-
-static void ATTR_NULL(3)
-client_send_reply_raw(struct client *client,
-		      const char *prefix, const char *resp_code,
-		      const char *text, bool tagged)
-{
-	struct imap_client *imap_client = (struct imap_client *)client;
-
-	T_BEGIN {
-		string_t *line = t_str_new(256);
-
-		if (tagged)
-			str_append(line, imap_client->cmd_tag);
-		else
-			str_append_c(line, '*');
-		str_append_c(line, ' ');
-		str_append(line, prefix);
-		str_append_c(line, ' ');
-		if (resp_code != NULL)
-			str_printfa(line, "[%s] ", resp_code);
-		str_append(line, text);
-		str_append(line, "\r\n");
-
-		client_send_raw_data(client, str_data(line), str_len(line));
-	} T_END;
-}
-
-void client_send_reply_code(struct client *client, enum imap_cmd_reply reply,
-			    const char *resp_code, const char *text)
-{
-	const char *prefix = "NO";
-	bool tagged = TRUE;
-
-	switch (reply) {
-	case IMAP_CMD_REPLY_OK:
-		prefix = "OK";
-		break;
-	case IMAP_CMD_REPLY_NO:
-		break;
-	case IMAP_CMD_REPLY_BAD:
-		prefix = "BAD";
-		break;
-	case IMAP_CMD_REPLY_BYE:
-		prefix = "BYE";
-		tagged = FALSE;
-		break;
-	}
-	client_send_reply_raw(client, prefix, resp_code, text, tagged);
-}
-
-void client_send_reply(struct client *client, enum imap_cmd_reply reply,
-		       const char *text)
-{
-	client_send_reply_code(client, reply, NULL, text);
-}
-
-static void
-imap_client_notify_status(struct client *client, bool bad, const char *text)
-{
-	if (bad)
-		client_send_reply_raw(client, "BAD", "ALERT", text, FALSE);
-	else
-		client_send_reply_raw(client, "OK", NULL, text, FALSE);
-}
-
-static void 
-imap_client_notify_disconnect(struct client *client,
-			      enum client_disconnect_reason reason,
-			      const char *text)
-{
-	if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) {
-		client_send_reply_code(client, IMAP_CMD_REPLY_BYE,
-				       IMAP_RESP_CODE_UNAVAILABLE, text);
-	} else {
-		client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text);
-	}
-}
-
-static void imap_login_preinit(void)
-{
-	login_set_roots = imap_login_setting_roots;
-}
-
-static const struct imap_login_command imap_login_commands[] = {
-	{ "LOGIN", cmd_login },
-	{ "CAPABILITY", cmd_capability },
-	{ "STARTTLS", cmd_starttls },
-	{ "NOOP", cmd_noop },
-	{ "LOGOUT", cmd_logout },
-	{ "ENABLE", cmd_enable }
-};
-
-static void imap_login_init(void)
-{
-	imap_login_commands_init();
-	imap_login_commands_register(imap_login_commands,
-				     N_ELEMENTS(imap_login_commands));
-}
-
-static void imap_login_deinit(void)
-{
-	clients_destroy_all();
-	imap_login_commands_deinit();
-}
-
-static struct client_vfuncs imap_client_vfuncs = {
-	imap_client_alloc,
-	imap_client_create,
-	imap_client_destroy,
-	imap_client_notify_auth_ready,
-	imap_client_notify_disconnect,
-	imap_client_notify_status,
-	imap_client_notify_starttls,
-	imap_client_starttls,
-	imap_client_input,
-	NULL,
-	NULL,
-	imap_client_auth_result,
-	imap_proxy_reset,
-	imap_proxy_parse_line,
-	imap_proxy_error
-};
-
-static const struct login_binary imap_login_binary = {
-	.protocol = "imap",
-	.process_name = "imap-login",
-	.default_port = 143,
-	.default_ssl_port = 993,
-
-	.client_vfuncs = &imap_client_vfuncs,
-	.preinit = imap_login_preinit,
-	.init = imap_login_init,
-	.deinit = imap_login_deinit,
-
-	.sasl_support_final_reply = FALSE
-};
-
-int main(int argc, char *argv[])
-{
-	return login_binary_run(&imap_login_binary, argc, argv);
-}
--- a/src/imap-login/client.h	Fri Jan 15 16:46:34 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-#ifndef CLIENT_H
-#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
-};
-
-struct imap_client_cmd_id {
-	struct imap_parser *parser;
-
-	enum imap_client_id_state state;
-	char key[IMAP_ID_KEY_MAX_LEN+1];
-
-	char **log_keys;
-	string_t *log_reply;
-};
-
-struct imap_client {
-	struct client common;
-
-	const struct imap_login_settings *set;
-	struct imap_parser *parser;
-	char *proxy_backend_capability;
-
-	const char *cmd_tag, *cmd_name;
-	struct imap_client_cmd_id *cmd_id;
-
-	unsigned int cmd_finished:1;
-	unsigned int proxy_sasl_ir:1;
-	unsigned int proxy_seen_banner:1;
-	unsigned int skip_line:1;
-	unsigned int id_logged:1;
-	unsigned int proxy_capability_request_sent:1;
-	unsigned int client_ignores_capability_resp_code:1;
-	unsigned int auth_mech_name_parsed:1;
-};
-
-bool client_skip_line(struct imap_client *client);
-
-enum imap_cmd_reply {
-	IMAP_CMD_REPLY_OK,
-	IMAP_CMD_REPLY_NO,
-	IMAP_CMD_REPLY_BAD,
-	IMAP_CMD_REPLY_BYE
-};
-
-void client_send_reply(struct client *client,
-		       enum imap_cmd_reply reply, const char *text);
-
-void client_send_reply_code(struct client *client,
-			    enum imap_cmd_reply reply, const char *resp_code,
-			    const char *text) ATTR_NULL(3);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/imap-login-client.c	Fri Jan 15 16:49:05 2016 +0200
@@ -0,0 +1,717 @@
+/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
+
+#include "login-common.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "safe-memset.h"
+#include "str.h"
+#include "strescape.h"
+#include "imap-parser.h"
+#include "imap-id.h"
+#include "imap-resp-code.h"
+#include "master-service.h"
+#include "master-service-ssl-settings.h"
+#include "master-auth.h"
+#include "imap-login-client.h"
+#include "client-authenticate.h"
+#include "auth-client.h"
+#include "ssl-proxy.h"
+#include "imap-proxy.h"
+#include "imap-login-commands.h"
+#include "imap-login-settings.h"
+
+#if LOGIN_MAX_INBUF_SIZE < 1024+2
+#  error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
+#endif
+
+/* maximum length for IMAP command line. */
+#define MAX_IMAP_LINE 8192
+
+/* Disconnect client when it sends too many bad commands */
+#define CLIENT_MAX_BAD_COMMANDS 3
+
+/* Skip incoming data until newline is found,
+   returns TRUE if newline was found. */
+bool client_skip_line(struct imap_client *client)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(client->common.input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == '\n') {
+			i_stream_skip(client->common.input, i+1);
+			return TRUE;
+		}
+	}
+
+	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) {
+		if (auth_client_find_mech(auth_client, "PLAIN") == NULL) {
+			/* no PLAIN authentication, can't use LOGIN command */
+			return TRUE;
+		}
+		return FALSE;
+	}
+	if (client->set->disable_plaintext_auth)
+		return TRUE;
+	if (strcmp(client->ssl_set->ssl, "required") == 0)
+		return TRUE;
+	return FALSE;
+}
+
+static const char *get_capability(struct client *client)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+	string_t *cap_str = t_str_new(256);
+
+	if (*imap_client->set->imap_capability == '\0')
+		str_append(cap_str, CAPABILITY_BANNER_STRING);
+	else if (*imap_client->set->imap_capability != '+')
+		str_append(cap_str, imap_client->set->imap_capability);
+	else {
+		str_append(cap_str, CAPABILITY_BANNER_STRING);
+		str_append_c(cap_str, ' ');
+		str_append(cap_str, imap_client->set->imap_capability + 1);
+	}
+
+	if (client_is_tls_enabled(client) && !client->tls)
+		str_append(cap_str, " STARTTLS");
+	if (is_login_cmd_disabled(client))
+		str_append(cap_str, " LOGINDISABLED");
+
+	client_authenticate_get_capabilities(client, cap_str);
+	return str_c(cap_str);
+}
+
+static int cmd_capability(struct imap_client *imap_client,
+			  const struct imap_arg *args ATTR_UNUSED)
+{
+	struct client *client = &imap_client->common;
+
+	/* Client is required to send CAPABILITY after STARTTLS, so the
+	   capability resp-code workaround checks only pre-STARTTLS
+	   CAPABILITY commands. */
+	if (!client->starttls)
+		imap_client->client_ignores_capability_resp_code = TRUE;
+	client_send_raw(client, t_strconcat(
+		"* CAPABILITY ", get_capability(client), "\r\n", NULL));
+	client_send_reply(client, IMAP_CMD_REPLY_OK,
+		"Pre-login capabilities listed, post-login capabilities have more.");
+	return 1;
+}
+
+static int cmd_starttls(struct imap_client *client,
+			const struct imap_arg *args ATTR_UNUSED)
+{
+	client_cmd_starttls(&client->common);
+	return 1;
+}
+
+static void
+imap_client_notify_starttls(struct client *client,
+			    bool success, const char *text)
+{
+	if (success)
+		client_send_reply(client, IMAP_CMD_REPLY_OK, text);
+	else
+		client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
+}
+
+static void
+client_update_info(struct imap_client *client,
+		   const char *key, const char *value)
+{
+	if (strcasecmp(key, "x-originating-ip") == 0) {
+		(void)net_addr2ip(value, &client->common.ip);
+	} else if (strcasecmp(key, "x-originating-port") == 0) {
+		(void)net_str2port(value, &client->common.remote_port);
+	} else if (strcasecmp(key, "x-connected-ip") == 0) {
+		(void)net_addr2ip(value, &client->common.local_ip);
+	} else if (strcasecmp(key, "x-connected-port") == 0) {
+		(void)net_str2port(value, &client->common.local_port);
+	}	else if (strcasecmp(key, "x-proxy-ttl") == 0) {
+		if (str_to_uint(value, &client->common.proxy_ttl) < 0) {
+			/* nothing */
+		}
+	} else if (strcasecmp(key, "x-session-id") == 0 ||
+		 strcasecmp(key, "x-session-ext-id") == 0) {
+		if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
+			client->common.session_id =
+				p_strdup(client->common.pool, value);
+		}
+	}
+}
+
+static void cmd_id_handle_keyvalue(struct imap_client *client,
+				   const char *key, 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->cmd_id->log_reply != NULL) {
+			client_log(&client->common, t_strdup_printf(
+				"ID sent: %s", str_c(client->cmd_id->log_reply)));
+		}
+	}
+
+	client_send_raw(&client->common,
+		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.");
+}
+
+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,
+		    const struct imap_arg *args ATTR_UNUSED)
+{
+	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
+			  "NOOP completed.");
+	return 1;
+}
+
+static int cmd_logout(struct imap_client *client,
+		      const struct imap_arg *args ATTR_UNUSED)
+{
+	client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
+	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
+			  "Logout completed.");
+	client_destroy(&client->common, "Aborted login");
+	return 1;
+}
+
+static int cmd_enable(struct imap_client *client,
+		      const struct imap_arg *args ATTR_UNUSED)
+{
+	client_send_raw(&client->common, "* ENABLED\r\n");
+	client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
+			  "ENABLE ignored in non-authenticated state.");
+	return 1;
+}
+
+static int client_command_execute(struct imap_client *client, const char *cmd,
+				  const struct imap_arg *args)
+{
+	struct imap_login_command *login_cmd;
+
+	login_cmd = imap_login_command_lookup(cmd);
+	if (login_cmd == NULL)
+		return -2;
+	return login_cmd->func(client, args);
+}
+
+static bool imap_is_valid_tag(const char *tag)
+{
+	for (; *tag != '\0'; tag++) {
+		switch (*tag) {
+		case '+':
+		/* atom-specials: */
+		case '(':
+		case ')':
+		case '{':
+		case '/':
+		case ' ':
+		/* list-wildcards: */
+		case '%':
+		case '*':
+		/* quoted-specials: */
+		case '"':
+		case '\\':
+			return FALSE;
+		default:
+			if (*tag < ' ') /* CTL */
+				return FALSE;
+			break;
+		}
+	}
+	return TRUE;
+}
+
+static int client_parse_command(struct imap_client *client,
+				const struct imap_arg **args_r)
+{
+	switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
+	case -1:
+		/* error */
+		if (!client_handle_parser_error(client, client->parser)) {
+			/* client destroyed */
+			return 0;
+		}
+		return -1;
+	case -2:
+		/* not enough data */
+		return 0;
+	default:
+		/* we read the entire line - skip over the CRLF */
+		if (!client_skip_line(client))
+			i_unreached();
+		return 1;
+	}
+}
+
+static bool client_handle_input(struct imap_client *client)
+{
+	const struct imap_arg *args;
+	bool parsed;
+	int ret;
+
+	i_assert(!client->common.authenticating);
+
+	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) {
+			if (!client_skip_line(client))
+				return FALSE;
+                        client->skip_line = FALSE;
+		}
+
+		client->cmd_finished = FALSE;
+	}
+
+	if (client->cmd_tag == NULL) {
+                client->cmd_tag = imap_parser_read_word(client->parser);
+		if (client->cmd_tag == NULL)
+			return FALSE; /* need more data */
+		if (!imap_is_valid_tag(client->cmd_tag) ||
+		    strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) {
+			/* the tag is invalid, don't allow it and don't
+			   send it back. this attempts to prevent any
+			   potentially dangerous replies in case someone tries
+			   to access us using HTTP protocol. */
+			client->cmd_tag = "";
+		}
+	}
+
+	if (client->cmd_name == NULL) {
+                client->cmd_name = imap_parser_read_word(client->parser);
+		if (client->cmd_name == NULL)
+			return FALSE; /* need more data */
+	}
+
+	if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
+		/* SASL-IR may need more space than input buffer's size,
+		   so we'll handle it as a special case. */
+		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)
+			return TRUE;
+		if (ret == 0)
+			return FALSE;
+		ret = *client->cmd_tag == '\0' ? -1 :
+			client_command_execute(client, client->cmd_name, args);
+	}
+
+	client->cmd_finished = TRUE;
+	if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
+		client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
+			"First parameter in line is IMAP's command tag, "
+			"not the command name. Add that before the command, "
+			"like: a login user pass");
+	} else if (ret < 0) {
+		if (*client->cmd_tag == '\0')
+			client->cmd_tag = "*";
+		if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+			client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
+				"Too many invalid IMAP commands.");
+			client_destroy(&client->common,
+				"Disconnected: Too many invalid commands");
+			return FALSE;
+		}
+		client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
+			"Error in IMAP command received by server.");
+	}
+
+	return ret != 0 && !client->common.destroyed;
+}
+
+static void imap_client_input(struct client *client)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+
+	if (!client_read(client))
+		return;
+
+	client_ref(client);
+	o_stream_cork(imap_client->common.output);
+	for (;;) {
+		if (!auth_client_is_connected(auth_client)) {
+			/* we're not currently connected to auth process -
+			   don't allow any commands */
+			client_notify_status(client, FALSE,
+					     AUTH_SERVER_WAITING_MSG);
+			if (client->to_auth_waiting != NULL)
+				timeout_remove(&client->to_auth_waiting);
+
+			client->input_blocked = TRUE;
+			break;
+		} else {
+			if (!client_handle_input(imap_client))
+				break;
+		}
+	}
+	o_stream_uncork(imap_client->common.output);
+	client_unref(&client);
+}
+
+static struct client *imap_client_alloc(pool_t pool)
+{
+	struct imap_client *imap_client;
+
+	imap_client = p_new(pool, struct imap_client, 1);
+	return &imap_client->common;
+}
+
+static void imap_client_create(struct client *client, void **other_sets)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+
+	imap_client->set = other_sets[0];
+	imap_client->parser =
+		imap_parser_create(imap_client->common.input,
+				   imap_client->common.output, MAX_IMAP_LINE);
+	client->io = io_add(client->fd, IO_READ, client_input, client);
+}
+
+static void imap_client_destroy(struct client *client)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+
+	i_free_and_null(imap_client->proxy_backend_capability);
+	imap_parser_unref(&imap_client->parser);
+}
+
+static void imap_client_notify_auth_ready(struct client *client)
+{
+	string_t *greet;
+
+	greet = t_str_new(128);
+	str_append(greet, "* OK ");
+	str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
+	str_append(greet, client->set->login_greeting);
+	str_append(greet, "\r\n");
+
+	client_send_raw(client, str_c(greet));
+}
+
+static void imap_client_starttls(struct client *client)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+
+	imap_parser_unref(&imap_client->parser);
+	imap_client->parser =
+		imap_parser_create(imap_client->common.input,
+				   imap_client->common.output, MAX_IMAP_LINE);
+
+	/* CRLF is lost from buffer when streams are reopened. */
+	imap_client->skip_line = FALSE;
+}
+
+static void ATTR_NULL(3)
+client_send_reply_raw(struct client *client,
+		      const char *prefix, const char *resp_code,
+		      const char *text, bool tagged)
+{
+	struct imap_client *imap_client = (struct imap_client *)client;
+
+	T_BEGIN {
+		string_t *line = t_str_new(256);
+
+		if (tagged)
+			str_append(line, imap_client->cmd_tag);
+		else
+			str_append_c(line, '*');
+		str_append_c(line, ' ');
+		str_append(line, prefix);
+		str_append_c(line, ' ');
+		if (resp_code != NULL)
+			str_printfa(line, "[%s] ", resp_code);
+		str_append(line, text);
+		str_append(line, "\r\n");
+
+		client_send_raw_data(client, str_data(line), str_len(line));
+	} T_END;
+}
+
+void client_send_reply_code(struct client *client, enum imap_cmd_reply reply,
+			    const char *resp_code, const char *text)
+{
+	const char *prefix = "NO";
+	bool tagged = TRUE;
+
+	switch (reply) {
+	case IMAP_CMD_REPLY_OK:
+		prefix = "OK";
+		break;
+	case IMAP_CMD_REPLY_NO:
+		break;
+	case IMAP_CMD_REPLY_BAD:
+		prefix = "BAD";
+		break;
+	case IMAP_CMD_REPLY_BYE:
+		prefix = "BYE";
+		tagged = FALSE;
+		break;
+	}
+	client_send_reply_raw(client, prefix, resp_code, text, tagged);
+}
+
+void client_send_reply(struct client *client, enum imap_cmd_reply reply,
+		       const char *text)
+{
+	client_send_reply_code(client, reply, NULL, text);
+}
+
+static void
+imap_client_notify_status(struct client *client, bool bad, const char *text)
+{
+	if (bad)
+		client_send_reply_raw(client, "BAD", "ALERT", text, FALSE);
+	else
+		client_send_reply_raw(client, "OK", NULL, text, FALSE);
+}
+
+static void 
+imap_client_notify_disconnect(struct client *client,
+			      enum client_disconnect_reason reason,
+			      const char *text)
+{
+	if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) {
+		client_send_reply_code(client, IMAP_CMD_REPLY_BYE,
+				       IMAP_RESP_CODE_UNAVAILABLE, text);
+	} else {
+		client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text);
+	}
+}
+
+static void imap_login_preinit(void)
+{
+	login_set_roots = imap_login_setting_roots;
+}
+
+static const struct imap_login_command imap_login_commands[] = {
+	{ "LOGIN", cmd_login },
+	{ "CAPABILITY", cmd_capability },
+	{ "STARTTLS", cmd_starttls },
+	{ "NOOP", cmd_noop },
+	{ "LOGOUT", cmd_logout },
+	{ "ENABLE", cmd_enable }
+};
+
+static void imap_login_init(void)
+{
+	imap_login_commands_init();
+	imap_login_commands_register(imap_login_commands,
+				     N_ELEMENTS(imap_login_commands));
+}
+
+static void imap_login_deinit(void)
+{
+	clients_destroy_all();
+	imap_login_commands_deinit();
+}
+
+static struct client_vfuncs imap_client_vfuncs = {
+	imap_client_alloc,
+	imap_client_create,
+	imap_client_destroy,
+	imap_client_notify_auth_ready,
+	imap_client_notify_disconnect,
+	imap_client_notify_status,
+	imap_client_notify_starttls,
+	imap_client_starttls,
+	imap_client_input,
+	NULL,
+	NULL,
+	imap_client_auth_result,
+	imap_proxy_reset,
+	imap_proxy_parse_line,
+	imap_proxy_error
+};
+
+static const struct login_binary imap_login_binary = {
+	.protocol = "imap",
+	.process_name = "imap-login",
+	.default_port = 143,
+	.default_ssl_port = 993,
+
+	.client_vfuncs = &imap_client_vfuncs,
+	.preinit = imap_login_preinit,
+	.init = imap_login_init,
+	.deinit = imap_login_deinit,
+
+	.sasl_support_final_reply = FALSE
+};
+
+int main(int argc, char *argv[])
+{
+	return login_binary_run(&imap_login_binary, argc, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/imap-login-client.h	Fri Jan 15 16:49:05 2016 +0200
@@ -0,0 +1,63 @@
+#ifndef IMAP_LOGIN_CLIENT_H
+#define IMAP_LOGIN_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
+};
+
+struct imap_client_cmd_id {
+	struct imap_parser *parser;
+
+	enum imap_client_id_state state;
+	char key[IMAP_ID_KEY_MAX_LEN+1];
+
+	char **log_keys;
+	string_t *log_reply;
+};
+
+struct imap_client {
+	struct client common;
+
+	const struct imap_login_settings *set;
+	struct imap_parser *parser;
+	char *proxy_backend_capability;
+
+	const char *cmd_tag, *cmd_name;
+	struct imap_client_cmd_id *cmd_id;
+
+	unsigned int cmd_finished:1;
+	unsigned int proxy_sasl_ir:1;
+	unsigned int proxy_seen_banner:1;
+	unsigned int skip_line:1;
+	unsigned int id_logged:1;
+	unsigned int proxy_capability_request_sent:1;
+	unsigned int client_ignores_capability_resp_code:1;
+	unsigned int auth_mech_name_parsed:1;
+};
+
+bool client_skip_line(struct imap_client *client);
+
+enum imap_cmd_reply {
+	IMAP_CMD_REPLY_OK,
+	IMAP_CMD_REPLY_NO,
+	IMAP_CMD_REPLY_BAD,
+	IMAP_CMD_REPLY_BYE
+};
+
+void client_send_reply(struct client *client,
+		       enum imap_cmd_reply reply, const char *text);
+
+void client_send_reply_code(struct client *client,
+			    enum imap_cmd_reply reply, const char *resp_code,
+			    const char *text) ATTR_NULL(3);
+
+#endif
--- a/src/imap-login/imap-proxy.c	Fri Jan 15 16:46:34 2016 +0200
+++ b/src/imap-login/imap-proxy.c	Fri Jan 15 16:49:05 2016 +0200
@@ -10,7 +10,7 @@
 #include "str-sanitize.h"
 #include "safe-memset.h"
 #include "dsasl-client.h"
-#include "client.h"
+#include "imap-login-client.h"
 #include "client-authenticate.h"
 #include "imap-resp-code.h"
 #include "imap-quote.h"