imap-client.h revision 08406452bf0deae412437463618ed3f870ed03d5
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#ifndef IMAP_CLIENT_H
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#define IMAP_CLIENT_H
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "imap-commands.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "message-size.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#define CLIENT_COMMAND_QUEUE_MAX_SIZE 4
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen/* Maximum number of CONTEXT=SEARCH UPDATEs. Clients probably won't need more
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen than a few, so this is mainly to avoid more or less accidental pointless
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen resource usage. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#define CLIENT_MAX_SEARCH_UPDATES 10
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct client;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainenstruct mail_storage;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct mail_storage_service_ctx;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct lda_settings;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct imap_parser;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct imap_arg;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct imap_urlauth_context;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct mailbox_keywords {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* All keyword names. The array itself exists in mail_index.
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen Keywords are currently only appended, they're never removed. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const ARRAY_TYPE(keywords) *names;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Number of keywords announced to client via FLAGS/PERMANENTFLAGS.
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen This relies on keywords not being removed while mailbox is
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen selected. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen unsigned int announce_count;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct imap_search_update {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen char *tag;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct mail_search_result *result;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool return_uids;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen pool_t fetch_pool;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct imap_fetch_context *fetch_ctx;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenenum client_command_state {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Waiting for more input */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_WAIT_INPUT,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Waiting to be able to send more output */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_WAIT_OUTPUT,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Waiting for external interaction */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_WAIT_EXTERNAL,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Wait for other commands to finish execution */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Waiting for other commands to finish so we can sync */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_WAIT_SYNC,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Command is finished */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen CLIENT_COMMAND_STATE_DONE
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct client_command_stats {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* time when command handling was started - typically this is after
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen reading all the parameters. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeval start_time;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* time when command handling was last finished. this is before
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen mailbox syncing is done. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeval last_run_timeval;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* io_loop_get_wait_usecs()'s value when the command was started */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t start_ioloop_wait_usecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* how many usecs this command itself has spent running */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t running_usecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* how many usecs this command itself has spent waiting for locks */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t lock_wait_usecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* how many bytes of client input/output command has used */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t bytes_in, bytes_out;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct client_command_stats_start {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeval timeval;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t lock_wait_usecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen uint64_t bytes_in, bytes_out;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct client_command_context {
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen struct client_command_context *prev, *next;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen struct client *client;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen pool_t pool;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen /* IMAP command tag */
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen const char *tag;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen /* Name of this command */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const char *name;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Parameters for this command. These are generated from parsed IMAP
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen arguments, so they may not be exactly the same as how client sent
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen them. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const char *args;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Parameters for this command generated with
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen imap_write_args_for_human(), so it's suitable for logging. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const char *human_args;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen enum command_flags cmd_flags;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const char *tagline_reply;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen command_func_t *func;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen void *context;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Module-specific contexts. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen ARRAY(union imap_module_context *) module_contexts;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct imap_parser *parser;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen enum client_command_state state;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct client_command_stats stats;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct client_command_stats_start stats_start;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct imap_client_sync_context *sync;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool uid:1; /* used UID command */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool cancel:1; /* command is wanted to be cancelled */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool param_error:1;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool search_save_result:1; /* search result is being updated */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool search_save_result_used:1; /* command uses search save */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool temp_executed:1; /* temporary execution state tracking */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool tagline_sent:1;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool executing:1;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct imap_client_vfuncs {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Export client state into buffer. Returns 1 if ok, 0 if some state
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen couldn't be preserved, -1 if temporary internal error occurred. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen int (*state_export)(struct client *client, bool internal,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen buffer_t *dest, const char **error_r);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* Import a single block of client state from the given data. Returns
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen number of bytes successfully imported from the block, or 0 if state
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen is corrupted or contains unknown data (e.g. some plugin is no longer
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen loaded), -1 if temporary internal error occurred. */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen ssize_t (*state_import)(struct client *client, bool internal,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const unsigned char *data, size_t size,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen const char **error_r);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen void (*destroy)(struct client *client, const char *reason);
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);
};
struct client {
struct client *prev, *next;
struct imap_client_vfuncs v;
const char *session_id;
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 lda_settings *lda_set;
string_t *capability_string;
struct mail_user *user;
struct mailbox *mailbox;
struct mailbox_keywords keywords;
unsigned int sync_counter;
uint32_t messages_count, recent_count, uidvalidity;
enum mailbox_feature 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;
/* 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 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 imap_metadata_enabled: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;
/* 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, const char *session_id,
struct mail_user *user,
struct mail_storage_service_user *service_user,
const struct imap_settings *set,
const struct lda_settings *lda_set);
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 *msg);
/* 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. msg 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 *msg);
/* 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, ...);
/* 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);
int client_enable(struct client *client, enum mailbox_feature features);
/* Send client processing to imap-idle process. If successful, returns TRUE
and destroys the client. */
bool imap_client_hibernate(struct client **client);
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_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_destroy_all(struct mail_storage_service_ctx *storage_service);
#endif