imap-proxy.c revision b3ae85f3552d98bb2f7a6efd8039e1d3f0423058
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2004 Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "common.h"
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "ostream.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "safe-memset.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "imap-quote.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "imap-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int proxy_input_line(struct imap_client *client,
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen struct ostream *output, const char *line)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *str;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!client->destroyed);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->proxy_login_sent) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* this is a banner */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strncmp(line, "* OK ", 5) != 0) {
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen i_error("imap-proxy(%s): "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Remote returned invalid banner: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.virtual_user, line);
afa201e7e1d2447e8dfa1aff43de0fdad564105fTimo Sirainen client_destroy_internal_failure(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* send LOGIN command */
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen str = t_str_new(128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "P LOGIN ");
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen imap_quote_append_string(str, client->proxy_user, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(str, ' ');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen imap_quote_append_string(str, client->proxy_password, FALSE);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen str_append(str, "\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)o_stream_send(output, str_data(str), str_len(str));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen safe_memset(client->proxy_password, 0,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strlen(client->proxy_password));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(client->proxy_password);
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen client->proxy_password = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy_login_sent = TRUE;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return 0;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen } else if (strncmp(line, "P OK ", 5) == 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen /* Login successful. Send this line to client. */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (void)o_stream_send_str(client->output, client->cmd_tag);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (void)o_stream_send_str(client->output, line + 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)o_stream_send(client->output, "\r\n", 2);
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)client_skip_line(client);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen login_proxy_detach(client->proxy, client->input,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen client->output);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen client->proxy = NULL;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen client->input = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->output = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.fd = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, t_strdup_printf("proxy(%s): started",
d22301419109ed4a38351715e6760011421dadecTimo Sirainen client->common.virtual_user));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (strncmp(line, "P ", 2) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Login failed. Send our own failure reply so client can't
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen figure out if user exists or not just by looking at the
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reply string. */
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen client_send_tagline(client, "NO "AUTH_FAILED_MSG);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* allow client input again */
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen i_assert(client->io == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen client_input, client);
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_proxy_free(client->proxy);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen i_free(client->proxy_user);
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen client->proxy_user = NULL;
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen client_unref(client);
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen return -1;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen } else {
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen /* probably some untagged reply */
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen return 0;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen }
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen}
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainenstatic void proxy_input(struct istream *input, struct ostream *output,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen void *context)
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen{
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen struct imap_client *client = context;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *line;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (input == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* remote authentication failed, we're just
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen freeing the proxy */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* failed for some reason, probably server disconnected */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "* BYE Temporary login failure.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (i_stream_read(input)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case -2:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* buffer full */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("imap-proxy(%s): Remote input buffer full",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.virtual_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy_internal_failure(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case -1:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* disconnected */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Proxy: Remote disconnected");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (proxy_input_line(client, output, line) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint imap_proxy_new(struct imap_client *client, const char *host,
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen unsigned int port, const char *user, const char *password)
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen{
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen i_assert(user != NULL);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen i_assert(!client->destroyed);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen if (password == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("proxy(%s): password not given",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.virtual_user);
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen return -1;
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen }
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->refcount > 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen connection_queue_add(1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->destroyed) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* connection_queue_add() decided that we were the oldest
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen connection and killed us. */
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen return -1;
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen }
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy = login_proxy_new(&client->common, host, port,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen proxy_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->proxy == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy_login_sent = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy_user = i_strdup(user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->proxy_password = i_strdup(password);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* disable input until authentication is finished */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
fa7c76955c6bc62689fbdf39318194f85905e6e2Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen