dovecot-1.2: imap-proxy: Send backend's CAPABILITY if it's diffe...

dovecot at dovecot.org dovecot at dovecot.org
Wed Apr 22 20:54:59 EEST 2009


details:   http://hg.dovecot.org/dovecot-1.2/rev/f8404c0f14de
changeset: 8979:f8404c0f14de
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Apr 22 13:54:54 2009 -0400
description:
imap-proxy: Send backend's CAPABILITY if it's different from what was sent to client before.

diffstat:

3 files changed, 89 insertions(+), 20 deletions(-)
src/imap-login/client.c     |    2 
src/imap-login/client.h     |    2 
src/imap-login/imap-proxy.c |  105 ++++++++++++++++++++++++++++++++++---------

diffs (218 lines):

diff -r 2e3baa171e20 -r f8404c0f14de src/imap-login/client.c
--- a/src/imap-login/client.c	Wed Apr 22 12:28:22 2009 -0400
+++ b/src/imap-login/client.c	Wed Apr 22 13:54:54 2009 -0400
@@ -105,6 +105,7 @@ static const char *get_capability(struct
 
 static int cmd_capability(struct imap_client *client)
 {
+	client->capability_command_used = TRUE;
 	client_send_line(client, t_strconcat(
 		"* CAPABILITY ", get_capability(client, TRUE), NULL));
 	client_send_tagline(client, "OK Capability completed.");
@@ -588,6 +589,7 @@ void client_destroy(struct imap_client *
 
 	i_free_and_null(client->proxy_user);
 	i_free_and_null(client->proxy_master_user);
+	i_free_and_null(client->proxy_backend_capability);
 
 	if (client->proxy != NULL)
 		login_proxy_free(&client->proxy);
diff -r 2e3baa171e20 -r f8404c0f14de src/imap-login/client.h
--- a/src/imap-login/client.h	Wed Apr 22 12:28:22 2009 -0400
+++ b/src/imap-login/client.h	Wed Apr 22 13:54:54 2009 -0400
@@ -22,6 +22,7 @@ struct imap_client {
 
 	struct login_proxy *proxy;
 	char *proxy_user, *proxy_master_user, *proxy_password;
+	char *proxy_backend_capability;
 
 	unsigned int bad_counter;
 
@@ -36,6 +37,7 @@ struct imap_client {
 	unsigned int greeting_sent:1;
 	unsigned int id_logged:1;
 	unsigned int auth_initializing:1;
+	unsigned int capability_command_used:1;
 };
 
 void client_destroy(struct imap_client *client, const char *reason);
diff -r 2e3baa171e20 -r f8404c0f14de src/imap-login/imap-proxy.c
--- a/src/imap-login/imap-proxy.c	Wed Apr 22 12:28:22 2009 -0400
+++ b/src/imap-login/imap-proxy.c	Wed Apr 22 13:54:54 2009 -0400
@@ -1,6 +1,7 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
+#include "array.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
@@ -14,26 +15,35 @@
 #include "imap-quote.h"
 #include "imap-proxy.h"
 
+#include <stdlib.h>
+
 #define PROXY_FAILURE_MSG \
 	"NO ["IMAP_RESP_CODE_UNAVAILABLE"] "AUTH_TEMP_FAILED_MSG
 
-static bool imap_banner_has_capability(const char *line, const char *capability)
-{
-	unsigned int capability_len = strlen(capability);
-
-	while (strncasecmp(line, capability, capability_len) != 0 ||
-	       (line[capability_len] != ' ' &&
-		line[capability_len] != '\0' &&
-		line[capability_len] != ']')) {
-		/* skip over the capability */
-		while (*line != ' ') {
-			if (*line == '\0')
-				return FALSE;
-			line++;
-		}
-		line++;
-	}
-	return TRUE;
+static const char *const *
+capabilities_strip_prelogin(const char *const *capabilities)
+{
+	ARRAY_TYPE(const_string) new_caps_arr;
+	const char **new_caps, *str;
+	unsigned int count;
+
+	t_array_init(&new_caps_arr, 64);
+	for (; *capabilities != NULL; capabilities++) {
+		if (strncasecmp(*capabilities, "AUTH=", 5) == 0 ||
+		    strcasecmp(*capabilities, "STARTTLS") == 0 ||
+		    strcasecmp(*capabilities, "SASL-IR") == 0 ||
+		    strcasecmp(*capabilities, "LOGINDISABLED") == 0 ||
+		    strcasecmp(*capabilities, "LOGIN-REFERRALS") == 0)
+			continue;
+
+		str = *capabilities;
+		array_append(&new_caps_arr, &str, 1);
+	}
+	new_caps = array_get_modifiable(&new_caps_arr, &count);
+	qsort(new_caps, count, sizeof(*new_caps), i_strcasecmp_p);
+
+	(void)array_append_space(&new_caps_arr);
+	return array_idx(&new_caps_arr, 0);
 }
 
 static void proxy_write_id(struct imap_client *client, string_t *str)
@@ -85,9 +95,51 @@ static void get_plain_auth(struct imap_c
 	base64_encode(str_data(str), str_len(str), dest);
 }
 
+static bool str_array_icmp(const char *const *arr1, const char *const *arr2)
+{
+	unsigned int i;
+
+	for (i = 0; arr1[i] != NULL; i++) {
+		if (arr2[i] == NULL || strcasecmp(arr1[i], arr2[i]) != 0)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+client_send_capability_if_needed(struct imap_client *client, string_t *str,
+				 const char *capability)
+{
+	const char *const *backend_capabilities;
+	const char *const *proxy_capabilities;
+
+	if (!client->capability_command_used || capability == NULL)
+		return;
+
+	/* reset this so that we don't re-send the CAPABILITY in case server
+	   sends it multiple times */
+	client->capability_command_used = FALSE;
+
+	/* client has used CAPABILITY command, so it didn't understand the
+	   capabilities in the banner. if backend server has different
+	   capabilities than we advertised already, there's a problem.
+	   to solve that we'll send the backend's untagged CAPABILITY reply
+	   and hope that the client understands it */
+	backend_capabilities =
+		capabilities_strip_prelogin(t_strsplit(capability, " "));
+	proxy_capabilities =
+		capabilities_strip_prelogin(t_strsplit(capability_string, " "));
+
+	if (str_array_icmp(backend_capabilities, proxy_capabilities))
+		return;
+
+	str_printfa(str, "* CAPABILITY %s\r\n", capability);
+}
+
 static int proxy_input_banner(struct imap_client *client,
 			      struct ostream *output, const char *line)
 {
+	const char *const *capabilities = NULL;
 	string_t *str;
 
 	if (strncmp(line, "* OK ", 5) != 0) {
@@ -99,9 +151,12 @@ static int proxy_input_banner(struct ima
 
 	str = t_str_new(128);
 	if (strncmp(line + 5, "[CAPABILITY ", 12) == 0) {
-		if (imap_banner_has_capability(line + 5 + 12, "ID"))
+		capabilities = t_strsplit(t_strcut(line + 5 + 12, ']'), " ");
+		if (str_array_icase_find(capabilities, "ID"))
 			proxy_write_id(client, str);
 	}
+	if (client->capability_command_used)
+		str_append(str, "C CAPABILITY\r\n");
 
 	if (client->proxy_master_user == NULL) {
 		/* logging in normally - use LOGIN command */
@@ -111,7 +166,8 @@ static int proxy_input_banner(struct ima
 		imap_quote_append_string(str, client->proxy_password, FALSE);
 
 		proxy_free_password(client);
-	} else if (imap_banner_has_capability(line + 5, "SASL-IR")) {
+	} else if (capabilities != NULL &&
+		   str_array_icase_find(capabilities, "SASL-IR")) {
 		/* master user login with SASL initial response support */
 		str_append(str, "L AUTHENTICATE PLAIN ");
 		get_plain_auth(client, str);
@@ -120,7 +176,6 @@ static int proxy_input_banner(struct ima
 		/* master user login without SASL initial response */
 		str_append(str, "L AUTHENTICATE PLAIN");
 	}
-
 	str_append(str, "\r\n");
 	(void)o_stream_send(output, str_data(str), str_len(str));
 	client->proxy_login_sent = TRUE;
@@ -130,6 +185,7 @@ static int proxy_input_line(struct imap_
 static int proxy_input_line(struct imap_client *client,
 			    struct ostream *output, const char *line)
 {
+	const char *capability;
 	string_t *str;
 
 	i_assert(!client->destroyed);
@@ -152,7 +208,12 @@ static int proxy_input_line(struct imap_
 		return 0;
 	} else if (strncmp(line, "L OK ", 5) == 0) {
 		/* Login successful. Send this line to client. */
+		capability = client->proxy_backend_capability;
+		if (strncmp(line + 5, "[CAPABILITY ", 12) == 0)
+			capability = t_strcut(line + 5 + 12, ']');
+
 		str = t_str_new(128);
+		client_send_capability_if_needed(client, str, capability);
 		str_append(str, client->cmd_tag);
 		str_append(str, line + 1);
 		str_append(str, "\r\n");
@@ -235,6 +296,10 @@ static int proxy_input_line(struct imap_
 
 		proxy_failed(client, FALSE);
 		return -1;
+	} else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
+		i_free(client->proxy_backend_capability);
+		client->proxy_backend_capability = i_strdup(line + 13);
+		return 0;
 	} else {
 		/* probably some untagged reply */
 		return 0;


More information about the dovecot-cvs mailing list