diff options
Diffstat (limited to '')
-rw-r--r-- | src/imap/imap-client.h | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/imap/imap-client.h b/src/imap/imap-client.h new file mode 100644 index 0000000..a307955 --- /dev/null +++ b/src/imap/imap-client.h @@ -0,0 +1,361 @@ +#ifndef IMAP_CLIENT_H +#define IMAP_CLIENT_H + +#include "imap-commands.h" +#include "message-size.h" + +#define CLIENT_COMMAND_QUEUE_MAX_SIZE 4 +/* Maximum number of CONTEXT=SEARCH UPDATEs. Clients probably won't need more + than a few, so this is mainly to avoid more or less accidental pointless + resource usage. */ +#define CLIENT_MAX_SEARCH_UPDATES 10 + +struct client; +struct mail_storage; +struct mail_storage_service_ctx; +struct lda_settings; +struct imap_parser; +struct imap_arg; +struct imap_urlauth_context; + +struct mailbox_keywords { + /* All keyword names. The array itself exists in mail_index. + Keywords are currently only appended, they're never removed. */ + const ARRAY_TYPE(keywords) *names; + /* Number of keywords announced to client via FLAGS/PERMANENTFLAGS. + This relies on keywords not being removed while mailbox is + selected. */ + unsigned int announce_count; +}; + +struct imap_search_update { + char *tag; + struct mail_search_result *result; + bool return_uids; + + pool_t fetch_pool; + struct imap_fetch_context *fetch_ctx; +}; + +enum client_command_state { + /* Waiting for more input */ + CLIENT_COMMAND_STATE_WAIT_INPUT, + /* Waiting to be able to send more output */ + CLIENT_COMMAND_STATE_WAIT_OUTPUT, + /* Waiting for external interaction */ + CLIENT_COMMAND_STATE_WAIT_EXTERNAL, + /* Wait for other commands to finish execution */ + CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY, + /* Waiting for other commands to finish so we can sync */ + CLIENT_COMMAND_STATE_WAIT_SYNC, + /* Command is finished */ + CLIENT_COMMAND_STATE_DONE +}; + +struct client_command_stats { + /* time when command handling was started - typically this is after + reading all the parameters. */ + struct timeval start_time; + /* time when command handling was last finished. this is before + mailbox syncing is done. */ + struct timeval last_run_timeval; + /* io_loop_get_wait_usecs()'s value when the command was started */ + uint64_t start_ioloop_wait_usecs; + /* how many usecs this command itself has spent running */ + uint64_t running_usecs; + /* how many usecs this command itself has spent waiting for locks */ + uint64_t lock_wait_usecs; + /* how many bytes of client input/output command has used */ + uint64_t bytes_in, bytes_out; +}; + +struct client_command_stats_start { + struct timeval timeval; + uint64_t lock_wait_usecs; + uint64_t bytes_in, bytes_out; +}; + +struct client_command_context { + struct client_command_context *prev, *next; + struct client *client; + struct event *event; + /* global_event is pushed to the global event stack while the command + is running. It has only the minimal fields that are actually wanted + to be in all the events while it's being run. */ + struct event *global_event; + + pool_t pool; + /* IMAP command tag */ + const char *tag; + /* Name of this command */ + const char *name; + /* Parameters for this command. These are generated from parsed IMAP + arguments, so they may not be exactly the same as how client sent + them. */ + const char *args; + /* Parameters for this command generated with + imap_write_args_for_human(), so it's suitable for logging. */ + const char *human_args; + enum command_flags cmd_flags; + const char *tagline_reply; + + command_func_t *func; + void *context; + + /* Module-specific contexts. */ + ARRAY(union imap_module_context *) module_contexts; + + struct imap_parser *parser; + enum client_command_state state; + struct client_command_stats stats; + struct client_command_stats_start stats_start; + + struct imap_client_sync_context *sync; + + bool uid:1; /* used UID command */ + bool cancel:1; /* command is wanted to be cancelled */ + bool param_error:1; + bool search_save_result:1; /* search result is being updated */ + bool search_save_result_used:1; /* command uses search save */ + bool temp_executed:1; /* temporary execution state tracking */ + bool tagline_sent:1; + bool executing:1; +}; + +struct imap_client_vfuncs { + /* Perform client initialization. This is called when client creation is + finished completely. Particulary, at this point the namespaces are + fully initialized, which is not the case for the client create hook. + */ + void (*init)(struct client *client); + /* Destroy the client.*/ + void (*destroy)(struct client *client, const char *reason); + + /* Send a tagged response line. */ + void (*send_tagline)(struct client_command_context *cmd, + const char *data); + /* Run "mailbox syncing". This can send any unsolicited untagged + replies. Returns 1 = done, 0 = wait for more space in output buffer, + -1 = failed. */ + int (*sync_notify_more)(struct imap_sync_context *ctx); + + /* Export client state into buffer. Returns 1 if ok, 0 if some state + couldn't be preserved, -1 if temporary internal error occurred. */ + int (*state_export)(struct client *client, bool internal, + buffer_t *dest, const char **error_r); + /* Import a single block of client state from the given data. Returns + number of bytes successfully imported from the block, or 0 if state + is corrupted or contains unknown data (e.g. some plugin is no longer + loaded), -1 if temporary internal error occurred. */ + ssize_t (*state_import)(struct client *client, bool internal, + const unsigned char *data, size_t size, + const char **error_r); +}; + +struct client { + struct client *prev, *next; + + struct imap_client_vfuncs v; + struct event *event; + const char *const *userdb_fields; /* for internal session saving/restoring */ + + int fd_in, fd_out; + struct io *io; + struct istream *input; + struct ostream *output; + struct timeout *to_idle, *to_idle_output, *to_delayed_input; + + pool_t pool; + struct mail_storage_service_user *service_user; + const struct imap_settings *set; + const struct smtp_submit_settings *smtp_set; + string_t *capability_string; + const char *disconnect_reason; + + struct mail_user *user; + struct mailbox *mailbox; + struct mailbox_keywords keywords; + unsigned int sync_counter; + uint32_t messages_count, recent_count, uidvalidity; + ARRAY(bool) enabled_features; + + time_t last_input, last_output; + unsigned int bad_counter; + + /* one parser is kept here to be used for new commands */ + struct imap_parser *free_parser; + /* command_pool is cleared when the command queue gets empty */ + pool_t command_pool; + /* New commands are always prepended to the queue */ + struct client_command_context *command_queue; + unsigned int command_queue_size; + + char *last_cmd_name; + struct client_command_stats last_cmd_stats; + + uint64_t sync_last_full_modseq; + uint64_t highest_fetch_modseq; + ARRAY_TYPE(seq_range) fetch_failed_uids; + + /* For imap_logout_format statistics: */ + unsigned int fetch_hdr_count, fetch_body_count; + uint64_t fetch_hdr_bytes, fetch_body_bytes; + unsigned int deleted_count, expunged_count, trashed_count; + unsigned int autoexpunged_count, append_count; + + /* SEARCHRES extension: Last saved SEARCH result */ + ARRAY_TYPE(seq_range) search_saved_uidset; + /* SEARCH=CONTEXT extension: Searches that get updated */ + ARRAY(struct imap_search_update) search_updates; + /* NOTIFY extension */ + struct imap_notify_context *notify_ctx; + uint32_t notify_uidnext; + + /* client input/output is locked by this command */ + struct client_command_context *input_lock; + struct client_command_context *output_cmd_lock; + /* command changing the mailbox */ + struct client_command_context *mailbox_change_lock; + + /* IMAP URLAUTH context (RFC4467) */ + struct imap_urlauth_context *urlauth_ctx; + + /* Module-specific contexts. */ + ARRAY(union imap_module_context *) module_contexts; + + /* syncing marks this TRUE when it sees \Deleted flags. this is by + EXPUNGE for Outlook-workaround. */ + bool sync_seen_deletes:1; + bool logged_out:1; + bool disconnected:1; + bool hibernated:1; + bool destroyed:1; + bool handling_input:1; + bool syncing:1; + bool id_logged:1; + bool mailbox_examined:1; + bool anvil_sent:1; + bool tls_compression:1; + bool input_skip_line:1; /* skip all the data until we've + found a new line */ + bool modseqs_sent_since_sync:1; + bool notify_immediate_expunges:1; + bool notify_count_changes:1; + bool notify_flag_changes:1; + bool nonpermanent_modseqs:1; + bool state_import_bad_idle_done:1; + bool state_import_idle_continue:1; +}; + +struct imap_module_register { + unsigned int id; +}; + +union imap_module_context { + struct imap_client_vfuncs super; + struct imap_module_register *reg; +}; +extern struct imap_module_register imap_module_register; + +extern struct client *imap_clients; +extern unsigned int imap_client_count; + +extern unsigned int imap_feature_condstore; +extern unsigned int imap_feature_qresync; + +/* Create new client with specified input/output handles. socket specifies + if the handle is a socket. */ +struct client *client_create(int fd_in, int fd_out, + struct event *event, struct mail_user *user, + struct mail_storage_service_user *service_user, + const struct imap_settings *set, + const struct smtp_submit_settings *smtp_set); +void client_create_finish_io(struct client *client); +/* Finish creating the client. Returns 0 if ok, -1 if there's an error. */ +int client_create_finish(struct client *client, const char **error_r); +void client_add_istream_prefix(struct client *client, + const unsigned char *data, size_t size); +void client_destroy(struct client *client, const char *reason) ATTR_NULL(2); + +/* Disconnect client connection */ +void client_disconnect(struct client *client, const char *reason); +void client_disconnect_with_error(struct client *client, + const char *client_error); + +/* Add the given capability to the CAPABILITY reply. If imap_capability setting + has an explicit capability, nothing is changed. */ +void client_add_capability(struct client *client, const char *capability); + +/* Send a line of data to client. */ +void client_send_line(struct client *client, const char *data); +/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full, + -1 if error. This should be used when you're (potentially) sending a lot of + lines to client. */ +int client_send_line_next(struct client *client, const char *data); +/* Send line of data to client, prefixed with client->tag. You need to prefix + the data with "OK ", "NO " or "BAD ". */ +void client_send_tagline(struct client_command_context *cmd, const char *data); + +/* Send a BAD command reply to client via client_send_tagline(). If there have + been too many command errors, the client is disconnected. client_error may + be NULL, in which case the error is looked up from imap_parser. */ +void client_send_command_error(struct client_command_context *cmd, + const char *client_error); + +/* Send a NO command reply with the default internal error message to client + via client_send_tagline(). */ +void client_send_internal_error(struct client_command_context *cmd); + +/* Read a number of arguments. Returns TRUE if everything was read or + FALSE if either needs more data or error occurred. */ +bool client_read_args(struct client_command_context *cmd, unsigned int count, + unsigned int flags, const struct imap_arg **args_r); +/* Reads a number of string arguments. ... is a list of pointers where to + store the arguments. */ +bool client_read_string_args(struct client_command_context *cmd, + unsigned int count, ...); +void client_args_finished(struct client_command_context *cmd, + const struct imap_arg *args); + +/* SEARCHRES extension: Call if $ is being used/updated, returns TRUE if we + have to wait for an existing SEARCH SAVE to finish. */ +bool client_handle_search_save_ambiguity(struct client_command_context *cmd); + +void client_enable(struct client *client, unsigned int feature_idx); +/* Returns TRUE if the given feature is enabled */ +bool client_has_enabled(struct client *client, unsigned int feature_idx); +/* Returns mailbox features that are currently enabled. */ +enum mailbox_feature client_enabled_mailbox_features(struct client *client); +/* Returns all enabled features as strings. */ +const char *const *client_enabled_features(struct client *client); + +/* Send client processing to imap-idle process. If successful, returns TRUE + and destroys the client. If hibernation failed, the exact reason is + returned (mainly for unit tests). */ +bool imap_client_hibernate(struct client **client, const char **reason_r); + +struct imap_search_update * +client_search_update_lookup(struct client *client, const char *tag, + unsigned int *idx_r); +void client_search_updates_free(struct client *client); + +struct client_command_context *client_command_alloc(struct client *client); +void client_command_init_finished(struct client_command_context *cmd); +void client_command_cancel(struct client_command_context **cmd); +void client_command_free(struct client_command_context **cmd); + +bool client_handle_unfinished_cmd(struct client_command_context *cmd); +/* Handle any pending command input. This must be run at the end of all + I/O callbacks after they've (potentially) finished some commands. */ +void client_continue_pending_input(struct client *client); +void client_add_missing_io(struct client *client); +const char *client_stats(struct client *client); + +void client_input(struct client *client); +bool client_handle_input(struct client *client); +int client_output(struct client *client); + +void clients_init(void); +void clients_destroy_all(void); + +#endif |