diff -r e528d9e3cdc8 src/auth/auth-request.c
--- a/src/auth/auth-request.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/auth/auth-request.c	Sun Jun 01 21:07:12 2008 +0300
@@ -868,38 +868,12 @@
 	return request->requested_login_user != NULL;
 }
 
-static int is_ip_in_network(const char *network, const struct ip_addr *ip)
-{
-	struct ip_addr src_ip, net_ip;
-	const char *p;
-	unsigned int max_bits, bits;
-
-	if (net_ipv6_mapped_ipv4_convert(ip, &src_ip) == 0)
-		ip = &src_ip;
-
-	max_bits = IPADDR_IS_V4(ip) ? 32 : 128;
-	p = strchr(network, '/');
-	if (p == NULL) {
-		/* full IP address must match */
-		bits = max_bits;
-	} else {
-		/* get the network mask */
-		network = t_strdup_until(network, p);
-		bits = strtoul(p+1, NULL, 10);
-		if (bits > max_bits)
-			bits = max_bits;
-	}
-
-	if (net_addr2ip(network, &net_ip) < 0)
-		return -1;
-
-	return net_is_in_network(ip, &net_ip, bits);
-}
-
 static void auth_request_validate_networks(struct auth_request *request,
 					   const char *networks)
 {
 	const char *const *net;
+	struct ip_addr net_ip;
+	unsigned int bits;
 	bool found = FALSE;
 
 	if (request->remote_ip.family == 0) {
@@ -913,15 +887,14 @@
 	for (net = t_strsplit_spaces(networks, ", "); *net != NULL; net++) {
 		auth_request_log_debug(request, "auth",
 			"allow_nets: Matching for network %s", *net);
-		switch (is_ip_in_network(*net, &request->remote_ip)) {
-		case 1:
-			found = TRUE;
-			break;
-		case -1:
+
+		if (net_parse_range(*net, &net_ip, &bits) < 0) {
 			auth_request_log_info(request, "passdb",
 				"allow_nets: Invalid network '%s'", *net);
-			break;
-		default:
+		}
+
+		if (net_is_in_network(&request->remote_ip, &net_ip, bits)) {
+			found = TRUE;
 			break;
 		}
 	}
diff -r e528d9e3cdc8 src/imap-login/client-authenticate.c
--- a/src/imap-login/client-authenticate.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/imap-login/client-authenticate.c	Sun Jun 01 21:07:12 2008 +0300
@@ -91,12 +91,16 @@
 	string_t *reply;
 	unsigned int port = 143;
 	bool proxy = FALSE, temp = FALSE, nologin = !success, proxy_self;
+	enum imap_proxy_flags proxy_flags = 0;
 
 	for (; *args != NULL; args++) {
 		if (strcmp(*args, "nologin") == 0)
 			nologin = TRUE;
 		else if (strcmp(*args, "proxy") == 0)
 			proxy = TRUE;
+		else if (strcmp(*args, "xforward") == 0 ||
+			 strncmp(*args, "xforward=", 9) == 0)
+			proxy_flags |= IMAP_PROXY_FLAG_XFORWARD;
 		else if (strcmp(*args, "temp") == 0)
 			temp = TRUE;
 		else if (strncmp(*args, "reason=", 7) == 0)
@@ -124,7 +128,8 @@
 		   proxy host=.. [port=..] [destuser=..] pass=.. */
 		if (!success)
 			return FALSE;
-		if (imap_proxy_new(client, host, port, destuser, pass) < 0)
+		if (imap_proxy_new(client, host, port, destuser, pass,
+				   proxy_flags) < 0)
 			client_destroy_internal_failure(client);
 		return TRUE;
 	}
diff -r e528d9e3cdc8 src/imap-login/client.c
--- a/src/imap-login/client.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/imap-login/client.c	Sun Jun 01 21:07:12 2008 +0300
@@ -15,6 +15,8 @@
 #include "auth-client.h"
 #include "ssl-proxy.h"
 #include "imap-proxy.h"
+
+#include <stdlib.h>
 
 /* max. size of one parameter in line, or max reply length in SASL
    authentication */
@@ -216,6 +218,35 @@
 	return 1;
 }
 
+static int cmd_xforward(struct imap_client *client, const struct imap_arg *args)
+{
+	const char *key, *value;
+
+	if (!client_has_xforward_access(&client->common)) {
+		client_send_tagline(client, "NO Permission denied.");
+		return 1;
+	}
+	for (; args->type != IMAP_ARG_EOL; args += 2) {
+		if (args->type != IMAP_ARG_ATOM ||
+		    !IMAP_ARG_TYPE_IS_STRING(args[1].type)) {
+			client_send_tagline(client, "BAD Invalid args.");
+			return 1;
+		}
+		key = IMAP_ARG_STR_NONULL(&args[0]);
+		value = IMAP_ARG_STR_NONULL(&args[1]);
+		if (strcmp(key, "rip") == 0)
+			(void)net_addr2ip(value, &client->common.ip);
+		else if (strcmp(key, "lip") == 0)
+			(void)net_addr2ip(value, &client->common.local_ip);
+		else if (strcmp(key, "rport") == 0)
+			client->common.remote_port = atoi(value);
+		else if (strcmp(key, "lport") == 0)
+			client->common.local_port = atoi(value);
+	}
+	client_send_tagline(client, "OK");
+	return 1;
+}
+
 static int client_command_execute(struct imap_client *client, const char *cmd,
 				  const struct imap_arg *args)
 {
@@ -232,6 +263,8 @@
 		return cmd_noop(client);
 	if (strcmp(cmd, "LOGOUT") == 0)
 		return cmd_logout(client);
+	if (strcmp(cmd, "XFORWARD") == 0)
+		return cmd_xforward(client, args);
 
 	return -1;
 }
diff -r e528d9e3cdc8 src/imap-login/client.h
--- a/src/imap-login/client.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/imap-login/client.h	Sun Jun 01 21:07:12 2008 +0300
@@ -4,6 +4,10 @@
 #include "network.h"
 #include "master.h"
 #include "client-common.h"
+
+enum imap_proxy_flags {
+	IMAP_PROXY_FLAG_XFORWARD	= 0x01
+};
 
 struct imap_client {
 	struct client common;
@@ -19,6 +23,7 @@
 
 	struct login_proxy *proxy;
 	char *proxy_user, *proxy_password;
+	enum imap_proxy_flags proxy_flags;
 
 	unsigned int bad_counter;
 
diff -r e528d9e3cdc8 src/imap-login/imap-proxy.c
--- a/src/imap-login/imap-proxy.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/imap-login/imap-proxy.c	Sun Jun 01 21:07:12 2008 +0300
@@ -29,8 +29,18 @@
 			return -1;
 		}
 
+		str = t_str_new(128);
+		if ((client->proxy_flags & IMAP_PROXY_FLAG_XFORWARD) != 0) {
+			/* Send XFORWARD command */
+			str_printfa(str, "F XFORWARD rip %s rport %u "
+				    "lip %s lport %u\r\n",
+				    net_ip2addr(&client->common.ip),
+				    client->common.remote_port,
+				    net_ip2addr(&client->common.local_ip),
+				    client->common.local_port);
+		}
+
 		/* 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, ' ');
@@ -146,7 +156,8 @@
 }
 
 int imap_proxy_new(struct imap_client *client, const char *host,
-		   unsigned int port, const char *user, const char *password)
+		   unsigned int port, const char *user, const char *password,
+		   enum imap_proxy_flags flags)
 {
 	i_assert(user != NULL);
 	i_assert(!client->destroyed);
@@ -173,6 +184,7 @@
 	client->proxy_login_sent = FALSE;
 	client->proxy_user = i_strdup(user);
 	client->proxy_password = i_strdup(password);
+	client->proxy_flags = flags;
 
 	/* disable input until authentication is finished */
 	if (client->io != NULL)
diff -r e528d9e3cdc8 src/imap-login/imap-proxy.h
--- a/src/imap-login/imap-proxy.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/imap-login/imap-proxy.h	Sun Jun 01 21:07:12 2008 +0300
@@ -4,6 +4,7 @@
 #include "login-proxy.h"
 
 int imap_proxy_new(struct imap_client *client, const char *host,
-		   unsigned int port, const char *user, const char *password);
+		   unsigned int port, const char *user, const char *password,
+		   enum imap_proxy_flags flags);
 
 #endif
diff -r e528d9e3cdc8 src/lib/network.c
--- a/src/lib/network.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/lib/network.c	Sun Jun 01 21:07:12 2008 +0300
@@ -5,6 +5,7 @@
 #include "fd-set-nonblock.h"
 #include "network.h"
 
+#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <ctype.h>
@@ -704,12 +705,45 @@
 	return TRUE;
 }
 
