changeset 2768:d344be0bb70f HEAD

Added IMAP and POP3 proxying support.
author Timo Sirainen <tss@iki.fi>
date Mon, 18 Oct 2004 22:21:40 +0300
parents 54dfccbe7f11
children 0058f0b70402
files src/auth/auth-client-connection.c src/auth/mech.h src/auth/passdb-sql.c src/imap-login/Makefile.am src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/imap-login/imap-proxy.h src/login-common/Makefile.am src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/main.c src/login-common/sasl-server.c src/pop3-login/Makefile.am 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 20 files changed, 556 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-client-connection.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/auth/auth-client-connection.c	Mon Oct 18 22:21:40 2004 +0300
@@ -80,10 +80,11 @@
 		break;
 	case AUTH_CLIENT_RESULT_FAILURE:
 		str = t_str_new(128);
-		str_printfa(str, "FAIL\t%u", request->id);
-		str_append_c(str, '\t');
+		str_printfa(str, "FAIL\t%u\t", request->id);
 		if (reply != NULL)
 			str_append(str, reply);
+		if (request->user != NULL)
+			str_printfa(str, "\tuser=%s", request->user);
 		if (request->extra_fields) {
 			str_append_c(str, '\t');
 			str_append(str, request->extra_fields);
--- a/src/auth/mech.h	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/auth/mech.h	Mon Oct 18 22:21:40 2004 +0300
@@ -37,6 +37,7 @@
 	unsigned int accept_input:1;
 	unsigned int no_failure_delay:1;
 	unsigned int no_login:1;
+	unsigned int proxy:1;
 	/* ... mechanism specific data ... */
 };
 
--- a/src/auth/passdb-sql.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/auth/passdb-sql.c	Mon Oct 18 22:21:40 2004 +0300
@@ -30,6 +30,7 @@
 static struct sql_connection *passdb_sql_conn;
 
 static void result_save_extra_fields(struct sql_result *result,
+                                     struct passdb_sql_request *sql_request,
 				     struct auth_request *auth_request)
 {
 	unsigned int i, fields_count;
@@ -66,6 +67,21 @@
 					str_append_c(str, '\t');
 				str_append(str, name);
 			}
