imap-client.c revision c9b76ca218d93dc97e27d6ec04a645e8dc6f228b
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-common.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "ioloop.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "llist.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "str.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "hostpid.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "net.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "iostream.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "istream.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "ostream.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "time-util.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "var-expand.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "master-service.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-resp-code.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-util.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-urlauth.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "mail-error.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "mail-namespace.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "mail-storage-service.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-state.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-search.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-notify.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include "imap-commands.h"
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include <stdlib.h>
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#include <unistd.h>
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikextern struct mail_storage_callbacks mail_storage_callbacks;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikextern struct imap_client_vfuncs imap_client_vfuncs;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstruct imap_module_register imap_module_register = { 0 };
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstruct client *imap_clients = NULL;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikunsigned int imap_client_count = 0;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic void client_idle_timeout(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->output_cmd_lock == NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_line(client, "* BYE Disconnected for inactivity.");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_destroy(client, "Disconnected for inactivity");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic void client_init_urlauth(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct imap_urlauth_config config;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik memset(&config, 0, sizeof(config));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.url_host = client->set->imap_urlauth_host;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.url_port = client->set->imap_urlauth_port;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.socket_path = t_strconcat(client->user->set->base_dir,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "/"IMAP_URLAUTH_SOCKET_NAME, NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.session_id = client->session_id;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.access_anonymous = client->user->anonymous;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik config.access_user = client->user->username;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->urlauth_ctx = imap_urlauth_init(client->user, &config);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic bool user_has_special_use_mailboxes(struct mail_user *user)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct mail_namespace *ns;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik for (ns = user->namespaces; ns != NULL; ns = ns->next) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (ns->special_use_mailboxes)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstruct client *client_create(int fd_in, int fd_out, const char *session_id,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct mail_user *user,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct mail_storage_service_user *service_user,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const struct imap_settings *set)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const struct mail_storage_settings *mail_set;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client *client;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *ident;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik pool_t pool;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bool explicit_capability = FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* always use nonblocking I/O */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik net_set_nonblock(fd_in, TRUE);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik net_set_nonblock(fd_out, TRUE);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik pool = pool_alloconly_create("imap client", 2048);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client = p_new(pool, struct client, 1);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->pool = pool;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->v = imap_client_vfuncs;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->set = set;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->service_user = service_user;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->session_id = p_strdup(pool, session_id);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->fd_in = fd_in;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->fd_out = fd_out;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->input = i_stream_create_fd(fd_in,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik set->imap_max_line_length, FALSE);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_set_no_error_handling(client->output, TRUE);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_stream_set_name(client->input, "<imap client>");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_set_name(client->output, "<imap client>");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_set_flush_callback(client->output, client_output, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik p_array_init(&client->module_contexts, client->pool, 5);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->io = io_add_istream(client->input, client_input, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->last_input = ioloop_time;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_idle_timeout, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->command_pool =
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik pool_alloconly_create(MEMPOOL_GROWING"client command", 1024*2);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->user = user;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->notify_count_changes = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->notify_flag_changes = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mail_namespaces_set_storage_callbacks(user->namespaces,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik &mail_storage_callbacks, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->capability_string =
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_new(client->pool, sizeof(CAPABILITY_STRING)+64);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (*set->imap_capability == '\0')
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, CAPABILITY_STRING);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik else if (*set->imap_capability != '+') {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik explicit_capability = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, set->imap_capability);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, CAPABILITY_STRING);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append_c(client->capability_string, ' ');
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, set->imap_capability + 1);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (user->fuzzy_search && !explicit_capability) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* Enable FUZZY capability only when it actually has
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik a chance of working */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, " SEARCH=FUZZY");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mail_set = mail_user_set_get_storage_set(user);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (mail_set->mailbox_list_index && !explicit_capability) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* NOTIFY is enabled only when mailbox list indexes are
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik enabled, although even that doesn't necessarily guarantee
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik it always */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, " NOTIFY");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (*set->imap_urlauth_host != '\0' &&
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik *mail_set->mail_attribute_dict != '\0') {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* Enable URLAUTH capability only when dict is
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik configured correctly */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_init_urlauth(client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!explicit_capability)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, " URLAUTH URLAUTH=BINARY");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (set->imap_metadata && *mail_set->mail_attribute_dict != '\0') {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->imap_metadata_enabled = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!explicit_capability)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, " METADATA");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!explicit_capability && user_has_special_use_mailboxes(user)) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* Advertise SPECIAL-USE only if there are actually some
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik SPECIAL-USE flags in mailbox configuration. */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(client->capability_string, " SPECIAL-USE");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik ident = mail_user_get_anvil_userip_ident(client->user);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (ident != NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik master_service_anvil_send(master_service, t_strconcat(
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "CONNECT\t", my_pid, "\timap/", ident, "\n", NULL));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->anvil_sent = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_client_count++;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik DLLIST_PREPEND(&imap_clients, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (hook_client_created != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik hook_client_created(&client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_refresh_proctitle();
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return client;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_command_cancel(struct client_command_context **_cmd)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd = *_cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bool cmd_ret;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik switch (cmd->state) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_WAIT_INPUT:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* a bit kludgy check: cancel command only if it has context
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik set. currently only append command matches this check. all
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik other commands haven't even started the processing yet. */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->context == NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik break;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* fall through */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_WAIT_EXTERNAL:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_WAIT_OUTPUT:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->cancel = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik break;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_WAIT_SYNC:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* commands haven't started yet */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik break;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik case CLIENT_COMMAND_STATE_DONE:
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_unreached();
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd_ret = !cmd->cancel || cmd->func == NULL ? TRUE :
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik command_exec(cmd);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!cmd_ret) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->client->output->closed)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_panic("command didn't cancel itself: %s", cmd->name);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_command_free(*_cmd != NULL ? _cmd : &cmd);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikconst char *client_stats(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik static struct var_expand_table static_tab[] = {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { 'i', NULL, "input" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { 'o', NULL, "output" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "session" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "fetch_hdr_count" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "fetch_hdr_bytes" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "fetch_body_count" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "fetch_body_bytes" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "deleted" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "expunged" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, "trashed" },
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik { '\0', NULL, NULL }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik };
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct var_expand_table *tab;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik string_t *str;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab = t_malloc(sizeof(static_tab));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik memcpy(tab, static_tab, sizeof(static_tab));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[0].value = dec2str(i_stream_get_absolute_offset(client->input));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[1].value = dec2str(client->output->offset);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[2].value = client->session_id;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik tab[3].value = dec2str(client->fetch_hdr_count);
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik tab[4].value = dec2str(client->fetch_hdr_bytes);
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik tab[5].value = dec2str(client->fetch_body_count);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[6].value = dec2str(client->fetch_body_bytes);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[7].value = dec2str(client->deleted_count);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[8].value = dec2str(client->expunged_count);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tab[9].value = dec2str(client->trashed_count);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str = t_str_new(128);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik var_expand(str, client->set->imap_logout_format, tab);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return str_c(str);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_destroy(struct client *client, const char *reason)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->v.destroy(client, reason);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic const char *client_get_commands_status(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned int msecs_in_ioloop;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik uint64_t running_usecs = 0, ioloop_wait_usecs;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned long long bytes_in = 0, bytes_out = 0;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik string_t *str;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->command_queue == NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return "";
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str = t_str_new(128);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(str, " (");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(str, cmd->name);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->next != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append_c(str, ',');
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik running_usecs += cmd->running_usecs;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bytes_in += cmd->bytes_in;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bytes_out += cmd->bytes_out;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik ioloop_wait_usecs = io_loop_get_wait_usecs(current_ioloop);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_ioloop = (ioloop_wait_usecs -
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->command_queue->start_ioloop_wait_usecs + 999) / 1000;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_printfa(str, " running for %d.%03d + waiting for %d.%03d secs",
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik (int)((running_usecs+999)/1000 / 1000),
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik (int)((running_usecs+999)/1000 % 1000),
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_ioloop / 1000, msecs_in_ioloop % 1000);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_printfa(str, ", %llu B in + %llu+%"PRIuSIZE_T" B out)",
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bytes_in, bytes_out,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_get_buffer_used_size(client->output));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return str_c(str);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic void client_default_destroy(struct client *client, const char *reason)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *cmd_status = "";
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_assert(!client->destroyed);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->destroyed = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!client->disconnected) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->disconnected = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (reason == NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik reason = io_stream_get_disconnect_reason(client->input,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd_status = client_get_commands_status(client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_info("%s%s %s", reason, cmd_status, client_stats(client));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_stream_close(client->input);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_close(client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* finish off all the queued commands. */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->output_cmd_lock != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_command_cancel(&client->output_cmd_lock);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik while (client->command_queue != NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd = client->command_queue;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_command_cancel(&cmd);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* handle the input_lock command last. it might have been waiting on
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik other queued commands (although we probably should just drop the
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik command at that point since it hasn't started running. but this may
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik change in future). */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->input_lock != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_command_cancel(&client->input_lock);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->mailbox != NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_search_updates_free(client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mailbox_free(&client->mailbox);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->notify_ctx != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_notify_deinit(&client->notify_ctx);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->urlauth_ctx != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_urlauth_deinit(&client->urlauth_ctx);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->anvil_sent) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik master_service_anvil_send(master_service, t_strconcat(
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "DISCONNECT\t", my_pid, "\timap/",
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mail_user_get_anvil_userip_ident(client->user),
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "\n", NULL));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mail_user_unref(&client->user);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->free_parser != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_parser_unref(&client->free_parser);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->io != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik io_remove(&client->io);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->to_idle_output != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik timeout_remove(&client->to_idle_output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->to_delayed_input != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik timeout_remove(&client->to_delayed_input);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik timeout_remove(&client->to_idle);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_stream_destroy(&client->input);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_destroy(&client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik net_disconnect(client->fd_in);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->fd_in != client->fd_out)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik net_disconnect(client->fd_out);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (array_is_created(&client->search_saved_uidset))
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik array_free(&client->search_saved_uidset);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (array_is_created(&client->search_updates))
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik array_free(&client->search_updates);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik pool_unref(&client->command_pool);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik mail_storage_service_user_free(&client->service_user);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_client_count--;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik DLLIST_REMOVE(&imap_clients, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik pool_unref(&client->pool);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik master_service_client_connection_destroyed(master_service);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_refresh_proctitle();
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic void client_destroy_timeout(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_destroy(client, NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_disconnect(struct client *client, const char *reason)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_assert(reason != NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->disconnected)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_info("Disconnected: %s %s", reason, client_stats(client));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->disconnected = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_nflush(client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_uncork(client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_stream_close(client->input);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_close(client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->to_idle != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik timeout_remove(&client->to_idle);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->to_idle = timeout_add(0, client_destroy_timeout, client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_disconnect_with_error(struct client *client, const char *msg)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_line(client, t_strconcat("* BYE ", msg, NULL));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_disconnect(client, msg);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_send_line(struct client *client, const char *data)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik (void)client_send_line_next(client, data);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikint client_send_line_next(struct client *client, const char *data)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct const_iovec iov[2];
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->output->closed)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return -1;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik iov[0].iov_base = data;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik iov[0].iov_len = strlen(data);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik iov[1].iov_base = "\r\n";
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik iov[1].iov_len = 2;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (o_stream_sendv(client->output, iov, 2) < 0)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return -1;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->last_output = ioloop_time;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (o_stream_get_buffer_used_size(client->output) >=
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik CLIENT_OUTPUT_OPTIMAL_SIZE) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* buffer full, try flushing */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return o_stream_flush(client->output);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return 1;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic void
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikclient_cmd_append_timing_stats(struct client_command_context *cmd,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik string_t *str)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned int msecs_in_cmd, msecs_in_ioloop;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik uint64_t ioloop_wait_usecs;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->start_time.tv_sec == 0)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik ioloop_wait_usecs = io_loop_get_wait_usecs(current_ioloop);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_cmd = (cmd->running_usecs + 999) / 1000;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_ioloop = (ioloop_wait_usecs -
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->start_ioloop_wait_usecs + 999) / 1000;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (str_data(str)[str_len(str)-1] == '.')
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_truncate(str, str_len(str)-1);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_printfa(str, " (%d.%03d + %d.%03d secs).",
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_cmd / 1000, msecs_in_cmd % 1000,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msecs_in_ioloop / 1000, msecs_in_ioloop % 1000);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_send_tagline(struct client_command_context *cmd, const char *data)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client *client = cmd->client;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *tag = cmd->tag;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->output->closed || cmd->cancel)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_assert(!cmd->tagline_sent);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->tagline_sent = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (tag == NULL || *tag == '\0')
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik tag = "*";
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik T_BEGIN {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik string_t *str = t_str_new(256);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_printfa(str, "%s %s", tag, data);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_cmd_append_timing_stats(cmd, str);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str_append(str, "\r\n");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik o_stream_nsend(client->output, str_data(str), str_len(str));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } T_END;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->last_output = ioloop_time;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_send_command_error(struct client_command_context *cmd,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *msg)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client *client = cmd->client;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *error, *cmd_name;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bool fatal;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (msg == NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik msg = imap_parser_get_error(cmd->parser, &fatal);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (fatal) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_disconnect_with_error(client, msg);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->tag == NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik else if (cmd->name == NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik error = t_strconcat("BAD Error in IMAP command: ", msg, NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd_name = t_str_ucase(cmd->name);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik error = t_strconcat("BAD Error in IMAP command ",
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd_name, ": ", msg, NULL);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_tagline(cmd, error);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_disconnect_with_error(client,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "Too many invalid IMAP commands.");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->param_error = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* client_read_args() failures rely on this being set, so that the
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik command processing is stopped even while command function returns
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik FALSE. */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->state = CLIENT_COMMAND_STATE_DONE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikvoid client_send_internal_error(struct client_command_context *cmd)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_tagline(cmd,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik t_strflocaltime("NO "MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikbool client_read_args(struct client_command_context *cmd, unsigned int count,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned int flags, const struct imap_arg **args_r)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik string_t *str;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik int ret;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_assert(count <= INT_MAX);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik ret = imap_parser_read_args(cmd->parser, count, flags, args_r);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (ret >= (int)count) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* all parameters read successfully */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik i_assert(cmd->client->input_lock == NULL ||
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->client->input_lock == cmd);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik str = t_str_new(256);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik imap_write_args(str, *args_r);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->args = p_strdup(cmd->pool, str_c(str));
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->start_time = ioloop_timeval;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->start_ioloop_wait_usecs =
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik io_loop_get_wait_usecs(current_ioloop);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->client->input_lock = NULL;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else if (ret == -2) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* need more data */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->client->input->closed) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* disconnected */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->state = CLIENT_COMMAND_STATE_DONE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* error, or missing arguments */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_command_error(cmd, ret < 0 ? NULL :
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "Missing arguments");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikbool client_read_string_args(struct client_command_context *cmd,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned int count, ...)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const struct imap_arg *imap_args;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik va_list va;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char *str;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik unsigned int i;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!client_read_args(cmd, count, 0, &imap_args))
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik va_start(va, count);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik for (i = 0; i < count; i++) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik const char **ret = va_arg(va, const char **);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (IMAP_ARG_IS_EOL(&imap_args[i])) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_command_error(cmd, "Missing arguments.");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik break;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (!imap_arg_get_astring(&imap_args[i], &str)) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_command_error(cmd, "Invalid arguments.");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik break;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (ret != NULL)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik *ret = str;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik va_end(va);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return i == count;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic struct client_command_context *
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikclient_command_find_with_flags(struct client_command_context *new_cmd,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik enum command_flags flags,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik enum client_command_state max_state)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd = new_cmd->client->command_queue;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik for (; cmd != NULL; cmd = cmd->next) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->state <= max_state &&
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd != new_cmd && (cmd->cmd_flags & flags) != 0)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return NULL;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic bool client_command_is_ambiguous(struct client_command_context *cmd)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik enum command_flags flags;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik enum client_command_state max_state =
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik bool broken_client = FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if ((cmd->cmd_flags & COMMAND_FLAG_REQUIRES_SYNC) != 0 &&
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik !imap_sync_is_allowed(cmd->client))
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->search_save_result_used) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* if there are pending commands that update the search
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik save result, wait */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *old_cmd = cmd->next;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik for (; old_cmd != NULL; old_cmd = old_cmd->next) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (old_cmd->search_save_result)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_MAILBOX) ==
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik COMMAND_FLAG_BREAKS_MAILBOX) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* there must be no other command running that uses the
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik selected mailbox */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik flags = COMMAND_FLAG_USES_MAILBOX;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik max_state = CLIENT_COMMAND_STATE_DONE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else if ((cmd->cmd_flags & COMMAND_FLAG_USES_SEQS) != 0) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* no existing command must be breaking sequences */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik flags = COMMAND_FLAG_BREAKS_SEQS;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik broken_client = TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_SEQS) != 0) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* if existing command uses sequences, we'll have to block */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik flags = COMMAND_FLAG_USES_SEQS;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client_command_find_with_flags(cmd, flags, max_state) == NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->client->syncing) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* don't do anything until syncing is finished */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (cmd->client->mailbox_change_lock != NULL &&
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->client->mailbox_change_lock != cmd) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik /* don't do anything until mailbox is fully
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik opened/closed */
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return FALSE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (broken_client) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client_send_line(cmd->client,
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "* BAD ["IMAP_RESP_CODE_CLIENTBUG"] "
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik "Command pipelining results in ambiguity.");
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik }
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return TRUE;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstruct client_command_context *client_command_alloc(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd = p_new(client->command_pool, struct client_command_context, 1);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->client = client;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->pool = client->command_pool;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik p_array_init(&cmd->module_contexts, cmd->pool, 5);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik DLLIST_PREPEND(&client->command_queue, cmd);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->command_queue_size++;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik return cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik}
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikstatic struct client_command_context *
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnikclient_command_new(struct client *client)
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik{
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik struct client_command_context *cmd;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd = client_command_alloc(client);
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik if (client->free_parser != NULL) {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->parser = client->free_parser;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik client->free_parser = NULL;
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik } else {
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik cmd->parser =
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik imap_parser_create(client->input, client->output,
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik client->set->imap_max_line_length);
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik }
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik return cmd;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik}
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnikvoid client_add_missing_io(struct client *client)
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik{
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik if (client->io == NULL && !client->disconnected)
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik client->io = io_add_istream(client->input, client_input, client);
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik}
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnikvoid client_command_free(struct client_command_context **_cmd)
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik{
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik struct client_command_context *cmd = *_cmd;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik struct client *client = cmd->client;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik enum client_command_state state = cmd->state;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik *_cmd = NULL;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik i_assert(client->output_cmd_lock == NULL);
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik /* reset input idle time because command output might have taken a
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik long time and we don't want to disconnect client immediately then */
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik client->last_input = ioloop_time;
1ba26271952de1beeb9e041bedf87d720d3f5680Lukas Slebodnik timeout_reset(client->to_idle);
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (cmd->cancel) {
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik cmd->cancel = FALSE;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik client_send_tagline(cmd, "NO Command cancelled.");
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik }
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (!cmd->param_error)
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik client->bad_counter = 0;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (client->input_lock == cmd)
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik client->input_lock = NULL;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (client->mailbox_change_lock == cmd)
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik client->mailbox_change_lock = NULL;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (cmd->parser != NULL) {
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik if (client->free_parser == NULL) {
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik imap_parser_reset(cmd->parser);
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik client->free_parser = cmd->parser;
e59b73366d3067c576e39a214a34ace2f9a84878Lukas Slebodnik } else {
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);
}
}
}
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);
}
}
/* Skip incoming data until newline is found,
returns TRUE if newline was found. */
static bool 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++;
break;
}
}
i_stream_skip(client->input, i);
return !client->input_skip_line;
}
static void client_idle_output_timeout(struct client *client)
{
client_destroy(client,
"Disconnected for inactivity in reading our output");
}
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;
}
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);
}
}
static bool client_handle_next_command(struct client *client, bool *remove_io_r)
{
*remove_io_r = FALSE;
if (client->input_lock != NULL) {
if (client->input_lock->state ==
CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) {
*remove_io_r = TRUE;
return FALSE;
}
return client_command_input(client->input_lock);
}
if (client->input_skip_line) {
/* first eat the previous command line */
if (!client_skip_line(client))
return FALSE;
client->input_skip_line = FALSE;
}
/* don't bother creating a new client command before there's at least
some input */
if (i_stream_get_data_size(client->input) == 0)
return FALSE;
/* beginning a new command */
if (client->command_queue_size >= CLIENT_COMMAND_QUEUE_MAX_SIZE ||
client->output_cmd_lock != NULL) {
/* wait for some of the commands to finish */
*remove_io_r = TRUE;
return FALSE;
}
client->input_lock = client_command_new(client);
return client_command_input(client->input_lock);
}
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_input(struct client *client)
{
struct client_command_context *cmd;
struct ostream *output = client->output;
ssize_t bytes;
i_assert(client->io != NULL);
client->last_input = ioloop_time;
timeout_reset(client->to_idle);
if (client->to_delayed_input != NULL)
timeout_remove(&client->to_delayed_input);
bytes = i_stream_read(client->input);
if (bytes == -1) {
/* disconnected */
client_destroy(client, NULL);
return;
}
o_stream_ref(output);
o_stream_cork(output);
if (!client_handle_input(client) && bytes == -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;
cmd = client->input_lock != NULL ? client->input_lock :
client_command_new(client);
cmd->param_error = TRUE;
client_send_command_error(cmd, "Too long argument.");
client_command_free(&cmd);
}
o_stream_uncork(output);
o_stream_unref(&output);
imap_refresh_proctitle();
if (client->disconnected)
client_destroy(client, NULL);
else
client_continue_pending_input(client);
}
static void client_output_cmd(struct client_command_context *cmd)
{
bool finished;
/* continue processing command */
finished = command_exec(cmd);
if (!finished)
(void)client_handle_unfinished_cmd(cmd);
else {
/* command execution was finished */
client_command_free(&cmd);
}
}
static void client_output_commands(struct client *client)
{
struct client_command_context *cmd;
/* mark all commands non-executed */
for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next)
cmd->temp_executed = FALSE;
if (client->output_cmd_lock != NULL) {
client->output_cmd_lock->temp_executed = TRUE;
client_output_cmd(client->output_cmd_lock);
}
while (client->output_cmd_lock == NULL) {
/* go through the entire commands list every time in case
multiple commands were freed. temp_executed keeps track of
which messages we've called so far */
cmd = client->command_queue;
for (; cmd != NULL; cmd = cmd->next) {
if (!cmd->temp_executed &&
cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT) {
cmd->temp_executed = TRUE;
client_output_cmd(cmd);
break;
}
}
if (cmd == NULL) {
/* all commands executed */
break;
}
}
}
int client_output(struct client *client)
{
int ret;
i_assert(!client->destroyed);
client->last_output = ioloop_time;
timeout_reset(client->to_idle);
if (client->to_idle_output != NULL)
timeout_reset(client->to_idle_output);
o_stream_cork(client->output);
if ((ret = o_stream_flush(client->output)) < 0) {
client_destroy(client, NULL);
return 1;
}
client_output_commands(client);
(void)cmd_sync_delayed(client);
o_stream_uncork(client->output);
imap_refresh_proctitle();
if (client->disconnected)
client_destroy(client, NULL);
else
client_continue_pending_input(client);
return ret;
}
bool client_handle_search_save_ambiguity(struct client_command_context *cmd)
{
struct client_command_context *old_cmd = cmd->next;
/* search only commands that were added before this command
(commands are prepended to the queue, so they're after ourself) */
for (; old_cmd != NULL; old_cmd = old_cmd->next) {
if (old_cmd->search_save_result)
break;
}
if (old_cmd == NULL)
return FALSE;
/* ambiguity, wait until it's over */
i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT);
cmd->client->input_lock = cmd;
cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
cmd->search_save_result_used = TRUE;
io_remove(&cmd->client->io);
return TRUE;
}
int client_enable(struct client *client, enum mailbox_feature features)
{
struct mailbox_status status;
int ret;
if ((client->enabled_features & features) == features)
return 0;
client->enabled_features |= features;
if (client->mailbox == NULL)
return 0;
ret = mailbox_enable(client->mailbox, features);
if ((features & MAILBOX_FEATURE_CONDSTORE) != 0 && ret == 0) {
/* CONDSTORE being enabled while mailbox is selected.
Notify client of the latest HIGHESTMODSEQ. */
ret = mailbox_get_status(client->mailbox,
STATUS_HIGHESTMODSEQ, &status);
if (ret == 0) {
client_send_line(client, t_strdup_printf(
"* OK [HIGHESTMODSEQ %llu] Highest",
(unsigned long long)status.highest_modseq));
}
}
if (ret < 0) {
client_send_untagged_storage_error(client,
mailbox_get_storage(client->mailbox));
}
return ret;
}
struct imap_search_update *
client_search_update_lookup(struct client *client, const char *tag,
unsigned int *idx_r)
{
struct imap_search_update *updates;
unsigned int i, count;
if (!array_is_created(&client->search_updates))
return NULL;
updates = array_get_modifiable(&client->search_updates, &count);
for (i = 0; i < count; i++) {
if (strcmp(updates[i].tag, tag) == 0) {
*idx_r = i;
return &updates[i];
}
}
return NULL;
}
void client_search_updates_free(struct client *client)
{
struct imap_search_update *update;
if (!array_is_created(&client->search_updates))
return;
array_foreach_modifiable(&client->search_updates, update)
imap_search_update_free(update);
array_clear(&client->search_updates);
}
void clients_destroy_all(struct mail_storage_service_ctx *storage_service)
{
while (imap_clients != NULL) {
client_send_line(imap_clients, "* BYE Server shutting down.");
mail_storage_service_io_activate_user(imap_clients->service_user);
client_destroy(imap_clients, "Server shutting down.");
}
mail_storage_service_io_deactivate(storage_service);
}
struct imap_client_vfuncs imap_client_vfuncs = {
imap_state_export_base,
imap_state_import_base,
client_default_destroy
};