diff options
Diffstat (limited to 'src/imap/imap-commands.c')
-rw-r--r-- | src/imap/imap-commands.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/imap/imap-commands.c b/src/imap/imap-commands.c new file mode 100644 index 0000000..b78d0a1 --- /dev/null +++ b/src/imap/imap-commands.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "imap-common.h" +#include "array.h" +#include "buffer.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "time-util.h" +#include "imap-commands.h" + + +struct command_hook { + command_hook_callback_t *pre; + command_hook_callback_t *post; +}; + +static const struct command imap4rev1_commands[] = { + { "CAPABILITY", cmd_capability, 0 }, + { "LOGOUT", cmd_logout, COMMAND_FLAG_BREAKS_MAILBOX }, + { "NOOP", cmd_noop, COMMAND_FLAG_BREAKS_SEQS }, + + { "APPEND", cmd_append, COMMAND_FLAG_BREAKS_SEQS | + /* finish syncing and sending + all tagged commands before + we wait for APPEND input */ + COMMAND_FLAG_BREAKS_MAILBOX }, + { "EXAMINE", cmd_examine, COMMAND_FLAG_BREAKS_MAILBOX }, + { "CREATE", cmd_create, 0 }, + { "DELETE", cmd_delete, COMMAND_FLAG_BREAKS_MAILBOX | + COMMAND_FLAG_USE_NONEXISTENT }, + { "RENAME", cmd_rename, COMMAND_FLAG_USE_NONEXISTENT }, + { "LIST", cmd_list, 0 }, + { "LSUB", cmd_lsub, 0 }, + { "SELECT", cmd_select, COMMAND_FLAG_BREAKS_MAILBOX }, + { "STATUS", cmd_status, 0 }, + { "SUBSCRIBE", cmd_subscribe, 0 }, + { "UNSUBSCRIBE", cmd_unsubscribe, COMMAND_FLAG_USE_NONEXISTENT }, + + { "CHECK", cmd_check, COMMAND_FLAG_BREAKS_SEQS }, + { "CLOSE", cmd_close, COMMAND_FLAG_BREAKS_MAILBOX }, + { "COPY", cmd_copy, COMMAND_FLAG_USES_SEQS | + COMMAND_FLAG_BREAKS_SEQS }, + { "EXPUNGE", cmd_expunge, COMMAND_FLAG_BREAKS_SEQS }, + { "FETCH", cmd_fetch, COMMAND_FLAG_USES_SEQS }, + { "SEARCH", cmd_search, COMMAND_FLAG_USES_SEQS }, + { "STORE", cmd_store, COMMAND_FLAG_USES_SEQS }, + { "UID COPY", cmd_copy, COMMAND_FLAG_BREAKS_SEQS }, + { "UID FETCH", cmd_fetch, COMMAND_FLAG_BREAKS_SEQS }, + { "UID SEARCH", cmd_search, COMMAND_FLAG_BREAKS_SEQS }, + { "UID STORE", cmd_store, COMMAND_FLAG_BREAKS_SEQS } +}; +#define IMAP4REV1_COMMANDS_COUNT N_ELEMENTS(imap4rev1_commands) + +static const struct command imap_ext_commands[] = { + /* IMAP extensions: */ + { "CANCELUPDATE", cmd_cancelupdate,0 }, + { "ENABLE", cmd_enable, 0 }, + { "ID", cmd_id, 0 }, + { "IDLE", cmd_idle, COMMAND_FLAG_BREAKS_SEQS | + COMMAND_FLAG_REQUIRES_SYNC | + /* finish syncing and sending + all tagged commands before + IDLE is started */ + COMMAND_FLAG_BREAKS_MAILBOX }, + { "GETMETADATA", cmd_getmetadata, 0 }, + { "SETMETADATA", cmd_setmetadata, 0 }, + { "NAMESPACE", cmd_namespace, 0 }, + { "NOTIFY", cmd_notify, COMMAND_FLAG_BREAKS_SEQS }, + { "SORT", cmd_sort, COMMAND_FLAG_USES_SEQS }, + { "THREAD", cmd_thread, COMMAND_FLAG_USES_SEQS }, + { "UID EXPUNGE", cmd_uid_expunge, COMMAND_FLAG_BREAKS_SEQS }, + { "MOVE", cmd_move, COMMAND_FLAG_USES_SEQS | + COMMAND_FLAG_BREAKS_SEQS }, + { "UID MOVE", cmd_move, COMMAND_FLAG_BREAKS_SEQS }, + { "UID SORT", cmd_sort, COMMAND_FLAG_BREAKS_SEQS }, + { "UID THREAD", cmd_thread, COMMAND_FLAG_BREAKS_SEQS }, + { "UNSELECT", cmd_unselect, COMMAND_FLAG_BREAKS_MAILBOX }, + { "X-CANCEL", cmd_x_cancel, 0 }, + { "X-STATE", cmd_x_state, COMMAND_FLAG_REQUIRES_SYNC }, + { "XLIST", cmd_list, 0 }, + /* IMAP URLAUTH (RFC4467): */ + { "GENURLAUTH", cmd_genurlauth, 0 }, + { "RESETKEY", cmd_resetkey, 0 }, + { "URLFETCH", cmd_urlfetch, 0 } +}; +#define IMAP_EXT_COMMANDS_COUNT N_ELEMENTS(imap_ext_commands) + +ARRAY_TYPE(command) imap_commands; +static bool commands_unsorted; +static ARRAY(struct command_hook) command_hooks; + +void command_register(const char *name, command_func_t *func, + enum command_flags flags) +{ + struct command cmd; + + i_zero(&cmd); + cmd.name = name; + cmd.func = func; + cmd.flags = flags; + array_push_back(&imap_commands, &cmd); + + commands_unsorted = TRUE; +} + +void command_unregister(const char *name) +{ + const struct command *cmd; + unsigned int i, count; + + cmd = array_get(&imap_commands, &count); + for (i = 0; i < count; i++) { + if (strcasecmp(cmd[i].name, name) == 0) { + array_delete(&imap_commands, i, 1); + return; + } + } + + i_error("Trying to unregister unknown command '%s'", name); +} + +void command_register_array(const struct command *cmdarr, unsigned int count) +{ + commands_unsorted = TRUE; + array_append(&imap_commands, cmdarr, count); +} + +void command_unregister_array(const struct command *cmdarr, unsigned int count) +{ + while (count > 0) { + command_unregister(cmdarr->name); + count--; cmdarr++; + } +} + +void command_hook_register(command_hook_callback_t *pre, + command_hook_callback_t *post) +{ + struct command_hook hook; + + hook.pre = pre; + hook.post = post; + array_push_back(&command_hooks, &hook); +} + +void command_hook_unregister(command_hook_callback_t *pre, + command_hook_callback_t *post) +{ + const struct command_hook *hooks; + unsigned int i, count; + + hooks = array_get(&command_hooks, &count); + for (i = 0; i < count; i++) { + if (hooks[i].pre == pre && hooks[i].post == post) { + array_delete(&command_hooks, i, 1); + return; + } + } + i_panic("command_hook_unregister(): hook not registered"); +} + +void command_stats_start(struct client_command_context *cmd) +{ + cmd->stats_start.timeval = ioloop_timeval; + cmd->stats_start.lock_wait_usecs = file_lock_wait_get_total_usecs(); + cmd->stats_start.bytes_in = i_stream_get_absolute_offset(cmd->client->input); + cmd->stats_start.bytes_out = cmd->client->output->offset; +} + +void command_stats_flush(struct client_command_context *cmd) +{ + io_loop_time_refresh(); + cmd->stats.running_usecs += + timeval_diff_usecs(&ioloop_timeval, &cmd->stats_start.timeval); + cmd->stats.lock_wait_usecs += + file_lock_wait_get_total_usecs() - + cmd->stats_start.lock_wait_usecs; + cmd->stats.bytes_in += i_stream_get_absolute_offset(cmd->client->input) - + cmd->stats_start.bytes_in; + cmd->stats.bytes_out += cmd->client->output->offset - + cmd->stats_start.bytes_out; + /* allow flushing multiple times */ + command_stats_start(cmd); +} + +bool command_exec(struct client_command_context *cmd) +{ + const struct command_hook *hook; + bool finished; + + i_assert(!cmd->executing); + + io_loop_time_refresh(); + command_stats_start(cmd); + + event_push_global(cmd->global_event); + cmd->executing = TRUE; + array_foreach(&command_hooks, hook) + hook->pre(cmd); + finished = cmd->func(cmd); + array_foreach(&command_hooks, hook) + hook->post(cmd); + cmd->executing = FALSE; + event_pop_global(cmd->global_event); + if (cmd->state == CLIENT_COMMAND_STATE_DONE) + finished = TRUE; + + command_stats_flush(cmd); + return finished; +} + +static int command_cmp(const struct command *c1, const struct command *c2) +{ + return strcasecmp(c1->name, c2->name); +} + +static int command_bsearch(const char *name, const struct command *cmd) +{ + return strcasecmp(name, cmd->name); +} + +struct command *command_find(const char *name) +{ + if (commands_unsorted) { + array_sort(&imap_commands, command_cmp); + commands_unsorted = FALSE; + } + + return array_bsearch(&imap_commands, name, command_bsearch); +} + +void commands_init(void) +{ + i_array_init(&imap_commands, 64); + i_array_init(&command_hooks, 4); + commands_unsorted = FALSE; + + command_register_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT); + command_register_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT); +} + +void commands_deinit(void) +{ + array_free(&imap_commands); + array_free(&command_hooks); +} |