+		} else if (strcmp(name, "proxy") == 0) {
+			if (*value == 'Y') {
+				/* we're proxying authentication for this
+				   user. send password back if using plaintext
+				   authentication. */
+				auth_request->proxy = TRUE;
+				if (str_len(str) > 0)
+					str_append_c(str, '\t');
+				str_append(str, name);
+
+				if (*sql_request->password != '\0') {
+					str_printfa(str, "\tpass=%s",
+						    sql_request->password);
+				}
+			}
 		} else {
 			if (str_len(str) > 0)
 				str_append_c(str, '\t');
@@ -102,7 +118,7 @@
 			get_log_prefix(auth_request));
 	} else {
 		password = t_strdup(sql_result_get_field_value(result, idx));
-                result_save_extra_fields(result, auth_request);
+                result_save_extra_fields(result, sql_request, auth_request);
 	}
 
 	if (ret > 0) {
--- a/src/imap-login/Makefile.am	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/imap-login/Makefile.am	Mon Oct 18 22:21:40 2004 +0300
@@ -10,15 +10,17 @@
 
 imap_login_LDADD = \
 	../login-common/liblogin-common.a \
-	../lib-imap/imap-parser.o \
+	../lib-imap/libimap.a \
 	../lib-auth/libauth.a \
 	../lib/liblib.a \
 	$(SSL_LIBS)
 
 imap_login_SOURCES = \
 	client.c \
-	client-authenticate.c
+	client-authenticate.c \
+	imap-proxy.c
 
 noinst_HEADERS = \
 	client.h \
-	client-authenticate.h
+	client-authenticate.h \
+	imap-proxy.h
--- a/src/imap-login/client-authenticate.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/imap-login/client-authenticate.c	Mon Oct 18 22:21:40 2004 +0300
@@ -13,6 +13,9 @@
 #include "auth-client.h"
 #include "client.h"
 #include "client-authenticate.h"
+#include "imap-proxy.h"
+
+#include <stdlib.h>
 
 const char *client_authenticate_get_capabilities(int secured)
 {
@@ -76,39 +79,85 @@
 	safe_memset(line, 0, strlen(line));
 }
 
-static int client_handle_success_args(struct imap_client *client,
-				      const char *const *args, int nologin)
+static int client_handle_args(struct imap_client *client,
+			      const char *const *args, int nologin)
 {
-	const char *reason = NULL, *referral = NULL;
+	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
 	string_t *reply;
+	unsigned int port = 143;
+	int proxy = FALSE;
 
 	for (; *args != NULL; args++) {
 		if (strcmp(*args, "nologin") == 0)
 			nologin = TRUE;
+		else if (strcmp(*args, "proxy") == 0)
+			proxy = TRUE;
 		else if (strncmp(*args, "reason=", 7) == 0)
 			reason = *args + 7;
-		else  if (strncmp(*args, "referral=", 9) == 0)
-			referral = *args + 9;
+		else if (strncmp(*args, "host=", 5) == 0)
+			host = *args + 5;
+		else if (strncmp(*args, "port=", 5) == 0)
+			port = atoi(*args + 5);
+		else if (strncmp(*args, "destuser=", 9) == 0)
+			destuser = *args + 9;
+		else if (strncmp(*args, "pass=", 5) == 0)
+			pass = *args + 5;
 	}
 
-	if (!nologin && referral == NULL)
-		return FALSE;
+	if (destuser == NULL)
+		destuser = client->common.virtual_user;
+
+	if (proxy) {
+		/* we want to proxy the connection to another server.
 
-	reply = t_str_new(128);
-	str_append(reply, nologin ? "NO " : "OK ");
-	if (referral != NULL)
-		str_printfa(reply, "[REFERRAL %s] ", referral);
+		   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
+
+		   [nologin] referral host=.. [port=..] [destuser=..]
+		   [reason=..]
 
-	if (reason != NULL)
-		str_append(reply, reason);
-	else if (!nologin)
-		str_append(reply, "Logged in.");
-	else if (referral != NULL)
-		str_append(reply, "Try this server instead.");
-	else
-		str_append(reply, "Login disabled.");
+		   NO [REFERRAL imap://destuser;AUTH=..@host:port/] Can't login.
+		   OK [...] Logged in, but you should use this server instead.
+		   .. [REFERRAL ..] (Reason from auth server)
+		*/
+		reply = t_str_new(128);
+		str_append(reply, nologin ? "NO " : "OK ");
+		str_printfa(reply, "[REFERRAL imap://%s;AUTH=%s@%s",
+			    destuser, client->common.auth_mech_name, host);
+		if (port != 143)
+			str_printfa(reply, ":%u", port);
+		str_append(reply, "/] ");
+		if (reason != NULL)
+			str_append(reply, reason);
+		else if (nologin)
+			str_append(reply, "Try this server instead.");
+		else {
+			str_append(reply, "Logged in, but you should use "
+				   "this server instead.");
+		}
+		client_send_tagline(client, str_c(reply));
+	} else if (nologin) {
+		/* Authentication went ok, but for some reason user isn't
+		   allowed to log in. Shouldn't probably happen. */
+		reply = t_str_new(128);
+		if (reason != NULL)
+			str_printfa(reply, "NO %s", reason);
+		else
+			str_append(reply, "NO Login not allowed.");
+		client_send_tagline(client, str_c(reply));
+	} else {
+		/* normal login/failure */
+		return FALSE;
+	}
 
-	client_send_tagline(client, str_c(reply));
 	if (!nologin) {
 		client_destroy(client, t_strconcat(
 			"Login: ", client->common.virtual_user, NULL));
@@ -133,7 +182,7 @@
 	switch (reply) {
 	case SASL_SERVER_REPLY_SUCCESS:
 		if (args != NULL) {
-			if (client_handle_success_args(client, args, FALSE))
+			if (client_handle_args(client, args, FALSE))
 				break;
 		}
 
@@ -143,7 +192,7 @@
 		break;
 	case SASL_SERVER_REPLY_AUTH_FAILED:
 		if (args != NULL) {
-			if (client_handle_success_args(client, args, TRUE))
+			if (client_handle_args(client, args, TRUE))
 				break;
 		}
 
@@ -161,11 +210,7 @@
 				    client_input, client);
 		break;
 	case SASL_SERVER_REPLY_MASTER_FAILED:
-		client_send_line(client, "* BYE Internal login failure. "
-				 "Refer to server log for more information.");
-		client_destroy(client, t_strconcat("Internal login failure: ",
-						   client->common.virtual_user,
-						   NULL));
+		client_destroy_internal_failure(client);
 		break;
 	case SASL_SERVER_REPLY_CONTINUE:
 		data_len = strlen(data);
--- a/src/imap-login/client.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/imap-login/client.c	Mon Oct 18 22:21:40 2004 +0300
@@ -15,6 +15,7 @@
 #include "client-authenticate.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
+#include "imap-proxy.h"
 
 /* max. size of one parameter in line, or max reply length in SASL
    authentication */
@@ -442,7 +443,8 @@
 	hash_remove(clients, client);
 
 	i_stream_close(client->input);
-	o_stream_close(client->output);
+	if (client->output != NULL)
+		o_stream_close(client->output);
 
 	if (client->common.auth_request != NULL) {
 		auth_client_request_abort(client->common.auth_request);
@@ -467,6 +469,14 @@
 	client_unref(client);
 }
 
+void client_destroy_internal_failure(struct imap_client *client)
+{
+	client_send_line(client, "* BYE Internal login failure. "
+			 "Refer to server log for more information.");
+	client_destroy(client, t_strconcat("Internal login failure: ",
+					   client->common.virtual_user, NULL));
+}
+
 void client_ref(struct imap_client *client)
 {
 	client->refcount++;
@@ -480,7 +490,8 @@
 	imap_parser_destroy(client->parser);
 
 	i_stream_unref(client->input);
-	o_stream_unref(client->output);
+	if (client->output != NULL)
+		o_stream_unref(client->output);
 
 	i_free(client->common.virtual_user);
 	i_free(client->common.auth_mech_name);
--- a/src/imap-login/client.h	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/imap-login/client.h	Mon Oct 18 22:21:40 2004 +0300
@@ -28,6 +28,7 @@
 };
 
 void client_destroy(struct imap_client *client, const char *reason);
+void client_destroy_internal_failure(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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/imap-proxy.c	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,41 @@
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "str.h"
+#include "client.h"
+#include "imap-quote.h"
+#include "login-proxy.h"
+#include "imap-proxy.h"
+
+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) {
+		i_error("proxy(%s): password not given",
+			client->common.virtual_user);
+		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)
+		return -1;
+
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap-login/imap-proxy.h	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,7 @@
+#ifndef __IMAP_PROXY_H
+#define __IMAP_PROXY_H
+
+int imap_proxy_new(struct imap_client *client, const char *host,
+		   unsigned int port, const char *user, const char *password);
+
+#endif
--- a/src/login-common/Makefile.am	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/login-common/Makefile.am	Mon Oct 18 22:21:40 2004 +0300
@@ -8,6 +8,7 @@
 
 liblogin_common_a_SOURCES = \
 	client-common.c \
+	login-proxy.c \
 	main.c \
 	master.c \
 	sasl-server.c \
@@ -17,6 +18,7 @@
 
 noinst_HEADERS = \
 	client-common.h \
+	login-proxy.h \
 	common.h \
 	master.h \
 	sasl-server.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/login-proxy.c	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,266 @@
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "common.h"
+#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 OUTBUF_THRESHOLD 1024
+
+struct login_proxy {
+	struct client *client;
+
+	int client_fd, proxy_fd;
+	struct io *client_io, *proxy_io;
+	struct ostream *client_output, *proxy_output;
+
+	char *host, *user, *login_cmd;
+	unsigned int port;
+};
+
+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;
+	unsigned char buf[OUTBUF_THRESHOLD];
+	ssize_t ret;
+
+	if (o_stream_get_buffer_used_size(proxy->client_output) >
+	    OUTBUF_THRESHOLD) {
+		/* client's output buffer is already quite full.
+		   don't send more until we're below threshold. */
+		io_remove(proxy->proxy_io);
+		proxy->proxy_io = NULL;
+		return;
+	}
+
+	ret = net_receive(proxy->proxy_fd, buf, sizeof(buf));
+	if (ret > 0)
+		(void)o_stream_send(proxy->client_output, buf, ret);
+	else if (ret < 0)
+                login_proxy_free(proxy);
+}
+
+static void proxy_client_input(void *context)
+{
+	struct login_proxy *proxy = context;
+	unsigned char buf[OUTBUF_THRESHOLD];
+	ssize_t ret;
+
+	if (o_stream_get_buffer_used_size(proxy->proxy_output) >
+	    OUTBUF_THRESHOLD) {
+		/* proxy's output buffer is already quite full.
+		   don't send more until we're below threshold. */
+		io_remove(proxy->client_io);
+		proxy->client_io = NULL;
+		return;
+	}
+
+	ret = net_receive(proxy->client_fd, buf, sizeof(buf));
+	if (ret > 0)
+		(void)o_stream_send(proxy->proxy_output, buf, ret);
+	else if (ret < 0)
+                login_proxy_free(proxy);
+}
+
+static void proxy_output(void *context)
+{
+	struct login_proxy *proxy = context;
+
+	if (o_stream_flush(proxy->proxy_output) < 0) {
+                login_proxy_free(proxy);
+		return;
+	}
+
+	if (proxy->client_io == NULL &&
+	    o_stream_get_buffer_used_size(proxy->proxy_output) <
+	    OUTBUF_THRESHOLD) {
+		/* there's again space in proxy's output buffer, so we can
+		   read more from client. */
+		proxy->client_io = io_add(proxy->client_fd, IO_READ,
+					  proxy_client_input, proxy);
+	}
+}
+
+static void proxy_client_output(void *context)
+{
+	struct login_proxy *proxy = context;
+
+	if (o_stream_flush(proxy->client_output) < 0) {
+                login_proxy_free(proxy);
+		return;
+	}
+
+	if (proxy->proxy_io == NULL &&
+	    o_stream_get_buffer_used_size(proxy->client_output) <
+	    OUTBUF_THRESHOLD) {
+		/* there's again space in client's output buffer, so we can
+		   read more from proxy. */
+		proxy->proxy_io =
+			io_add(proxy->proxy_fd, IO_READ, proxy_input, proxy);
+	}
+}
+
+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);
+}
+
+static void proxy_wait_connect(void *context)
+{
+	struct login_proxy *proxy = context;
+	int err;
+
+	err = net_geterror(proxy->proxy_fd);
+	if (err != 0) {
+		i_error("proxy: connect(%s, %u) failed: %s",
+			proxy->host, proxy->port, strerror(err));
+                login_proxy_free(proxy);
+		return;
+	}
+
+	/* initialize proxy's streams */
+	proxy->proxy_output =
+		o_stream_create_file(proxy->proxy_fd, default_pool,
+				     (size_t)-1, FALSE);
+
+	io_remove(proxy->proxy_io);
+	proxy->proxy_io =
+		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 *proxy;
+	struct ip_addr ip;
+	int fd;
+
+	if (host == NULL) {
+		i_error("proxy(%s): host not given", client->virtual_user);
+		return -1;
+	}
+
+	if (net_addr2ip(host, &ip) < 0) {
+		i_error("proxy(%s): %s is not a valid IP",
+			client->virtual_user, host);
+		return -1;
+	}
+
+	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;
+	}
+
+	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;
+
+	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)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	if (login_proxies == NULL)
+		return;
+
+	iter = hash_iterate_init(login_proxies);
+	while (hash_iterate(iter, &key, &value))
+		login_proxy_free(value);
+	hash_iterate_deinit(iter);
+	hash_destroy(login_proxies);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/login-proxy.h	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,11 @@
+#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);
+
+void login_proxy_deinit(void);
+
+#endif
--- a/src/login-common/main.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/login-common/main.c	Mon Oct 18 22:21:40 2004 +0300
@@ -12,6 +12,7 @@
 #include "client-common.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
