imapc-storage.c revision 56956c0bba7b2bf734699ed198a1b2ad84a494cf
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic struct imapc_resp_code_map imapc_resp_code_map[] = {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_UNAVAILABLE, MAIL_ERROR_TEMP },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_AUTHFAILED, MAIL_ERROR_PERM },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_AUTHZFAILED, MAIL_ERROR_PERM },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_PRIVACYREQUIRED, MAIL_ERROR_PERM },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_CONTACTADMIN, MAIL_ERROR_PERM },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_EXPUNGEISSUED, MAIL_ERROR_EXPUNGED },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_CORRUPTION, MAIL_ERROR_TEMP },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_SERVERBUG, MAIL_ERROR_TEMP },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* { IMAP_RESP_CODE_CLIENTBUG, 0 }, */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_CANNOT, MAIL_ERROR_NOTPOSSIBLE },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_LIMIT, MAIL_ERROR_NOTPOSSIBLE },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_OVERQUOTA, MAIL_ERROR_NOSPACE },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_ALREADYEXISTS, MAIL_ERROR_EXISTS },
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen { IMAP_RESP_CODE_NONEXISTENT, MAIL_ERROR_NOTFOUND }
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainenstatic void imapc_untagged_status(const struct imapc_untagged_reply *reply,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenimap_resp_text_code_parse(const char *str, enum mail_error *error_r)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen unsigned int i;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen for (i = 0; i < N_ELEMENTS(imapc_resp_code_map); i++) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen if (strcmp(imapc_resp_code_map[i].code, str) == 0) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic struct mail_storage *imapc_storage_alloc(void)
df48643c3c240ad5b8a3e2e2132c46f7dc541b5eTimo Sirainen pool = pool_alloconly_create("imapc storage", 2048);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen storage = p_new(pool, struct imapc_storage, 1);
689b06e33729491b593fe34ad3267d65b79be149Timo Sirainenvoid imapc_copy_error_from_reply(struct imapc_storage *storage,
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen if (imap_resp_text_code_parse(reply->resp_text_key, &error)) {
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen mail_storage_set_error(&storage->storage, error,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen mail_storage_set_error(&storage->storage, default_error,
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainenvoid imapc_simple_context_init(struct imapc_simple_context *sctx,
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainenvoid imapc_simple_run(struct imapc_simple_context *sctx)
951c92a29c36d22a60e56cae4b47d5d0fa5dd6b5Timo Sirainenvoid imapc_storage_run(struct imapc_storage *storage)
951c92a29c36d22a60e56cae4b47d5d0fa5dd6b5Timo Sirainen array_foreach(&storage->client->conns, connp) {
951c92a29c36d22a60e56cae4b47d5d0fa5dd6b5Timo Sirainen client_box = imapc_connection_get_mailbox((*connp)->conn);
951c92a29c36d22a60e56cae4b47d5d0fa5dd6b5Timo Sirainen mbox->to_idle_delay = io_loop_move_timeout(&mbox->to_idle_delay);
90de49eb151c2be7655ce6aef5aa3b58295d5c84Timo Sirainenvoid imapc_simple_callback(const struct imapc_command_reply *reply,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen else if (reply->state == IMAPC_COMMAND_STATE_NO) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_copy_error_from_reply(ctx->storage, MAIL_ERROR_PARAMS, reply);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen mail_storage_set_critical(&ctx->storage->storage,
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen "imapc: Command failed: %s", reply->text_full);
e809db9220c804b16d4d74782433a1075da12274Timo Sirainenstatic void imapc_noop_callback(const struct imapc_command_reply *reply,
bf6d72f9304187cd52205be4628865ff56e2bf57Timo Sirainen else if (reply->state == IMAPC_COMMAND_STATE_NO)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_copy_error_from_reply(storage, MAIL_ERROR_PARAMS, reply);
bf6d72f9304187cd52205be4628865ff56e2bf57Timo Sirainen else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED)
bf6d72f9304187cd52205be4628865ff56e2bf57Timo Sirainen mail_storage_set_internal_error(&storage->storage);
e809db9220c804b16d4d74782433a1075da12274Timo Sirainenvoid imapc_noop_stop_callback(const struct imapc_command_reply *reply,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic void imapc_storage_untagged_cb(const struct imapc_untagged_reply *reply,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_mailbox *mbox = reply->untagged_box_context;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen const struct imapc_storage_event_callback *cb;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen const struct imapc_mailbox_event_callback *mcb;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen array_foreach(&storage->untagged_callbacks, cb) {
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen array_foreach(&mbox->untagged_callbacks, mcb) {
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen array_foreach(&mbox->resp_text_callbacks, mcb) {
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen if (strcasecmp(reply->resp_text_key, mcb->name) == 0)
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainenimapc_storage_get_hierarchy_sep(struct imapc_storage *storage,
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen const char **error_r)
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen imapc_client_cmdf(storage->client, imapc_simple_callback, &sctx,
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen "LIST \"\" \"\"");
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen mail_storage_get_last_error(&storage->storage, NULL));
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen *error_r = "LIST didn't return hierarchy separator";
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenimapc_storage_create(struct mail_storage *_storage,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen const char **error_r)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_storage *storage = (struct imapc_storage *)_storage;
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen storage->set = mail_storage_get_driver_settings(_storage);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen t_strconcat(_storage->user->set->base_dir, "/",
8a26102b8b1e08a774398980a8f92ae8f8575da8Timo Sirainen mail_user_set_get_temp_prefix(str, _storage->user->set);
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen set.ssl_ca_dir = storage->set->imapc_ssl_ca_dir;
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen if (strcmp(storage->set->imapc_ssl, "imaps") == 0)
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen set.ssl_mode = IMAPC_CLIENT_SSL_MODE_IMMEDIATE;
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen else if (strcmp(storage->set->imapc_ssl, "starttls") == 0)
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen set.ssl_mode = IMAPC_CLIENT_SSL_MODE_STARTTLS;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen storage->list = (struct imapc_mailbox_list *)ns->list;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen p_array_init(&storage->untagged_callbacks, _storage->pool, 16);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_client_register_untagged(storage->client,
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen imapc_storage_register_untagged(storage, "STATUS",
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen /* connect to imap server and get the hierarchy separator. */
b720f55c568cce0a1c8c2be74e588b66bb467e82Timo Sirainen if (imapc_storage_get_hierarchy_sep(storage, error_r) < 0) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic void imapc_storage_destroy(struct mail_storage *_storage)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_storage *storage = (struct imapc_storage *)_storage;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainenvoid imapc_storage_register_untagged(struct imapc_storage *storage,
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen cb = array_append_space(&storage->untagged_callbacks);
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen cb->name = p_strdup(storage->storage.pool, name);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenimapc_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic struct mailbox *
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenimapc_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen pool = pool_alloconly_create("imapc mailbox", 1024*3);
d83e46e7cd1ffd76210823dadcac549124c96d4eTimo Sirainen index_storage_mailbox_alloc(&mbox->box, vname, flags,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen mbox->storage = (struct imapc_storage *)storage;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen p_array_init(&mbox->untagged_callbacks, pool, 16);
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen p_array_init(&mbox->resp_text_callbacks, pool, 16);
d1b3f17d857237ea9a27bb58785bd5c6f0d3a185Timo Sirainen p_array_init(&mbox->permanent_keywords, pool, 32);
d30c35e25ea6d935393e031509e6e22422b1e006Timo Sirainen p_array_init(&mbox->delayed_expunged_uids, pool, 16);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenimapc_mailbox_open_callback(const struct imapc_command_reply *reply,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen else if (reply->state == IMAPC_COMMAND_STATE_NO) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_copy_error_from_reply(ctx->mbox->storage,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen mail_storage_set_critical(ctx->mbox->box.storage,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen "imapc: Opening mailbox '%s' failed: %s",
92f9aaf8957c8542d7497b00b5e1859645f7b3c0Timo Sirainen imapc_client_stop(ctx->mbox->storage->client);
56956c0bba7b2bf734699ed198a1b2ad84a494cfTimo Sirainenconst char *imapc_mutf7_mailbox_name(struct mailbox *box)
56956c0bba7b2bf734699ed198a1b2ad84a494cfTimo Sirainen if (t_imap_utf8_to_utf7(box->name, &mutf7_name) < 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic int imapc_mailbox_open(struct mailbox *box)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen if (index_storage_mailbox_open(box, FALSE) < 0)
689b06e33729491b593fe34ad3267d65b79be149Timo Sirainen if (box->deleting || (box->flags & MAILBOX_FLAG_SAVEONLY) != 0) {
51130f00bbd1e119ec042d63c148a78ac06ab85eTimo Sirainen /* We don't actually want to SELECT the mailbox. */
0d5b4840dbb0abe79e1bddc77608c89fd0419e53Timo Sirainen examine = (box->flags & MAILBOX_FLAG_READONLY) != 0 &&
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen imapc_client_mailbox_open(mbox->storage->client,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic void imapc_mailbox_close(struct mailbox *box)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
51130f00bbd1e119ec042d63c148a78ac06ab85eTimo Sirainen imapc_client_mailbox_close(&mbox->client_box);
51130f00bbd1e119ec042d63c148a78ac06ab85eTimo Sirainen mail_index_view_close(&mbox->delayed_sync_view);
51130f00bbd1e119ec042d63c148a78ac06ab85eTimo Sirainen if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen const struct mailbox_update *update ATTR_UNUSED,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
56956c0bba7b2bf734699ed198a1b2ad84a494cfTimo Sirainen const char *name = imapc_mutf7_mailbox_name(box);
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen imapc_simple_context_init(&sctx, mbox->storage);
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen imapc_client_cmdf(mbox->storage->client, imapc_simple_callback, &sctx,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic int imapc_mailbox_update(struct mailbox *box,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen const struct mailbox_update *update ATTR_UNUSED)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen "Not supported");
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainenstatic void imapc_untagged_status(const struct imapc_untagged_reply *reply,
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen unsigned int i;
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen if (!imap_arg_get_astring(&reply->args[0], &name) ||
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen strcmp(storage->cur_status_box->box.name, name) != 0)
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) {
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen else if (strcasecmp(key, "HIGHESTMODSEQ") == 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic void imapc_mailbox_get_selected_status(struct imapc_mailbox *mbox,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen index_storage_get_status(&mbox->box, items, status_r);
d1b3f17d857237ea9a27bb58785bd5c6f0d3a185Timo Sirainen status_r->keywords = &mbox->permanent_keywords;
d1b3f17d857237ea9a27bb58785bd5c6f0d3a185Timo Sirainen status_r->permanent_flags = mbox->permanent_flags;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic int imapc_mailbox_get_status(struct mailbox *box,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_mailbox_get_selected_status(mbox, items, status_r);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* mailbox isn't opened yet */
d1b3f17d857237ea9a27bb58785bd5c6f0d3a185Timo Sirainen if ((items & (STATUS_FIRST_UNSEEN_SEQ | STATUS_KEYWORDS |
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* getting these requires opening the mailbox */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen imapc_mailbox_get_selected_status(mbox, items, status_r);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* nothing requested */
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen imapc_simple_context_init(&sctx, mbox->storage);
56956c0bba7b2bf734699ed198a1b2ad84a494cfTimo Sirainen "STATUS %s (%1s)", imapc_mutf7_mailbox_name(box),
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic int imapc_mailbox_get_metadata(struct mailbox *box,
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen /* a bit ugly way to do this, but better than nothing for now.
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen FIXME: if indexes are enabled, keep this there. */
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen mail_generate_guid_128_hash(box->name, metadata_r->guid);
719bda7961b0ceced935b56a4f4494f2f6191b15Timo Sirainen return index_mailbox_get_metadata(box, items, metadata_r);
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainenstatic void imapc_idle_timeout(struct imapc_mailbox *mbox)
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen imapc_client_mailbox_cmd(mbox->client_box, "NOOP",
6021cfec086e455d5bf5db35522953e13a97bb61Timo Sirainenstatic void imapc_idle_noop_callback(const struct imapc_command_reply *reply,
e809db9220c804b16d4d74782433a1075da12274Timo Sirainen imapc_noop_callback(reply, mbox->box.storage);
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainenstatic void imapc_notify_changes(struct mailbox *box)
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen capa = imapc_client_get_capabilities(mbox->storage->client);
847112173a65f36251cf39a7fd7d86eba6739953Timo Sirainen /* remote server is already in IDLE. but since some servers
847112173a65f36251cf39a7fd7d86eba6739953Timo Sirainen don't notice changes immediately, we'll force them to check
847112173a65f36251cf39a7fd7d86eba6739953Timo Sirainen here by sending a NOOP. this helps with clients that break
847112173a65f36251cf39a7fd7d86eba6739953Timo Sirainen IDLE when clicking "get mail". */
847112173a65f36251cf39a7fd7d86eba6739953Timo Sirainen imapc_client_mailbox_cmd(mbox->client_box, "NOOP",
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen /* remote server doesn't support IDLE.
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen check for changes with NOOP every once in a while. */
a2d397c3a8024156eb3b4b53c37d07d60588f1e4Timo Sirainen i_assert(!imapc_client_is_running(mbox->storage->client));
df48643c3c240ad5b8a3e2e2132c46f7dc541b5eTimo Sirainenstatic bool imapc_is_inconsistent(struct mailbox *box)
df48643c3c240ad5b8a3e2e2132c46f7dc541b5eTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
df48643c3c240ad5b8a3e2e2132c46f7dc541b5eTimo Sirainen if (mail_index_view_is_inconsistent(box->view))
df48643c3c240ad5b8a3e2e2132c46f7dc541b5eTimo Sirainen return !imapc_client_mailbox_is_connected(mbox->client_box);