view src/imap-login/imap-proxy.c @ 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 d344be0bb70f
children 9aaa737f8215
line wrap: on
line source

/* Copyright (C) 2004 Timo Sirainen */

#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 "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)
{
	i_assert(user != NULL);

	if (password == NULL) {
		i_error("proxy(%s): password not given",
			client->common.virtual_user);
		return -1;
	}

	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;
	}

	return 0;
}