+#include "login-proxy.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -219,6 +220,7 @@
 	if (io_ssl_listen != NULL) io_remove(io_ssl_listen);
 
 	ssl_proxy_deinit();
+	login_proxy_deinit();
 
 	auth_client_free(auth_client);
 	clients_deinit();
--- a/src/login-common/sasl-server.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/login-common/sasl-server.c	Mon Oct 18 22:21:40 2004 +0300
@@ -88,6 +88,16 @@
 	case -1:
 		client->auth_request = NULL;
 
+		if (args != NULL) {
+			for (i = 0; args[i] != NULL; i++) {
+				if (strncmp(args[i], "user=", 5) == 0) {
+					i_free(client->virtual_user);
+					client->virtual_user =
+						i_strdup(args[i] + 5);
+				}
+			}
+		}
+
 		/* base64 contains error message, if there is one */
 		if (verbose_auth && data_base64 != NULL) {
 			client_syslog(client, "Authenticate %s failed: %s",
--- a/src/pop3-login/Makefile.am	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/pop3-login/Makefile.am	Mon Oct 18 22:21:40 2004 +0300
@@ -15,8 +15,10 @@
 
 pop3_login_SOURCES = \
 	client.c \
-	client-authenticate.c
+	client-authenticate.c \
+	pop3-proxy.c
 
 noinst_HEADERS = \
 	client.h \
-	client-authenticate.h
+	client-authenticate.h \
+	pop3-proxy.h
--- a/src/pop3-login/client-authenticate.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/pop3-login/client-authenticate.c	Mon Oct 18 22:21:40 2004 +0300
@@ -15,6 +15,9 @@
 #include "ssl-proxy.h"
 #include "client.h"
 #include "client-authenticate.h"
+#include "pop3-proxy.h"
+
+#include <stdlib.h>
 
 int cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
 {
@@ -77,18 +80,45 @@
 	safe_memset(line, 0, strlen(line));
 }
 
-static int client_handle_success_args(struct pop3_client *client,
-				      const char *const *args)
+static int client_handle_args(struct pop3_client *client,
+			      const char *const *args, int nologin)
 {
-	const char *reason = NULL;
+	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
 	string_t *reply;
-	int nologin = FALSE;
+	unsigned int port = 110;
+	int proxy = FALSE;
 
 	for (; *args != NULL; args++) {
 		if (strcmp(*args, "nologin") == 0)
 			nologin = TRUE;
+		else if (strcmp(*args, "proxy") == 0)
+			proxy = TRUE;
 		else if (strncmp(*args, "reason=", 7) == 0)
 			reason = *args + 7;
+		else if (strncmp(*args, "host=", 5) == 0)
+			host = *args + 5;
+		else if (strncmp(*args, "port=", 5) == 0)
+			port = atoi(*args + 5);
+		else if (strncmp(*args, "destuser=", 9) == 0)
+			destuser = *args + 9;
+		else if (strncmp(*args, "pass=", 5) == 0)
+			pass = *args + 5;
+	}
+
+	if (destuser == NULL)
+		destuser = client->common.virtual_user;
+
+	if (proxy) {
+		/* we want to proxy the connection to another server.
+
+		   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;
 	}
 
 	if (!nologin)
@@ -122,7 +152,7 @@
 	switch (reply) {
 	case SASL_SERVER_REPLY_SUCCESS:
 		if (args != NULL) {
-			if (client_handle_success_args(client, args))
+			if (client_handle_args(client, args, FALSE))
 				break;
 		}
 
@@ -131,6 +161,11 @@
 			"Login: ", client->common.virtual_user, NULL));
 		break;
 	case SASL_SERVER_REPLY_AUTH_FAILED:
+		if (args != NULL) {
+			if (client_handle_args(client, args, TRUE))
+				break;
+		}
+
 		if (data == NULL)
 			client_send_line(client, "-ERR Authentication failed");
 		else {
@@ -145,12 +180,7 @@
 				    client_input, client);
 		break;
 	case SASL_SERVER_REPLY_MASTER_FAILED:
-		client_send_line(client,
-				 "-ERR [IN-USE] Internal login failure. "
-				 "Refer to server log for more information.");
-		client_destroy(client, t_strconcat("Internal login failure: ",
-						   client->common.virtual_user,
-						   NULL));
+		client_destroy_internal_failure(client);
 		break;
 	case SASL_SERVER_REPLY_CONTINUE:
 		data_len = strlen(data);
--- a/src/pop3-login/client.c	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/pop3-login/client.c	Mon Oct 18 22:21:40 2004 +0300
@@ -366,6 +366,14 @@
 	client_unref(client);
 }
 
+void client_destroy_internal_failure(struct pop3_client *client)
+{
+	client_send_line(client, "-ERR [IN-USE] Internal login failure. "
+			 "Refer to server log for more information.");
+	client_destroy(client, t_strconcat("Internal login failure: ",
+					   client->common.virtual_user, NULL));
+}
+
 void client_ref(struct pop3_client *client)
 {
 	client->refcount++;
--- a/src/pop3-login/client.h	Mon Oct 18 11:17:31 2004 +0300
+++ b/src/pop3-login/client.h	Mon Oct 18 22:21:40 2004 +0300
@@ -30,6 +30,7 @@
 };
 
 void client_destroy(struct pop3_client *client, const char *reason);
+void client_destroy_internal_failure(struct pop3_client *client);
 
 void client_send_line(struct pop3_client *client, const char *line);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/pop3-proxy.c	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,44 @@
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "base64.h"
+#include "str.h"
+#include "client.h"
+#include "login-proxy.h"
+#include "pop3-proxy.h"
+
+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) {
+		i_error("proxy(%s): password not given",
+			client->common.virtual_user);
+		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)
+		return -1;
+
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3-login/pop3-proxy.h	Mon Oct 18 22:21:40 2004 +0300
@@ -0,0 +1,7 @@
+#ifndef __POP3_PROXY_H
+#define __POP3_PROXY_H
+
+int pop3_proxy_new(struct pop3_client *client, const char *host,
+		   unsigned int port, const char *user, const char *password);
+
+#endif