bool client_handle_input(struct client *client) { bool ret, remove_io, handled_commands = FALSE; i_assert(!client->disconnected); client->handling_input = TRUE; do { T_BEGIN { ret = client_handle_next_command(client, &remove_io); } T_END; if (ret) handled_commands = TRUE; } while (ret && !client->disconnected && client->io != NULL); client->handling_input = FALSE; if (remove_io) io_remove(&client->io); else client_add_missing_io(client); if (!handled_commands) return FALSE; if (client->input_lock == NULL) cmd_sync_delayed(client); return TRUE; }
void client_continue_pending_input(struct client *client) { i_assert(!client->handling_input); if (client->input_lock != NULL) { /* there's a command that has locked the input */ struct client_command_context *cmd = client->input_lock; if (cmd->state != CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) return; /* the command is waiting for existing ambiguity causing commands to finish. */ if (client_command_is_ambiguous(cmd)) { /* we could be waiting for existing sync to finish */ if (!cmd_sync_delayed(client)) return; if (client_command_is_ambiguous(cmd)) return; } cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; } client_add_missing_io(client); /* if there's unread data in buffer, handle it. */ if (i_stream_get_data_size(client->input) > 0 && !client->disconnected) { if (client_handle_input(client)) client_continue_pending_input(client); } }
bool client_handle_unfinished_cmd(struct client_command_context *cmd) { if (cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT) { /* need more input */ return FALSE; } if (cmd->state != CLIENT_COMMAND_STATE_WAIT_OUTPUT) { /* waiting for something */ if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) { /* this is mainly for APPEND. */ client_add_missing_io(cmd->client); } return TRUE; } /* output is blocking, we can execute more commands */ o_stream_set_flush_pending(cmd->client->output, TRUE); if (cmd->client->to_idle_output == NULL) { /* disconnect sooner if client isn't reading our output */ cmd->client->to_idle_output = timeout_add(CLIENT_OUTPUT_TIMEOUT_MSECS, client_idle_output_timeout, cmd->client); } return TRUE; }
void client_command_free(struct client_command_context **_cmd) { struct client_command_context *cmd = *_cmd; struct client *client = cmd->client; enum client_command_state state = cmd->state; *_cmd = NULL; i_assert(client->output_cmd_lock == NULL); /* reset input idle time because command output might have taken a long time and we don't want to disconnect client immediately then */ client->last_input = ioloop_time; timeout_reset(client->to_idle); if (cmd->cancel) { cmd->cancel = FALSE; client_send_tagline(cmd, "NO Command cancelled."); } if (!cmd->param_error) client->bad_counter = 0; if (client->input_lock == cmd) client->input_lock = NULL; if (client->mailbox_change_lock == cmd) client->mailbox_change_lock = NULL; if (client->free_parser == NULL) { imap_parser_reset(cmd->parser); client->free_parser = cmd->parser; } else if (cmd->parser != NULL) { imap_parser_unref(&cmd->parser); } client->command_queue_size--; DLLIST_REMOVE(&client->command_queue, cmd); cmd = NULL; if (client->command_queue == NULL) { /* no commands left in the queue, we can clear the pool */ p_clear(client->command_pool); if (client->to_idle_output != NULL) timeout_remove(&client->to_idle_output); } imap_client_notify_command_freed(client); imap_refresh_proctitle(); /* if command finished from external event, check input for more unhandled commands since we may not be executing from client_input or client_output. */ if (state == CLIENT_COMMAND_STATE_WAIT_EXTERNAL && !client->disconnected) { client_add_missing_io(client); if (client->to_delayed_input == NULL) { client->to_delayed_input = timeout_add(0, client_input, client); } } }
static bool client_command_input(struct client_command_context *cmd) { struct client *client = cmd->client; struct command *command; if (cmd->func != NULL) { /* command is being executed - continue it */ if (command_exec(cmd)) { /* command execution was finished */ client_command_free(&cmd); client_add_missing_io(client); return TRUE; } return client_handle_unfinished_cmd(cmd); } if (cmd->tag == NULL) { cmd->tag = imap_parser_read_word(cmd->parser); if (cmd->tag == NULL) return FALSE; /* need more data */ cmd->tag = p_strdup(cmd->pool, cmd->tag); } if (cmd->name == NULL) { cmd->name = imap_parser_read_word(cmd->parser); if (cmd->name == NULL) return FALSE; /* need more data */ /* UID commands are a special case. better to handle them here. */ if (!cmd->uid && strcasecmp(cmd->name, "UID") == 0) { cmd->uid = TRUE; cmd->name = imap_parser_read_word(cmd->parser); if (cmd->name == NULL) return FALSE; /* need more data */ } cmd->name = !cmd->uid ? p_strdup(cmd->pool, cmd->name) : p_strconcat(cmd->pool, "UID ", cmd->name, NULL); imap_refresh_proctitle(); } client->input_skip_line = TRUE; if (cmd->name[0] == '\0') { /* command not given - cmd->func is already NULL. */ } else if ((command = command_find(cmd->name)) != NULL) { cmd->func = command->func; cmd->cmd_flags = command->flags; if (client_command_is_ambiguous(cmd)) { /* do nothing until existing commands are finished */ i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; io_remove(&client->io); return FALSE; } } if (cmd->func == NULL) { /* unknown command */ client_send_command_error(cmd, "Unknown command."); cmd->param_error = TRUE; client_command_free(&cmd); return TRUE; } else { i_assert(!client->disconnected); return client_command_input(cmd); } }