changeset 2773:e624a9ad6a30 HEAD

More smart IMAP and POP3 proxies. Now if remote login fails, it just destroys the proxy and allows trying another username which can go elsewhere. Also now replies with the same old "Authentication failed" error message instead of showing remote server's failure message.
author Timo Sirainen <tss@iki.fi>
date Tue, 19 Oct 2004 02:07:01 +0300
parents 7ac5ee00278c
children 689e4235681f
files src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/imap-login/imap-proxy.h src/login-common/common.h src/login-common/login-proxy.c src/login-common/login-proxy.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c src/pop3-login/pop3-proxy.h
diffstat 13 files changed, 374 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/imap-login/client-authenticate.c	Tue Oct 19 02:07:01 2004 +0300
@@ -113,10 +113,6 @@
 		   proxy host=.. [port=..] [destuser=..] pass=.. */
 		if (imap_proxy_new(client, host, port, destuser, pass) < 0)
 			client_destroy_internal_failure(client);
-		else {
-			client_destroy(client, t_strconcat(
-				"Proxy: ", client->common.virtual_user, NULL));
-		}
 		return TRUE;
 	} else if (host != NULL) {
 		/* IMAP referral
@@ -151,7 +147,7 @@
 		if (reason != NULL)
 			str_printfa(reply, "NO %s", reason);
 		else
-			str_append(reply, "NO Login not allowed.");
+			str_append(reply, "NO "AUTH_FAILED_MSG);
 		client_send_tagline(client, str_c(reply));
 	} else {
 		/* normal login/failure */
@@ -196,12 +192,7 @@
 				break;
 		}
 
-		if (data == NULL)
-			client_send_tagline(client, "Authentication failed");
-		else {
-			client_send_tagline(client, t_strconcat(
-				"NO Authentication failed: ", data, NULL));
-		}
+		client_send_tagline(client, "NO "AUTH_FAILED_MSG);
 
 		/* get back to normal client input. */
 		if (client->io != NULL)
--- a/src/imap-login/client.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/imap-login/client.c	Tue Oct 19 02:07:01 2004 +0300
@@ -464,8 +464,24 @@
 		client->common.fd = -1;
 	}
 
-	if (client->common.proxy != NULL)
+	if (client->proxy_user != NULL) {
+		safe_memset(client->proxy_password, 0,
+			    strlen(client->proxy_password));
+		i_free(client->proxy_user);
+		i_free(client->proxy_password);
+		client->proxy_user = NULL;
+		client->proxy_password = NULL;
+	}
+
+	if (client->proxy != NULL) {
+		login_proxy_free(client->proxy);
+		client->proxy = NULL;
+	}
+
+	if (client->common.proxy != NULL) {
 		ssl_proxy_free(client->common.proxy);
+		client->common.proxy = NULL;
+	}
 	client_unref(client);
 }
 
--- a/src/imap-login/client.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/imap-login/client.h	Tue Oct 19 02:07:01 2004 +0300
@@ -16,6 +16,9 @@
 	struct ostream *output;
 	struct imap_parser *parser;
 
+	struct login_proxy *proxy;
+	char *proxy_user, *proxy_password;
+
 	time_t last_input;
 	unsigned int bad_counter;
 
--- a/src/imap-login/imap-proxy.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/imap-login/imap-proxy.c	Tue Oct 19 02:07:01 2004 +0300
@@ -2,17 +2,121 @@
 
 #include "common.h"
 #include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
 #include "str.h"
+#include "safe-memset.h"
 #include "client.h"
 #include "imap-quote.h"
-#include "login-proxy.h"
 #include "imap-proxy.h"
 
