changeset 1049:c41787e8c3f4 HEAD

Moved common login process code to login-common, created pop3-login.
author Timo Sirainen <tss@iki.fi>
date Tue, 28 Jan 2003 23:35:25 +0200
parents 526415575295
children ff63007297c3
files configure.in src/Makefile.am src/imap-login/.cvsignore src/imap-login/Makefile.am src/imap-login/client-authenticate.c src/imap-login/client-authenticate.h src/imap-login/client.c src/imap-login/client.h src/imap-login/common.h src/login-common/.cvsignore src/login-common/Makefile.am src/login-common/auth-connection.c src/login-common/auth-connection.h src/login-common/client-common.h src/login-common/common.h src/login-common/main.c src/login-common/master.c src/login-common/master.h src/login-common/ssl-proxy-gnutls.c src/login-common/ssl-proxy-openssl.c src/login-common/ssl-proxy.c src/login-common/ssl-proxy.h src/login/.cvsignore src/login/Makefile.am src/login/auth-connection.c src/login/auth-connection.h src/login/client-authenticate.c src/login/client-authenticate.h src/login/client.c src/login/client.h src/login/common.h src/login/main.c src/login/master.c src/login/master.h src/login/ssl-proxy-gnutls.c src/login/ssl-proxy-openssl.c src/login/ssl-proxy.c src/login/ssl-proxy.h src/pop3-login/.cvsignore src/pop3-login/Makefile.am src/pop3-login/client-authenticate.c src/pop3-login/client-authenticate.h src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/common.h
diffstat 45 files changed, 3497 insertions(+), 2667 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Tue Jan 28 21:32:18 2003 +0200
+++ b/configure.in	Tue Jan 28 23:35:25 2003 +0200
@@ -857,9 +857,11 @@
 src/lib-storage/register/Makefile
 src/auth/Makefile
 src/imap/Makefile
-src/login/Makefile
+src/imap-login/Makefile
+src/login-common/Makefile
 src/master/Makefile
 src/pop3/Makefile
+src/pop3-login/Makefile
 stamp.h)
 
 echo
