client.c revision 345648b341f228bd7f0b89f8aa3ecb9c470d817e
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#include "common.h"
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#include "buffer.h"
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen#include "hash.h"
c5f932968281763df360b9c97cef60f5f80d5e3dTimo Sirainen#include "ioloop.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "istream.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ostream.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "process-title.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "safe-memset.h"
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#include "strescape.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "imap-parser.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "client.h"
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen#include "client-authenticate.h"
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen#include "ssl-proxy.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* max. size of one parameter in line */
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen#define MAX_INBUF_SIZE 512
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen#define MAX_OUTBUF_SIZE 1024
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* max. number of IMAP argument elements to accept. The maximum memory usage
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#define MAX_IMAP_ARG_ELEMENTS 4
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch/* Disconnect client after idling this many seconds */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch#define CLIENT_LOGIN_IDLE_TIMEOUT 60
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch/* Disconnect client when it sends too many bad commands */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch#define CLIENT_MAX_BAD_COMMANDS 10
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch/* When max. number of simultaneous connections is reached, few of the
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch oldest connections are disconnected. Since we have to go through the whole
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch client hash, it's faster if we disconnect multiple clients. */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch#define CLIENT_DESTROY_OLDEST_COUNT 16
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch# error client idle timeout must be smaller than authentication timeout
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch#endif
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic struct hash_table *clients;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic struct timeout *to_idle;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic void client_set_title(struct imap_client *client)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch{
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch const char *host;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (!verbose_proctitle || !process_per_connection)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch return;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch host = net_ip2host(&client->common.ip);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (host == NULL)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch host = "??";
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch host));
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen{
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen client->input = i_stream_create_file(fd, default_pool,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAX_INBUF_SIZE, FALSE);
d1e7425048c61d71f41f737ba947687198842dc2Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IO_PRIORITY_DEFAULT, FALSE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAX_INBUF_SIZE,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAX_IMAP_ARG_ELEMENTS);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen/* Skip incoming data until newline is found,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen returns TRUE if newline was found. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int client_skip_line(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const unsigned char *data;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen size_t i, data_size;
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen data = i_stream_get_data(client->input, &data_size);
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; i < data_size; i++) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (data[i] == '\n') {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_skip(client->input, i+1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstatic int cmd_capability(struct imap_client *client)
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *capability;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen ssl_initialized ? " STARTTLS" : "",
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen disable_plaintext_auth && !client->tls ?
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen " LOGINDISABLED" : "",
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_authenticate_get_capabilities(),
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen NULL);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_line(client, capability);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "OK Capability completed.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return TRUE;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen}
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainenstatic int cmd_starttls(struct imap_client *client)
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen{
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen int fd_ssl;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (client->tls) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "BAD TLS is already active.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return TRUE;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (!ssl_initialized) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return TRUE;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen o_stream_flush(client->output);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen /* must be removed before ssl_proxy_new(), since it may
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen io_add() the same fd. */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (client->io != NULL) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen io_remove(client->io);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client->io = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fd_ssl != -1) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client->tls = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_set_title(client);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we skipped it already, so don't ignore next command */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->skip_line = FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->common.fd = fd_ssl;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_unref(client->input);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_unref(client->output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen imap_parser_destroy(client->parser);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_open_streams(client, fd_ssl);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch client_send_line(client, " * BYE TLS handehake failed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "TLS handshake failed");
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainenstatic int cmd_noop(struct imap_client *client)
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen{
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen return TRUE;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch}
009217abb57a24a4076092e8e4e165545747839eStephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Boschstatic int cmd_logout(struct imap_client *client)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch{
009217abb57a24a4076092e8e4e165545747839eStephan Bosch client_send_line(client, "* BYE Logging out");
009217abb57a24a4076092e8e4e165545747839eStephan Bosch client_send_tagline(client, "OK Logout completed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "Aborted login");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Boschstatic int client_command_execute(struct imap_client *client, const char *cmd,
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch struct imap_arg *args)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch{
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch cmd = str_ucase(t_strdup_noconst(cmd));
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch if (strcmp(cmd, "LOGIN") == 0)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch return cmd_login(client, args);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch if (strcmp(cmd, "AUTHENTICATE") == 0)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch return cmd_authenticate(client, args);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch if (strcmp(cmd, "CAPABILITY") == 0)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch return cmd_capability(client);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch if (strcmp(cmd, "STARTTLS") == 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return cmd_starttls(client);
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen if (strcmp(cmd, "NOOP") == 0)
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen return cmd_noop(client);
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen if (strcmp(cmd, "LOGOUT") == 0)
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen return cmd_logout(client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainenstatic void client_handle_input(struct imap_client *client)
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen{
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen struct imap_arg *args;
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (client->cmd_finished) {
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen /* clear the previous command from memory. don't do this
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen immediately after handling command since we need the
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen cmd_tag to stay some time after authentication commands. */
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen client->cmd_tag = NULL;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch client->cmd_name = NULL;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch imap_parser_reset(client->parser);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch /* remove \r\n */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (client->skip_line) {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (!client_skip_line(client))
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen return;
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen client->skip_line = FALSE;
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client->cmd_finished = FALSE;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (client->cmd_tag == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk if (client->cmd_tag == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return; /* need more data */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (client->cmd_name == NULL) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (client->cmd_name == NULL)
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return; /* need more data */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
2e2a1d720ed53490e8e5c5031e773d395bd5683dTimo Sirainen case -1:
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen /* error */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case -2:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* not enough data */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->skip_line = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*client->cmd_tag == '\0' ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen !client_command_execute(client, client->cmd_name, args)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "* BYE Too many invalid IMAP commands.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "Disconnected: "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Too many invalid commands");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client,
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen "BAD Error in IMAP command received by server.");
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen }
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen client->cmd_finished = TRUE;
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen}
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainenint client_read(struct imap_client *client)
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen{
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen switch (i_stream_read(client->input)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case -2:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* buffer full */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case -1:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* disconnected */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "Disconnected");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen default:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* something was read */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_input(void *context)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct imap_client *client = context;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen client->last_input = ioloop_time;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!client_read(client))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_ref(client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_cork(client->output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_handle_input(client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (client_unref(client))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_flush(client->output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen void *context)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *client = key;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *const *destroy_clients;
cbaac1e9a69099a2c25e09b1db19bcbf9037e342Timo Sirainen buffer_t *destroy_buf = context;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t i, count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen count /= sizeof(struct imap_client *);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; i < count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (destroy_clients[i]->created > client->created) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer_insert(destroy_buf,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i * sizeof(struct imap_client *),
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen &client, sizeof(struct imap_client *));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_destroy_oldest(void)
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *const *destroy_clients;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer_t *destroy_buf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t i, count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen destroy_buf = buffer_create_static_hard(data_stack_pool,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen sizeof(struct imap_client *) *
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen CLIENT_DESTROY_OLDEST_COUNT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* then kill them */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen count /= sizeof(struct imap_client *);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(destroy_clients[i],
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Disconnected: Connection queue full");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct client *client_create(int fd, struct ip_addr *ip, int ssl)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *client;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_size(clients) >= max_logging_users) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* reached max. users count, kill few of the
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen oldest connections */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy_oldest();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* always use nonblocking I/O */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen net_set_nonblock(fd, TRUE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client = i_new(struct imap_client, 1);
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen client->created = ioloop_time;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen client->refcount = 1;
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen client->tls = ssl;
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen client->common.ip = *ip;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen client->common.fd = fd;
c5f932968281763df360b9c97cef60f5f80d5e3dTimo Sirainen
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client_open_streams(client, fd);
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->last_input = ioloop_time;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_insert(clients, client, client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen main_ref();
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen client_send_line(client, "* OK " PACKAGE " ready.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_set_title(client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return &client->common;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (reason != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_syslog(client, reason);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_remove(clients, client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen imap_parser_destroy(client->parser);
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen i_stream_close(client->input);
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen o_stream_close(client->output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (client->io != NULL) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen io_remove(client->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->io = NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen net_disconnect(client->common.fd);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->common.fd = -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_free(client->virtual_user);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_unref(client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainenvoid client_ref(struct imap_client *client)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen client->refcount++;
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenint client_unref(struct imap_client *client)
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (--client->refcount > 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return TRUE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_stream_unref(client->input);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen o_stream_unref(client->output);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen buffer_free(client->plain_login);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_free(client);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen main_unref();
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return FALSE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_send_str(client->output, line);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_send(client->output, "\r\n", 2);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_syslog(struct imap_client *client, const char *text)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen const char *host;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen host = net_ip2host(&client->common.ip);
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen if (host == NULL)
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen host = "??";
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen i_info("%s [%s]", text, host);
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen}
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainenstatic void client_hash_check_idle(void *key, void *value __attr_unused__,
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen void *context __attr_unused__)
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen{
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen struct imap_client *client = key;
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen client_send_line(client, "* BYE Disconnected for inactivity.");
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen }
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen}
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainenstatic void idle_timeout(void *context __attr_unused__)
2ef0e8ee48c9683f7bd6698798efa3328e4322d1Timo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen hash_foreach(clients, client_hash_check_idle, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenunsigned int clients_get_count(void)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return hash_size(clients);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainenstatic void client_hash_destroy(void *key, void *value __attr_unused__,
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen void *context __attr_unused__)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(key, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid clients_destroy_all(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_foreach(clients, client_hash_destroy, NULL);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid clients_init(void)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
280503e88a6b2f72a32a8fbe363794abaaa845d6Timo Sirainen clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen to_idle = timeout_add(1000, idle_timeout, NULL);
280503e88a6b2f72a32a8fbe363794abaaa845d6Timo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid clients_deinit(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen clients_destroy_all();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_destroy(clients);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen timeout_remove(to_idle);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen