Mercurial > dovecot > core-2.2
view src/imap/client.c @ 903:fd8888f6f037 HEAD
Naming style changes, finally got tired of most of the typedefs. Also the
previous enum -> macro change reverted so that we don't use the highest bit
anymore, that's incompatible with old indexes so they will be rebuilt.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 05 Jan 2003 15:09:51 +0200 |
parents | 35abd7a5d381 |
children | 411006be3c66 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "ioloop.h" #include "network.h" #include "istream.h" #include "ostream.h" #include "commands.h" #include <stdlib.h> /* max. size of one parameter in line */ #define MAX_INBUF_SIZE 8192 /* max. number of IMAP argument elements to accept. The maximum memory usage for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */ #define MAX_IMAP_ARG_ELEMENTS 128 /* If we can't send a buffer in a minute, disconnect the client */ #define CLIENT_OUTPUT_TIMEOUT (60*1000) /* If we don't soon receive expected data from client while processing a command, disconnect the client */ #define CLIENT_CMDINPUT_TIMEOUT CLIENT_OUTPUT_TIMEOUT /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 20 /* Disconnect client after idling this many seconds */ #define CLIENT_IDLE_TIMEOUT (60*30) extern struct mail_storage_callbacks mail_storage_callbacks; static struct client *my_client; /* we don't need more than one currently */ static struct timeout *to_idle; static void client_input(struct client *client); static void client_output_timeout(void *context) { struct client *client = context; i_stream_close(client->input); o_stream_close(client->output); } static void client_input_timeout(void *context) { struct client *client = context; client_send_line(my_client, "* BYE Disconnected for inactivity " "while waiting for command data."); o_stream_close(client->output); } struct client *client_create(int hin, int hout, struct mail_storage *storage) { struct client *client; client = i_new(struct client, 1); client->input = i_stream_create_file(hin, default_pool, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_file(hout, default_pool, 4096, IO_PRIORITY_DEFAULT, FALSE); /* set timeout for reading expected data (eg. APPEND). This is different from the actual idle time. */ i_stream_set_blocking(client->input, CLIENT_CMDINPUT_TIMEOUT, client_input_timeout, client); /* set timeout for sending data */ o_stream_set_blocking(client->output, CLIENT_OUTPUT_TIMEOUT, client_output_timeout, client); client->io = io_add(hin, IO_READ, (IOFunc) client_input, client); client->parser = imap_parser_create(client->input, client->output, MAX_INBUF_SIZE, MAX_IMAP_ARG_ELEMENTS); client->last_input = ioloop_time; client->storage = storage; storage->set_callbacks(storage, &mail_storage_callbacks, client); i_assert(my_client == NULL); my_client = client; return client; } void client_destroy(struct client *client) { o_stream_flush(client->output); if (client->mailbox != NULL) client->mailbox->close(client->mailbox); mail_storage_destroy(client->storage); imap_parser_destroy(client->parser); io_remove(client->io); i_stream_unref(client->input); o_stream_unref(client->output); i_free(client); /* quit the program */ my_client = NULL; io_loop_stop(ioloop); } void client_disconnect(struct client *client) { o_stream_flush(client->output); i_stream_close(client->input); o_stream_close(client->output); } void client_send_line(struct client *client, const char *data) { if (client->output->closed) return; (void)o_stream_send_str(client->output, data); (void)o_stream_send(client->output, "\r\n", 2); } void client_send_tagline(struct client *client, const char *data) { const char *tag = client->cmd_tag; if (client->output->closed) return; if (tag == NULL || *tag == '\0') tag = "*"; (void)o_stream_send_str(client->output, tag); (void)o_stream_send(client->output, " ", 1); (void)o_stream_send_str(client->output, data); (void)o_stream_send(client->output, "\r\n", 2); } void client_send_command_error(struct client *client, const char *msg) { const char *error; if (msg == NULL) msg = imap_parser_get_error(client->parser); error = t_strconcat("BAD Error in IMAP command: ", msg, NULL); client->cmd_error = TRUE; client_send_tagline(client, error); if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_send_line(client, "* BYE Too many invalid IMAP commands."); client_disconnect(client); } } int client_read_args(struct client *client, unsigned int count, unsigned int flags, struct imap_arg **args) { int ret; ret = imap_parser_read_args(client->parser, count, flags, args); if (ret >= (int)count) { /* all parameters read successfully */ return TRUE; } else if (ret == -2) { /* need more data */ return FALSE; } else { /* error, or missing arguments */ client_send_command_error(client, ret < 0 ? NULL : "Missing arguments"); return FALSE; } } int client_read_string_args(struct client *client, unsigned int count, ...) { struct imap_arg *imap_args; va_list va; const char *str; unsigned int i; if (!client_read_args(client, count, 0, &imap_args)) return FALSE; va_start(va, count); for (i = 0; i < count; i++) { const char **ret = va_arg(va, const char **); str = imap_arg_string(&imap_args[i]); if (str == NULL) { client_send_command_error(client, "Missing arguments."); va_end(va); return FALSE; } if (ret != NULL) *ret = str; } va_end(va); return TRUE; } static void client_reset_command(struct client *client) { client->cmd_tag = NULL; client->cmd_name = NULL; client->cmd_func = NULL; client->cmd_error = FALSE; client->cmd_uid = FALSE; imap_parser_reset(client->parser); } static void client_command_finished(struct client *client) { client->input_skip_line = TRUE; client_reset_command(client); } /* Skip incoming data until newline is found, returns TRUE if newline was found. */ static int client_skip_line(struct client *client) { const unsigned char *data; size_t i, data_size; data = i_stream_get_data(client->input, &data_size); for (i = 0; i < data_size; i++) { if (data[i] == '\n') { client->input_skip_line = FALSE; i_stream_skip(client->input, i+1); break; } } return !client->input_skip_line; } static int client_handle_input(struct client *client) { if (client->cmd_func != NULL) { /* command is being executed - continue it */ if (client->cmd_func(client) || client->cmd_error) { /* command execution was finished */ client_command_finished(client); return TRUE; } return FALSE; } if (client->input_skip_line) { /* we're just waiting for new line.. */ if (!client_skip_line(client)) return FALSE; /* got the newline */ client_reset_command(client); /* pass through to parse next command */ } if (client->cmd_tag == NULL) { client->cmd_tag = imap_parser_read_word(client->parser); if (client->cmd_tag == NULL) return FALSE; /* need more data */ } if (client->cmd_name == NULL) { client->cmd_name = imap_parser_read_word(client->parser); if (client->cmd_name == NULL) return FALSE; /* need more data */ } if (client->cmd_name == '\0') { /* command not given - cmd_func is already NULL. */ } else { /* find the command function */ client->cmd_func = client_command_find(client->cmd_name); } if (client->cmd_func == NULL) { /* unknown command */ client_send_command_error(client, t_strconcat( "Unknown command '", client->cmd_name, "'", NULL)); client_command_finished(client); } else { if (client->cmd_func(client) || client->cmd_error) { /* command execution was finished */ client_command_finished(client); } } return TRUE; } static void client_input(struct client *client) { client->last_input = ioloop_time; switch (i_stream_read(client->input)) { case -1: /* disconnected */ client_destroy(client); return; case -2: /* parameter word is longer than max. input buffer size. this is most likely an error, so skip the new data until newline is found. */ client->input_skip_line = TRUE; client_send_command_error(client, "Too long argument."); client_command_finished(client); break; } o_stream_cork(client->output); while (client_handle_input(client)) ; o_stream_flush(client->output); if (client->output->closed) client_destroy(client); } static void idle_timeout(void *context __attr_unused__, struct timeout *timeout __attr_unused__) { if (my_client == NULL) return; if (ioloop_time - my_client->last_input >= CLIENT_IDLE_TIMEOUT) { client_send_line(my_client, "* BYE Disconnected for inactivity."); client_destroy(my_client); } } void clients_init(void) { my_client = NULL; to_idle = timeout_add(10000, idle_timeout, NULL); } void clients_deinit(void) { if (my_client != NULL) { client_send_line(my_client, "* BYE Server shutting down."); client_destroy(my_client); } timeout_remove(to_idle); }