+int net_parse_range(const char *network, struct ip_addr *ip_r,
+		    unsigned int *bits_r)
+{
+	const char *p;
+	int bits, max_bits;
+
+	p = strchr(network, '/');
+	if (p != NULL)
+		network = t_strdup_until(network, p++);
+
+	if (net_addr2ip(network, ip_r) < 0)
+		return -1;
+
+	max_bits = IPADDR_IS_V4(ip_r) ? 32 : 128;
+	if (p == NULL) {
+		/* full IP address must match */
+		bits = max_bits;
+	} else {
+		/* get the network mask */
+		bits = atoi(p);
+		if (bits < 0 || bits > max_bits)
+			return -1;
+	}
+	*bits_r = bits;
+	return 0;
+}
+
 bool net_is_in_network(const struct ip_addr *ip,
 		       const struct ip_addr *net_ip, unsigned int bits)
 {
+	struct ip_addr tmp_ip;
 	const uint32_t *ip1, *ip2;
 	uint32_t mask, i1, i2;
 	unsigned int pos, i;
+
+	if (net_ipv6_mapped_ipv4_convert(ip, &tmp_ip) == 0) {
+		/* IPv4 address mapped disguised as IPv6 address */
+		ip = &tmp_ip;
+	}
 
 	if (IPADDR_IS_V4(ip) != IPADDR_IS_V4(net_ip)) {
 		/* one is IPv6 and one is IPv4 */
diff -r e528d9e3cdc8 src/lib/network.h
--- a/src/lib/network.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/lib/network.h	Sun Jun 01 21:07:12 2008 +0300
@@ -110,7 +110,11 @@
 bool is_ipv4_address(const char *addr);
 bool is_ipv6_address(const char *addr);
 
-/* Returns TRUE if ip is in net_ip/bits network. */
+/* Parse network as ip/bits. Returns 0 if successful, -1 if invalid input. */
+int net_parse_range(const char *network, struct ip_addr *ip_r,
+		    unsigned int *bits_r);
+/* Returns TRUE if ip is in net_ip/bits network. IPv6 mapped IPv4 addresses
+   are converted to plain IPv4 addresses before matching. */
 bool net_is_in_network(const struct ip_addr *ip,
 		       const struct ip_addr *net_ip, unsigned int bits);
 
diff -r e528d9e3cdc8 src/login-common/client-common.c
--- a/src/login-common/client-common.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/login-common/client-common.c	Sun Jun 01 21:07:12 2008 +0300
@@ -151,3 +151,25 @@
 		client_syslog_real(client, msg);
 	} T_END;
 }
