client.c revision 2cfe9983ce7a6280636ee12beccc2e865111967b
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2002 Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* max. size of one parameter in line, or max reply length in SASL
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen authentication */
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/* maximum length for IMAP command line. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Disconnect client after idling this many seconds */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Disconnect client when it sends too many bad commands */
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. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# error client idle timeout must be smaller than authentication timeout
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenconst char *capability_string = CAPABILITY_STRING;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic void client_set_title(struct imap_client *client)
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen if (!verbose_proctitle || !process_per_connection)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->input = i_stream_create_file(fd, default_pool,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
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)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const unsigned char *data;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen data = i_stream_get_data(client->input, &data_size);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen for (i = 0; i < data_size; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const char *get_capability(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auths = client_authenticate_get_capabilities(client->common.secured);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen disable_plaintext_auth && !client->common.secured ?
e2eac5bb5637c2d4aaf453389750740931822b92Timo Sirainenstatic int cmd_capability(struct imap_client *client)
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen client_send_line(client, t_strconcat("* CAPABILITY ",
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen client_send_tagline(client, "OK Capability completed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_start_tls(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected: TLS initialization failed.");
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen /* CRLF is lost from buffer when streams are reopened. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainenstatic int client_output_starttls(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_unset_flush_callback(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
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 client_send_tagline(client, "OK Begin TLS negotiation now.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* uncork the old fd */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* the buffer has to be flushed */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int cmd_noop(struct imap_client *client)
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenstatic int cmd_logout(struct imap_client *client)
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen client_send_line(client, "* BYE Logging out");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, "OK Logout completed.");
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *msg;
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 /* remove \r\n */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen msg = imap_parser_get_error(client->parser, &fatal);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client_send_line(client, t_strconcat("* BYE ",
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen client_destroy(client, t_strconcat("Disconnected: ",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen /* not enough data */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = client_command_execute(client, client->cmd_name, args);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen "* BYE Too many invalid IMAP commands.");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen "Too many invalid commands");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen "BAD Error in IMAP command received by server.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret != 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* buffer full */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen /* disconnected */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* something was read */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we're not yet connected to auth process -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen don't allow any commands */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "* OK Waiting for authentication process to respond..");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen unsigned int i, destroy_count;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
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 for (i = 0; i < destroy_count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* @UNSAFE */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* then kill them */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < destroy_count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected: Connection queue full");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_send_greeting(struct imap_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(auth_client_is_connected(auth_client));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* always use nonblocking I/O */
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen client->common.secured = ssl || net_ip_compare(ip, local_ip);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!greeting_capability || auth_client_is_connected(auth_client))
main_unref();
return TRUE;
return FALSE;
unsigned int clients_get_count(void)
void clients_notify_auth_connected(void)
void clients_destroy_all(void)
void clients_init(void)
void clients_deinit(void)