client.c revision 2cfe9983ce7a6280636ee12beccc2e865111967b
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2002 Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "common.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "buffer.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "hash.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ostream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "process-title.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "safe-memset.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "strescape.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "imap-parser.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client-authenticate.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "auth-client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ssl-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "imap-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* max. size of one parameter in line, or max reply length in SASL
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen authentication */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAX_INBUF_SIZE 4096
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen SASL authentication gives the largest output. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAX_OUTBUF_SIZE 4096
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* maximum length for IMAP command line. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAX_IMAP_LINE 8192
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Disconnect client after idling this many seconds */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define CLIENT_LOGIN_IDLE_TIMEOUT (3*60)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Disconnect client when it sends too many bad commands */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define CLIENT_MAX_BAD_COMMANDS 10
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen oldest connections are disconnected. Since we have to go through the whole
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen client hash, it's faster if we disconnect multiple clients. */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen#define CLIENT_DESTROY_OLDEST_COUNT 16
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# error client idle timeout must be smaller than authentication timeout
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenconst char *login_protocol = "IMAP";
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenconst char *capability_string = CAPABILITY_STRING;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic struct hash_table *clients;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic struct timeout *to_idle;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic void client_set_title(struct imap_client *client)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const char *addr;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen if (!verbose_proctitle || !process_per_connection)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen return;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen addr = net_ip2addr(&client->common.ip);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (addr == NULL)
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen addr = "??";
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen "[%s TLS]" : "[%s]", addr));
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->input = i_stream_create_file(fd, default_pool,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MAX_INBUF_SIZE, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MAX_IMAP_LINE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Skip incoming data until newline is found,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen returns TRUE if newline was found. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool client_skip_line(struct imap_client *client)
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const unsigned char *data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t i, data_size;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen data = i_stream_get_data(client->input, &data_size);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen for (i = 0; i < data_size; i++) {
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen if (data[i] == '\n') {
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen i_stream_skip(client->input, i+1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const char *get_capability(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *auths;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auths = client_authenticate_get_capabilities(client->common.secured);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return t_strconcat(capability_string,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (ssl_initialized && !client->common.tls) ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen " STARTTLS" : "",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen disable_plaintext_auth && !client->common.secured ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen " LOGINDISABLED" : "", auths, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e2eac5bb5637c2d4aaf453389750740931822b92Timo Sirainenstatic int cmd_capability(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen client_send_line(client, t_strconcat("* CAPABILITY ",
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen get_capability(client), NULL));
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen client_send_tagline(client, "OK Capability completed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_start_tls(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen int fd_ssl;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen client_ref(client);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen connection_queue_add(1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client_unref(client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &client->common.proxy);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fd_ssl == -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected: TLS initialization failed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen client->common.tls = TRUE;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen client->common.secured = TRUE;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen client_set_title(client);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen client->common.fd = fd_ssl;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen i_stream_unref(&client->input);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen o_stream_unref(&client->output);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen imap_parser_destroy(&client->parser);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen /* CRLF is lost from buffer when streams are reopened. */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen client->skip_line = FALSE;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen client_open_streams(client, fd_ssl);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainenstatic int client_output_starttls(struct imap_client *client)
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_unset_flush_callback(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_start_tls(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->common.tls) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (!ssl_initialized) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen }
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen remove it in case we have to wait for buffer to be flushed */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* uncork the old fd */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_uncork(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (o_stream_flush(client->output) <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* the buffer has to be flushed */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_set_flush_callback(client->output,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_output_starttls, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_start_tls(client);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int cmd_noop(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenstatic int cmd_logout(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen client_send_line(client, "* BYE Logging out");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "OK Logout completed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Aborted login");
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen struct imap_arg *args)
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen{
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen cmd = t_str_ucase(cmd);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen if (strcmp(cmd, "LOGIN") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_login(client, args);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(cmd, "AUTHENTICATE") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_authenticate(client, args);
836e57b1e7817d008f8ae05cd4b506f420fed80dTimo Sirainen if (strcmp(cmd, "CAPABILITY") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_capability(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(cmd, "STARTTLS") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_starttls(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(cmd, "NOOP") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_noop(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(cmd, "LOGOUT") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return cmd_logout(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct imap_arg *args;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *msg;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool fatal;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!client->common.authenticating);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->cmd_finished) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* clear the previous command from memory. don't do this
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen immediately after handling command since we need the
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen cmd_tag to stay some time after authentication commands. */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_tag = NULL;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_name = NULL;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen imap_parser_reset(client->parser);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* remove \r\n */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (client->skip_line) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (!client_skip_line(client))
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return FALSE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->skip_line = FALSE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_finished = FALSE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (client->cmd_tag == NULL) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (client->cmd_tag == NULL)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return FALSE; /* need more data */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (client->cmd_name == NULL) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (client->cmd_name == NULL)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return FALSE; /* need more data */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case -1:
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* error */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen msg = imap_parser_get_error(client->parser, &fatal);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (fatal) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client_send_line(client, t_strconcat("* BYE ",
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen msg, NULL));
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client_destroy(client, t_strconcat("Disconnected: ",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen msg, NULL));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->cmd_finished = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->skip_line = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case -2:
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen /* not enough data */
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen return FALSE;
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen }
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen client->skip_line = TRUE;
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen if (*client->cmd_tag == '\0')
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen ret = -1;
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen else
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = client_command_execute(client, client->cmd_name, args);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen client->cmd_finished = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (*client->cmd_tag == '\0')
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->cmd_tag = "*";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client_send_line(client,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen "* BYE Too many invalid IMAP commands.");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen client_destroy(client, "Disconnected: "
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen "Too many invalid commands");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen return FALSE;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen client_send_tagline(client,
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen "BAD Error in IMAP command received by server.");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret != 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool client_read(struct imap_client *client)
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen{
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen switch (i_stream_read(client->input)) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case -2:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* buffer full */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen return FALSE;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen case -1:
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen /* disconnected */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen default:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* something was read */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_input(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->last_input = ioloop_time;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if (!client_read(client))
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_ref(client);
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!auth_client_is_connected(auth_client)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we're not yet connected to auth process -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen don't allow any commands */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen client_send_line(client,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "* OK Waiting for authentication process to respond..");
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen client->input_blocked = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_cork(client->output);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen while (client_handle_input(client)) ;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen o_stream_uncork(client->output);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen client_unref(client);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen}
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainenvoid client_destroy_oldest(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen struct hash_iterate_context *iter;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen void *key, *value;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen unsigned int i, destroy_count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen memset(destroy_buf, 0, sizeof(destroy_buf));
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
d22301419109ed4a38351715e6760011421dadecTimo Sirainen CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iter = hash_iterate_init(clients);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen while (hash_iterate(iter, &key, &value)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct imap_client *client = key;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < destroy_count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (destroy_buf[i] == NULL ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen destroy_buf[i]->created > client->created) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* @UNSAFE */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memmove(destroy_buf+i+1, destroy_buf+i,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(destroy_buf) -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (i+1) * sizeof(struct imap_client *));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen destroy_buf[i] = client;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen hash_iterate_deinit(iter);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* then kill them */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < destroy_count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (destroy_buf[i] == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(destroy_buf[i],
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected: Connection queue full");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_send_greeting(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *greet;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen greet = t_str_new(128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(greet, "* OK ");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (greeting_capability) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(auth_client_is_connected(auth_client));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(greet, greeting);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, str_c(greet));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->greeting_sent = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct ip_addr *ip)
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct imap_client *client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(fd != -1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen connection_queue_add(1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* always use nonblocking I/O */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen net_set_nonblock(fd, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client = i_new(struct imap_client, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->created = ioloop_time;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->refcount = 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.tls = ssl;
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen client->common.secured = ssl || net_ip_compare(ip, local_ip);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.local_ip = *local_ip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.ip = *ip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.fd = fd;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen client_open_streams(client, fd);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen client->last_input = ioloop_time;
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen hash_insert(clients, client, client);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen main_ref();
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!greeting_capability || auth_client_is_connected(auth_client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_greeting(client);
client_set_title(client);
return &client->common;
}
void client_destroy(struct imap_client *client, const char *reason)
{
if (client->destroyed)
return;
client->destroyed = TRUE;
if (reason != NULL)
client_syslog(&client->common, reason);
hash_remove(clients, client);
if (client->input != NULL)
i_stream_close(client->input);
if (client->output != NULL)
o_stream_close(client->output);
if (client->common.master_tag != 0)
master_request_abort(&client->common);
if (client->common.auth_request != NULL) {
i_assert(client->common.authenticating);
sasl_server_auth_client_error(&client->common, NULL);
} else {
i_assert(!client->common.authenticating);
}
if (client->io != NULL)
io_remove(&client->io);
if (client->common.fd != -1) {
net_disconnect(client->common.fd);
client->common.fd = -1;
}
if (client->proxy_password != NULL) {
safe_memset(client->proxy_password, 0,
strlen(client->proxy_password));
i_free(client->proxy_password);
client->proxy_password = NULL;
}
i_free(client->proxy_user);
client->proxy_user = NULL;
if (client->proxy != NULL) {
login_proxy_free(client->proxy);
client->proxy = NULL;
}
if (client->common.proxy != NULL) {
ssl_proxy_free(client->common.proxy);
client->common.proxy = NULL;
}
client_unref(client);
main_listen_start();
main_unref();
}
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, "Internal login failure");
}
void client_ref(struct imap_client *client)
{
client->refcount++;
}
bool client_unref(struct imap_client *client)
{
i_assert(client->refcount > 0);
if (--client->refcount > 0)
return TRUE;
i_assert(client->destroyed);
imap_parser_destroy(&client->parser);
if (client->input != NULL)
i_stream_unref(&client->input);
if (client->output != NULL)
o_stream_unref(&client->output);
i_free(client->common.virtual_user);
i_free(client->common.auth_mech_name);
i_free(client);
return FALSE;
}
void client_send_line(struct imap_client *client, const char *line)
{
struct const_iovec iov[2];
ssize_t ret;
iov[0].iov_base = line;
iov[0].iov_len = strlen(line);
iov[1].iov_base = "\r\n";
iov[1].iov_len = 2;
ret = o_stream_sendv(client->output, iov, 2);
if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
/* either disconnection or buffer full. in either case we
want this connection destroyed. however destroying it here
might break things if client is still tried to be accessed
without being referenced.. */
i_stream_close(client->input);
}
}
void client_send_tagline(struct imap_client *client, const char *line)
{
client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
}
static void client_check_idle(struct imap_client *client)
{
if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
client_send_line(client, "* BYE Disconnected for inactivity.");
client_destroy(client, "Disconnected: Inactivity");
}
}
static void idle_timeout(void *context __attr_unused__)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct imap_client *client = key;
client_check_idle(client);
}
hash_iterate_deinit(iter);
}
unsigned int clients_get_count(void)
{
return hash_size(clients);
}
void clients_notify_auth_connected(void)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct imap_client *client = key;
if (!client->greeting_sent)
client_send_greeting(client);
if (client->input_blocked) {
client->input_blocked = FALSE;
client_input(client);
}
}
hash_iterate_deinit(iter);
}
void clients_destroy_all(void)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct imap_client *client = key;
client_destroy(client, "Disconnected: Shutting down");
}
hash_iterate_deinit(iter);
}
void clients_init(void)
{
clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
to_idle = timeout_add(1000, idle_timeout, NULL);
}
void clients_deinit(void)
{
clients_destroy_all();
hash_destroy(clients);
timeout_remove(&to_idle);
}