+
+bool client_has_xforward_access(struct client *client)
+{
+	const char *const *net;
+	struct ip_addr net_ip;
+	unsigned int bits;
+
+	if (xforward_networks == NULL)
+		return FALSE;
+
+	net = t_strsplit_spaces(xforward_networks, ", ");
+	for (; *net != NULL; net++) {
+		if (net_parse_range(*net, &net_ip, &bits) < 0) {
+			i_error("xforward: Invalid network '%s'", *net);
+			break;
+		}
+
+		if (net_is_in_network(&client->ip, &net_ip, bits))
+			return TRUE;
+	}
+	return FALSE;
+}
diff -r e528d9e3cdc8 src/login-common/client-common.h
--- a/src/login-common/client-common.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/login-common/client-common.h	Sun Jun 01 21:07:12 2008 +0300
@@ -42,6 +42,7 @@
 unsigned int clients_get_count(void);
 
 void client_syslog(struct client *client, const char *msg);
+bool client_has_xforward_access(struct client *client);
 
 void clients_notify_auth_connected(void);
 void client_destroy_oldest(void);
diff -r e528d9e3cdc8 src/login-common/common.h
--- a/src/login-common/common.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/login-common/common.h	Sun Jun 01 21:07:12 2008 +0300
@@ -18,6 +18,7 @@
 extern const char *greeting, *log_format;
 extern const char *const *log_format_elements;
 extern const char *capability_string;