+static int proxy_input_line(struct imap_client *client,
+			    struct ostream *output, const char *line)
+{
+	string_t *str;
+
+	if (client->proxy_user != NULL) {
+		/* this is a banner */
+		if (strncmp(line, "* OK ", 5) != 0) {
+			i_error("imap-proxy(%s): "
+				"Remote returned invalid banner: %s",
+				client->common.virtual_user, line);
+			client_destroy_internal_failure(client);
+			return -1;
+		}
+
+		/* send LOGIN command */
+		str = t_str_new(128);
+		str_append(str, "P LOGIN ");
+		imap_quote_append_string(str, client->proxy_user, FALSE);
+		str_append_c(str, ' ');
+		imap_quote_append_string(str, client->proxy_password, FALSE);
+		str_append(str, "\r\n");
+		(void)o_stream_send(output, str_data(str), str_len(str));
+
+		safe_memset(client->proxy_password, 0,
+			    strlen(client->proxy_password));
+		i_free(client->proxy_user);
+		i_free(client->proxy_password);
+		client->proxy_user = NULL;
+		client->proxy_password = NULL;
+		return 0;
+	} else if (strncmp(line, "P OK ", 5) == 0) {
+		/* Login successful. Send this line to client. */
+		(void)o_stream_send_str(client->output, client->cmd_tag);
+		(void)o_stream_send_str(client->output, line + 2);
+		(void)o_stream_send(client->output, "\r\n", 2);
+
+		login_proxy_detach(client->proxy, client->input,
+				   client->output);
+
+		client->proxy = NULL;
+		client->input = NULL;
+		client->output = NULL;
+		client->common.fd = -1;
+		client_destroy(client, t_strconcat(
+			"Proxy: ", client->common.virtual_user, NULL));
+		return -1;
+	} else if (strncmp(line, "P ", 2) == 0) {
+		/* Login failed. Send our own failure reply so client can't
+		   figure out if user exists or not just by looking at the
+		   reply string. */
+		client_send_tagline(client, "NO "AUTH_FAILED_MSG);
+
+		/* allow client input again */
+		i_assert(client->io == NULL);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_input, client);
+
+		login_proxy_free(client->proxy);
+		client->proxy = NULL;
+		return -1;
+	} else {
+		/* probably some untagged reply */
+		return 0;
+	}
+}
+
+static void proxy_input(struct istream *input, struct ostream *output,
+			void *context)
+{
+	struct imap_client *client = context;
+	const char *line;
+
+	if (input == NULL) {
+		if (client->io != NULL) {
+			/* remote authentication failed, we're just
+			   freeing the proxy */
+			return;
+		}
+
+		/* failed for some reason */
+		client_destroy_internal_failure(client);
+		return;
+	}
+
+	switch (i_stream_read(input)) {
+	case -2:
+		/* buffer full */
+		i_error("imap-proxy(%s): Remote input buffer full",
+			client->common.virtual_user);
+		client_destroy_internal_failure(client);
+		return;
+	case -1:
+		/* disconnected */
+		client_destroy(client, "Proxy: Remote disconnected");
+		return;
+	}
+
+	while ((line = i_stream_next_line(input)) != NULL) {
+		if (proxy_input_line(client, output, line) < 0)
+			break;
+	}
+}
+
 int imap_proxy_new(struct imap_client *client, const char *host,
 		   unsigned int port, const char *user, const char *password)
 {
-	string_t *str;
-
 	i_assert(user != NULL);
 
 	if (password == NULL) {
@@ -21,17 +125,15 @@
 		return -1;
 	}
 
-	str = t_str_new(128);
-	str_append(str, client->cmd_tag);
-	str_append(str, " LOGIN ");
-	imap_quote_append_string(str, user, FALSE);
-	str_append_c(str, ' ');
-	imap_quote_append_string(str, password, FALSE);
-	str_append(str, "\r\n");
-
-	if (login_proxy_new(&client->common, host, port, str_c(str)) < 0)
+	client->proxy = login_proxy_new(&client->common, host, port,
+					proxy_input, client);
+	if (client->proxy == NULL)
 		return -1;
 
+	client->proxy_user = i_strdup(user);
+	client->proxy_password = i_strdup(password);
+
+	/* disable input until authentication is finished */
 	if (client->io != NULL) {
 		io_remove(client->io);
 		client->io = NULL;
--- a/src/imap-login/imap-proxy.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/imap-login/imap-proxy.h	Tue Oct 19 02:07:01 2004 +0300
@@ -1,6 +1,8 @@
 #ifndef __IMAP_PROXY_H
 #define __IMAP_PROXY_H
 
+#include "login-proxy.h"
+
 int imap_proxy_new(struct imap_client *client, const char *host,
 		   unsigned int port, const char *user, const char *password);
 
--- a/src/login-common/common.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/login-common/common.h	Tue Oct 19 02:07:01 2004 +0300
@@ -3,6 +3,8 @@
 
 #include "lib.h"
 
+#define AUTH_FAILED_MSG "Authentication failed."
+
 extern int disable_plaintext_auth, process_per_connection, greeting_capability;
 extern int verbose_proctitle, verbose_ssl, verbose_auth;
 char *greeting;
--- a/src/login-common/login-proxy.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/login-common/login-proxy.c	Tue Oct 19 02:07:01 2004 +0300
@@ -4,29 +4,30 @@
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
-#include "safe-memset.h"
 #include "hash.h"
 #include "client-common.h"
 #include "login-proxy.h"
 
-#define MAX_INBUF_SIZE 4096
+#define MAX_PROXY_INPUT_SIZE 4096
 #define OUTBUF_THRESHOLD 1024
 
 struct login_proxy {
-	struct client *client;
-
 	int client_fd, proxy_fd;
 	struct io *client_io, *proxy_io;
+	struct istream *proxy_input;
 	struct ostream *client_output, *proxy_output;
 
 	char *host, *user, *login_cmd;
 	unsigned int port;
+
+	proxy_callback_t *callback;
+	void *context;
+
+	unsigned int destroying:1;
 };
 
 static struct hash_table *login_proxies;
 
-static void login_proxy_free(struct login_proxy *proxy);
-
 static void proxy_input(void *context)
 {
 	struct login_proxy *proxy = context;
@@ -112,42 +113,9 @@
 static void proxy_prelogin_input(void *context)
 {
 	struct login_proxy *proxy = context;
-	unsigned char buf[1024];
-	ssize_t ret;
 
-	ret = net_receive(proxy->proxy_fd, buf, sizeof(buf));
-	if (ret < 0) {
-		/* disconnected */
-		login_proxy_free(proxy);
-		return;
-	}
-
-	/* we just want to eat away the first banner line. be dummy and don't
-	   check any errors, so we don't need any IMAP/POP3-specific things
-	   here (except for assuming the banner line..) */
-	if (buf[ret-1] != '\n')
-		return;
-
-	/* send LOGIN command */
-	(void)o_stream_send_str(proxy->proxy_output, proxy->login_cmd);
-
-	safe_memset(proxy->login_cmd, 0, strlen(proxy->login_cmd));
-	i_free(proxy->login_cmd);
-        proxy->login_cmd = NULL;
-
-	/* from now on, just do dummy proxying */
-	io_remove(proxy->proxy_io);
-	proxy->proxy_io = io_add(proxy->proxy_fd, IO_READ, proxy_input, proxy);
-	proxy->client_io = io_add(proxy->client_fd, IO_READ,
-				  proxy_client_input, proxy);
-
-	proxy->client_output =
-		o_stream_create_file(proxy->client_fd, default_pool,
-				     (size_t)-1, FALSE);
-
-	o_stream_set_flush_callback(proxy->client_output,
-				    proxy_client_output, proxy);
-	o_stream_set_flush_callback(proxy->proxy_output, proxy_output, proxy);
+	proxy->callback(proxy->proxy_input, proxy->proxy_output,
+			proxy->context);
 }
 
 static void proxy_wait_connect(void *context)
@@ -163,7 +131,10 @@
 		return;
 	}
 
-	/* initialize proxy's streams */
+	/* connect successful */
+	proxy->proxy_input =
+		i_stream_create_file(proxy->proxy_fd, default_pool,
+				     MAX_PROXY_INPUT_SIZE, FALSE);
 	proxy->proxy_output =
 		o_stream_create_file(proxy->proxy_fd, default_pool,
 				     (size_t)-1, FALSE);
@@ -173,8 +144,9 @@
 		io_add(proxy->proxy_fd, IO_READ, proxy_prelogin_input, proxy);
 }
 
-int login_proxy_new(struct client *client, const char *host,
-		    unsigned int port, const char *login_cmd)
+struct login_proxy *
+login_proxy_new(struct client *client, const char *host, unsigned int port,
+		proxy_callback_t *callback, void *context)
 {
 	struct login_proxy *proxy;
 	struct ip_addr ip;
@@ -182,72 +154,102 @@
 
 	if (host == NULL) {
 		i_error("proxy(%s): host not given", client->virtual_user);
-		return -1;
+		return NULL;
 	}
 
 	if (net_addr2ip(host, &ip) < 0) {
 		i_error("proxy(%s): %s is not a valid IP",
 			client->virtual_user, host);
-		return -1;
+		return NULL;
 	}
 
 	fd = net_connect_ip(&ip, port, NULL);
 	if (fd < 0) {
 		i_error("proxy(%s): connect(%s, %u) failed: %m",
 			client->virtual_user, host, port);
-		return -1;
+		return NULL;
 	}
 
 	proxy = i_new(struct login_proxy, 1);
 	proxy->host = i_strdup(host);
-	proxy->login_cmd = i_strdup(login_cmd);
 	proxy->port = port;
 
 	proxy->proxy_fd = fd;
 	proxy->proxy_io = io_add(fd, IO_WRITE, proxy_wait_connect, proxy);
 
-	/* move client fd */
-	proxy->client_fd = client->fd;
-	client->fd = -1;
+	proxy->callback = callback;
+	proxy->context = context;
+
+	proxy->client_fd = -1;
+	return proxy;
+}
+
+void login_proxy_free(struct login_proxy *proxy)
+{
+	if (proxy->destroying)
+		return;
+
+	if (proxy->client_fd != -1) {
+		/* detached proxy */
+		main_unref();
+		hash_remove(login_proxies, proxy);
+
+		if (proxy->client_io != NULL)
+			io_remove(proxy->client_io);
+		if (proxy->client_output != NULL)
+			o_stream_unref(proxy->client_output);
+		net_disconnect(proxy->client_fd);
+	} else {
+		proxy->destroying = TRUE;
+		proxy->callback(NULL, NULL, proxy->context);
+	}
+
+	if (proxy->proxy_io != NULL)
+		io_remove(proxy->proxy_io);
+	if (proxy->proxy_input != NULL)
+		i_stream_unref(proxy->proxy_input);
+	if (proxy->proxy_output != NULL)
+		o_stream_unref(proxy->proxy_output);
+	net_disconnect(proxy->proxy_fd);
+
+	i_free(proxy->host);
+	i_free(proxy);
+}
+
+void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
+			struct ostream *client_output)
+{
+	const unsigned char *data;
+	size_t size;
+
+	proxy->client_fd = i_stream_get_fd(client_input);
+	proxy->client_output = client_output;
+
+	o_stream_set_max_buffer_size(client_output, (size_t)-1);
+	o_stream_set_flush_callback(client_output, proxy_client_output, proxy);
+
+	/* send all pending client input to proxy and get rid of the stream */
+	data = i_stream_get_data(client_input, &size);
+	if (size != 0)
+		(void)o_stream_send(proxy->proxy_output, data, size);
+	i_stream_unref(client_input);
+
+	/* from now on, just do dummy proxying */
+	io_remove(proxy->proxy_io);
+	proxy->proxy_io = io_add(proxy->proxy_fd, IO_READ, proxy_input, proxy);
+	proxy->client_io = io_add(proxy->client_fd, IO_READ,
+				  proxy_client_input, proxy);
+	o_stream_set_flush_callback(proxy->proxy_output, proxy_output, proxy);
+
+	i_stream_unref(proxy->proxy_input);
+        proxy->proxy_input = NULL;
 
 	if (login_proxies == NULL) {
 		login_proxies = hash_create(default_pool, default_pool,
 					    0, NULL, NULL);
 	}
 	hash_insert(login_proxies, proxy, proxy);
-
-	proxy->client = client;
 	main_ref();
-	return 0;
-}
-
-static void login_proxy_free(struct login_proxy *proxy)
-{
-	main_unref();
-
-	hash_remove(login_proxies, proxy);
-
-	if (proxy->proxy_io != NULL)
-		io_remove(proxy->proxy_io);
-	if (proxy->proxy_output != NULL)
-		o_stream_unref(proxy->proxy_output);
-
-	if (proxy->client_io != NULL)
-		io_remove(proxy->client_io);
-	if (proxy->client_output != NULL)
-		o_stream_unref(proxy->client_output);
-
-	net_disconnect(proxy->proxy_fd);
-	net_disconnect(proxy->client_fd);
-
-	if (proxy->login_cmd != NULL) {
-		safe_memset(proxy->login_cmd, 0, strlen(proxy->login_cmd));
-		i_free(proxy->login_cmd);
-	}
-
-	i_free(proxy->host);
-	i_free(proxy->user);
-	i_free(proxy);
 }
 
 void login_proxy_deinit(void)
