diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c index 4931160350..9404b8c426 100644 --- a/src/imap/imap-client.c +++ b/src/imap/imap-client.c @@ -24,6 +24,7 @@ #include "imap-search.h" #include "imap-notify.h" #include "imap-commands.h" +#include "imap-feature.h" #include @@ -39,8 +40,8 @@ struct imap_module_register imap_module_register = { 0 }; struct client *imap_clients = NULL; unsigned int imap_client_count = 0; -unsigned int imap_feature_condstore = MAILBOX_FEATURE_CONDSTORE; -unsigned int imap_feature_qresync = MAILBOX_FEATURE_QRESYNC; +unsigned int imap_feature_condstore = UINT_MAX; +unsigned int imap_feature_qresync = UINT_MAX; static const char *client_command_state_names[CLIENT_COMMAND_STATE_DONE+1] = { "wait-input", @@ -153,6 +154,7 @@ struct client *client_create(int fd_in, int fd_out, const char *session_id, client->user = user; client->notify_count_changes = TRUE; client->notify_flag_changes = TRUE; + p_array_init(&client->enabled_features, client->pool, 8); if (set->rawlog_dir[0] != '\0') { (void)iostream_rawlog_create(set->rawlog_dir, &client->input, @@ -1449,24 +1451,39 @@ bool client_handle_search_save_ambiguity(struct client_command_context *cmd) void client_enable(struct client *client, unsigned int feature_idx) { - enum mailbox_feature features = feature_idx; + if (client_has_enabled(client, feature_idx)) + return; + + const struct imap_feature *feat = imap_feature_idx(feature_idx); + feat->callback(client); + /* set after the callback, so the callback can see what features were + previously set */ + bool value = TRUE; + array_idx_set(&client->enabled_features, feature_idx, &value); +} + +bool client_has_enabled(struct client *client, unsigned int feature_idx) +{ + if (feature_idx >= array_count(&client->enabled_features)) + return FALSE; + const bool *featurep = + array_idx(&client->enabled_features, feature_idx); + return *featurep; +} + +static void imap_client_enable_condstore(struct client *client) +{ struct mailbox_status status; - bool had_condstore = client_has_enabled(client, imap_feature_condstore); int ret; - if ((features & imap_feature_qresync) != 0) - features |= imap_feature_condstore; - - if ((client->enabled_features & features) == features) + if (client->mailbox == NULL) return; - client->enabled_features |= features; - if (client->mailbox == NULL) + if ((client_enabled_mailbox_features(client) & MAILBOX_FEATURE_CONDSTORE) != 0) return; - ret = mailbox_enable(client->mailbox, features); - if (ret == 0 && !had_condstore && - client_has_enabled(client, imap_feature_condstore)) { + ret = mailbox_enable(client->mailbox, MAILBOX_FEATURE_CONDSTORE); + if (ret == 0) { /* CONDSTORE being enabled while mailbox is selected. Notify client of the latest HIGHESTMODSEQ. */ ret = mailbox_get_status(client->mailbox, @@ -1483,31 +1500,46 @@ void client_enable(struct client *client, unsigned int feature_idx) } } -bool client_has_enabled(struct client *client, unsigned int feature_idx) +static void imap_client_enable_qresync(struct client *client) { - enum mailbox_feature features = feature_idx; - - return (client->enabled_features & features) != 0; + /* enable also CONDSTORE */ + client_enable(client, imap_feature_condstore); } enum mailbox_feature client_enabled_mailbox_features(struct client *client) { - return client->enabled_features; + enum mailbox_feature mailbox_features = 0; + const struct imap_feature *feature; + const bool *client_enabled; + unsigned int count; + + client_enabled = array_get(&client->enabled_features, &count); + for (unsigned int idx = 0; idx < count; idx++) { + if (client_enabled[idx]) { + feature = imap_feature_idx(idx); + mailbox_features |= feature->mailbox_features; + } + } + return mailbox_features; } const char *const *client_enabled_features(struct client *client) { - static const char *condstore_str = "CONDSTORE"; - static const char *qresync_str = "QRESYNC"; - ARRAY_TYPE(const_string) features; - t_array_init(&features, 8); - - if ((client->enabled_features & imap_feature_condstore) != 0) - array_append(&features, &condstore_str, 1); - if ((client->enabled_features & imap_feature_qresync) != 0) - array_append(&features, &qresync_str, 1); - array_append_zero(&features); - return array_idx(&features, 0); + ARRAY_TYPE(const_string) feature_strings; + const struct imap_feature *feature; + const bool *client_enabled; + unsigned int count; + + t_array_init(&feature_strings, 8); + client_enabled = array_get(&client->enabled_features, &count); + for (unsigned int idx = 0; idx < count; idx++) { + if (client_enabled[idx]) { + feature = imap_feature_idx(idx); + array_append(&feature_strings, &feature->feature, 1); + } + } + array_append_zero(&feature_strings); + return array_idx(&feature_strings, 0); } struct imap_search_update * @@ -1542,6 +1574,17 @@ void client_search_updates_free(struct client *client) array_clear(&client->search_updates); } +void clients_init(void) +{ + imap_feature_condstore = + imap_feature_register("CONDSTORE", MAILBOX_FEATURE_CONDSTORE, + imap_client_enable_condstore); + imap_feature_qresync = + imap_feature_register("QRESYNC", MAILBOX_FEATURE_QRESYNC | + MAILBOX_FEATURE_CONDSTORE, + imap_client_enable_qresync); +} + void clients_destroy_all(void) { while (imap_clients != NULL) { diff --git a/src/imap/imap-client.h b/src/imap/imap-client.h index d391587029..11e541730c 100644 --- a/src/imap/imap-client.h +++ b/src/imap/imap-client.h @@ -166,7 +166,7 @@ struct client { struct mailbox_keywords keywords; unsigned int sync_counter; uint32_t messages_count, recent_count, uidvalidity; - enum mailbox_feature enabled_features; + ARRAY(bool) enabled_features; time_t last_input, last_output; unsigned int bad_counter; @@ -338,6 +338,7 @@ 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 diff --git a/src/imap/imap-feature.c b/src/imap/imap-feature.c index fe556a672f..d8d52b8c7e 100644 --- a/src/imap/imap-feature.c +++ b/src/imap/imap-feature.c @@ -3,13 +3,44 @@ #include "imap-common.h" #include "imap-feature.h" +static ARRAY_TYPE(imap_feature) feature_register = ARRAY_INIT; + bool imap_feature_lookup(const char *name, unsigned int *feature_idx_r) { - if (strcasecmp(name, "CONDSTORE") == 0) - *feature_idx_r = imap_feature_condstore; - else if (strcasecmp(name, "QRESYNC") == 0) - *feature_idx_r = imap_feature_qresync; - else - return FALSE; - return TRUE; + for (unsigned int idx = 0; idx < array_count(&feature_register); idx++) { + const struct imap_feature *feat = + array_idx(&feature_register, idx); + if (strcasecmp(name, feat->feature) == 0) { + *feature_idx_r = idx; + return TRUE; + } + } + return FALSE; +} + +const struct imap_feature *imap_feature_idx(unsigned int feature_idx) +{ + return array_idx(&feature_register, feature_idx); +} + +unsigned int +imap_feature_register(const char *feature, enum mailbox_feature mailbox_features, + imap_client_enable_callback_t *callback) +{ + struct imap_feature *feat = array_append_space(&feature_register); + feat->feature = feature; + feat->mailbox_features = mailbox_features; + feat->callback = callback; + return array_count(&feature_register)-1; +} + +void imap_features_init(void) +{ + i_assert(!array_is_created(&feature_register)); + i_array_init(&feature_register, 8); +} + +void imap_features_deinit(void) +{ + array_free(&feature_register); } diff --git a/src/imap/imap-feature.h b/src/imap/imap-feature.h index 7b0742a24f..a96aa6e4c5 100644 --- a/src/imap/imap-feature.h +++ b/src/imap/imap-feature.h @@ -1,6 +1,24 @@ #ifndef IMAP_FEATURE_H #define IMAP_FEATURE_H +typedef void imap_client_enable_callback_t(struct client *); + +struct imap_feature { + const char *feature; + enum mailbox_feature mailbox_features; + imap_client_enable_callback_t *callback; + bool enabled; +}; +ARRAY_DEFINE_TYPE(imap_feature, struct imap_feature); + bool imap_feature_lookup(const char *name, unsigned int *feature_idx_r); +const struct imap_feature *imap_feature_idx(unsigned int feature_idx); + +unsigned int +imap_feature_register(const char *feature, enum mailbox_feature mailbox_features, + imap_client_enable_callback_t *callback); + +void imap_features_init(void); +void imap_features_deinit(void); #endif diff --git a/src/imap/main.c b/src/imap/main.c index b275ae9c89..6b43de1ce9 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -21,6 +21,7 @@ #include "imap-master-client.h" #include "imap-resp-code.h" #include "imap-commands.h" +#include "imap-feature.h" #include "imap-fetch.h" #include @@ -481,6 +482,8 @@ int main(int argc, char *argv[]) /* plugins may want to add commands, so this needs to be called early */ commands_init(); imap_fetch_handlers_init(); + imap_features_init(); + clients_init(); imap_master_clients_init(); const char *error; @@ -525,6 +528,8 @@ int main(int argc, char *argv[]) mail_storage_service_deinit(&storage_service); imap_fetch_handlers_deinit(); + imap_features_deinit(); + commands_deinit(); imap_master_clients_deinit();