--- a/src/Makefile.am	Tue Jan 28 21:32:18 2003 +0200
+++ b/src/Makefile.am	Tue Jan 28 23:35:25 2003 +0200
@@ -1,1 +1,1 @@
-SUBDIRS = lib lib-charset lib-mail lib-imap lib-index lib-storage auth master login imap
+SUBDIRS = lib lib-charset lib-mail lib-imap lib-index lib-storage auth master login-common imap-login imap pop3-login pop3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/.cvsignore	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,9 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+imap-login
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/Makefile.am	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,22 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = imap-login
+
+INCLUDES = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-imap \
+	-I$(top_srcdir)/src/login-common
+
+imap_login_LDADD = \
+	../login-common/liblogin-common.a \
+	../lib-imap/imap-parser.o \
+	../lib/liblib.a \
+	$(SSL_LIBS)
+
+imap_login_SOURCES = \
+	client.c \
+	client-authenticate.c
+
+noinst_HEADERS = \
+	common.h \
+	client.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/client-authenticate.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,355 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "istream.h"
+#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"
+#include "client-authenticate.h"
+#include "master.h"
+
+static enum auth_mech auth_mechs = 0;
+static char *auth_mechs_capability = NULL;
+
+const char *client_authenticate_get_capabilities(void)
+{
+	string_t *str;
+	int i;
+
+	if (auth_mechs == available_auth_mechs)
+		return auth_mechs_capability;
+
+	auth_mechs = available_auth_mechs;
+	i_free(auth_mechs_capability);
+
+	str = t_str_new(128);
+
+	for (i = 0; i < AUTH_MECH_COUNT; i++) {
+		if ((auth_mechs & auth_mech_desc[i].mech) &&
+		    auth_mech_desc[i].name != NULL) {
+			str_append_c(str, ' ');
+			str_append(str, "AUTH=");
+			str_append(str, auth_mech_desc[i].name);
+		}
+	}
+
+	auth_mechs_capability = i_strdup_empty(str_c(str));
+	return auth_mechs_capability;
+}
+
+static struct auth_mech_desc *auth_mech_find(const char *name)
+{
+	int i;
+
+	for (i = 0; i < AUTH_MECH_COUNT; i++) {
+		if (auth_mech_desc[i].name != NULL &&
+		    strcasecmp(auth_mech_desc[i].name, name) == 0)
+			return &auth_mech_desc[i];
+	}
+
+	return NULL;
+}
+
+static void client_auth_abort(struct imap_client *client, const char *msg)
+{
+	if (client->auth_request != NULL) {
+		auth_abort_request(client->auth_request);
+		client->auth_request = NULL;
+	}
+
+	client_send_tagline(client, msg != NULL ? msg :
+			    "NO Authentication failed.");
+	o_stream_flush(client->output);
+
+	/* get back to normal client input */
+	if (client->io != NULL)
+		io_remove(client->io);
+	client->io = client->common.fd == -1 ? NULL :
+		io_add(client->common.fd, IO_READ, client_input, client);
+
+	client_unref(client);
+}
+
+static void master_callback(struct client *_client, int success)
+{
+	struct imap_client *client = (struct imap_client *) _client;
+	const char *reason = NULL;
+
+	if (success)
+		reason = t_strconcat("Login: ", client->virtual_user, NULL);
+	else {
+		reason = t_strconcat("Internal login failure: ",
+				     client->virtual_user, NULL);
+		client_send_line(client, "* BYE Internal login failure.");
+	}
+
+	client_destroy(client, reason);
+	client_unref(client);
+}
+
+static void client_send_auth_data(struct imap_client *client,
+				  const unsigned char *data, size_t size)
+{
+	buffer_t *buf;
+
+	t_push();
+
+	buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1);
+	buffer_append(buf, "+ ", 2);
+	base64_encode(data, size, buf);
+	buffer_append(buf, "\r\n", 2);
+
+	o_stream_send(client->output, buffer_get_data(buf, NULL),
+		      buffer_get_used_size(buf));
+	o_stream_flush(client->output);
+
+	t_pop();
+}
+
+static const char *auth_login_get_str(struct auth_login_reply *reply,
+				      const unsigned char *data, size_t idx)
+{
+	size_t stop;
+
+	if (idx >= reply->data_size || idx >= reply->reply_idx)
+		return NULL;
+
+	stop = reply->reply_idx < reply->data_size ?
+		reply->reply_idx-1 : reply->data_size;
+
+	return t_strndup(data, stop);
+}
+
+static int auth_callback(struct auth_request *request,
+			 struct auth_login_reply *reply,
+			 const unsigned char *data, void *context)
+{
+	struct imap_client *client = context;
+	const char *user, *realm;
+
+	if (reply == NULL) {
+		/* failed */
+		client->auth_request = NULL;
+		client_auth_abort(client, "NO Authentication process died.");
+		return FALSE;
+	}
+
+	switch (reply->result) {
+	case AUTH_LOGIN_RESULT_CONTINUE:
+		client->auth_request = request;
+		return TRUE;
+
+	case AUTH_LOGIN_RESULT_SUCCESS:
+		client->auth_request = NULL;
+
+		user = auth_login_get_str(reply, data, reply->username_idx);
+		realm = auth_login_get_str(reply, data, reply->realm_idx);
+
+		i_free(client->virtual_user);
+		client->virtual_user = realm == NULL ?
+			i_strdup(user) : i_strconcat(user, "@", realm, NULL);
+
+		/* we should be able to log in. if we fail, just
+		   disconnect the client. */
+		client_send_tagline(client, "OK Logged in.");
+
+		master_request_imap(&client->common, master_callback,
+				    request->conn->pid, request->id);
+
+		/* disable IO until we're back from master */
+		if (client->io != NULL) {
+			io_remove(client->io);
+			client->io = NULL;
+		}
+		return FALSE;
+
+	case AUTH_LOGIN_RESULT_FAILURE:
+		/* see if we have error message */
+		client->auth_request = NULL;
+
+		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
+			client_auth_abort(client, t_strconcat(
+				"NO Authentication failed: ",
+				(const char *) data, NULL));
+		} else {
+			/* default error message */
+			client_auth_abort(client, NULL);
+		}
+		return FALSE;
+	}
+
+	i_unreached();
+}
+
+static void login_callback(struct auth_request *request,
+			   struct auth_login_reply *reply,
+			   const unsigned char *data, struct client *_client)
+{
+	struct imap_client *client = (struct imap_client *) _client;
+	const void *ptr;
+	size_t size;
+
+	if (auth_callback(request, reply, data, client)) {
+		ptr = buffer_get_data(client->plain_login, &size);
+		auth_continue_request(request, ptr, size);
+
+		buffer_set_used_size(client->plain_login, 0);
+	}
+}
+
+int cmd_login(struct imap_client *client, struct imap_arg *args)
+{
+	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,
+				    "NO Plaintext authentication disabled.");
+		return TRUE;
+	}
+
+	/* authorization ID \0 authentication ID \0 pass */
+	buffer_set_used_size(client->plain_login, 0);
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, user, strlen(user));
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, pass, strlen(pass));
+
+	client_ref(client);
+	if (auth_init_request(AUTH_MECH_PLAIN, login_callback,
+			      &client->common, &error)) {
+		/* don't read any input from client until login is finished */
+		if (client->io != NULL) {
+			io_remove(client->io);
+			client->io = NULL;
+		}
+		return TRUE;
+	} else {
+		client_send_tagline(client, t_strconcat(
+			"NO Login failed: ", error, NULL));
+		client_unref(client);
+		return TRUE;
+	}
+}
+
+static void authenticate_callback(struct auth_request *request,
+				  struct auth_login_reply *reply,
+				  const unsigned char *data,
+				  struct client *_client)
+{
+	struct imap_client *client = (struct imap_client *) _client;
+
+	if (auth_callback(request, reply, data, client))
+		client_send_auth_data(client, data, reply->data_size);
+}
+
+static void client_auth_input(void *context)
+{
+	struct imap_client *client = context;
+	buffer_t *buf;
+	char *line;
+	size_t linelen, bufsize;
+
+	if (!client_read(client))
+		return;
+
+	if (client->skip_line) {
+		if (i_stream_next_line(client->input) == NULL)
+			return;
+
+		client->skip_line = FALSE;
+	}
+
+	/* @UNSAFE */
+	line = i_stream_next_line(client->input);
+	if (line == NULL)
+		return;
+
+	if (strcmp(line, "*") == 0) {
+		client_auth_abort(client, "NO Authentication aborted");
+		return;
+	}
+
+	linelen = strlen(line);
+	buf = buffer_create_static_hard(data_stack_pool, linelen);
+
+	if (base64_decode((const unsigned char *) line, linelen,
+			  NULL, buf) <= 0) {
+		/* failed */
+		client_auth_abort(client, "NO Invalid base64 data");
+	} else if (client->auth_request == NULL) {
+		client_auth_abort(client, "NO Don't send unrequested data");
+	} else {
+		auth_continue_request(client->auth_request,
+				      buffer_get_data(buf, NULL),
+				      buffer_get_used_size(buf));
+	}
+
+	/* clear sensitive data */
+	safe_memset(line, 0, linelen);
+
+	bufsize = buffer_get_used_size(buf);
+	safe_memset(buffer_free_without_data(buf), 0, bufsize);
+}
+
+int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
+{
+	struct auth_mech_desc *mech;
+	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;
+
+	mech = auth_mech_find(mech_name);
+	if (mech == NULL) {
+		client_send_tagline(client,
+				    "NO Unsupported authentication mechanism.");
+		return TRUE;
+	}
+
+	if (!client->tls && mech->plaintext && disable_plaintext_auth) {
+		client_send_tagline(client,
+				    "NO Plaintext authentication disabled.");
+		return TRUE;
+	}
+
+	client_ref(client);
+	if (auth_init_request(mech->mech, authenticate_callback,
+			      &client->common, &error)) {
+		/* following input data will go to authentication */
+		if (client->io != NULL)
+			io_remove(client->io);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_auth_input, client);
+	} else {
+		client_send_tagline(client, t_strconcat(
+			"NO Authentication failed: ", error, NULL));
+		client_unref(client);
+	}
+
+	return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/client-authenticate.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,9 @@
+#ifndef __CLIENT_AUTHENTICATE_H
+#define __CLIENT_AUTHENTICATE_H
+
+const char *client_authenticate_get_capabilities(void);
+
+int cmd_login(struct imap_client *client, struct imap_arg *args);
+int cmd_authenticate(struct imap_client *client, struct imap_arg *args);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/client.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,459 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "buffer.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#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"
+
+/* 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
+
+/* Disconnect client when it sends too many bad commands */
+#define CLIENT_MAX_BAD_COMMANDS 10
+
+/* When max. number of simultaneous connections is reached, few of the
+   oldest connections are disconnected. Since we have to go through the whole
+   client hash, it's faster if we disconnect multiple clients. */
+#define CLIENT_DESTROY_OLDEST_COUNT 16
+
+static struct hash_table *clients;
+static struct timeout *to_idle;
+
+static void client_set_title(struct imap_client *client)
+{
+	const char *host;
+
+	if (!verbose_proctitle || !process_per_connection)
+		return;
+
+	host = net_ip2host(&client->common.ip);
+	if (host == NULL)
+		host = "??";
+
+	process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
+					  host));
+}
+
+static int cmd_capability(struct imap_client *client)
+{
+	const char *capability;
+
+	capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
+				 ssl_initialized ? " STARTTLS" : "",
+				 disable_plaintext_auth && !client->tls ?
+				 " LOGINDISABLED" : "",
+				 client_authenticate_get_capabilities(),
+				 NULL);
+	client_send_line(client, capability);
+	client_send_tagline(client, "OK Capability completed.");
+	return TRUE;
+}
+
+static int cmd_starttls(struct imap_client *client)
+{
+	int fd_ssl;
+
+	if (client->tls) {
+		client_send_tagline(client, "BAD TLS is already active.");
+		return TRUE;
+	}
+
+	if (!ssl_initialized) {
+		client_send_tagline(client, "BAD TLS support isn't enabled.");
+		return TRUE;
+	}
+
+	client_send_tagline(client, "OK Begin TLS negotiation now.");
+	o_stream_flush(client->output);
+
+	/* must be removed before ssl_proxy_new(), since it may
+	   io_add() the same fd. */
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	fd_ssl = ssl_proxy_new(client->common.fd);
+	if (fd_ssl != -1) {
+		client->tls = TRUE;
+                client_set_title(client);
+
+		client->common.fd = fd_ssl;
+
+		i_stream_unref(client->input);
+		o_stream_unref(client->output);
+
+		client->input = i_stream_create_file(fd_ssl, default_pool,
+						     8192, FALSE);
+		client->output = o_stream_create_file(fd_ssl, default_pool,
+						      1024, IO_PRIORITY_DEFAULT,
+						      FALSE);
+	} else {
+		client_send_line(client, " * BYE TLS handehake failed.");
+		client_destroy(client, "TLS handshake failed");
+	}
+
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
+	return TRUE;
+}
+
+static int cmd_noop(struct imap_client *client)
+{
+	client_send_tagline(client, "OK NOOP completed.");
+	return TRUE;
+}
+
+static int cmd_logout(struct imap_client *client)
+{
+	client_send_line(client, "* BYE Logging out");
+	client_send_tagline(client, "OK Logout completed.");
+	client_destroy(client, "Aborted login");
+	return TRUE;
+}
+
+static int client_command_execute(struct imap_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 imap_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 imap_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) {
+			if (!client_skip_line(client))
+				return;
+                        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; /* 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;
+	}
+	client->skip_line = TRUE;
+
+	if (*client->cmd_tag == '\0' ||
+	    !client_command_execute(client, client->cmd_name, args)) {
+		if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+			client_send_line(client,
+				"* BYE Too many invalid IMAP commands.");
+			client_destroy(client, "Disconnected: "
+				       "Too many invalid commands");
+			return;
+		} 
+		client_send_tagline(client,
+			"BAD Error in IMAP command received by server.");
+	}
+
+	client->cmd_finished = TRUE;
+}
+
+int client_read(struct imap_client *client)
+{
+	switch (i_stream_read(client->input)) {
+	case -2:
+		/* buffer full */
+		client_send_line(client, "* BYE Input buffer full, aborting");
+		client_destroy(client, "Disconnected: Input buffer full");
+		return FALSE;
+	case -1:
+		/* disconnected */
+		client_destroy(client, "Disconnected");
+		return FALSE;
+	default:
+		/* something was read */
+		return TRUE;
+	}
+}
+
+void client_input(void *context)
+{
+	struct imap_client *client = context;
+
+	client->last_input = ioloop_time;
+
+	if (!client_read(client))
+		return;
+
+	client_ref(client);
+
+	o_stream_cork(client->output);
+	client_handle_input(client);
+
+	if (client_unref(client))
+		o_stream_flush(client->output);
+}
+
+static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
+				       void *context)
+{
+	struct imap_client *client = key;
+	struct imap_client *const *destroy_clients;
+	buffer_t *destroy_buf = context;
+	size_t i, count;
+
+	destroy_clients = buffer_get_data(destroy_buf, &count);
+	count /= sizeof(struct imap_client *);
+
+	for (i = 0; i < count; i++) {
+		if (destroy_clients[i]->created > client->created) {
+			buffer_insert(destroy_buf,
+				      i * sizeof(struct imap_client *),
+				      &client, sizeof(struct imap_client *));
+			break;
+		}
+	}
+}
+
+static void client_destroy_oldest(void)
+{
+	struct imap_client *const *destroy_clients;
+	buffer_t *destroy_buf;
+	size_t i, count;
+
+	/* find the oldest clients and put them to destroy-buffer */
+	destroy_buf = buffer_create_static_hard(data_stack_pool,
+						sizeof(struct imap_client *) *
+						CLIENT_DESTROY_OLDEST_COUNT);
+	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
+
+	/* then kill them */
+	destroy_clients = buffer_get_data(destroy_buf, &count);
+	count /= sizeof(struct imap_client *);
+
+	for (i = 0; i < count; i++) {
+		client_destroy(destroy_clients[i],
+			       "Disconnected: Connection queue full");
+	}
+}
+
+struct client *client_create(int fd, struct ip_addr *ip, int ssl)
+{
+	struct imap_client *client;
+
+	if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
+	    hash_size(clients) >= max_logging_users) {
+		/* reached max. users count, kill few of the
+		   oldest connections */
+		client_destroy_oldest();
+	}
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd, TRUE);
+
+	client = i_new(struct imap_client, 1);
+	client->created = ioloop_time;
+	client->refcount = 1;
+	client->tls = ssl;
+
+	client->common.ip = *ip;
+	client->common.fd = fd;
+	client->io = io_add(fd, IO_READ, client_input, client);
+	client->input = i_stream_create_file(fd, default_pool,
+					     MAX_INBUF_SIZE, 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);
+
+	main_ref();
+
+	client_send_line(client, "* OK " PACKAGE " ready.");
+	client_set_title(client);
+	return &client->common;
+}
+
+void client_destroy(struct imap_client *client, const char *reason)
+{
+	if (reason != NULL)
+		client_syslog(client, reason);
+
+	hash_remove(clients, client);
+
+	imap_parser_destroy(client->parser);
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	net_disconnect(client->common.fd);
+	client->common.fd = -1;
+
+	i_free(client->virtual_user);
+	client_unref(client);
+}
+
+void client_ref(struct imap_client *client)
+{
+	client->refcount++;
+}
+
+int client_unref(struct imap_client *client)
+{
+	if (--client->refcount > 0)
+		return TRUE;
+
+	i_stream_unref(client->input);
+	o_stream_unref(client->output);
+
+	buffer_free(client->plain_login);
+	i_free(client);
+
+	main_unref();
+	return FALSE;
+}
+
+void client_send_line(struct imap_client *client, const char *line)
+{
+	o_stream_send_str(client->output, line);
+	o_stream_send(client->output, "\r\n", 2);
+}
+
+void client_send_tagline(struct imap_client *client, const char *line)
+{
+	client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
+}
+
+void client_syslog(struct imap_client *client, const char *text)
+{
+	const char *host;
+
+	host = net_ip2host(&client->common.ip);
+	if (host == NULL)
+		host = "??";
+
+	i_info("%s [%s]", text, host);
+}
+
+static void client_hash_check_idle(void *key, void *value __attr_unused__,
+				   void *context __attr_unused__)
+{
+	struct imap_client *client = key;
+
+	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
+		client_send_line(client, "* BYE Disconnected for inactivity.");
+		client_destroy(client, "Disconnected: Inactivity");
+	}
+}
+
+static void idle_timeout(void *context __attr_unused__)
+{
+	hash_foreach(clients, client_hash_check_idle, NULL);
+}
+
+unsigned int clients_get_count(void)
+{
+	return hash_size(clients);
+}
+
+static void client_hash_destroy(void *key, void *value __attr_unused__,
+				void *context __attr_unused__)
+{
+	client_destroy(key, NULL);
+}
+
+void clients_destroy_all(void)
+{
+	hash_foreach(clients, client_hash_destroy, NULL);
+}
+
+void clients_init(void)
+{
+	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
+	to_idle = timeout_add(1000, idle_timeout, NULL);
+}
+
+void clients_deinit(void)
+{
+	clients_destroy_all();
+	hash_destroy(clients);
+
+	timeout_remove(to_idle);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/client.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,52 @@
+#ifndef __CLIENT_H
+#define __CLIENT_H
+
+#include "network.h"
+#include "master.h"
+#include "client-common.h"
+
+struct imap_client {
+	struct client common;
+
+	time_t created;
+	int refcount;
+
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+	struct imap_parser *parser;
+
+	time_t last_input;
+	unsigned int bad_counter;
+
+	const char *cmd_tag, *cmd_name;
+
+	buffer_t *plain_login;
+	struct auth_request *auth_request;
+	char *virtual_user;
+
+	unsigned int tls:1;
+	unsigned int cmd_finished:1;
+	unsigned int skip_line:1;
+};
+
+struct client *client_create(int fd, struct ip_addr *ip, int ssl);
+void client_destroy(struct imap_client *client, const char *reason);
+
+void client_ref(struct imap_client *client);
+int client_unref(struct imap_client *client);
+
+void client_send_line(struct imap_client *client, const char *line);
+void client_send_tagline(struct imap_client *client, const char *line);
+void client_syslog(struct imap_client *client, const char *text);
+
+int client_read(struct imap_client *client);
+void client_input(void *context);
+
+unsigned int clients_get_count(void);
+void clients_destroy_all(void);
+
+void clients_init(void);
+void clients_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/common.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,16 @@
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "lib.h"
+#include "../auth/auth-login-interface.h"
+
+extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
+extern unsigned int max_logging_users;
+extern unsigned int login_process_uid;
+
+void main_ref(void);
+void main_unref(void);
+
+void main_close_listen(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/.cvsignore	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/Makefile.am	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,19 @@
+noinst_LIBRARIES = liblogin-common.a
+
+INCLUDES = \
+	-I$(top_srcdir)/src/lib
+
+liblogin_common_a_SOURCES = \
+	auth-connection.c \
+	main.c \
+	master.c \
+	ssl-proxy.c \
+	ssl-proxy-gnutls.c \
+	ssl-proxy-openssl.c
+
+noinst_HEADERS = \
+	auth-connection.h \
+	common.h \
+	client-common.h \
+	master.h \
+	ssl-proxy.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/auth-connection.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,374 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "auth-connection.h"
+
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+/* Maximum size for an auth reply. 50kB should be more than enough. */
+#define MAX_INBUF_SIZE (1024*50)
+
+#define MAX_OUTBUF_SIZE \
+	(sizeof(struct auth_login_request_continue) + \
+	 AUTH_LOGIN_MAX_REQUEST_DATA_SIZE)
+
+enum auth_mech available_auth_mechs;
+
+static int auth_reconnect;
+static unsigned int request_id_counter;
+static struct auth_connection *auth_connections;
+static struct timeout *to;
+
+static void auth_connection_destroy(struct auth_connection *conn);
+static void auth_input(void *context);
+static void auth_connect_missing(void);
+
+static struct auth_connection *auth_connection_find(const char *path)
+{
+	struct auth_connection *conn;
+
+	for (conn = auth_connections; conn != NULL; conn = conn->next) {
+		if (strcmp(conn->path, path) == 0)
+			return conn;
+	}
+
+	return NULL;
+}
+
+static struct auth_connection *auth_connection_new(const char *path)
+{
+	struct auth_connection *conn;
+        struct auth_login_handshake_input handshake;
+	int fd;
+
+	fd = net_connect_unix(path);
+	if (fd == -1) {
+		i_error("Can't connect to imap-auth at %s: %m", path);
+                auth_reconnect = TRUE;
+		return NULL;
+	}
+
+	/* we depend on auth process - if it's slow, just wait */
+        net_set_nonblock(fd, FALSE);
+
+	conn = i_new(struct auth_connection, 1);
+	conn->path = i_strdup(path);
+	conn->fd = fd;
+	conn->io = io_add(fd, IO_READ, auth_input, conn);
+	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
+					   FALSE);
+	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
+					    IO_PRIORITY_DEFAULT, FALSE);
+	conn->requests = hash_create(default_pool, default_pool, 100,
+				     NULL, NULL);
+
+	conn->next = auth_connections;
+	auth_connections = conn;
+
+	/* send our handshake */
+	memset(&handshake, 0, sizeof(handshake));
+	handshake.pid = login_process_uid;
+	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
+                auth_connection_destroy(conn);
+		return NULL;
+	}
+	return conn;
+}
+
+static void request_destroy(struct auth_request *request)
+{
+	hash_remove(request->conn->requests, POINTER_CAST(request->id));
+	i_free(request);
+}
+
+static void request_hash_destroy(void *key __attr_unused__, void *value,
+				 void *context __attr_unused__)
+{
+	struct auth_request *request = value;
+
+	request->callback(request, NULL, NULL, request->context);
+	request_destroy(request);
+}
+
+static void auth_connection_destroy(struct auth_connection *conn)
+{
+	struct auth_connection **pos;
+
+	for (pos = &auth_connections; *pos != NULL; pos = &(*pos)->next) {
+		if (*pos == conn) {
+			*pos = conn->next;
+			break;
+		}
+	}
+
+	hash_foreach(conn->requests, request_hash_destroy, NULL);
+	hash_destroy(conn->requests);
+
+	if (close(conn->fd) < 0)
+		i_error("close(imap-auth) failed: %m");
+	io_remove(conn->io);
+	i_stream_unref(conn->input);
+	o_stream_unref(conn->output);
+	i_free(conn->path);
+	i_free(conn);
+}
+
+static struct auth_connection *
+auth_connection_get(enum auth_mech mech, size_t size, const char **error)
+{
+	struct auth_connection *conn;
+	int found;
+
+	found = FALSE;
+	for (conn = auth_connections; conn != NULL; conn = conn->next) {
+		if ((conn->available_auth_mechs & mech)) {
+			if (o_stream_have_space(conn->output, size) > 0)
+				return conn;
+
+			found = TRUE;
+		}
+	}
+
+	if (!found) {
+		if ((available_auth_mechs & mech) == 0)
+			*error = "Unsupported authentication mechanism";
+		else {
+			*error = "Authentication server isn't connected, "
+				"try again later..";
+			auth_reconnect = TRUE;
+		}
+	} else {
+		*error = "Authentication servers are busy, wait..";
+		i_warning("Authentication servers are busy");
+	}
+
+	return NULL;
+}
+
+static void update_available_auth_mechs(void)
+{
+	struct auth_connection *conn;
+
+        available_auth_mechs = 0;
+	for (conn = auth_connections; conn != NULL; conn = conn->next)
+                available_auth_mechs |= conn->available_auth_mechs;
+}
+
+static void auth_handle_handshake(struct auth_connection *conn,
+				  struct auth_login_handshake_output *handshake)
+{
+	conn->pid = handshake->pid;
+	conn->available_auth_mechs = handshake->auth_mechanisms;
+	conn->handshake_received = TRUE;
+
+	update_available_auth_mechs();
+}
+
+static void auth_handle_reply(struct auth_connection *conn,
+			      struct auth_login_reply *reply,
+			      const unsigned char *data)
+{
+	struct auth_request *request;
+
+	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
+	if (request == NULL) {
+		i_error("BUG: imap-auth sent us reply with unknown ID %u",
+			reply->id);
+		return;
+	}
+
+	request->callback(request, reply, data, request->context);
+
+	if (reply->result != AUTH_LOGIN_RESULT_CONTINUE)
+		request_destroy(request);
+}
+
+static void auth_input(void *context)
+{
+	struct auth_connection *conn = context;
+        struct auth_login_handshake_output handshake;
+	const unsigned char *data;
+	size_t size;
+
+	switch (i_stream_read(conn->input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+                auth_reconnect = TRUE;
+		auth_connection_destroy(conn);
+		return;
+	case -2:
+		/* buffer full - can't happen unless imap-auth is buggy */
+		i_error("BUG: imap-auth sent us more than %d bytes of data",
+			MAX_INBUF_SIZE);
+		auth_connection_destroy(conn);
+		return;
+	}
+
+	if (!conn->handshake_received) {
+		data = i_stream_get_data(conn->input, &size);
+		if (size == sizeof(handshake)) {
+			memcpy(&handshake, data, sizeof(handshake));
+			i_stream_skip(conn->input, sizeof(handshake));
+
+			auth_handle_handshake(conn, &handshake);
+		} else if (size > sizeof(handshake)) {
+			i_error("BUG: imap-auth sent us too large handshake "
+				"(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
+				sizeof(handshake));
+			auth_connection_destroy(conn);
+		}
+		return;
+	}
+
+	if (!conn->reply_received) {
+		data = i_stream_get_data(conn->input, &size);
+		if (size < sizeof(conn->reply))
+			return;
+
+		memcpy(&conn->reply, data, sizeof(conn->reply));
+		i_stream_skip(conn->input, sizeof(conn->reply));
+		conn->reply_received = TRUE;
+	}
+
+	data = i_stream_get_data(conn->input, &size);
+	if (size < conn->reply.data_size)
+		return;
+
+	/* we've got a full reply */
+	conn->reply_received = FALSE;
+	auth_handle_reply(conn, &conn->reply, data);
+	i_stream_skip(conn->input, conn->reply.data_size);
+}
+
+int auth_init_request(enum auth_mech mech, auth_callback_t callback,
+		      void *context, const char **error)
+{
+	struct auth_connection *conn;
+	struct auth_request *request;
+	struct auth_login_request_new auth_request;
+
+	if (auth_reconnect)
+		auth_connect_missing();
+
+	conn = auth_connection_get(mech, sizeof(auth_request), error);
+	if (conn == NULL)
+		return FALSE;
+
+	/* create internal request structure */
+	request = i_new(struct auth_request, 1);
+	request->mech = mech;
+	request->conn = conn;
+	request->id = ++request_id_counter;
+	if (request->id == 0) {
+		/* wrapped - ID 0 not allowed */
+		request->id = ++request_id_counter;
+	}
+	request->callback = callback;
+	request->context = context;
+
+	hash_insert(conn->requests, POINTER_CAST(request->id), request);
+
+	/* send request to auth */
+	auth_request.type = AUTH_LOGIN_REQUEST_NEW;
+	auth_request.mech = request->mech;
+	auth_request.id = request->id;
+	if (o_stream_send(request->conn->output, &auth_request,
+			  sizeof(auth_request)) < 0)
+		auth_connection_destroy(request->conn);
+	return TRUE;
+}
+
+void auth_continue_request(struct auth_request *request,
+			   const unsigned char *data, size_t data_size)
+{
+	struct auth_login_request_continue auth_request;
+
+	/* send continued request to auth */
+	auth_request.type = AUTH_LOGIN_REQUEST_CONTINUE;
+	auth_request.id = request->id;
+	auth_request.data_size = data_size;
+
+	if (o_stream_send(request->conn->output, &auth_request,
+			  sizeof(auth_request)) < 0)
+		auth_connection_destroy(request->conn);
+	else if (o_stream_send(request->conn->output, data, data_size) < 0)
+		auth_connection_destroy(request->conn);
+}
+
+void auth_abort_request(struct auth_request *request)
+{
+        request_destroy(request);
+}
+
+static void auth_connect_missing(void)
+{
+	DIR *dirp;
+	struct dirent *dp;
+	struct stat st;
+
+	auth_reconnect = TRUE;
+
+	/* we're chrooted into */
+	dirp = opendir(".");
+	if (dirp == NULL) {
+		i_error("opendir(\".\") failed when trying to get list of "
+			"authentication servers: %m");
+		return;
+	}
+
+	while ((dp = readdir(dirp)) != NULL) {
+		if (dp->d_name[0] == '.')
+			continue;
+
+		if (auth_connection_find(dp->d_name) != NULL) {
+			/* already connected */
+			continue;
+		}
+
+		if (stat(dp->d_name, &st) == 0 && S_ISSOCK(st.st_mode)) {
+			if (auth_connection_new(dp->d_name) != NULL)
+				auth_reconnect = FALSE;
+		}
+	}
+
+	(void)closedir(dirp);
+}
+
+static void
+auth_connect_missing_timeout(void *context __attr_unused__)
+{
+	if (auth_reconnect)
+                auth_connect_missing();
+}
+
+void auth_connection_init(void)
+{
+	auth_connections = NULL;
+	request_id_counter = 0;
+        auth_reconnect = FALSE;
+
+	auth_connect_missing();
+	to = timeout_add(1000, auth_connect_missing_timeout, NULL);
+}
+
+void auth_connection_deinit(void)
+{
+	struct auth_connection *next;
+
+	while (auth_connections != NULL) {
+		next = auth_connections->next;
+		auth_connection_destroy(auth_connections);
+		auth_connections = next;
+	}
+
+	timeout_remove(to);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/auth-connection.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,56 @@
+#ifndef __AUTH_CONNECTION_H
+#define __AUTH_CONNECTION_H
+
+struct client;
+struct auth_request;
+
+/* reply is NULL if auth connection died */
+typedef void auth_callback_t(struct auth_request *request,
+			     struct auth_login_reply *reply,
+			     const unsigned char *data, struct client *client);
+
+struct auth_connection {
+	struct auth_connection *next;
+
+	char *path;
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	unsigned int pid;
+	enum auth_mech available_auth_mechs;
+        struct auth_login_reply reply;
+
+        struct hash_table *requests;
+
+	unsigned int handshake_received:1;
+	unsigned int reply_received:1;
+};
+
+struct auth_request {
+        enum auth_mech mech;
+        struct auth_connection *conn;
+
+	unsigned int id;
+
+	auth_callback_t *callback;
+	void *context;
+
+	unsigned int init_sent:1;
+};
+
+extern enum auth_mech available_auth_mechs;
+
+int auth_init_request(enum auth_mech mech, auth_callback_t *callback,
+		      void *context, const char **error);
+
+void auth_continue_request(struct auth_request *request,
+			   const unsigned char *data, size_t data_size);
+
+void auth_abort_request(struct auth_request *request);
+
+void auth_connection_init(void);
+void auth_connection_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/client-common.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,22 @@
+#ifndef __CLIENT_COMMON_H
+#define __CLIENT_COMMON_H
+
+#include "network.h"
+
+struct client {
+	struct ip_addr ip;
+	int fd;
+
+	master_callback_t *master_callback;
+	/* ... */
+};
+
+struct client *client_create(int fd, struct ip_addr *ip, int ssl);
+
+unsigned int clients_get_count(void);
+void clients_destroy_all(void);
+
+void clients_init(void);
+void clients_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/common.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,16 @@
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "lib.h"
+#include "../auth/auth-login-interface.h"
+
+extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
+extern unsigned int max_logging_users;
+extern unsigned int login_process_uid;
+
+void main_ref(void);
+void main_unref(void);
+
+void main_close_listen(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/main.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,237 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "lib-signals.h"
+#include "restrict-access.h"
+#include "process-title.h"
+#include "fd-close-on-exec.h"
+#include "auth-connection.h"
+#include "master.h"
+#include "client-common.h"
+#include "ssl-proxy.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+int disable_plaintext_auth, process_per_connection, verbose_proctitle;
+unsigned int max_logging_users;
+unsigned int login_process_uid;
+
+static struct ioloop *ioloop;
+static struct io *io_listen, *io_listen_ssl;
+static int main_refcount;
+static int closing_down;
+
+void main_ref(void)
+{
+	main_refcount++;
+}
+
+void main_unref(void)
+{
+	if (--main_refcount == 0) {
+		/* nothing to do, quit */
+		io_loop_stop(ioloop);
+	} else if (closing_down && clients_get_count() == 0) {
+		/* last login finished, close all communications
+		   to master process */
+		master_close();
+	}
+}
+
+void main_close_listen(void)
+{
+	if (closing_down)
+		return;
+
+	if (io_listen != NULL) {
+		if (close(LOGIN_LISTEN_FD) < 0)
+			i_fatal("can't close() IMAP listen handle");
+
+		io_remove(io_listen);
+		io_listen = NULL;
+	}
+
+	if (io_listen_ssl != NULL) {
+		if (close(LOGIN_SSL_LISTEN_FD) < 0)
+			i_fatal("can't close() IMAPS listen handle");
+
+		io_remove(io_listen_ssl);
+		io_listen_ssl = NULL;
+	}
+
+	closing_down = TRUE;
+	master_notify_finished();
+}
+
+static void sig_quit(int signo __attr_unused__)
+{
+	io_loop_stop(ioloop);
+}
+
+static void login_accept(void *context __attr_unused__)
+{
+	struct ip_addr ip;
+	int fd;
+
+	fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL);
+	if (fd < 0) {
+		if (fd < -1)
+			i_fatal("accept() failed: %m");
+		return;
+	}
+
+	if (process_per_connection)
+		main_close_listen();
+
+	(void)client_create(fd, &ip, FALSE);
+}
+
+static void login_accept_ssl(void *context __attr_unused__)
+{
+	struct client *client;
+	struct ip_addr ip;
+	int fd, fd_ssl;
+
+	fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL);
+	if (fd < 0) {
+		if (fd < -1)
+			i_fatal("accept() failed: %m");
+		return;
+	}
+
+	if (process_per_connection)
+		main_close_listen();
+
+	fd_ssl = ssl_proxy_new(fd);
+	if (fd_ssl == -1)
+		net_disconnect(fd);
+	else
+		client = client_create(fd_ssl, &ip, TRUE);
+}
+
+static void open_logfile(const char *name)
+{
+	if (getenv("IMAP_USE_SYSLOG") != NULL)
+		i_set_failure_syslog(name, LOG_NDELAY, LOG_MAIL);
+	else {
+		/* log to file or stderr */
+		i_set_failure_file(getenv("LOGFILE"), name);
+	}
+
+	if (getenv("INFOLOGFILE") != NULL)
+		i_set_info_file(getenv("INFOLOGFILE"));
+
+	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
+}
+
+static void drop_privileges(const char *name)
+{
+	/* Log file or syslog opening probably requires roots */
+	open_logfile(name);
+
+	/* Initialize SSL proxy so it can read certificate and private
+	   key file. */
+	ssl_proxy_init();
+
+	/* Refuse to run as root - we should never need it and it's
+	   dangerous with SSL. */
+	restrict_access_by_env(TRUE);
+}
+
+static void main_init(void)
+{
+	const char *value;
+
+	lib_init_signals(sig_quit);
+
+	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
+	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
+        verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;
+
+	value = getenv("MAX_LOGGING_USERS");
+	max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10);
+
+	value = getenv("PROCESS_UID");
+	if (value == NULL)
+		i_fatal("BUG: PROCESS_UID environment not given");
+        login_process_uid = strtoul(value, NULL, 10);
+	if (login_process_uid == 0)
+		i_fatal("BUG: PROCESS_UID environment is 0");
+
+        closing_down = FALSE;
+	main_refcount = 0;
+
+	auth_connection_init();
+	clients_init();
+
+	io_listen = io_listen_ssl = NULL;
+
+	if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) {
+		/* we're listening for imap */
+		io_listen = io_add(LOGIN_LISTEN_FD, IO_READ,
+				   login_accept, NULL);
+	}
+
+	if (net_getsockname(LOGIN_SSL_LISTEN_FD, NULL, NULL) == 0) {
+		/* we're listening for imaps */
+		if (!ssl_initialized) {
+			/* this shouldn't happen, master should have
+			   disabled the imaps socket.. */
+			i_fatal("BUG: SSL initialization parameters not given "
+				"while they should have been");
+		}
+
+		io_listen_ssl = io_add(LOGIN_SSL_LISTEN_FD, IO_READ,
+				       login_accept_ssl, NULL);
+	}
+
+	/* initialize master last - it sends the "we're ok" notification */
+	master_init();
+}
+
+static void main_deinit(void)
+{
+        if (lib_signal_kill != 0)
+		i_warning("Killed with signal %d", lib_signal_kill);
+
+	if (io_listen != NULL) io_remove(io_listen);
+	if (io_listen_ssl != NULL) io_remove(io_listen_ssl);
+
+	clients_deinit();
+	master_deinit();
+	auth_connection_deinit();
+
+	ssl_proxy_deinit();
+
+	closelog();
+}
+
+int main(int argc __attr_unused__, char *argv[], char *envp[])
+{
+	const char *name;
+
+#ifdef DEBUG
+        fd_debug_verify_leaks(3, 1024);
+#endif
+	/* NOTE: we start rooted, so keep the code minimal until
+	   restrict_access_by_env() is called */
+	lib_init();
+
+	name = strrchr(argv[0], '/');
+	drop_privileges(name == NULL ? argv[0] : name+1);
+
+	process_title_init(argv, envp);
+	ioloop = io_loop_create(system_pool);
+
+	main_init();
+        io_loop_run(ioloop);
+	main_deinit();
+
+	io_loop_destroy(ioloop);
+	lib_deinit();
+
+        return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/master.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,125 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "network.h"
+#include "fdpass.h"
+#include "master.h"
+#include "client-common.h"
+
+#include <unistd.h>
+
+static struct io *io_master;
+static struct hash_table *master_requests;
+
+static unsigned int master_pos;
+static char master_buf[sizeof(struct master_login_reply)];
+
+static void request_handle(struct master_login_reply *reply)
+{
+	struct client *client;
+
+	client = hash_lookup(master_requests, POINTER_CAST(reply->tag));
+	if (client == NULL)
+		i_fatal("Master sent reply with unknown tag %u", reply->tag);
+
+	client->master_callback(client, reply->success);
+
+	hash_remove(master_requests, POINTER_CAST(reply->tag));
+}
+
+void master_request_imap(struct client *client, master_callback_t *callback,
+			 unsigned int auth_pid, unsigned int auth_id)
+{
+	struct master_login_request req;
+
+	memset(&req, 0, sizeof(req));
+	req.tag = client->fd;
+	req.auth_pid = auth_pid;
+	req.auth_id = auth_id;
+	req.ip = client->ip;
+
+	if (fd_send(LOGIN_MASTER_SOCKET_FD,
+		    client->fd, &req, sizeof(req)) != sizeof(req))
+		i_fatal("fd_send() failed: %m");
+
+	client->master_callback = callback;
+	hash_insert(master_requests, POINTER_CAST(req.tag), client);
+}
+
+void master_notify_finished(void)
+{
+	struct master_login_request req;
+
+	if (io_master == NULL)
+		return;
+
+	memset(&req, 0, sizeof(req));
+
+	/* sending -1 as fd does the notification */
+	if (fd_send(LOGIN_MASTER_SOCKET_FD,
+		    -1, &req, sizeof(req)) != sizeof(req))
+		i_fatal("fd_send() failed: %m");
+}
+
+void master_close(void)
+{
+	if (io_master == NULL)
+		return;
+
+	clients_destroy_all();
+
+	if (close(LOGIN_MASTER_SOCKET_FD) < 0)
+		i_fatal("close(master) failed: %m");
+
+	io_remove(io_master);
+	io_master = NULL;
+
+        main_close_listen();
+	main_unref();
+}
+
+static void master_input(void *context __attr_unused__)
+{
+	int ret;
+
+	ret = net_receive(LOGIN_MASTER_SOCKET_FD, master_buf + master_pos,
+			  sizeof(master_buf) - master_pos);
+	if (ret < 0) {
+		/* master died, kill all clients logging in */
+		master_close();
+		return;
+	}
+
+	master_pos += ret;
+	if (master_pos < sizeof(master_buf))
+		return;
+
+	/* reply is now read */
+	request_handle((struct master_login_reply *) master_buf);
+	master_pos = 0;
+}
+
+void master_init(void)
+{
+	main_ref();
+
+	master_requests = hash_create(default_pool, default_pool,
+				      0, NULL, NULL);
+
+        master_pos = 0;
+	io_master = io_add(LOGIN_MASTER_SOCKET_FD, IO_READ, master_input, NULL);
+
+	/* just a note to master that we're ok. if we die before,
+	   master should shutdown itself. */
+        master_notify_finished();
+}
+
+void master_deinit(void)
+{
+	hash_destroy(master_requests);
+
+	if (io_master != NULL)
+		io_remove(io_master);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/master.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,22 @@
+#ifndef __MASTER_H
+#define __MASTER_H
+
+struct client;
+
+#include "../master/master-login-interface.h"
+
+typedef void master_callback_t(struct client *client, int success);
+
+void master_request_imap(struct client *client, master_callback_t *callback,
+			 unsigned int auth_pid, unsigned int auth_id);
+
+/* Notify master that we're not listening for new connections anymore. */
+void master_notify_finished(void);
+
+/* Close connection to master process */
+void master_close(void);
+
+void master_init(void);
+void master_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/ssl-proxy-gnutls.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,512 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "ssl-proxy.h"
+
+#ifdef HAVE_GNUTLS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+
+struct ssl_proxy {
+	int refcount;
+
+	gnutls_session session;
+	int fd_ssl, fd_plain;
+	struct io *io_ssl, *io_plain;
+	int io_ssl_dir;
+
+	unsigned char outbuf_plain[1024];
+	unsigned int outbuf_pos_plain;
+
+	size_t send_left_ssl, send_left_plain;
+};
+
+const int protocol_priority[] =
+	{ GNUTLS_TLS1, GNUTLS_SSL3, 0 };
+const int kx_priority[] =
+	{ GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
+const int cipher_priority[] =
+	{ GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
+	  GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
+const int comp_priority[] =
+	{ GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
+const int mac_priority[] =
+	{ GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
+const int cert_type_priority[] =
+	{ GNUTLS_CRT_X509, 0 };
+
+static gnutls_certificate_credentials x509_cred;
+static gnutls_dh_params dh_params;
+static gnutls_rsa_params rsa_params;
+
+static void ssl_input(void *context);
+static void plain_input(void *context);
+static int ssl_proxy_destroy(struct ssl_proxy *proxy);
+
+static const char *get_alert_text(struct ssl_proxy *proxy)
+{
+	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
+}
+
+static int handle_ssl_error(struct ssl_proxy *proxy, int error)
+{
+	if (!gnutls_error_is_fatal(error)) {
+		if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+			i_warning("Received SSL warning alert: %s",
+				  get_alert_text(proxy));
+		}
+		return 0;
+	}
+
+	/* fatal error occured */
+	if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+		i_warning("Received SSL fatal alert: %s",
+			  get_alert_text(proxy));
+	} else {
+		i_warning("Error reading from SSL client: %s",
+			  gnutls_strerror(error));
+	}
+
+        gnutls_alert_send_appropriate(proxy->session, error);
+	ssl_proxy_destroy(proxy);
+	return -1;
+}
+
+static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size)
+{
+	int rcvd;
+
+	rcvd = gnutls_record_recv(proxy->session, data, size);
+	if (rcvd > 0)
+		return rcvd;
+
+	if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
+		/* disconnected, either by nicely telling us that we'll
+		   close the connection, or by simply killing the
+		   connection which gives us the packet length error. */
+		ssl_proxy_destroy(proxy);
+		return -1;
+	}
+
+	return handle_ssl_error(proxy, rcvd);
+}
+
+static int proxy_send_ssl(struct ssl_proxy *proxy,
+			  const void *data, size_t size)
+{
+	int sent;
+
+	sent = gnutls_record_send(proxy->session, data, size);
+	if (sent >= 0)
+		return sent;
+
+	if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
+		/* don't warn about errors related to unexpected
+		   disconnection */
+		ssl_proxy_destroy(proxy);
+		return -1;
+	}
+
+	return handle_ssl_error(proxy, sent);
+}
+
+static int ssl_proxy_destroy(struct ssl_proxy *proxy)
+{
+	if (--proxy->refcount > 0)
+		return TRUE;
+
+	gnutls_deinit(proxy->session);
+
+	(void)net_disconnect(proxy->fd_ssl);
+	(void)net_disconnect(proxy->fd_plain);
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	if (proxy->io_plain != NULL)
+		io_remove(proxy->io_plain);
+
+	i_free(proxy);
+
+	main_unref();
+	return FALSE;
+}
+
+static void ssl_output(void *context)
+{
+        struct ssl_proxy *proxy = context;
+	int sent;
+
+	sent = net_transmit(proxy->fd_plain,
+			    proxy->outbuf_plain + proxy->outbuf_pos_plain,
+			    proxy->send_left_plain);
+	if (sent < 0) {
+		/* disconnected */
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	proxy->send_left_plain -= sent;
+	proxy->outbuf_pos_plain += sent;
+
+	if (proxy->send_left_plain > 0)
+		return;
+
+	/* everything is sent, start reading again */
+	io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy);
+}
+
+static void ssl_input(void *context)
+{
+        struct ssl_proxy *proxy = context;
+	int rcvd, sent;
+
+	rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain,
+			      sizeof(proxy->outbuf_plain));
+	if (rcvd <= 0)
+		return;
+
+	sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd);
+	if (sent == rcvd)
+		return;
+
+	if (sent < 0) {
+		/* disconnected */
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	/* everything wasn't sent - don't read anything until we've
+	   sent it all */
+        proxy->outbuf_pos_plain = 0;
+	proxy->send_left_plain = rcvd - sent;
+
+	io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy);
+}
+
+static void plain_output(void *context)
+{
+	struct ssl_proxy *proxy = context;
+	int sent;
+
+	sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl);
+	if (sent <= 0)
+		return;
+
+	proxy->send_left_ssl -= sent;
+	if (proxy->send_left_ssl > 0)
+		return;
+
+	/* everything is sent, start reading again */
+	io_remove(proxy->io_plain);
+	proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy);
+}
+
+static void plain_input(void *context)
+{
+	struct ssl_proxy *proxy = context;
+	char buf[1024];
+	ssize_t rcvd, sent;
+
+	rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf));
+	if (rcvd < 0) {
+		/* disconnected */
+		gnutls_bye(proxy->session, 1);
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	sent = proxy_send_ssl(proxy, buf, (size_t)rcvd);
+	if (sent < 0 || sent == rcvd)
+		return;
+
+	/* everything wasn't sent - don't read anything until we've
+	   sent it all */
+	proxy->send_left_ssl = rcvd - sent;
+
+	io_remove(proxy->io_plain);
+	proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
+}
+
+static void ssl_handshake(void *context)
+{
+	struct ssl_proxy *proxy = context;
+	int ret, dir;
+
+        ret = gnutls_handshake(proxy->session);
+	if (ret >= 0) {
+		/* handshake done, now we can start reading */
+		if (proxy->io_ssl != NULL)
+			io_remove(proxy->io_ssl);
+
+		proxy->io_plain = io_add(proxy->fd_plain, IO_READ,
+					 plain_input, proxy);
+		proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ,
+				       ssl_input, proxy);
+		return;
+	}
+
+	if (handle_ssl_error(proxy, ret) < 0)
+		return;
+
+	/* i/o interrupted */
+	dir = gnutls_handshake_get_direction(proxy->session) == 0 ?
+		IO_READ : IO_WRITE;
+	if (proxy->io_ssl_dir != dir) {
+		if (proxy->io_ssl != NULL)
+			io_remove(proxy->io_ssl);
+		proxy->io_ssl = io_add(proxy->fd_ssl, dir,
+				       ssl_handshake, proxy);
+		proxy->io_ssl_dir = dir;
+	}
+}
+
+static gnutls_session initialize_state(void)
+{
+	gnutls_session session;
+
+	gnutls_init(&session, GNUTLS_SERVER);
+
+	gnutls_protocol_set_priority(session, protocol_priority);
+	gnutls_cipher_set_priority(session, cipher_priority);
+	gnutls_compression_set_priority(session, comp_priority);
+	gnutls_kx_set_priority(session, kx_priority);
+	gnutls_mac_set_priority(session, mac_priority);
+	gnutls_cert_type_set_priority(session, cert_type_priority);
+
+	gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+	return session;
+}
+
+int ssl_proxy_new(int fd)
+{
+        struct ssl_proxy *proxy;
+	gnutls_session session;
+	int sfd[2];
+
+	if (!ssl_initialized)
+		return -1;
+
+	session = initialize_state();
+	gnutls_transport_set_ptr(session, fd);
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
+		i_error("socketpair() failed: %m");
+		gnutls_deinit(session);
+		return -1;
+	}
+
+	net_set_nonblock(sfd[0], TRUE);
+	net_set_nonblock(sfd[1], TRUE);
+
+	proxy = i_new(struct ssl_proxy, 1);
+	proxy->refcount = 1;
+	proxy->session = session;
+	proxy->fd_ssl = fd;
+	proxy->fd_plain = sfd[0];
+
+	proxy->refcount++;
+	ssl_handshake(proxy);
+	if (!ssl_proxy_destroy(proxy))
+		return -1;
+
+        main_ref();
+	return sfd[1];
+}
+
+static void read_next_field(int fd, gnutls_datum *datum,
+			    const char *fname, const char *field_name)
+{
+        ssize_t ret;
+
+	/* get size */
+	ret = read(fd, &datum->size, sizeof(datum->size));
+	if (ret < 0)
+		i_fatal("read() failed for %s: %m", fname);
+
+	if (ret != sizeof(datum->size)) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: File too small",
+			fname);
+	}
+
+	if (datum->size > 10240) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: "
+			"Field '%s' too large (%u)",
+			fname, field_name, datum->size);
+	}
+
+	/* read the actual data */
+	datum->data = t_malloc(datum->size);
+	ret = read(fd, datum->data, datum->size);
+	if (ret < 0)
+		i_fatal("read() failed for %s: %m", fname);
+
+	if ((size_t)ret != datum->size) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: "
+			"Field '%s' not fully in file (%u < %u)",
+			fname, field_name, datum->size - ret, datum->size);
+	}
+}
+
+static void read_dh_parameters(int fd, const char *fname)
+{
+	gnutls_datum dbits, prime, generator;
+	int ret, bits;
+
+	if ((ret = gnutls_dh_params_init(&dh_params)) < 0) {
+		i_fatal("gnutls_dh_params_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* read until bits field is 0 */
+	for (;;) {
+		read_next_field(fd, &dbits, fname, "DH bits");
+
+		if (dbits.size != sizeof(int)) {
+			(void)unlink(fname);
+			i_fatal("Corrupted SSL parameter file %s: "
+				"Field 'DH bits' has invalid size %u",
+				fname, dbits.size);
+		}
+
+		bits = *((int *) dbits.data);
+		if (bits == 0)
+			break;
+
+		read_next_field(fd, &prime, fname, "DH prime");
+		read_next_field(fd, &generator, fname, "DH generator");
+
+		ret = gnutls_dh_params_set(dh_params, prime, generator, bits);
+		if (ret < 0) {
+			i_fatal("gnutls_dh_params_set() failed: %s",
+				gnutls_strerror(ret));
+		}
+	}
+}
+
+static void read_rsa_parameters(int fd, const char *fname)
+{
+	gnutls_datum m, e, d, p, q, u;
+	int ret;
+
+	read_next_field(fd, &m, fname, "RSA m");
+	read_next_field(fd, &e, fname, "RSA e");
+	read_next_field(fd, &d, fname, "RSA d");
+	read_next_field(fd, &p, fname, "RSA p");
+	read_next_field(fd, &q, fname, "RSA q");
+	read_next_field(fd, &u, fname, "RSA u");
+
+	if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) {
+		i_fatal("gnutls_rsa_params_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* only 512bit is allowed */
+	ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512);
+	if (ret < 0) {
+		i_fatal("gnutls_rsa_params_set() failed: %s",
+			gnutls_strerror(ret));
+	}
+}
+
+static void read_parameters(const char *fname)
+{
+	int fd;
+
+	/* we'll wait until parameter file exists */
+	for (;;) {
+		fd = open(fname, O_RDONLY);
+		if (fd != -1)
+			break;
+
+		if (errno != ENOENT)
+			i_fatal("Can't open SSL parameter file %s: %m", fname);
+
+		sleep(1);
+	}
+
+	read_dh_parameters(fd, fname);
+	read_rsa_parameters(fd, fname);
+
+	(void)close(fd);
+}
+
+static void gcrypt_log_handler(void *context __attr_unused__, int level,
+			       const char *fmt, va_list args)
+{
+	if (level == GCRY_LOG_FATAL) {
+		t_push();
+		i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args));
+		t_pop();
+	}
+}
+
+void ssl_proxy_init(void)
+{
+	const char *certfile, *keyfile, *paramfile;
+	unsigned char buf[4];
+	int ret;
+
+	certfile = getenv("SSL_CERT_FILE");
+	keyfile = getenv("SSL_KEY_FILE");
+	paramfile = getenv("SSL_PARAM_FILE");
+
+	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
+		/* SSL support is disabled */
+		return;
+	}
+
+	if ((ret = gnutls_global_init() < 0)) {
+		i_fatal("gnu_tls_global_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* gcrypt initialization - set log handler and make sure randomizer
+	   opens /dev/urandom now instead of after we've chrooted */
+	gcry_set_log_handler(gcrypt_log_handler, NULL);
+	gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM);
+
+	read_parameters(paramfile);
+
+	if ((ret = gnutls_certificate_allocate_cred(&x509_cred)) < 0) {
+		i_fatal("gnutls_certificate_allocate_cred() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile,
+						   GNUTLS_X509_FMT_PEM);
+	if (ret < 0) {
+		i_fatal("Can't load certificate files %s and %s: %s",
+			certfile, keyfile, gnutls_strerror(ret));
+	}
+
+        ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
+	if (ret < 0)
+		i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
+	ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
+	if (ret < 0)
+		i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
+
+	ssl_initialized = TRUE;
+}
+
+void ssl_proxy_deinit(void)
+{
+	if (ssl_initialized) {
+		gnutls_certificate_free_cred(x509_cred);
+		gnutls_global_deinit();
+	}
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/ssl-proxy-openssl.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,386 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "ssl-proxy.h"
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+enum ssl_state {
+	SSL_STATE_HANDSHAKE,
+	SSL_STATE_READ,
+	SSL_STATE_WRITE
+};
+
+struct ssl_proxy {
+	int refcount;
+
+	SSL *ssl;
+        enum ssl_state state;
+
+	int fd_ssl, fd_plain;
+	struct io *io_ssl, *io_plain_read, *io_plain_write;
+	int io_ssl_dir;
+
+	unsigned char plainout_buf[1024];
+	unsigned int plainout_pos, plainout_size;
+
+	unsigned char sslout_buf[1024];
+	unsigned int sslout_pos, sslout_size;
+};
+
+static SSL_CTX *ssl_ctx;
+
+static void plain_read(struct ssl_proxy *proxy);
+static void plain_write(struct ssl_proxy *proxy);
+
+static int ssl_proxy_destroy(struct ssl_proxy *proxy);
+static void ssl_set_direction(struct ssl_proxy *proxy, int dir);
+
+static void plain_block_input(struct ssl_proxy *proxy, int block)
+{
+	if (block) {
+		if (proxy->io_plain_read != NULL) {
+			io_remove(proxy->io_plain_read);
+			proxy->io_plain_read = NULL;
+		}
+	} else {
+		if (proxy->io_plain_read == NULL) {
+			proxy->io_plain_read =
+				io_add(proxy->fd_plain, IO_READ,
+				       (io_callback_t *)plain_read, proxy);
+		}
+	}
+}
+
+static void ssl_block(struct ssl_proxy *proxy, int block)
+{
+	i_assert(proxy->state == SSL_STATE_READ);
+
+	if (block) {
+		if (proxy->io_ssl != NULL) {
+			io_remove(proxy->io_ssl);
+			proxy->io_ssl = NULL;
+		}
+
+		proxy->io_ssl_dir = -2;
+	} else {
+		proxy->io_ssl_dir = -1;
+		ssl_set_direction(proxy, IO_READ);
+	}
+}
+
+static void plain_read(struct ssl_proxy *proxy)
+{
+	ssize_t ret;
+
+	i_assert(proxy->sslout_size == 0);
+
+	ret = net_receive(proxy->fd_plain, proxy->sslout_buf,
+			  sizeof(proxy->sslout_buf));
+	if (ret < 0)
+		ssl_proxy_destroy(proxy);
+	else if (ret > 0) {
+		proxy->sslout_size = ret;
+		proxy->sslout_pos = 0;
+
+		proxy->state = SSL_STATE_WRITE;
+		ssl_set_direction(proxy, IO_WRITE);
+
+		plain_block_input(proxy, TRUE);
+	}
+}
+
+static void plain_write(struct ssl_proxy *proxy)
+{
+	ssize_t ret;
+
+	ret = net_transmit(proxy->fd_plain,
+			   proxy->plainout_buf + proxy->plainout_pos,
+			   proxy->plainout_size);
+	if (ret < 0)
+		ssl_proxy_destroy(proxy);
+	else {
+		proxy->plainout_size -= ret;
+		proxy->plainout_pos += ret;
+
+		if (proxy->plainout_size > 0) {
+			ssl_block(proxy, TRUE);
+			if (proxy->io_plain_write == NULL) {
+				proxy->io_plain_write =
+					io_add(proxy->fd_plain, IO_WRITE,
+					       (io_callback_t *)plain_write,
+					       proxy);
+			}
+		} else {
+			proxy->plainout_pos = 0;
+			ssl_block(proxy, FALSE);
+
+			if (proxy->io_plain_write != NULL) {
+				io_remove(proxy->io_plain_write);
+                                proxy->io_plain_write = NULL;
+			}
+		}
+	}
+
+}
+
+static const char *ssl_last_error(void)
+{
+	unsigned long err;
+	char *buf;
+	size_t err_size = 256;
+
+	err = ERR_get_error();
+	if (err == 0)
+		return strerror(errno);
+
+	buf = t_malloc(err_size);
+	buf[err_size-1] = '\0';
+	ERR_error_string_n(err, buf, err_size-1);
+	return buf;
+}
+
+static void ssl_handle_error(struct ssl_proxy *proxy, int err, const char *func)
+{
+	err = SSL_get_error(proxy->ssl, err);
+
+	switch (err) {
+	case SSL_ERROR_WANT_READ:
+		ssl_set_direction(proxy, IO_READ);
+		break;
+	case SSL_ERROR_WANT_WRITE:
+		ssl_set_direction(proxy, IO_WRITE);
+		break;
+	case SSL_ERROR_SYSCALL:
+		/* eat up the error queue */
+		/*i_warning("%s failed: %s", func, ssl_last_error());*/
+		ssl_proxy_destroy(proxy);
+		break;
+	case SSL_ERROR_ZERO_RETURN:
+		/* clean connection closing */
+		ssl_proxy_destroy(proxy);
+		break;
+	case SSL_ERROR_SSL:
+		/*i_warning("%s failed: %s", func, ssl_last_error());*/
+		ssl_proxy_destroy(proxy);
+		break;
+	default:
+		i_warning("%s failed: unknown failure %d (%s)",
+			  func, err, ssl_last_error());
+		ssl_proxy_destroy(proxy);
+		break;
+	}
+}
+
+static void ssl_handshake_step(struct ssl_proxy *proxy)
+{
+	int ret;
+
+	ret = SSL_accept(proxy->ssl);
+	if (ret != 1) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_accept()");
+	} else {
+		plain_block_input(proxy, FALSE);
+		ssl_set_direction(proxy, IO_READ);
+		proxy->state = SSL_STATE_READ;
+	}
+}
+
+static void ssl_read_step(struct ssl_proxy *proxy)
+{
+	int ret;
+
+	i_assert(proxy->plainout_size == 0);
+
+	ret = SSL_read(proxy->ssl, proxy->plainout_buf,
+		       sizeof(proxy->plainout_buf));
+	if (ret <= 0) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_read()");
+	} else {
+		plain_block_input(proxy, FALSE);
+		ssl_set_direction(proxy, IO_READ);
+
+		proxy->plainout_pos = 0;
+		proxy->plainout_size = ret;
+		plain_write(proxy);
+	}
+}
+
+static void ssl_write_step(struct ssl_proxy *proxy)
+{
+	int ret;
+
+	ret = SSL_write(proxy->ssl, proxy->sslout_buf + proxy->sslout_pos,
+			proxy->sslout_size);
+	if (ret <= 0) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_write()");
+	} else {
+		proxy->sslout_size -= ret;
+		proxy->sslout_pos += ret;
+
+		if (proxy->sslout_size > 0) {
+			plain_block_input(proxy, TRUE);
+			ssl_set_direction(proxy, IO_WRITE);
+			proxy->state = SSL_STATE_WRITE;
+		} else {
+			plain_block_input(proxy, FALSE);
+			ssl_set_direction(proxy, IO_READ);
+			proxy->state = SSL_STATE_READ;
+			proxy->sslout_pos = 0;
+		}
+	}
+}
+
+static void ssl_step(void *context)
+{
+        struct ssl_proxy *proxy = context;
+
+	switch (proxy->state) {
+	case SSL_STATE_HANDSHAKE:
+		ssl_handshake_step(proxy);
+		break;
+	case SSL_STATE_READ:
+		ssl_read_step(proxy);
+		break;
+	case SSL_STATE_WRITE:
+		ssl_write_step(proxy);
+		break;
+	}
+}
+
+static void ssl_set_direction(struct ssl_proxy *proxy, int dir)
+{
+	i_assert(proxy->io_ssl_dir != -2);
+
+	if (proxy->io_ssl_dir == dir)
+		return;
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_step, proxy);
+}
+
+int ssl_proxy_new(int fd)
+{
+	struct ssl_proxy *proxy;
+	SSL *ssl;
+	int sfd[2];
+
+	if (!ssl_initialized)
+		return -1;
+
+	ssl = SSL_new(ssl_ctx);
+	if (ssl == NULL) {
+		i_error("SSL_new() failed: %s", ssl_last_error());
+		return -1;
+	}
+
+	SSL_set_accept_state(ssl);
+	if (SSL_set_fd(ssl, fd) != 1) {
+		i_error("SSL_set_fd() failed: %s", ssl_last_error());
+		return -1;
+	}
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
+		i_error("socketpair() failed: %m");
+		SSL_free(ssl);
+		return -1;
+	}
+
+	net_set_nonblock(sfd[0], TRUE);
+	net_set_nonblock(sfd[1], TRUE);
+
+	proxy = i_new(struct ssl_proxy, 1);
+	proxy->refcount = 1;
+	proxy->ssl = ssl;
+	proxy->fd_ssl = fd;
+	proxy->fd_plain = sfd[0];
+
+	proxy->state = SSL_STATE_HANDSHAKE;
+	ssl_set_direction(proxy, IO_READ);
+
+	proxy->refcount++;
+	ssl_handshake_step(proxy);
+	if (!ssl_proxy_destroy(proxy))
+		return -1;
+
+        main_ref();
+	return sfd[1];
+}
+
+static int ssl_proxy_destroy(struct ssl_proxy *proxy)
+{
+	if (--proxy->refcount > 0)
+		return TRUE;
+
+	SSL_free(proxy->ssl);
+
+	(void)net_disconnect(proxy->fd_ssl);
+	(void)net_disconnect(proxy->fd_plain);
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	if (proxy->io_plain_read != NULL)
+		io_remove(proxy->io_plain_read);
+	if (proxy->io_plain_write != NULL)
+		io_remove(proxy->io_plain_write);
+
+	i_free(proxy);
+
+	main_unref();
+	return FALSE;
+}
+
+void ssl_proxy_init(void)
+{
+	const char *certfile, *keyfile, *paramfile;
+	int ret;
+
+	certfile = getenv("SSL_CERT_FILE");
+	keyfile = getenv("SSL_KEY_FILE");
+	paramfile = getenv("SSL_PARAM_FILE");
+
+	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
+		/* SSL support is disabled */
+		return;
+	}
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
+		i_fatal("SSL_CTX_new() failed");
+
+        ret = SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);
+	if (ret != 1) {
+		i_fatal("Can't load certificate file %s: %s",
+			certfile, ssl_last_error());
+	}
+
+	ret = SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);
+	if (ret != 1) {
+		i_fatal("Can't load private key file %s: %s",
+			keyfile, ssl_last_error());
+	}
+
+	ssl_initialized = TRUE;
+}
+
+void ssl_proxy_deinit(void)
+{
+	if (ssl_initialized)
+                SSL_CTX_free(ssl_ctx);
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/ssl-proxy.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,16 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "lib.h"
+#include "ssl-proxy.h"
+
+int ssl_initialized = FALSE;
+
+#ifndef HAVE_SSL
+
+/* no SSL support */
+
+int ssl_proxy_new(int fd __attr_unused__) { return -1; }
+void ssl_proxy_init(void) {}
+void ssl_proxy_deinit(void) {}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/ssl-proxy.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,14 @@
+#ifndef __SSL_PROXY_H
+#define __SSL_PROXY_H
+
+extern int ssl_initialized;
+
+/* establish SSL connection with the given fd, returns a new fd which you
+   must use from now on, or -1 if error occured. Unless -1 is returned,
+   the given fd must be simply forgotten. */
+int ssl_proxy_new(int fd);
+
+void ssl_proxy_init(void);
+void ssl_proxy_deinit(void);
+
+#endif
--- a/src/login/.cvsignore	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-*.la
-*.lo
-*.o
-.deps
-.libs
-Makefile
-Makefile.in
-so_locations
-imap-login
--- a/src/login/Makefile.am	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-pkglibexecdir = $(libexecdir)/dovecot
-
-pkglibexec_PROGRAMS = imap-login
-
-INCLUDES = \
-	-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 = \
-	auth-connection.c \
-	client.c \
-	client-authenticate.c \
-	main.c \
-	master.c \
-	ssl-proxy.c \
-	ssl-proxy-gnutls.c \
-	ssl-proxy-openssl.c
-
-noinst_HEADERS = \
-	auth-connection.h \
-	common.h \
-	client.h \
-	client-authenticate.h \
-	master.h \
-	ssl-proxy.h
--- a/src/login/auth-connection.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,374 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "hash.h"
-#include "ioloop.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "auth-connection.h"
-
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-/* Maximum size for an auth reply. 50kB should be more than enough. */
-#define MAX_INBUF_SIZE (1024*50)
-
-#define MAX_OUTBUF_SIZE \
-	(sizeof(struct auth_login_request_continue) + \
-	 AUTH_LOGIN_MAX_REQUEST_DATA_SIZE)
-
-enum auth_mech available_auth_mechs;
-
-static int auth_reconnect;
-static unsigned int request_id_counter;
-static struct auth_connection *auth_connections;
-static struct timeout *to;
-
-static void auth_connection_destroy(struct auth_connection *conn);
-static void auth_input(void *context);
-static void auth_connect_missing(void);
-
-static struct auth_connection *auth_connection_find(const char *path)
-{
-	struct auth_connection *conn;
-
-	for (conn = auth_connections; conn != NULL; conn = conn->next) {
-		if (strcmp(conn->path, path) == 0)
-			return conn;
-	}
-
-	return NULL;
-}
-
-static struct auth_connection *auth_connection_new(const char *path)
-{
-	struct auth_connection *conn;
-        struct auth_login_handshake_input handshake;
-	int fd;
-
-	fd = net_connect_unix(path);
-	if (fd == -1) {
-		i_error("Can't connect to imap-auth at %s: %m", path);
-                auth_reconnect = TRUE;
-		return NULL;
-	}
-
-	/* we depend on auth process - if it's slow, just wait */
-        net_set_nonblock(fd, FALSE);
-
-	conn = i_new(struct auth_connection, 1);
-	conn->path = i_strdup(path);
-	conn->fd = fd;
-	conn->io = io_add(fd, IO_READ, auth_input, conn);
-	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
-					   FALSE);
-	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
-					    IO_PRIORITY_DEFAULT, FALSE);
-	conn->requests = hash_create(default_pool, default_pool, 100,
-				     NULL, NULL);
-
-	conn->next = auth_connections;
-	auth_connections = conn;
-
-	/* send our handshake */
-	memset(&handshake, 0, sizeof(handshake));
-	handshake.pid = login_process_uid;
-	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
-                auth_connection_destroy(conn);
-		return NULL;
-	}
-	return conn;
-}
-
-static void request_destroy(struct auth_request *request)
-{
-	hash_remove(request->conn->requests, POINTER_CAST(request->id));
-	i_free(request);
-}
-
-static void request_hash_destroy(void *key __attr_unused__, void *value,
-				 void *context __attr_unused__)
-{
-	struct auth_request *request = value;
-
-	request->callback(request, NULL, NULL, request->context);
-	request_destroy(request);
-}
-
-static void auth_connection_destroy(struct auth_connection *conn)
-{
-	struct auth_connection **pos;
-
-	for (pos = &auth_connections; *pos != NULL; pos = &(*pos)->next) {
-		if (*pos == conn) {
-			*pos = conn->next;
-			break;
-		}
-	}
-
-	hash_foreach(conn->requests, request_hash_destroy, NULL);
-	hash_destroy(conn->requests);
-
-	if (close(conn->fd) < 0)
-		i_error("close(imap-auth) failed: %m");
-	io_remove(conn->io);
-	i_stream_unref(conn->input);
-	o_stream_unref(conn->output);
-	i_free(conn->path);
-	i_free(conn);
-}
-
-static struct auth_connection *
-auth_connection_get(enum auth_mech mech, size_t size, const char **error)
-{
-	struct auth_connection *conn;
-	int found;
-
-	found = FALSE;
-	for (conn = auth_connections; conn != NULL; conn = conn->next) {
-		if ((conn->available_auth_mechs & mech)) {
-			if (o_stream_have_space(conn->output, size) > 0)
-				return conn;
-
-			found = TRUE;
-		}
-	}
-
-	if (!found) {
-		if ((available_auth_mechs & mech) == 0)
-			*error = "Unsupported authentication mechanism";
-		else {
-			*error = "Authentication server isn't connected, "
-				"try again later..";
-			auth_reconnect = TRUE;
-		}
-	} else {
-		*error = "Authentication servers are busy, wait..";
-		i_warning("Authentication servers are busy");
-	}
-
-	return NULL;
-}
-
-static void update_available_auth_mechs(void)
-{
-	struct auth_connection *conn;
-
-        available_auth_mechs = 0;
-	for (conn = auth_connections; conn != NULL; conn = conn->next)
-                available_auth_mechs |= conn->available_auth_mechs;
-}
-
-static void auth_handle_handshake(struct auth_connection *conn,
-				  struct auth_login_handshake_output *handshake)
-{
-	conn->pid = handshake->pid;
-	conn->available_auth_mechs = handshake->auth_mechanisms;
-	conn->handshake_received = TRUE;
-
-	update_available_auth_mechs();
-}
-
-static void auth_handle_reply(struct auth_connection *conn,
-			      struct auth_login_reply *reply,
-			      const unsigned char *data)
-{
-	struct auth_request *request;
-
-	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
-	if (request == NULL) {
-		i_error("BUG: imap-auth sent us reply with unknown ID %u",
-			reply->id);
-		return;
-	}
-
-	request->callback(request, reply, data, request->context);
-
-	if (reply->result != AUTH_LOGIN_RESULT_CONTINUE)
-		request_destroy(request);
-}
-
-static void auth_input(void *context)
-{
-	struct auth_connection *conn = context;
-        struct auth_login_handshake_output handshake;
-	const unsigned char *data;
-	size_t size;
-
-	switch (i_stream_read(conn->input)) {
-	case 0:
-		return;
-	case -1:
-		/* disconnected */
-                auth_reconnect = TRUE;
-		auth_connection_destroy(conn);
-		return;
-	case -2:
-		/* buffer full - can't happen unless imap-auth is buggy */
-		i_error("BUG: imap-auth sent us more than %d bytes of data",
-			MAX_INBUF_SIZE);
-		auth_connection_destroy(conn);
-		return;
-	}
-
-	if (!conn->handshake_received) {
-		data = i_stream_get_data(conn->input, &size);
-		if (size == sizeof(handshake)) {
-			memcpy(&handshake, data, sizeof(handshake));
-			i_stream_skip(conn->input, sizeof(handshake));
-
-			auth_handle_handshake(conn, &handshake);
-		} else if (size > sizeof(handshake)) {
-			i_error("BUG: imap-auth sent us too large handshake "
-				"(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
-				sizeof(handshake));
-			auth_connection_destroy(conn);
-		}
-		return;
-	}
-
-	if (!conn->reply_received) {
-		data = i_stream_get_data(conn->input, &size);
-		if (size < sizeof(conn->reply))
-			return;
-
-		memcpy(&conn->reply, data, sizeof(conn->reply));
-		i_stream_skip(conn->input, sizeof(conn->reply));
-		conn->reply_received = TRUE;
-	}
-
-	data = i_stream_get_data(conn->input, &size);
-	if (size < conn->reply.data_size)
-		return;
-
-	/* we've got a full reply */
-	conn->reply_received = FALSE;
-	auth_handle_reply(conn, &conn->reply, data);
-	i_stream_skip(conn->input, conn->reply.data_size);
-}
-
-int auth_init_request(enum auth_mech mech, auth_callback_t callback,
-		      void *context, const char **error)
-{
-	struct auth_connection *conn;
-	struct auth_request *request;
-	struct auth_login_request_new auth_request;
-
-	if (auth_reconnect)
-		auth_connect_missing();
-
-	conn = auth_connection_get(mech, sizeof(auth_request), error);
-	if (conn == NULL)
-		return FALSE;
-
-	/* create internal request structure */
-	request = i_new(struct auth_request, 1);
-	request->mech = mech;
-	request->conn = conn;
-	request->id = ++request_id_counter;
-	if (request->id == 0) {
-		/* wrapped - ID 0 not allowed */
-		request->id = ++request_id_counter;
-	}
-	request->callback = callback;
-	request->context = context;
-
-	hash_insert(conn->requests, POINTER_CAST(request->id), request);
-
-	/* send request to auth */
-	auth_request.type = AUTH_LOGIN_REQUEST_NEW;
-	auth_request.mech = request->mech;
-	auth_request.id = request->id;
-	if (o_stream_send(request->conn->output, &auth_request,
-			  sizeof(auth_request)) < 0)
-		auth_connection_destroy(request->conn);
-	return TRUE;
-}
-
-void auth_continue_request(struct auth_request *request,
-			   const unsigned char *data, size_t data_size)
-{
-	struct auth_login_request_continue auth_request;
-
-	/* send continued request to auth */
-	auth_request.type = AUTH_LOGIN_REQUEST_CONTINUE;
-	auth_request.id = request->id;
-	auth_request.data_size = data_size;
-
-	if (o_stream_send(request->conn->output, &auth_request,
-			  sizeof(auth_request)) < 0)
-		auth_connection_destroy(request->conn);
-	else if (o_stream_send(request->conn->output, data, data_size) < 0)
-		auth_connection_destroy(request->conn);
-}
-
-void auth_abort_request(struct auth_request *request)
-{
-        request_destroy(request);
-}
-
-static void auth_connect_missing(void)
-{
-	DIR *dirp;
-	struct dirent *dp;
-	struct stat st;
-
-	auth_reconnect = TRUE;
-
-	/* we're chrooted into */
-	dirp = opendir(".");
-	if (dirp == NULL) {
-		i_error("opendir(\".\") failed when trying to get list of "
-			"authentication servers: %m");
-		return;
-	}
-
-	while ((dp = readdir(dirp)) != NULL) {
-		if (dp->d_name[0] == '.')
-			continue;
-
-		if (auth_connection_find(dp->d_name) != NULL) {
-			/* already connected */
-			continue;
-		}
-
-		if (stat(dp->d_name, &st) == 0 && S_ISSOCK(st.st_mode)) {
-			if (auth_connection_new(dp->d_name) != NULL)
-				auth_reconnect = FALSE;
-		}
-	}
-
-	(void)closedir(dirp);
-}
-
-static void
-auth_connect_missing_timeout(void *context __attr_unused__)
-{
-	if (auth_reconnect)
-                auth_connect_missing();
-}
-
-void auth_connection_init(void)
-{
-	auth_connections = NULL;
-	request_id_counter = 0;
-        auth_reconnect = FALSE;
-
-	auth_connect_missing();
-	to = timeout_add(1000, auth_connect_missing_timeout, NULL);
-}
-
-void auth_connection_deinit(void)
-{
-	struct auth_connection *next;
-
-	while (auth_connections != NULL) {
-		next = auth_connections->next;
-		auth_connection_destroy(auth_connections);
-		auth_connections = next;
-	}
-
-	timeout_remove(to);
-}
--- a/src/login/auth-connection.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-#ifndef __AUTH_CONNECTION_H
-#define __AUTH_CONNECTION_H
-
-struct client;
-struct auth_request;
-
-/* reply is NULL if auth connection died */
-typedef void auth_callback_t(struct auth_request *request,
-			     struct auth_login_reply *reply,
-			     const unsigned char *data, struct client *client);
-
-struct auth_connection {
-	struct auth_connection *next;
-
-	char *path;
-	int fd;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-
-	unsigned int pid;
-	enum auth_mech available_auth_mechs;
-        struct auth_login_reply reply;
-
-        struct hash_table *requests;
-
-	unsigned int handshake_received:1;
-	unsigned int reply_received:1;
-};
-
-struct auth_request {
-        enum auth_mech mech;
-        struct auth_connection *conn;
-
-	unsigned int id;
-
-	auth_callback_t *callback;
-	void *context;
-
-	unsigned int init_sent:1;
-};
-
-extern enum auth_mech available_auth_mechs;
-
-int auth_init_request(enum auth_mech mech, auth_callback_t *callback,
-		      void *context, const char **error);
-
-void auth_continue_request(struct auth_request *request,
-			   const unsigned char *data, size_t data_size);
-
-void auth_abort_request(struct auth_request *request);
-
-void auth_connection_init(void);
-void auth_connection_deinit(void);
-
-#endif
--- a/src/login/client-authenticate.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,351 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "base64.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "istream.h"
-#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"
-#include "client-authenticate.h"
-#include "master.h"
-
-static enum auth_mech auth_mechs = 0;
-static char *auth_mechs_capability = NULL;
-
-const char *client_authenticate_get_capabilities(void)
-{
-	string_t *str;
-	int i;
-
-	if (auth_mechs == available_auth_mechs)
-		return auth_mechs_capability;
-
-	auth_mechs = available_auth_mechs;
-	i_free(auth_mechs_capability);
-
-	str = t_str_new(128);
-
-	for (i = 0; i < AUTH_MECH_COUNT; i++) {
-		if ((auth_mechs & auth_mech_desc[i].mech) &&
-		    auth_mech_desc[i].name != NULL) {
-			str_append_c(str, ' ');
-			str_append(str, "AUTH=");
-			str_append(str, auth_mech_desc[i].name);
-		}
-	}
-
-	auth_mechs_capability = i_strdup_empty(str_c(str));
-	return auth_mechs_capability;
-}
-
-static struct auth_mech_desc *auth_mech_find(const char *name)
-{
-	int i;
-
-	for (i = 0; i < AUTH_MECH_COUNT; i++) {
-		if (auth_mech_desc[i].name != NULL &&
-		    strcasecmp(auth_mech_desc[i].name, name) == 0)
-			return &auth_mech_desc[i];
-	}
-
-	return NULL;
-}
-
-static void client_auth_abort(struct client *client, const char *msg)
-{
-	if (client->auth_request != NULL) {
-		auth_abort_request(client->auth_request);
-		client->auth_request = NULL;
-	}
-
-	client_send_tagline(client, msg != NULL ? msg :
-			    "NO Authentication failed.");
-	o_stream_flush(client->output);
-
-	/* get back to normal client input */
-	if (client->io != NULL)
-		io_remove(client->io);
-	client->io = client->fd == -1 ? NULL :
-		io_add(client->fd, IO_READ, client_input, client);
-
-	client_unref(client);
-}
-
-static void master_callback(struct client *client, int success)
-{
-	const char *reason = NULL;
-
-	if (success)
-		reason = t_strconcat("Login: ", client->virtual_user, NULL);
-	else {
-		reason = t_strconcat("Internal login failure: ",
-				     client->virtual_user, NULL);
-		client_send_line(client, "* BYE Internal login failure.");
-	}
-
-	client_destroy(client, reason);
-	client_unref(client);
-}
-
-static void client_send_auth_data(struct client *client,
-				  const unsigned char *data, size_t size)
-{
-	buffer_t *buf;
-
-	t_push();
-
-	buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1);
-	buffer_append(buf, "+ ", 2);
-	base64_encode(data, size, buf);
-	buffer_append(buf, "\r\n", 2);
-
-	o_stream_send(client->output, buffer_get_data(buf, NULL),
-		      buffer_get_used_size(buf));
-	o_stream_flush(client->output);
-
-	t_pop();
-}
-
-static const char *auth_login_get_str(struct auth_login_reply *reply,
-				      const unsigned char *data, size_t idx)
-{
-	size_t stop;
-
-	if (idx >= reply->data_size || idx >= reply->reply_idx)
-		return NULL;
-
-	stop = reply->reply_idx < reply->data_size ?
-		reply->reply_idx-1 : reply->data_size;
-
-	return t_strndup(data, stop);
-}
-
-static int auth_callback(struct auth_request *request,
-			 struct auth_login_reply *reply,
-			 const unsigned char *data, void *context)
-{
-	struct client *client = context;
-	const char *user, *realm;
-
-	if (reply == NULL) {
-		/* failed */
-		client->auth_request = NULL;
-		client_auth_abort(client, "NO Authentication process died.");
-		return FALSE;
-	}
-
-	switch (reply->result) {
-	case AUTH_LOGIN_RESULT_CONTINUE:
-		client->auth_request = request;
-		return TRUE;
-
-	case AUTH_LOGIN_RESULT_SUCCESS:
-		client->auth_request = NULL;
-
-		user = auth_login_get_str(reply, data, reply->username_idx);
-		realm = auth_login_get_str(reply, data, reply->realm_idx);
-
-		i_free(client->virtual_user);
-		client->virtual_user = realm == NULL ?
-			i_strdup(user) : i_strconcat(user, "@", realm, NULL);
-
-		/* we should be able to log in. if we fail, just
-		   disconnect the client. */
-		client_send_tagline(client, "OK Logged in.");
-
-		master_request_imap(client, master_callback,
-				    request->conn->pid, request->id);
-
-		/* disable IO until we're back from master */
-		if (client->io != NULL) {
-			io_remove(client->io);
-			client->io = NULL;
-		}
-		return FALSE;
-
-	case AUTH_LOGIN_RESULT_FAILURE:
-		/* see if we have error message */
-		client->auth_request = NULL;
-
-		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
-			client_auth_abort(client, t_strconcat(
-				"NO Authentication failed: ",
-				(const char *) data, NULL));
-		} else {
-			/* default error message */
-			client_auth_abort(client, NULL);
-		}
-		return FALSE;
-	}
-
-	i_unreached();
-}
-
-static void login_callback(struct auth_request *request,
-			   struct auth_login_reply *reply,
-			   const unsigned char *data, struct client *client)
-{
-	const void *ptr;
-	size_t size;
-
-	if (auth_callback(request, reply, data, client)) {
-		ptr = buffer_get_data(client->plain_login, &size);
-		auth_continue_request(request, ptr, size);
-
-		buffer_set_used_size(client->plain_login, 0);
-	}
-}
-
-int cmd_login(struct client *client, struct imap_arg *args)
-{
-	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,
-				    "NO Plaintext authentication disabled.");
-		return TRUE;
-	}
-
-	/* authorization ID \0 authentication ID \0 pass */
-	buffer_set_used_size(client->plain_login, 0);
-	buffer_append_c(client->plain_login, '\0');
-	buffer_append(client->plain_login, user, strlen(user));
-	buffer_append_c(client->plain_login, '\0');
-	buffer_append(client->plain_login, pass, strlen(pass));
-
-	client_ref(client);
-	if (auth_init_request(AUTH_MECH_PLAIN, login_callback,
-			      client, &error)) {
-		/* don't read any input from client until login is finished */
-		if (client->io != NULL) {
-			io_remove(client->io);
-			client->io = NULL;
-		}
-		return TRUE;
-	} else {
-		client_send_tagline(client, t_strconcat(
-			"NO Login failed: ", error, NULL));
-		client_unref(client);
-		return TRUE;
-	}
-}
-
-static void authenticate_callback(struct auth_request *request,
-				  struct auth_login_reply *reply,
-				  const unsigned char *data,
-				  struct client *client)
-{
-	if (auth_callback(request, reply, data, client))
-		client_send_auth_data(client, data, reply->data_size);
-}
-
-static void client_auth_input(void *context)
-{
-	struct client *client = context;
-	buffer_t *buf;
-	char *line;
-	size_t linelen, bufsize;
-
-	if (!client_read(client))
-		return;
-
-	if (client->skip_line) {
-		if (i_stream_next_line(client->input) == NULL)
-			return;
-
-		client->skip_line = FALSE;
-	}
-
-	/* @UNSAFE */
-	line = i_stream_next_line(client->input);
-	if (line == NULL)
-		return;
-
-	if (strcmp(line, "*") == 0) {
-		client_auth_abort(client, "NO Authentication aborted");
-		return;
-	}
-
-	linelen = strlen(line);
-	buf = buffer_create_static_hard(data_stack_pool, linelen);
-
-	if (base64_decode((const unsigned char *) line, linelen,
-			  NULL, buf) <= 0) {
-		/* failed */
-		client_auth_abort(client, "NO Invalid base64 data");
-	} else if (client->auth_request == NULL) {
-		client_auth_abort(client, "NO Don't send unrequested data");
-	} else {
-		auth_continue_request(client->auth_request,
-				      buffer_get_data(buf, NULL),
-				      buffer_get_used_size(buf));
-	}
-
-	/* clear sensitive data */
-	safe_memset(line, 0, linelen);
-
-	bufsize = buffer_get_used_size(buf);
-	safe_memset(buffer_free_without_data(buf), 0, bufsize);
-}
-
-int cmd_authenticate(struct client *client, struct imap_arg *args)
-{
-	struct auth_mech_desc *mech;
-	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;
-
-	mech = auth_mech_find(mech_name);
-	if (mech == NULL) {
-		client_send_tagline(client,
-				    "NO Unsupported authentication mechanism.");
-		return TRUE;
-	}
-
-	if (!client->tls && mech->plaintext && disable_plaintext_auth) {
-		client_send_tagline(client,
-				    "NO Plaintext authentication disabled.");
-		return TRUE;
-	}
-
-	client_ref(client);
-	if (auth_init_request(mech->mech, authenticate_callback,
-			      client, &error)) {
-		/* following input data will go to authentication */
-		if (client->io != NULL)
-			io_remove(client->io);
-		client->io = io_add(client->fd, IO_READ,
-				    client_auth_input, client);
-	} else {
-		client_send_tagline(client, t_strconcat(
-			"NO Authentication failed: ", error, NULL));
-		client_unref(client);
-	}
-
-	return TRUE;
-}
--- a/src/login/client-authenticate.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#ifndef __CLIENT_AUTHENTICATE_H
-#define __CLIENT_AUTHENTICATE_H
-
-const char *client_authenticate_get_capabilities(void);
-
-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 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,457 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "buffer.h"
-#include "hash.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "ostream.h"
-#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"
-
-/* 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
-
-/* Disconnect client when it sends too many bad commands */
-#define CLIENT_MAX_BAD_COMMANDS 10
-
-/* When max. number of simultaneous connections is reached, few of the
-   oldest connections are disconnected. Since we have to go through the whole
-   client hash, it's faster if we disconnect multiple clients. */
-#define CLIENT_DESTROY_OLDEST_COUNT 16
-
-static struct hash_table *clients;
-static struct timeout *to_idle;
-
-static void client_set_title(struct client *client)
-{
-	const char *host;
-
-	if (!verbose_proctitle || !process_per_connection)
-		return;
-
-	host = net_ip2host(&client->ip);
-	if (host == NULL)
-		host = "??";
-
-	process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
-					  host));
-}
-
-static int cmd_capability(struct client *client)
-{
-	const char *capability;
-
-	capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
-				 ssl_initialized ? " STARTTLS" : "",
-				 disable_plaintext_auth && !client->tls ?
-				 " LOGINDISABLED" : "",
-				 client_authenticate_get_capabilities(),
-				 NULL);
-	client_send_line(client, capability);
-	client_send_tagline(client, "OK Capability completed.");
-	return TRUE;
-}
-
-static int cmd_starttls(struct client *client)
-{
-	int fd_ssl;
-
-	if (client->tls) {
-		client_send_tagline(client, "BAD TLS is already active.");
-		return TRUE;
-	}
-
-	if (!ssl_initialized) {
-		client_send_tagline(client, "BAD TLS support isn't enabled.");
-		return TRUE;
-	}
-
-	client_send_tagline(client, "OK Begin TLS negotiation now.");
-	o_stream_flush(client->output);
-
-	/* must be removed before ssl_proxy_new(), since it may
-	   io_add() the same fd. */
-	if (client->io != NULL) {
-		io_remove(client->io);
-		client->io = NULL;
-	}
-
-	fd_ssl = ssl_proxy_new(client->fd);
-	if (fd_ssl != -1) {
-		client->tls = TRUE;
-                client_set_title(client);
-
-		client->fd = fd_ssl;
-
-		i_stream_unref(client->input);
-		o_stream_unref(client->output);
-
-		client->input = i_stream_create_file(fd_ssl, default_pool,
-						     8192, FALSE);
-		client->output = o_stream_create_file(fd_ssl, default_pool,
-						      1024, IO_PRIORITY_DEFAULT,
-						      FALSE);
-	} else {
-		client_send_line(client, " * BYE TLS handehake failed.");
-		client_destroy(client, "TLS handshake failed");
-	}
-
-	client->io = io_add(client->fd, IO_READ, client_input, client);
-	return TRUE;
-}
-
-static int cmd_noop(struct client *client)
-{
-	client_send_tagline(client, "OK NOOP completed.");
-	return TRUE;
-}
-
-static int cmd_logout(struct client *client)
-{
-	client_send_line(client, "* BYE Logging out");
-	client_send_tagline(client, "OK Logout completed.");
-	client_destroy(client, "Aborted login");
-	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) {
-			if (!client_skip_line(client))
-				return;
-                        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; /* 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;
-	}
-	client->skip_line = TRUE;
-
-	if (*client->cmd_tag == '\0' ||
-	    !client_command_execute(client, client->cmd_name, args)) {
-		if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
-			client_send_line(client,
-				"* BYE Too many invalid IMAP commands.");
-			client_destroy(client, "Disconnected: "
-				       "Too many invalid commands");
-			return;
-		} 
-		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)) {
-	case -2:
-		/* buffer full */
-		client_send_line(client, "* BYE Input buffer full, aborting");
-		client_destroy(client, "Disconnected: Input buffer full");
-		return FALSE;
-	case -1:
-		/* disconnected */
-		client_destroy(client, "Disconnected");
-		return FALSE;
-	default:
-		/* something was read */
-		return TRUE;
-	}
-}
-
-void client_input(void *context)
-{
-	struct client *client = context;
-
-	client->last_input = ioloop_time;
-
-	if (!client_read(client))
-		return;
-
-	client_ref(client);
-
-	o_stream_cork(client->output);
-	client_handle_input(client);
-
-	if (client_unref(client))
-		o_stream_flush(client->output);
-}
-
-static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
-				       void *context)
-{
-	struct client *client = key;
-	struct client *const *destroy_clients;
-	buffer_t *destroy_buf = context;
-	size_t i, count;
-
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct client *);
-
-	for (i = 0; i < count; i++) {
-		if (destroy_clients[i]->created > client->created) {
-			buffer_insert(destroy_buf, i * sizeof(struct client *),
-				      &client, sizeof(struct client *));
-			break;
-		}
-	}
-}
-
-static void client_destroy_oldest(void)
-{
-	struct client *const *destroy_clients;
-	buffer_t *destroy_buf;
-	size_t i, count;
-
-	/* find the oldest clients and put them to destroy-buffer */
-	destroy_buf = buffer_create_static_hard(data_stack_pool,
-						sizeof(struct client *) *
-						CLIENT_DESTROY_OLDEST_COUNT);
-	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
-
-	/* then kill them */
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct client *);
-
-	for (i = 0; i < count; i++) {
-		client_destroy(destroy_clients[i],
-			       "Disconnected: Connection queue full");
-	}
-}
-
-struct client *client_create(int fd, struct ip_addr *ip, int imaps)
-{
-	struct client *client;
-
-	if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
-	    hash_size(clients) >= max_logging_users) {
-		/* reached max. users count, kill few of the
-		   oldest connections */
-		client_destroy_oldest();
-	}
-
-	/* always use nonblocking I/O */
-	net_set_nonblock(fd, TRUE);
-
-	client = i_new(struct client, 1);
-	client->created = ioloop_time;
-	client->refcount = 1;
-	client->tls = imaps;
-
-	memcpy(&client->ip, ip, sizeof(struct ip_addr));
-	client->fd = fd;
-	client->io = io_add(fd, IO_READ, client_input, client);
-	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);
-
-	main_ref();
-
-	client_send_line(client, "* OK " PACKAGE " ready.");
-	client_set_title(client);
-	return client;
-}
-
-void client_destroy(struct client *client, const char *reason)
-{
-	if (reason != NULL)
-		client_syslog(client, reason);
-
-	hash_remove(clients, client);
-
-	imap_parser_destroy(client->parser);
-	i_stream_close(client->input);
-	o_stream_close(client->output);
-
-	if (client->io != NULL) {
-		io_remove(client->io);
-		client->io = NULL;
-	}
-
-	net_disconnect(client->fd);
-	client->fd = -1;
-
-	i_free(client->virtual_user);
-	client_unref(client);
-}
-
-void client_ref(struct client *client)
-{
-	client->refcount++;
-}
-
-int client_unref(struct client *client)
-{
-	if (--client->refcount > 0)
-		return TRUE;
-
-	i_stream_unref(client->input);
-	o_stream_unref(client->output);
-
-	buffer_free(client->plain_login);
-	i_free(client);
-
-	main_unref();
-	return FALSE;
-}
-
-void client_send_line(struct client *client, const char *line)
-{
-	o_stream_send_str(client->output, line);
-	o_stream_send(client->output, "\r\n", 2);
-}
-
-void client_send_tagline(struct client *client, const char *line)
-{
-	client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
-}
-
-void client_syslog(struct client *client, const char *text)
-{
-	const char *host;
-
-	host = net_ip2host(&client->ip);
-	if (host == NULL)
-		host = "??";
-
-	i_info("%s [%s]", text, host);
-}
-
-static void client_hash_check_idle(void *key, void *value __attr_unused__,
-				   void *context __attr_unused__)
-{
-	struct client *client = key;
-
-	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
-		client_send_line(client, "* BYE Disconnected for inactivity.");
-		client_destroy(client, "Disconnected: Inactivity");
-	}
-}
-
-static void idle_timeout(void *context __attr_unused__)
-{
-	hash_foreach(clients, client_hash_check_idle, NULL);
-}
-
-unsigned int clients_get_count(void)
-{
-	return hash_size(clients);
-}
-
-static void client_hash_destroy(void *key, void *value __attr_unused__,
-				void *context __attr_unused__)
-{
-	client_destroy(key, NULL);
-}
-
-void clients_destroy_all(void)
-{
-	hash_foreach(clients, client_hash_destroy, NULL);
-}
-
-void clients_init(void)
-{
-	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
-	to_idle = timeout_add(1000, idle_timeout, NULL);
-}
-
-void clients_deinit(void)
-{
-	clients_destroy_all();
-	hash_destroy(clients);
-
-	timeout_remove(to_idle);
-}
--- a/src/login/client.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#ifndef __CLIENT_H
-#define __CLIENT_H
-
-#include "network.h"
-#include "master.h"
-
-struct client {
-	time_t created;
-	int refcount;
-	struct ip_addr ip;
-
-	int fd;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-	struct imap_parser *parser;
-
-	time_t last_input;
-	unsigned int bad_counter;
-
-	const char *cmd_tag, *cmd_name;
-
-	buffer_t *plain_login;
-	struct auth_request *auth_request;
-	char *virtual_user;
-
-	master_callback_t *master_callback;
-
-	unsigned int tls:1;
-	unsigned int cmd_finished:1;
-	unsigned int skip_line:1;
-};
-
-struct client *client_create(int fd, struct ip_addr *ip, int imaps);
-void client_destroy(struct client *client, const char *reason);
-
-void client_ref(struct client *client);
-int client_unref(struct client *client);
-
-void client_send_line(struct client *client, const char *line);
-void client_send_tagline(struct client *client, const char *line);
-void client_syslog(struct client *client, const char *text);
-
-int client_read(struct client *client);
-void client_input(void *context);
-
-unsigned int clients_get_count(void);
-void clients_destroy_all(void);
-
-void clients_init(void);
-void clients_deinit(void);
-
-#endif
--- a/src/login/common.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#ifndef __COMMON_H
-#define __COMMON_H
-
-#include "lib.h"
-#include "../auth/auth-login-interface.h"
-
-extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
-extern unsigned int max_logging_users;
-extern unsigned int login_process_uid;
-
-void main_ref(void);
-void main_unref(void);
-
-void main_close_listen(void);
-
-#endif
--- a/src/login/main.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,235 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "lib-signals.h"
-#include "restrict-access.h"
-#include "process-title.h"
-#include "fd-close-on-exec.h"
-#include "auth-connection.h"
-#include "master.h"
-#include "client.h"
-#include "ssl-proxy.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <syslog.h>
-
-int disable_plaintext_auth, process_per_connection, verbose_proctitle;
-unsigned int max_logging_users;
-unsigned int login_process_uid;
-
-static struct ioloop *ioloop;
-static struct io *io_imap, *io_imaps;
-static int main_refcount;
-static int closing_down;
-
-void main_ref(void)
-{
-	main_refcount++;
-}
-
-void main_unref(void)
-{
-	if (--main_refcount == 0) {
-		/* nothing to do, quit */
-		io_loop_stop(ioloop);
-	} else if (closing_down && clients_get_count() == 0) {
-		/* last login finished, close all communications
-		   to master process */
-		master_close();
-	}
-}
-
-void main_close_listen(void)
-{
-	if (closing_down)
-		return;
-
-	if (io_imap != NULL) {
-		if (close(LOGIN_IMAP_LISTEN_FD) < 0)
-			i_fatal("can't close() IMAP listen handle");
-
-		io_remove(io_imap);
-		io_imap = NULL;
-	}
-
-	if (io_imaps != NULL) {
-		if (close(LOGIN_IMAPS_LISTEN_FD) < 0)
-			i_fatal("can't close() IMAPS listen handle");
-
-		io_remove(io_imaps);
-		io_imaps = NULL;
-	}
-
-	closing_down = TRUE;
-	master_notify_finished();
-}
-
-static void sig_quit(int signo __attr_unused__)
-{
-	io_loop_stop(ioloop);
-}
-
-static void login_accept(void *context __attr_unused__)
-{
-	struct ip_addr ip;
-	int fd;
-
-	fd = net_accept(LOGIN_IMAP_LISTEN_FD, &ip, NULL);
-	if (fd < 0) {
-		if (fd < -1)
-			i_fatal("accept() failed: %m");
-		return;
-	}
-
-	if (process_per_connection)
-		main_close_listen();
-
-	(void)client_create(fd, &ip, FALSE);
-}
-
-static void login_accept_ssl(void *context __attr_unused__)
-{
-	struct client *client;
-	struct ip_addr addr;
-	int fd, fd_ssl;
-
-	fd = net_accept(LOGIN_IMAPS_LISTEN_FD, &addr, NULL);
-	if (fd < 0) {
-		if (fd < -1)
-			i_fatal("accept() failed: %m");
-		return;
-	}
-
-	if (process_per_connection)
-		main_close_listen();
-
-	fd_ssl = ssl_proxy_new(fd);
-	if (fd_ssl == -1)
-		net_disconnect(fd);
-	else {
-		client = client_create(fd_ssl, &addr, TRUE);
-		client->tls = TRUE;
-	}
-}
-
-static void open_logfile(void)
-{
-	if (getenv("IMAP_USE_SYSLOG") != NULL)
-		i_set_failure_syslog("imap-login", LOG_NDELAY, LOG_MAIL);
-	else {
-		/* log to file or stderr */
-		i_set_failure_file(getenv("IMAP_LOGFILE"), "imap-login");
-	}
-
-	if (getenv("IMAP_INFOLOGFILE") != NULL)
-		i_set_info_file(getenv("IMAP_INFOLOGFILE"));
-
-	i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
-}
-
-static void drop_privileges(void)
-{
-	/* Log file or syslog opening probably requires roots */
-	open_logfile();
-
-	/* Initialize SSL proxy so it can read certificate and private
-	   key file. */
-	ssl_proxy_init();
-
-	/* Refuse to run as root - we should never need it and it's
-	   dangerous with SSL. */
-	restrict_access_by_env(TRUE);
-}
-
-static void main_init(void)
-{
-	const char *value;
-
-	lib_init_signals(sig_quit);
-
-	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
-	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
-        verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;
-
-	value = getenv("MAX_LOGGING_USERS");
-	max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10);
-
-	value = getenv("PROCESS_UID");
-	if (value == NULL)
-		i_fatal("BUG: PROCESS_UID environment not given");
-        login_process_uid = strtoul(value, NULL, 10);
-	if (login_process_uid == 0)
-		i_fatal("BUG: PROCESS_UID environment is 0");
-
-        closing_down = FALSE;
-	main_refcount = 0;
-
-	auth_connection_init();
-	clients_init();
-
-	io_imap = io_imaps = NULL;
-
-	if (net_getsockname(LOGIN_IMAP_LISTEN_FD, NULL, NULL) == 0) {
-		/* we're listening for imap */
-		io_imap = io_add(LOGIN_IMAP_LISTEN_FD, IO_READ,
-				 login_accept, NULL);
-	}
-
-	if (net_getsockname(LOGIN_IMAPS_LISTEN_FD, NULL, NULL) == 0) {
-		/* we're listening for imaps */
-		if (!ssl_initialized) {
-			/* this shouldn't happen, master should have
-			   disabled the imaps socket.. */
-			i_fatal("BUG: SSL initialization parameters not given "
-				"while they should have been");
-		}
-
-		io_imaps = io_add(LOGIN_IMAPS_LISTEN_FD, IO_READ,
-				  login_accept_ssl, NULL);
-	}
-
-	/* initialize master last - it sends the "we're ok" notification */
-	master_init();
-}
-
-static void main_deinit(void)
-{
-        if (lib_signal_kill != 0)
-		i_warning("Killed with signal %d", lib_signal_kill);
-
-	if (io_imap != NULL) io_remove(io_imap);
-	if (io_imaps != NULL) io_remove(io_imaps);
-
-	clients_deinit();
-	master_deinit();
-	auth_connection_deinit();
-
-	ssl_proxy_deinit();
-
-	closelog();
-}
-
-int main(int argc __attr_unused__, char *argv[], char *envp[])
-{
-#ifdef DEBUG
-        fd_debug_verify_leaks(3, 1024);
-#endif
-	/* NOTE: we start rooted, so keep the code minimal until
-	   restrict_access_by_env() is called */
-	lib_init();
-	drop_privileges();
-
-	process_title_init(argv, envp);
-	ioloop = io_loop_create(system_pool);
-
-	main_init();
-        io_loop_run(ioloop);
-	main_deinit();
-
-	io_loop_destroy(ioloop);
-	lib_deinit();
-
-        return 0;
-}
--- a/src/login/master.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "hash.h"
-#include "ioloop.h"
-#include "network.h"
-#include "fdpass.h"
-#include "master.h"
-#include "client.h"
-
-#include <unistd.h>
-
-static struct io *io_master;
-static struct hash_table *master_requests;
-
-static unsigned int master_pos;
-static char master_buf[sizeof(struct master_login_reply)];
-
-static void request_handle(struct master_login_reply *reply)
-{
-	struct client *client;
-
-	client = hash_lookup(master_requests, POINTER_CAST(reply->tag));
-	if (client == NULL)
-		i_fatal("Master sent reply with unknown tag %u", reply->tag);
-
-	client->master_callback(client, reply->success);
-
-	hash_remove(master_requests, POINTER_CAST(reply->tag));
-}
-
-void master_request_imap(struct client *client, master_callback_t *callback,
-			 unsigned int auth_pid, unsigned int auth_id)
-{
-	struct master_login_request req;
-
-	memset(&req, 0, sizeof(req));
-	req.tag = client->fd;
-	req.auth_pid = auth_pid;
-	req.auth_id = auth_id;
-	req.ip = client->ip;
-
-	if (fd_send(LOGIN_MASTER_SOCKET_FD,
-		    client->fd, &req, sizeof(req)) != sizeof(req))
-		i_fatal("fd_send() failed: %m");
-
-	client->master_callback = callback;
-	hash_insert(master_requests, POINTER_CAST(req.tag), client);
-}
-
-void master_notify_finished(void)
-{
-	struct master_login_request req;
-
-	if (io_master == NULL)
-		return;
-
-	memset(&req, 0, sizeof(req));
-
-	/* sending -1 as fd does the notification */
-	if (fd_send(LOGIN_MASTER_SOCKET_FD,
-		    -1, &req, sizeof(req)) != sizeof(req))
-		i_fatal("fd_send() failed: %m");
-}
-
-void master_close(void)
-{
-	if (io_master == NULL)
-		return;
-
-	clients_destroy_all();
-
-	if (close(LOGIN_MASTER_SOCKET_FD) < 0)
-		i_fatal("close(master) failed: %m");
-
-	io_remove(io_master);
-	io_master = NULL;
-
-        main_close_listen();
-	main_unref();
-}
-
-static void master_input(void *context __attr_unused__)
-{
-	int ret;
-
-	ret = net_receive(LOGIN_MASTER_SOCKET_FD, master_buf + master_pos,
-			  sizeof(master_buf) - master_pos);
-	if (ret < 0) {
-		/* master died, kill all clients logging in */
-		master_close();
-		return;
-	}
-
-	master_pos += ret;
-	if (master_pos < sizeof(master_buf))
-		return;
-
-	/* reply is now read */
-	request_handle((struct master_login_reply *) master_buf);
-	master_pos = 0;
-}
-
-void master_init(void)
-{
-	main_ref();
-
-	master_requests = hash_create(default_pool, default_pool,
-				      0, NULL, NULL);
-
-        master_pos = 0;
-	io_master = io_add(LOGIN_MASTER_SOCKET_FD, IO_READ, master_input, NULL);
-
-	/* just a note to master that we're ok. if we die before,
-	   master should shutdown itself. */
-        master_notify_finished();
-}
-
-void master_deinit(void)
-{
-	hash_destroy(master_requests);
-
-	if (io_master != NULL)
-		io_remove(io_master);
-}
--- a/src/login/master.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#ifndef __MASTER_H
-#define __MASTER_H
-
-struct client;
-
-#include "../master/master-login-interface.h"
-
-typedef void master_callback_t(struct client *client, int success);
-
-void master_request_imap(struct client *client, master_callback_t *callback,
-			 unsigned int auth_pid, unsigned int auth_id);
-
-/* Notify master that we're not listening for new connections anymore. */
-void master_notify_finished(void);
-
-/* Close connection to master process */
-void master_close(void);
-
-void master_init(void);
-void master_deinit(void);
-
-#endif
--- a/src/login/ssl-proxy-gnutls.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,512 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "network.h"
-#include "ssl-proxy.h"
-
-#ifdef HAVE_GNUTLS
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <gcrypt.h>
-#include <gnutls/gnutls.h>
-
-struct ssl_proxy {
-	int refcount;
-
-	gnutls_session session;
-	int fd_ssl, fd_plain;
-	struct io *io_ssl, *io_plain;
-	int io_ssl_dir;
-
-	unsigned char outbuf_plain[1024];
-	unsigned int outbuf_pos_plain;
-
-	size_t send_left_ssl, send_left_plain;
-};
-
-const int protocol_priority[] =
-	{ GNUTLS_TLS1, GNUTLS_SSL3, 0 };
-const int kx_priority[] =
-	{ GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
-const int cipher_priority[] =
-	{ GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
-	  GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
-const int comp_priority[] =
-	{ GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
-const int mac_priority[] =
-	{ GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
-const int cert_type_priority[] =
-	{ GNUTLS_CRT_X509, 0 };
-
-static gnutls_certificate_credentials x509_cred;
-static gnutls_dh_params dh_params;
-static gnutls_rsa_params rsa_params;
-
-static void ssl_input(void *context);
-static void plain_input(void *context);
-static int ssl_proxy_destroy(struct ssl_proxy *proxy);
-
-static const char *get_alert_text(struct ssl_proxy *proxy)
-{
-	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
-}
-
-static int handle_ssl_error(struct ssl_proxy *proxy, int error)
-{
-	if (!gnutls_error_is_fatal(error)) {
-		if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
-			i_warning("Received SSL warning alert: %s",
-				  get_alert_text(proxy));
-		}
-		return 0;
-	}
-
-	/* fatal error occured */
-	if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
-		i_warning("Received SSL fatal alert: %s",
-			  get_alert_text(proxy));
-	} else {
-		i_warning("Error reading from SSL client: %s",
-			  gnutls_strerror(error));
-	}
-
-        gnutls_alert_send_appropriate(proxy->session, error);
-	ssl_proxy_destroy(proxy);
-	return -1;
-}
-
-static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size)
-{
-	int rcvd;
-
-	rcvd = gnutls_record_recv(proxy->session, data, size);
-	if (rcvd > 0)
-		return rcvd;
-
-	if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
-		/* disconnected, either by nicely telling us that we'll
-		   close the connection, or by simply killing the
-		   connection which gives us the packet length error. */
-		ssl_proxy_destroy(proxy);
-		return -1;
-	}
-
-	return handle_ssl_error(proxy, rcvd);
-}
-
-static int proxy_send_ssl(struct ssl_proxy *proxy,
-			  const void *data, size_t size)
-{
-	int sent;
-
-	sent = gnutls_record_send(proxy->session, data, size);
-	if (sent >= 0)
-		return sent;
-
-	if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
-		/* don't warn about errors related to unexpected
-		   disconnection */
-		ssl_proxy_destroy(proxy);
-		return -1;
-	}
-
-	return handle_ssl_error(proxy, sent);
-}
-
-static int ssl_proxy_destroy(struct ssl_proxy *proxy)
-{
-	if (--proxy->refcount > 0)
-		return TRUE;
-
-	gnutls_deinit(proxy->session);
-
-	(void)net_disconnect(proxy->fd_ssl);
-	(void)net_disconnect(proxy->fd_plain);
-
-	if (proxy->io_ssl != NULL)
-		io_remove(proxy->io_ssl);
-	if (proxy->io_plain != NULL)
-		io_remove(proxy->io_plain);
-
-	i_free(proxy);
-
-	main_unref();
-	return FALSE;
-}
-
-static void ssl_output(void *context)
-{
-        struct ssl_proxy *proxy = context;
-	int sent;
-
-	sent = net_transmit(proxy->fd_plain,
-			    proxy->outbuf_plain + proxy->outbuf_pos_plain,
-			    proxy->send_left_plain);
-	if (sent < 0) {
-		/* disconnected */
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	proxy->send_left_plain -= sent;
-	proxy->outbuf_pos_plain += sent;
-
-	if (proxy->send_left_plain > 0)
-		return;
-
-	/* everything is sent, start reading again */
-	io_remove(proxy->io_ssl);
-	proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy);
-}
-
-static void ssl_input(void *context)
-{
-        struct ssl_proxy *proxy = context;
-	int rcvd, sent;
-
-	rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain,
-			      sizeof(proxy->outbuf_plain));
-	if (rcvd <= 0)
-		return;
-
-	sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd);
-	if (sent == rcvd)
-		return;
-
-	if (sent < 0) {
-		/* disconnected */
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	/* everything wasn't sent - don't read anything until we've
-	   sent it all */
-        proxy->outbuf_pos_plain = 0;
-	proxy->send_left_plain = rcvd - sent;
-
-	io_remove(proxy->io_ssl);
-	proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy);
-}
-
-static void plain_output(void *context)
-{
-	struct ssl_proxy *proxy = context;
-	int sent;
-
-	sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl);
-	if (sent <= 0)
-		return;
-
-	proxy->send_left_ssl -= sent;
-	if (proxy->send_left_ssl > 0)
-		return;
-
-	/* everything is sent, start reading again */
-	io_remove(proxy->io_plain);
-	proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy);
-}
-
-static void plain_input(void *context)
-{
-	struct ssl_proxy *proxy = context;
-	char buf[1024];
-	ssize_t rcvd, sent;
-
-	rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf));
-	if (rcvd < 0) {
-		/* disconnected */
-		gnutls_bye(proxy->session, 1);
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	sent = proxy_send_ssl(proxy, buf, (size_t)rcvd);
-	if (sent < 0 || sent == rcvd)
-		return;
-
-	/* everything wasn't sent - don't read anything until we've
-	   sent it all */
-	proxy->send_left_ssl = rcvd - sent;
-
-	io_remove(proxy->io_plain);
-	proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
-}
-
-static void ssl_handshake(void *context)
-{
-	struct ssl_proxy *proxy = context;
-	int ret, dir;
-
-        ret = gnutls_handshake(proxy->session);
-	if (ret >= 0) {
-		/* handshake done, now we can start reading */
-		if (proxy->io_ssl != NULL)
-			io_remove(proxy->io_ssl);
-
-		proxy->io_plain = io_add(proxy->fd_plain, IO_READ,
-					 plain_input, proxy);
-		proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ,
-				       ssl_input, proxy);
-		return;
-	}
-
-	if (handle_ssl_error(proxy, ret) < 0)
-		return;
-
-	/* i/o interrupted */
-	dir = gnutls_handshake_get_direction(proxy->session) == 0 ?
-		IO_READ : IO_WRITE;
-	if (proxy->io_ssl_dir != dir) {
-		if (proxy->io_ssl != NULL)
-			io_remove(proxy->io_ssl);
-		proxy->io_ssl = io_add(proxy->fd_ssl, dir,
-				       ssl_handshake, proxy);
-		proxy->io_ssl_dir = dir;
-	}
-}
-
-static gnutls_session initialize_state(void)
-{
-	gnutls_session session;
-
-	gnutls_init(&session, GNUTLS_SERVER);
-
-	gnutls_protocol_set_priority(session, protocol_priority);
-	gnutls_cipher_set_priority(session, cipher_priority);
-	gnutls_compression_set_priority(session, comp_priority);
-	gnutls_kx_set_priority(session, kx_priority);
-	gnutls_mac_set_priority(session, mac_priority);
-	gnutls_cert_type_set_priority(session, cert_type_priority);
-
-	gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
-	return session;
-}
-
-int ssl_proxy_new(int fd)
-{
-        struct ssl_proxy *proxy;
-	gnutls_session session;
-	int sfd[2];
-
-	if (!ssl_initialized)
-		return -1;
-
-	session = initialize_state();
-	gnutls_transport_set_ptr(session, fd);
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
-		i_error("socketpair() failed: %m");
-		gnutls_deinit(session);
-		return -1;
-	}
-
-	net_set_nonblock(sfd[0], TRUE);
-	net_set_nonblock(sfd[1], TRUE);
-
-	proxy = i_new(struct ssl_proxy, 1);
-	proxy->refcount = 1;
-	proxy->session = session;
-	proxy->fd_ssl = fd;
-	proxy->fd_plain = sfd[0];
-
-	proxy->refcount++;
-	ssl_handshake(proxy);
-	if (!ssl_proxy_destroy(proxy))
-		return -1;
-
-        main_ref();
-	return sfd[1];
-}
-
-static void read_next_field(int fd, gnutls_datum *datum,
-			    const char *fname, const char *field_name)
-{
-        ssize_t ret;
-
-	/* get size */
-	ret = read(fd, &datum->size, sizeof(datum->size));
-	if (ret < 0)
-		i_fatal("read() failed for %s: %m", fname);
-
-	if (ret != sizeof(datum->size)) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: File too small",
-			fname);
-	}
-
-	if (datum->size > 10240) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: "
-			"Field '%s' too large (%u)",
-			fname, field_name, datum->size);
-	}
-
-	/* read the actual data */
-	datum->data = t_malloc(datum->size);
-	ret = read(fd, datum->data, datum->size);
-	if (ret < 0)
-		i_fatal("read() failed for %s: %m", fname);
-
-	if ((size_t)ret != datum->size) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: "
-			"Field '%s' not fully in file (%u < %u)",
-			fname, field_name, datum->size - ret, datum->size);
-	}
-}
-
-static void read_dh_parameters(int fd, const char *fname)
-{
-	gnutls_datum dbits, prime, generator;
-	int ret, bits;
-
-	if ((ret = gnutls_dh_params_init(&dh_params)) < 0) {
-		i_fatal("gnutls_dh_params_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* read until bits field is 0 */
-	for (;;) {
-		read_next_field(fd, &dbits, fname, "DH bits");
-
-		if (dbits.size != sizeof(int)) {
-			(void)unlink(fname);
-			i_fatal("Corrupted SSL parameter file %s: "
-				"Field 'DH bits' has invalid size %u",
-				fname, dbits.size);
-		}
-
-		bits = *((int *) dbits.data);
-		if (bits == 0)
-			break;
-
-		read_next_field(fd, &prime, fname, "DH prime");
-		read_next_field(fd, &generator, fname, "DH generator");
-
-		ret = gnutls_dh_params_set(dh_params, prime, generator, bits);
-		if (ret < 0) {
-			i_fatal("gnutls_dh_params_set() failed: %s",
-				gnutls_strerror(ret));
-		}
-	}
-}
-
-static void read_rsa_parameters(int fd, const char *fname)
-{
-	gnutls_datum m, e, d, p, q, u;
-	int ret;
-
-	read_next_field(fd, &m, fname, "RSA m");
-	read_next_field(fd, &e, fname, "RSA e");
-	read_next_field(fd, &d, fname, "RSA d");
-	read_next_field(fd, &p, fname, "RSA p");
-	read_next_field(fd, &q, fname, "RSA q");
-	read_next_field(fd, &u, fname, "RSA u");
-
-	if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) {
-		i_fatal("gnutls_rsa_params_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* only 512bit is allowed */
-	ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512);
-	if (ret < 0) {
-		i_fatal("gnutls_rsa_params_set() failed: %s",
-			gnutls_strerror(ret));
-	}
-}
-
-static void read_parameters(const char *fname)
-{
-	int fd;
-
-	/* we'll wait until parameter file exists */
-	for (;;) {
-		fd = open(fname, O_RDONLY);
-		if (fd != -1)
-			break;
-
-		if (errno != ENOENT)
-			i_fatal("Can't open SSL parameter file %s: %m", fname);
-
-		sleep(1);
-	}
-
-	read_dh_parameters(fd, fname);
-	read_rsa_parameters(fd, fname);
-
-	(void)close(fd);
-}
-
-static void gcrypt_log_handler(void *context __attr_unused__, int level,
-			       const char *fmt, va_list args)
-{
-	if (level == GCRY_LOG_FATAL) {
-		t_push();
-		i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args));
-		t_pop();
-	}
-}
-
-void ssl_proxy_init(void)
-{
-	const char *certfile, *keyfile, *paramfile;
-	unsigned char buf[4];
-	int ret;
-
-	certfile = getenv("SSL_CERT_FILE");
-	keyfile = getenv("SSL_KEY_FILE");
-	paramfile = getenv("SSL_PARAM_FILE");
-
-	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
-		/* SSL support is disabled */
-		return;
-	}
-
-	if ((ret = gnutls_global_init() < 0)) {
-		i_fatal("gnu_tls_global_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* gcrypt initialization - set log handler and make sure randomizer
-	   opens /dev/urandom now instead of after we've chrooted */
-	gcry_set_log_handler(gcrypt_log_handler, NULL);
-	gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM);
-
-	read_parameters(paramfile);
-
-	if ((ret = gnutls_certificate_allocate_cred(&x509_cred)) < 0) {
-		i_fatal("gnutls_certificate_allocate_cred() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile,
-						   GNUTLS_X509_FMT_PEM);
-	if (ret < 0) {
-		i_fatal("Can't load certificate files %s and %s: %s",
-			certfile, keyfile, gnutls_strerror(ret));
-	}
-
-        ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
-	if (ret < 0)
-		i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
-	ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
-	if (ret < 0)
-		i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
-
-	ssl_initialized = TRUE;
-}
-
-void ssl_proxy_deinit(void)
-{
-	if (ssl_initialized) {
-		gnutls_certificate_free_cred(x509_cred);
-		gnutls_global_deinit();
-	}
-}
-
-#endif
--- a/src/login/ssl-proxy-openssl.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,386 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "network.h"
-#include "ssl-proxy.h"
-
-#ifdef HAVE_OPENSSL
-
-#include <openssl/crypto.h>
-#include <openssl/x509.h>
-#include <openssl/pem.h>
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-
-enum ssl_state {
-	SSL_STATE_HANDSHAKE,
-	SSL_STATE_READ,
-	SSL_STATE_WRITE
-};
-
-struct ssl_proxy {
-	int refcount;
-
-	SSL *ssl;
-        enum ssl_state state;
-
-	int fd_ssl, fd_plain;
-	struct io *io_ssl, *io_plain_read, *io_plain_write;
-	int io_ssl_dir;
-
-	unsigned char plainout_buf[1024];
-	unsigned int plainout_pos, plainout_size;
-
-	unsigned char sslout_buf[1024];
-	unsigned int sslout_pos, sslout_size;
-};
-
-static SSL_CTX *ssl_ctx;
-
-static void plain_read(struct ssl_proxy *proxy);
-static void plain_write(struct ssl_proxy *proxy);
-
-static int ssl_proxy_destroy(struct ssl_proxy *proxy);
-static void ssl_set_direction(struct ssl_proxy *proxy, int dir);
-
-static void plain_block_input(struct ssl_proxy *proxy, int block)
-{
-	if (block) {
-		if (proxy->io_plain_read != NULL) {
-			io_remove(proxy->io_plain_read);
-			proxy->io_plain_read = NULL;
-		}
-	} else {
-		if (proxy->io_plain_read == NULL) {
-			proxy->io_plain_read =
-				io_add(proxy->fd_plain, IO_READ,
-				       (io_callback_t *)plain_read, proxy);
-		}
-	}
-}
-
-static void ssl_block(struct ssl_proxy *proxy, int block)
-{
-	i_assert(proxy->state == SSL_STATE_READ);
-
-	if (block) {
-		if (proxy->io_ssl != NULL) {
-			io_remove(proxy->io_ssl);
-			proxy->io_ssl = NULL;
-		}
-
-		proxy->io_ssl_dir = -2;
-	} else {
-		proxy->io_ssl_dir = -1;
-		ssl_set_direction(proxy, IO_READ);
-	}
-}
-
-static void plain_read(struct ssl_proxy *proxy)
-{
-	ssize_t ret;
-
-	i_assert(proxy->sslout_size == 0);
-
-	ret = net_receive(proxy->fd_plain, proxy->sslout_buf,
-			  sizeof(proxy->sslout_buf));
-	if (ret < 0)
-		ssl_proxy_destroy(proxy);
-	else if (ret > 0) {
-		proxy->sslout_size = ret;
-		proxy->sslout_pos = 0;
-
-		proxy->state = SSL_STATE_WRITE;
-		ssl_set_direction(proxy, IO_WRITE);
-
-		plain_block_input(proxy, TRUE);
-	}
-}
-
-static void plain_write(struct ssl_proxy *proxy)
-{
-	ssize_t ret;
-
-	ret = net_transmit(proxy->fd_plain,
-			   proxy->plainout_buf + proxy->plainout_pos,
-			   proxy->plainout_size);
-	if (ret < 0)
-		ssl_proxy_destroy(proxy);
-	else {
-		proxy->plainout_size -= ret;
-		proxy->plainout_pos += ret;
-
-		if (proxy->plainout_size > 0) {
-			ssl_block(proxy, TRUE);
-			if (proxy->io_plain_write == NULL) {
-				proxy->io_plain_write =
-					io_add(proxy->fd_plain, IO_WRITE,
-					       (io_callback_t *)plain_write,
-					       proxy);
-			}
-		} else {
-			proxy->plainout_pos = 0;
-			ssl_block(proxy, FALSE);
-
-			if (proxy->io_plain_write != NULL) {
-				io_remove(proxy->io_plain_write);
-                                proxy->io_plain_write = NULL;
-			}
-		}
-	}
-
-}
-
-static const char *ssl_last_error(void)
-{
-	unsigned long err;
-	char *buf;
-	size_t err_size = 256;
-
-	err = ERR_get_error();
-	if (err == 0)
-		return strerror(errno);
-
-	buf = t_malloc(err_size);
-	buf[err_size-1] = '\0';
-	ERR_error_string_n(err, buf, err_size-1);
-	return buf;
-}
-
-static void ssl_handle_error(struct ssl_proxy *proxy, int err, const char *func)
-{
-	err = SSL_get_error(proxy->ssl, err);
-
-	switch (err) {
-	case SSL_ERROR_WANT_READ:
-		ssl_set_direction(proxy, IO_READ);
-		break;
-	case SSL_ERROR_WANT_WRITE:
-		ssl_set_direction(proxy, IO_WRITE);
-		break;
-	case SSL_ERROR_SYSCALL:
-		/* eat up the error queue */
-		/*i_warning("%s failed: %s", func, ssl_last_error());*/
-		ssl_proxy_destroy(proxy);
-		break;
-	case SSL_ERROR_ZERO_RETURN:
-		/* clean connection closing */
-		ssl_proxy_destroy(proxy);
-		break;
-	case SSL_ERROR_SSL:
-		/*i_warning("%s failed: %s", func, ssl_last_error());*/
-		ssl_proxy_destroy(proxy);
-		break;
-	default:
-		i_warning("%s failed: unknown failure %d (%s)",
-			  func, err, ssl_last_error());
-		ssl_proxy_destroy(proxy);
-		break;
-	}
-}
-
-static void ssl_handshake_step(struct ssl_proxy *proxy)
-{
-	int ret;
-
-	ret = SSL_accept(proxy->ssl);
-	if (ret != 1) {
-		plain_block_input(proxy, TRUE);
-		ssl_handle_error(proxy, ret, "SSL_accept()");
-	} else {
-		plain_block_input(proxy, FALSE);
-		ssl_set_direction(proxy, IO_READ);
-		proxy->state = SSL_STATE_READ;
-	}
-}
-
-static void ssl_read_step(struct ssl_proxy *proxy)
-{
-	int ret;
-
-	i_assert(proxy->plainout_size == 0);
-
-	ret = SSL_read(proxy->ssl, proxy->plainout_buf,
-		       sizeof(proxy->plainout_buf));
-	if (ret <= 0) {
-		plain_block_input(proxy, TRUE);
-		ssl_handle_error(proxy, ret, "SSL_read()");
-	} else {
-		plain_block_input(proxy, FALSE);
-		ssl_set_direction(proxy, IO_READ);
-
-		proxy->plainout_pos = 0;
-		proxy->plainout_size = ret;
-		plain_write(proxy);
-	}
-}
-
-static void ssl_write_step(struct ssl_proxy *proxy)
-{
-	int ret;
-
-	ret = SSL_write(proxy->ssl, proxy->sslout_buf + proxy->sslout_pos,
-			proxy->sslout_size);
-	if (ret <= 0) {
-		plain_block_input(proxy, TRUE);
-		ssl_handle_error(proxy, ret, "SSL_write()");
-	} else {
-		proxy->sslout_size -= ret;
-		proxy->sslout_pos += ret;
-
-		if (proxy->sslout_size > 0) {
-			plain_block_input(proxy, TRUE);
-			ssl_set_direction(proxy, IO_WRITE);
-			proxy->state = SSL_STATE_WRITE;
-		} else {
-			plain_block_input(proxy, FALSE);
-			ssl_set_direction(proxy, IO_READ);
-			proxy->state = SSL_STATE_READ;
-			proxy->sslout_pos = 0;
-		}
-	}
-}
-
-static void ssl_step(void *context)
-{
-        struct ssl_proxy *proxy = context;
-
-	switch (proxy->state) {
-	case SSL_STATE_HANDSHAKE:
-		ssl_handshake_step(proxy);
-		break;
-	case SSL_STATE_READ:
-		ssl_read_step(proxy);
-		break;
-	case SSL_STATE_WRITE:
-		ssl_write_step(proxy);
-		break;
-	}
-}
-
-static void ssl_set_direction(struct ssl_proxy *proxy, int dir)
-{
-	i_assert(proxy->io_ssl_dir != -2);
-
-	if (proxy->io_ssl_dir == dir)
-		return;
-
-	if (proxy->io_ssl != NULL)
-		io_remove(proxy->io_ssl);
-	proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_step, proxy);
-}
-
-int ssl_proxy_new(int fd)
-{
-	struct ssl_proxy *proxy;
-	SSL *ssl;
-	int sfd[2];
-
-	if (!ssl_initialized)
-		return -1;
-
-	ssl = SSL_new(ssl_ctx);
-	if (ssl == NULL) {
-		i_error("SSL_new() failed: %s", ssl_last_error());
-		return -1;
-	}
-
-	SSL_set_accept_state(ssl);
-	if (SSL_set_fd(ssl, fd) != 1) {
-		i_error("SSL_set_fd() failed: %s", ssl_last_error());
-		return -1;
-	}
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
-		i_error("socketpair() failed: %m");
-		SSL_free(ssl);
-		return -1;
-	}
-
-	net_set_nonblock(sfd[0], TRUE);
-	net_set_nonblock(sfd[1], TRUE);
-
-	proxy = i_new(struct ssl_proxy, 1);
-	proxy->refcount = 1;
-	proxy->ssl = ssl;
-	proxy->fd_ssl = fd;
-	proxy->fd_plain = sfd[0];
-
-	proxy->state = SSL_STATE_HANDSHAKE;
-	ssl_set_direction(proxy, IO_READ);
-
-	proxy->refcount++;
-	ssl_handshake_step(proxy);
-	if (!ssl_proxy_destroy(proxy))
-		return -1;
-
-        main_ref();
-	return sfd[1];
-}
-
-static int ssl_proxy_destroy(struct ssl_proxy *proxy)
-{
-	if (--proxy->refcount > 0)
-		return TRUE;
-
-	SSL_free(proxy->ssl);
-
-	(void)net_disconnect(proxy->fd_ssl);
-	(void)net_disconnect(proxy->fd_plain);
-
-	if (proxy->io_ssl != NULL)
-		io_remove(proxy->io_ssl);
-	if (proxy->io_plain_read != NULL)
-		io_remove(proxy->io_plain_read);
-	if (proxy->io_plain_write != NULL)
-		io_remove(proxy->io_plain_write);
-
-	i_free(proxy);
-
-	main_unref();
-	return FALSE;
-}
-
-void ssl_proxy_init(void)
-{
-	const char *certfile, *keyfile, *paramfile;
-	int ret;
-
-	certfile = getenv("SSL_CERT_FILE");
-	keyfile = getenv("SSL_KEY_FILE");
-	paramfile = getenv("SSL_PARAM_FILE");
-
-	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
-		/* SSL support is disabled */
-		return;
-	}
-
-	SSL_library_init();
-	SSL_load_error_strings();
-
-	if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
-		i_fatal("SSL_CTX_new() failed");
-
-        ret = SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);
-	if (ret != 1) {
-		i_fatal("Can't load certificate file %s: %s",
-			certfile, ssl_last_error());
-	}
-
-	ret = SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);
-	if (ret != 1) {
-		i_fatal("Can't load private key file %s: %s",
-			keyfile, ssl_last_error());
-	}
-
-	ssl_initialized = TRUE;
-}
-
-void ssl_proxy_deinit(void)
-{
-	if (ssl_initialized)
-                SSL_CTX_free(ssl_ctx);
-}
-
-#endif
--- a/src/login/ssl-proxy.c	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "ssl-proxy.h"
-
-int ssl_initialized = FALSE;
-
-#ifndef HAVE_SSL
-
-/* no SSL support */
-
-int ssl_proxy_new(int fd __attr_unused__) { return -1; }
-void ssl_proxy_init(void) {}
-void ssl_proxy_deinit(void) {}
-
-#endif
--- a/src/login/ssl-proxy.h	Tue Jan 28 21:32:18 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#ifndef __SSL_PROXY_H
-#define __SSL_PROXY_H
-
-extern int ssl_initialized;
-
-/* establish SSL connection with the given fd, returns a new fd which you
-   must use from now on, or -1 if error occured. Unless -1 is returned,
-   the given fd must be simply forgotten. */
-int ssl_proxy_new(int fd);
-
-void ssl_proxy_init(void);
-void ssl_proxy_deinit(void);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/.cvsignore	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,9 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+pop3-login
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/Makefile.am	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,20 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = pop3-login
+
+INCLUDES = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/login-common
+
+pop3_login_LDADD = \
+	../login-common/liblogin-common.a \
+	../lib/liblib.a \
+	$(SSL_LIBS)
+
+pop3_login_SOURCES = \
+	client.c \
+	client-authenticate.c
+
+noinst_HEADERS = \
+	common.h \
+	client.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/client-authenticate.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,311 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "istream.h"
+#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"
+#include "client-authenticate.h"
+#include "master.h"
+
+static struct auth_mech_desc *auth_mech_find(const char *name)
+{
+	int i;
+
+	for (i = 0; i < AUTH_MECH_COUNT; i++) {
+		if (auth_mech_desc[i].name != NULL &&
+		    strcasecmp(auth_mech_desc[i].name, name) == 0)
+			return &auth_mech_desc[i];
+	}
+
+	return NULL;
+}
+
+static void client_auth_abort(struct pop3_client *client, const char *msg)
+{
+	if (client->auth_request != NULL) {
+		auth_abort_request(client->auth_request);
+		client->auth_request = NULL;
+	}
+
+	client_send_line(client, msg != NULL ? msg :
+			 "-ERR Authentication failed.");
+	o_stream_flush(client->output);
+
+	/* get back to normal client input */
+	if (client->io != NULL)
+		io_remove(client->io);
+	client->io = client->common.fd == -1 ? NULL :
+		io_add(client->common.fd, IO_READ, client_input, client);
+
+	client_unref(client);
+}
+
+static void master_callback(struct client *_client, int success)
+{
+	struct pop3_client *client = (struct pop3_client *) _client;
+	const char *reason = NULL;
+
+	if (success)
+		reason = t_strconcat("Login: ", client->virtual_user, NULL);
+	else {
+		reason = t_strconcat("Internal login failure: ",
+				     client->virtual_user, NULL);
+		client_send_line(client, "* BYE Internal login failure.");
+	}
+
+	client_destroy(client, reason);
+	client_unref(client);
+}
+
+static void client_send_auth_data(struct pop3_client *client,
+				  const unsigned char *data, size_t size)
+{
+	buffer_t *buf;
+
+	t_push();
+
+	buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1);
+	buffer_append(buf, "+ ", 2);
+	base64_encode(data, size, buf);
+	buffer_append(buf, "\r\n", 2);
+
+	o_stream_send(client->output, buffer_get_data(buf, NULL),
+		      buffer_get_used_size(buf));
+	o_stream_flush(client->output);
+
+	t_pop();
+}
+
+static const char *auth_login_get_str(struct auth_login_reply *reply,
+				      const unsigned char *data, size_t idx)
+{
+	size_t stop;
+
+	if (idx >= reply->data_size || idx >= reply->reply_idx)
+		return NULL;
+
+	stop = reply->reply_idx < reply->data_size ?
+		reply->reply_idx-1 : reply->data_size;
+
+	return t_strndup(data, stop);
+}
+
+static int auth_callback(struct auth_request *request,
+			 struct auth_login_reply *reply,
+			 const unsigned char *data, void *context)
+{
+	struct pop3_client *client = context;
+	const char *user, *realm;
+
+	if (reply == NULL) {
+		/* failed */
+		client->auth_request = NULL;
+		client_auth_abort(client, "-ERR Authentication process died.");
+		return FALSE;
+	}
+
+	switch (reply->result) {
+	case AUTH_LOGIN_RESULT_CONTINUE:
+		client->auth_request = request;
+		return TRUE;
+
+	case AUTH_LOGIN_RESULT_SUCCESS:
+		client->auth_request = NULL;
+
+		user = auth_login_get_str(reply, data, reply->username_idx);
+		realm = auth_login_get_str(reply, data, reply->realm_idx);
+
+		i_free(client->virtual_user);
+		client->virtual_user = realm == NULL ?
+			i_strdup(user) : i_strconcat(user, "@", realm, NULL);
+
+		/* we should be able to log in. if we fail, just
+		   disconnect the client. */
+		client_send_line(client, "+OK Logged in.");
+
+		master_request_imap(&client->common, master_callback,
+				    request->conn->pid, request->id);
+
+		/* disable IO until we're back from master */
+		if (client->io != NULL) {
+			io_remove(client->io);
+			client->io = NULL;
+		}
+		return FALSE;
+
+	case AUTH_LOGIN_RESULT_FAILURE:
+		/* see if we have error message */
+		client->auth_request = NULL;
+
+		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
+			client_auth_abort(client, t_strconcat(
+				"-ERR Authentication failed: ",
+				(const char *) data, NULL));
+		} else {
+			/* default error message */
+			client_auth_abort(client, NULL);
+		}
+		return FALSE;
+	}
+
+	i_unreached();
+}
+
+static void login_callback(struct auth_request *request,
+			   struct auth_login_reply *reply,
+			   const unsigned char *data, struct client *_client)
+{
+	struct pop3_client *client = (struct pop3_client *) _client;
+	const void *ptr;
+	size_t size;
+
+	if (auth_callback(request, reply, data, client)) {
+		ptr = buffer_get_data(client->plain_login, &size);
+		auth_continue_request(request, ptr, size);
+
+		buffer_set_used_size(client->plain_login, 0);
+	}
+}
+
+int cmd_user(struct pop3_client *client, const char *args)
+{
+	if (!client->tls && disable_plaintext_auth) {
+		client_send_line(client,
+				 "-ERR Plaintext authentication disabled.");
+		return TRUE;
+	}
+
+	/* authorization ID \0 authentication ID \0 pass */
+	buffer_set_used_size(client->plain_login, 0);
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, args, strlen(args));
+
+	client_send_line(client, "+OK");
+	return TRUE;
+}
+
+int cmd_pass(struct pop3_client *client, const char *args)
+{
+	const char *error;
+
+	if (buffer_get_used_size(client->plain_login) == 0) {
+		client_send_line(client, "-ERR No username given.");
+		return TRUE;
+	}
+
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, args, strlen(args));
+
+	client_ref(client);
+	if (auth_init_request(AUTH_MECH_PLAIN, login_callback,
+			      &client->common, &error)) {
+		/* don't read any input from client until login is finished */
+		if (client->io != NULL) {
+			io_remove(client->io);
+			client->io = NULL;
+		}
+		return TRUE;
+	} else {
+		client_send_line(client,
+			t_strconcat("-ERR Login failed: ", error, NULL));
+		client_unref(client);
+		return TRUE;
+	}
+}
+
+static void authenticate_callback(struct auth_request *request,
+				  struct auth_login_reply *reply,
+				  const unsigned char *data,
+				  struct client *_client)
+{
+	struct pop3_client *client = (struct pop3_client *) _client;
+
+	if (auth_callback(request, reply, data, client))
+		client_send_auth_data(client, data, reply->data_size);
+}
+
+static void client_auth_input(void *context)
+{
+	struct pop3_client *client = context;
+	buffer_t *buf;
+	char *line;
+	size_t linelen, bufsize;
+
+	if (!client_read(client))
+		return;
+
+	/* @UNSAFE */
+	line = i_stream_next_line(client->input);
+	if (line == NULL)
+		return;
+
+	if (strcmp(line, "*") == 0) {
+		client_auth_abort(client, "-ERR Authentication aborted");
+		return;
+	}
+
+	linelen = strlen(line);
+	buf = buffer_create_static_hard(data_stack_pool, linelen);
+
+	if (base64_decode((const unsigned char *) line, linelen,
+			  NULL, buf) <= 0) {
+		/* failed */
+		client_auth_abort(client, "-ERR Invalid base64 data");
+	} else if (client->auth_request == NULL) {
+		client_auth_abort(client, "-ERR Don't send unrequested data");
+	} else {
+		auth_continue_request(client->auth_request,
+				      buffer_get_data(buf, NULL),
+				      buffer_get_used_size(buf));
+	}
+
+	/* clear sensitive data */
+	safe_memset(line, 0, linelen);
+
+	bufsize = buffer_get_used_size(buf);
+	safe_memset(buffer_free_without_data(buf), 0, bufsize);
+}
+
+int cmd_auth(struct pop3_client *client, const char *args)
+{
+	struct auth_mech_desc *mech;
+	const char *error;
+
+	/* we have only one argument: authentication mechanism name */
+	mech = auth_mech_find(args);
+	if (mech == NULL) {
+		client_send_line(client,
+				 "-ERR Unsupported authentication mechanism.");
+		return TRUE;
+	}
+
+	if (!client->tls && mech->plaintext && disable_plaintext_auth) {
+		client_send_line(client,
+				 "-ERR Plaintext authentication disabled.");
+		return TRUE;
+	}
+
+	client_ref(client);
+	if (auth_init_request(mech->mech, authenticate_callback,
+			      &client->common, &error)) {
+		/* following input data will go to authentication */
+		if (client->io != NULL)
+			io_remove(client->io);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_auth_input, client);
+	} else {
+		client_send_line(client, t_strconcat(
+			"-ERR Authentication failed: ", error, NULL));
+		client_unref(client);
+	}
+
+	return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/client-authenticate.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,8 @@
+#ifndef __CLIENT_AUTHENTICATE_H
+#define __CLIENT_AUTHENTICATE_H
+
+int cmd_user(struct pop3_client *client, const char *args);
+int cmd_pass(struct pop3_client *client, const char *args);
+int cmd_auth(struct pop3_client *client, const char *args);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/client.c	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,354 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "buffer.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "process-title.h"
+#include "safe-memset.h"
+#include "strescape.h"
+#include "client.h"
+#include "client-authenticate.h"
+#include "ssl-proxy.h"
+
+/* max. length of input command line (spec says 512) */
+#define MAX_INBUF_SIZE 2048
+
+/* Disconnect client after idling this many seconds */
+#define CLIENT_LOGIN_IDLE_TIMEOUT 60
+
+/* Disconnect client when it sends too many bad commands */
+#define CLIENT_MAX_BAD_COMMANDS 10
+
+/* When max. number of simultaneous connections is reached, few of the
+   oldest connections are disconnected. Since we have to go through the whole
+   client hash, it's faster if we disconnect multiple clients. */
+#define CLIENT_DESTROY_OLDEST_COUNT 16
+
+static struct hash_table *clients;
+static struct timeout *to_idle;
+
+static void client_set_title(struct pop3_client *client)
+{
+	const char *host;
+
+	if (!verbose_proctitle || !process_per_connection)
+		return;
+
+	host = net_ip2host(&client->common.ip);
+	if (host == NULL)
+		host = "??";
+
+	process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
+					  host));
+}
+
+static int cmd_stls(struct pop3_client *client)
+{
+	int fd_ssl;
+
+	if (client->tls) {
+		client_send_line(client, "-ERR TLS is already active.");
+		return TRUE;
+	}
+
+	if (!ssl_initialized) {
+		client_send_line(client, "-ERR TLS support isn't enabled.");
+		return TRUE;
+	}
+
+	client_send_line(client, "+OK Begin TLS negotiation now.");
+	o_stream_flush(client->output);
+
+	/* must be removed before ssl_proxy_new(), since it may
+	   io_add() the same fd. */
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	fd_ssl = ssl_proxy_new(client->common.fd);
+	if (fd_ssl != -1) {
+		client->tls = TRUE;
+                client_set_title(client);
+
+		client->common.fd = fd_ssl;
+
+		i_stream_unref(client->input);
+		o_stream_unref(client->output);
+
+		client->input = i_stream_create_file(fd_ssl, default_pool,
+						     8192, FALSE);
+		client->output = o_stream_create_file(fd_ssl, default_pool,
+						      1024, IO_PRIORITY_DEFAULT,
+						      FALSE);
+	} else {
+		client_send_line(client, "-ERR TLS handehake failed.");
+		client_destroy(client, "TLS handshake failed");
+	}
+
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
+	return TRUE;
+}
+
+static int cmd_quit(struct pop3_client *client)
+{
+	client_send_line(client, "+OK Logging out");
+	client_destroy(client, "Aborted login");
+	return TRUE;
+}
+
+static int client_command_execute(struct pop3_client *client, const char *cmd,
+				  const char *args)
+{
+	cmd = str_ucase(t_strdup_noconst(cmd));
+	if (strcmp(cmd, "USER") == 0)
+		return cmd_user(client, args);
+	if (strcmp(cmd, "PASS") == 0)
+		return cmd_pass(client, args);
+	if (strcmp(cmd, "AUTH") == 0)
+		return cmd_auth(client, args);
+	if (strcmp(cmd, "STLS") == 0)
+		return cmd_stls(client);
+	if (strcmp(cmd, "QUIT") == 0)
+		return cmd_quit(client);
+
+	return FALSE;
+}
+
+int client_read(struct pop3_client *client)
+{
+	switch (i_stream_read(client->input)) {
+	case -2:
+		/* buffer full */
+		client_send_line(client, "-ERR Input line too long, aborting");
+		client_destroy(client, "Disconnected: Input buffer full");
+		return FALSE;
+	case -1:
+		/* disconnected */
+		client_destroy(client, "Disconnected");
+		return FALSE;
+	default:
+		/* something was read */
+		return TRUE;
+	}
+}
+
+void client_input(void *context)
+{
+	struct pop3_client *client = context;
+	char *line, *args;
+
+	client->last_input = ioloop_time;
+
+	if (!client_read(client))
+		return;
+
+	client_ref(client);
+
+	o_stream_cork(client->output);
+	while (!client->output->closed &&
+	       (line = i_stream_next_line(client->input)) != NULL) {
+		args = strchr(line, ' ');
+		if (args == NULL)
+			args = "";
+		else
+			*args++ = '\0';
+
+		if (client_command_execute(client, line, args))
+			client->bad_counter = 0;
+		else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
+			client_send_line(client, "-ERR Too many bad commands.");
+			client_destroy(client,
+				       "Disconnected: Too many bad commands");
+		}
+	}
+
+	if (client_unref(client))
+		o_stream_flush(client->output);
+}
+
+static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
+				       void *context)
+{
+	struct pop3_client *client = key;
+	struct pop3_client *const *destroy_clients;
+	buffer_t *destroy_buf = context;
+	size_t i, count;
+
+	destroy_clients = buffer_get_data(destroy_buf, &count);
+	count /= sizeof(struct pop3_client *);
+
+	for (i = 0; i < count; i++) {
+		if (destroy_clients[i]->created > client->created) {
+			buffer_insert(destroy_buf,
+				      i * sizeof(struct pop3_client *),
+				      &client, sizeof(struct pop3_client *));
+			break;
+		}
+	}
+}
+
+static void client_destroy_oldest(void)
+{
+	struct pop3_client *const *destroy_clients;
+	buffer_t *destroy_buf;
+	size_t i, count;
+
+	/* find the oldest clients and put them to destroy-buffer */
+	destroy_buf = buffer_create_static_hard(data_stack_pool,
+						sizeof(struct pop3_client *) *
+						CLIENT_DESTROY_OLDEST_COUNT);
+	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
+
+	/* then kill them */
+	destroy_clients = buffer_get_data(destroy_buf, &count);
+	count /= sizeof(struct pop3_client *);
+
+	for (i = 0; i < count; i++) {
+		client_destroy(destroy_clients[i],
+			       "Disconnected: Connection queue full");
+	}
+}
+
+struct client *client_create(int fd, struct ip_addr *ip, int ssl)
+{
+	struct pop3_client *client;
+
+	if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
+	    hash_size(clients) >= max_logging_users) {
+		/* reached max. users count, kill few of the
+		   oldest connections */
+		client_destroy_oldest();
+	}
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd, TRUE);
+
+	client = i_new(struct pop3_client, 1);
+	client->created = ioloop_time;
+	client->refcount = 1;
+	client->tls = ssl;
+
+	client->common.ip = *ip;
+	client->common.fd = fd;
+	client->io = io_add(fd, IO_READ, client_input, client);
+	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->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
+
+	client->last_input = ioloop_time;
+	hash_insert(clients, client, client);
+
+	main_ref();
+
+	client_send_line(client, "+OK " PACKAGE " ready.");
+	client_set_title(client);
+	return &client->common;
+}
+
+void client_destroy(struct pop3_client *client, const char *reason)
+{
+	if (reason != NULL)
+		client_syslog(client, reason);
+
+	hash_remove(clients, client);
+
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	net_disconnect(client->common.fd);
+	client->common.fd = -1;
+
+	i_free(client->virtual_user);
+	client_unref(client);
+}
+
+void client_ref(struct pop3_client *client)
+{
+	client->refcount++;
+}
+
+int client_unref(struct pop3_client *client)
+{
+	if (--client->refcount > 0)
+		return TRUE;
+
+	i_stream_unref(client->input);
+	o_stream_unref(client->output);
+
+	buffer_free(client->plain_login);
+	i_free(client);
+
+	main_unref();
+	return FALSE;
+}
+
+void client_send_line(struct pop3_client *client, const char *line)
+{
+	o_stream_send_str(client->output, line);
+	o_stream_send(client->output, "\r\n", 2);
+}
+
+void client_syslog(struct pop3_client *client, const char *text)
+{
+	const char *host;
+
+	host = net_ip2host(&client->common.ip);
+	if (host == NULL)
+		host = "??";
+
+	i_info("%s [%s]", text, host);
+}
+
+static void client_hash_check_idle(void *key, void *value __attr_unused__,
+				   void *context __attr_unused__)
+{
+	struct pop3_client *client = key;
+
+	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
+		client_destroy(client, "Disconnected: Inactivity");
+}
+
+static void idle_timeout(void *context __attr_unused__)
+{
+	hash_foreach(clients, client_hash_check_idle, NULL);
+}
+
+unsigned int clients_get_count(void)
+{
+	return hash_size(clients);
+}
+
+static void client_hash_destroy(void *key, void *value __attr_unused__,
+				void *context __attr_unused__)
+{
+	client_destroy(key, NULL);
+}
+
+void clients_destroy_all(void)
+{
+	hash_foreach(clients, client_hash_destroy, NULL);
+}
+
+void clients_init(void)
+{
+	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
+	to_idle = timeout_add(1000, idle_timeout, NULL);
+}
+
+void clients_deinit(void)
+{
+	clients_destroy_all();
+	hash_destroy(clients);
+
+	timeout_remove(to_idle);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/client.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,46 @@
+#ifndef __CLIENT_H
+#define __CLIENT_H
+
+#include "network.h"
+#include "master.h"
+#include "client-common.h"
+
+struct pop3_client {
+	struct client common;
+
+	time_t created;
+	int refcount;
+
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	time_t last_input;
+	unsigned int bad_counter;
+
+	buffer_t *plain_login;
+	struct auth_request *auth_request;
+	char *virtual_user;
+
+	unsigned int tls:1;
+};
+
+struct client *client_create(int fd, struct ip_addr *ip, int ssl);
+void client_destroy(struct pop3_client *client, const char *reason);
+
+void client_ref(struct pop3_client *client);
+int client_unref(struct pop3_client *client);
+
+void client_send_line(struct pop3_client *client, const char *line);
+void client_syslog(struct pop3_client *client, const char *text);
+
+int client_read(struct pop3_client *client);
+void client_input(void *context);
+
+unsigned int clients_get_count(void);
+void clients_destroy_all(void);
+
+void clients_init(void);
+void clients_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/common.h	Tue Jan 28 23:35:25 2003 +0200
@@ -0,0 +1,16 @@
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "lib.h"
+#include "../auth/auth-login-interface.h"
+
+extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
+extern unsigned int max_logging_users;
+extern unsigned int login_process_uid;
+
+void main_ref(void);
+void main_unref(void);
+
+void main_close_listen(void);
+
+#endif