+extern const char *xforward_networks;
 extern unsigned int max_connections;
 extern unsigned int login_process_uid;
 extern struct auth_client *auth_client;
diff -r e528d9e3cdc8 src/login-common/main.c
--- a/src/login-common/main.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/login-common/main.c	Sun Jun 01 21:07:12 2008 +0300
@@ -23,6 +23,7 @@
 bool verbose_proctitle, verbose_ssl, verbose_auth;
 const char *greeting, *log_format;
 const char *const *log_format_elements;
+const char *xforward_networks;
 unsigned int max_connections;
 unsigned int login_process_uid;
 struct auth_client *auth_client;
@@ -320,6 +321,8 @@
 	if (log_format == NULL)
 		log_format = "%$: %s";
 
+	xforward_networks = getenv("XFORWARD_NETWORKS");
+
 	value = getenv("PROCESS_UID");
 	if (value == NULL)
 		i_fatal("BUG: PROCESS_UID environment not given");
diff -r e528d9e3cdc8 src/master/login-process.c
--- a/src/master/login-process.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/master/login-process.c	Sun Jun 01 21:07:12 2008 +0300
@@ -580,6 +580,10 @@
 				    set->imap_capability :
 				    set->imap_generated_capability, NULL));
 	}
+	if (*set->xforward_networks != '\0') {
+		env_put(t_strconcat("XFORWARD_NETWORKS=",
+				    set->xforward_networks, NULL));
+	}
 }
 
 static pid_t create_login_process(struct login_group *group)
diff -r e528d9e3cdc8 src/master/master-settings-defs.c
--- a/src/master/master-settings-defs.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/master/master-settings-defs.c	Sun Jun 01 21:07:12 2008 +0300
@@ -114,6 +114,7 @@
 	DEF_STR(imap_capability),
 	DEF_STR(imap_client_workarounds),
 	DEF_STR(imap_logout_format),
+	DEF_STR(xforward_networks),
 
 	/* pop3 */
 	DEF_BOOL(pop3_no_flag_updates),
diff -r e528d9e3cdc8 src/master/master-settings.c
--- a/src/master/master-settings.c	Sat May 31 14:27:27 2008 +0300
+++ b/src/master/master-settings.c	Sun Jun 01 21:07:12 2008 +0300
@@ -280,6 +280,7 @@
 	MEMBER(imap_capability) "",
 	MEMBER(imap_client_workarounds) "",
 	MEMBER(imap_logout_format) "bytes=%i/%o",
+	MEMBER(xforward_networks) "",
 
 	/* pop3 */
 	MEMBER(pop3_no_flag_updates) FALSE,
diff -r e528d9e3cdc8 src/master/master-settings.h
--- a/src/master/master-settings.h	Sat May 31 14:27:27 2008 +0300
+++ b/src/master/master-settings.h	Sun Jun 01 21:07:12 2008 +0300
@@ -126,6 +126,7 @@
 	const char *imap_capability;
 	const char *imap_client_workarounds;
 	const char *imap_logout_format;
+	const char *xforward_networks;
 
 	/* pop3 */
 	bool pop3_no_flag_updates;
