client.c revision 345648b341f228bd7f0b89f8aa3ecb9c470d817e
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* max. size of one parameter in line */
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 */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch/* Disconnect client after idling this many seconds */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch/* Disconnect client when it sends too many bad commands */
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#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch# error client idle timeout must be smaller than authentication timeout
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic void client_set_title(struct imap_client *client)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (!verbose_proctitle || !process_per_connection)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen client->input = i_stream_create_file(fd, default_pool,
d1e7425048c61d71f41f737ba947687198842dc2Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
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 const unsigned char *data;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen data = i_stream_get_data(client->input, &data_size);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; i < data_size; i++) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstatic int cmd_capability(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "OK Capability completed.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainenstatic int cmd_starttls(struct imap_client *client)
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "BAD TLS is already active.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen /* must be removed before ssl_proxy_new(), since it may
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen io_add() the same fd. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we skipped it already, so don't ignore next command */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch client_send_line(client, " * BYE TLS handehake failed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "TLS handshake failed");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainenstatic int cmd_noop(struct imap_client *client)
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
009217abb57a24a4076092e8e4e165545747839eStephan Boschstatic int cmd_logout(struct imap_client *client)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch client_send_line(client, "* BYE Logging out");
009217abb57a24a4076092e8e4e165545747839eStephan Bosch client_send_tagline(client, "OK Logout completed.");
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Boschstatic int client_command_execute(struct imap_client *client, const char *cmd,
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainenstatic void client_handle_input(struct imap_client *client)
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. */
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch /* remove \r\n */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return; /* need more data */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return; /* need more data */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* not enough data */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen !client_command_execute(client, client->cmd_name, args)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "* BYE Too many invalid IMAP commands.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Too many invalid commands");
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen "BAD Error in IMAP command received by server.");
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 /* disconnected */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* something was read */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; i < count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (destroy_clients[i]->created > client->created) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i * sizeof(struct imap_client *),
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 *) *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* then kill them */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Disconnected: Connection queue full");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct client *client_create(int fd, struct ip_addr *ip, int ssl)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* reached max. users count, kill few of the
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen oldest connections */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* always use nonblocking I/O */
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen client_send_line(client, "* OK " PACKAGE " ready.");
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid client_syslog(struct imap_client *client, const char *text)
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainenstatic void client_hash_check_idle(void *key, void *value __attr_unused__,
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");
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainenstatic void idle_timeout(void *context __attr_unused__)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen hash_foreach(clients, client_hash_check_idle, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenunsigned int clients_get_count(void)
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainenstatic void client_hash_destroy(void *key, void *value __attr_unused__,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_foreach(clients, client_hash_destroy, NULL);
280503e88a6b2f72a32a8fbe363794abaaa845d6Timo Sirainen clients = hash_create(default_pool, default_pool, 128, NULL, NULL);