--- a/src/login-common/login-proxy.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/login-common/login-proxy.h	Tue Oct 19 02:07:01 2004 +0300
@@ -1,10 +1,24 @@
 #ifndef __LOGIN_PROXY_H
 #define __LOGIN_PROXY_H
 
-/* Create a proxy to given host. Returns -1 if failed, or 0 if ok.
-   In any case the client should be destroyed after this call. */
-int login_proxy_new(struct client *client, const char *host,
-		    unsigned int port, const char *login_cmd);
+struct login_proxy;
+
+/* Called when new input comes from proxy. */
+typedef void proxy_callback_t(struct istream *input, struct ostream *output,
+			      void *context);
+
+/* Create a proxy to given host. Returns NULL if failed. Given callback is
+   called when new input is available from proxy. */
+struct login_proxy *
+login_proxy_new(struct client *client, const char *host, unsigned int port,
+		proxy_callback_t *callback, void *context);
+/* Free the proxy. This should be called if authentication fails. */
+void login_proxy_free(struct login_proxy *proxy);
+
+/* Detach proxy from client. This is done after the authentication is
+   successful and all that is left is the dummy proxying. */
+void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
+			struct ostream *client_output);
 
 void login_proxy_deinit(void);
 
--- a/src/pop3-login/client-authenticate.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/pop3-login/client-authenticate.c	Tue Oct 19 02:07:01 2004 +0300
@@ -114,10 +114,6 @@
 		   proxy host=.. [port=..] [destuser=..] pass=.. */
 		if (pop3_proxy_new(client, host, port, destuser, pass) < 0)
 			client_destroy_internal_failure(client);
-		else {
-			client_destroy(client, t_strconcat(
-				"Proxy: ", client->common.virtual_user, NULL));
-		}
 		return TRUE;
 	}
 
@@ -129,7 +125,7 @@
 	if (reason != NULL)
 		str_append(reply, reason);
 	else
-		str_append(reply, "Login disabled.");
+		str_append(reply, AUTH_FAILED_MSG);
 
 	client_send_line(client, str_c(reply));
 
@@ -166,12 +162,7 @@
 				break;
 		}
 
-		if (data == NULL)
-			client_send_line(client, "-ERR Authentication failed");
-		else {
-			client_send_line(client, t_strconcat(
-				"-ERR Authentication failed: ", data, NULL));
-		}
+		client_send_line(client, "-ERR "AUTH_FAILED_MSG);
 
 		/* get back to normal client input. */
 		if (client->io != NULL)
--- a/src/pop3-login/client.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/pop3-login/client.c	Tue Oct 19 02:07:01 2004 +0300
@@ -15,6 +15,7 @@
 #include "client-authenticate.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
+#include "pop3-proxy.h"
 #include "hostpid.h"
 
 /* max. length of input command line (spec says 512), or max reply length in
@@ -358,8 +359,24 @@
 		client->io = NULL;
 	}
 
-	net_disconnect(client->common.fd);
-	client->common.fd = -1;
+	if (client->common.fd != -1) {
+		net_disconnect(client->common.fd);
+		client->common.fd = -1;
+	}
+
+	if (client->proxy_user != NULL) {
+		safe_memset(client->proxy_password, 0,
+			    strlen(client->proxy_password));
+		i_free(client->proxy_user);
+		i_free(client->proxy_password);
+		client->proxy_user = NULL;
+		client->proxy_password = NULL;
+	}
+
+	if (client->proxy != NULL) {
+		login_proxy_free(client->proxy);
+		client->proxy = NULL;
+	}
 
 	if (client->common.proxy != NULL)
 		ssl_proxy_free(client->common.proxy);
--- a/src/pop3-login/client.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/pop3-login/client.h	Tue Oct 19 02:07:01 2004 +0300
@@ -16,6 +16,9 @@
 	struct istream *input;
 	struct ostream *output;
 
+	struct login_proxy *proxy;
+	char *proxy_user, *proxy_password;
+
 	time_t last_input;
 	unsigned int bad_counter;
 
--- a/src/pop3-login/pop3-proxy.c	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/pop3-login/pop3-proxy.c	Tue Oct 19 02:07:01 2004 +0300
@@ -2,17 +2,112 @@
 
 #include "common.h"
 #include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
 #include "base64.h"
+#include "safe-memset.h"
 #include "str.h"
 #include "client.h"
-#include "login-proxy.h"
 #include "pop3-proxy.h"
 
+static void proxy_input(struct istream *input, struct ostream *output,
+			void *context)
+{
+	struct pop3_client *client = context;
+	string_t *auth, *str;
+	const char *line;
+
+	if (input == NULL) {
+		if (client->io != NULL) {
+			/* remote authentication failed, we're just
+			   freeing the proxy */
+			return;
+		}
+
+		/* failed for some reason */
+		client_destroy_internal_failure(client);
+		return;
+	}
+
+	switch (i_stream_read(input)) {
+	case -2:
+		/* buffer full */
+		i_error("pop-proxy(%s): Remote input buffer full",
+			client->common.virtual_user);
+		client_destroy_internal_failure(client);
+		return;
+	case -1:
+		/* disconnected */
+		client_destroy(client, "Proxy: Remote disconnected");
+		return;
+	}
+
+	line = i_stream_next_line(input);
+	if (line == NULL)
+		return;
+
+	if (client->proxy_user != NULL) {
+		/* this is a banner */
+		if (strncmp(line, "+OK ", 4) != 0) {
+			i_error("pop3-proxy(%s): "
+				"Remote returned invalid banner: %s",
+				client->common.virtual_user, line);
+			client_destroy_internal_failure(client);
+			return;
+		}
+
+		/* send AUTH command */
+		auth = t_str_new(128);
+		str_append_c(auth, '\0');
+		str_append(auth, client->proxy_user);
+		str_append_c(auth, '\0');
+		str_append(auth, client->proxy_password);
+
+		str = t_str_new(128);
+		str_append(str, "AUTH ");
+		base64_encode(str_data(auth), str_len(auth), str);
+		str_append(str, "\r\n");
+		(void)o_stream_send(output, str_data(str), str_len(str));
+
+		safe_memset(client->proxy_password, 0,
+			    strlen(client->proxy_password));
+		i_free(client->proxy_user);
+		i_free(client->proxy_password);
+		client->proxy_user = NULL;
+		client->proxy_password = NULL;
+	} else if (strncmp(line, "+OK ", 4) == 0) {
+		/* Login successful. Send this line to client. */
+		(void)o_stream_send_str(client->output, line);
+		(void)o_stream_send(client->output, "\r\n", 2);
+
+		login_proxy_detach(client->proxy, client->input,
+				   client->output);
+
+		client->proxy = NULL;
+		client->input = NULL;
+		client->output = NULL;
+		client->common.fd = -1;
+		client_destroy(client, t_strconcat(
+			"Proxy: ", client->common.virtual_user, NULL));
+	} else {
+		/* Login failed. Send our own failure reply so client can't
+		   figure out if user exists or not just by looking at the
+		   reply string. */
+		client_send_line(client, "-ERR "AUTH_FAILED_MSG);
+
+		/* allow client input again */
+		i_assert(client->io == NULL);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_input, client);
+
+		login_proxy_free(client->proxy);
+		client->proxy = NULL;
+	}
+}
+
 int pop3_proxy_new(struct pop3_client *client, const char *host,
 		   unsigned int port, const char *user, const char *password)
 {
-	string_t *auth, *str;
-
 	i_assert(user != NULL);
 
 	if (password == NULL) {
@@ -21,20 +116,15 @@
 		return -1;
 	}
 
-	auth = t_str_new(128);
-	str_append_c(auth, '\0');
-	str_append(auth, user);
-	str_append_c(auth, '\0');
-	str_append(auth, password);
-
-	str = t_str_new(128);
-	str_append(str, "AUTH ");
-	base64_encode(str_data(auth), str_len(auth), str);
-	str_append(str, "\r\n");
-
-	if (login_proxy_new(&client->common, host, port, str_c(str)) < 0)
+	client->proxy = login_proxy_new(&client->common, host, port,
+					proxy_input, client);
+	if (client->proxy == NULL)
 		return -1;
 
+	client->proxy_user = i_strdup(user);
+	client->proxy_password = i_strdup(password);
+
+	/* disable input until authentication is finished */
 	if (client->io != NULL) {
 		io_remove(client->io);
 		client->io = NULL;
--- a/src/pop3-login/pop3-proxy.h	Tue Oct 19 02:03:54 2004 +0300
+++ b/src/pop3-login/pop3-proxy.h	Tue Oct 19 02:07:01 2004 +0300
@@ -1,6 +1,8 @@
 #ifndef __POP3_PROXY_H
 #define __POP3_PROXY_H
 
+#include "login-proxy.h"
+
 int pop3_proxy_new(struct pop3_client *client, const char *host,
 		   unsigned int port, const char *user, const char *password);