changeset 16586:620876853f6f

imapc: Reorganize code so that imapc_list works without imapc_storage. Most importantly fixes crashes when imapc_list is trying to lookup hierarchy separator before storage is created.
author Timo Sirainen <tss@iki.fi>
date Wed, 10 Jul 2013 06:54:57 +0300
parents e45ad5524f1a
children 331d0a4fe772
files src/lib-storage/index/imapc/imapc-list.c src/lib-storage/index/imapc/imapc-list.h src/lib-storage/index/imapc/imapc-mail-fetch.c src/lib-storage/index/imapc/imapc-save.c src/lib-storage/index/imapc/imapc-storage.c src/lib-storage/index/imapc/imapc-storage.h src/lib-storage/index/imapc/imapc-sync.c
diffstat 7 files changed, 259 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-list.c	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-list.c	Wed Jul 10 06:54:57 2013 +0300
@@ -41,6 +41,12 @@
 
 extern struct mailbox_list imapc_mailbox_list;
 
+static void imapc_list_send_hierarcy_sep_lookup(struct imapc_mailbox_list *list);
+static void imapc_untagged_list(const struct imapc_untagged_reply *reply,
+				struct imapc_storage_client *client);
+static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply,
+				struct imapc_storage_client *client);
+
 static struct mailbox_list *imapc_list_alloc(void)
 {
 	struct imapc_mailbox_list *list;
@@ -50,20 +56,41 @@
 	list = p_new(pool, struct imapc_mailbox_list, 1);
 	list->list = imapc_mailbox_list;
 	list->list.pool = pool;
-	/* separator is set when storage is created */
+	/* separator is set lazily */
 	list->mailboxes = mailbox_tree_init('\0');
 	mailbox_tree_set_parents_nonexistent(list->mailboxes);
 	return &list->list;
 }
 
-static int
-imapc_list_init(struct mailbox_list *_list, const char **error_r ATTR_UNUSED)
+static int imapc_list_init(struct mailbox_list *_list, const char **error_r)
 {
 	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+	char sep;
 
 	list->set = mail_user_set_get_driver_settings(_list->ns->user->set_info,
 						      _list->ns->user->set,
 						      IMAPC_STORAGE_NAME);
+	if (imapc_storage_client_create(_list->ns, list->set, _list->mail_set,
+					&list->client, error_r) < 0)
+		return -1;
+	list->client->_list = list;
+
+	imapc_storage_client_register_untagged(list->client, "LIST",
+					       imapc_untagged_list);
+	imapc_storage_client_register_untagged(list->client, "LSUB",
+					       imapc_untagged_lsub);
+	imapc_list_send_hierarcy_sep_lookup(list);
+	if ((_list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
+		/* we're using imapc for the INBOX namespace. wait and make
+		   sure we can successfully access the IMAP server (so if the
+		   username is invalid we don't just keep failing every
+		   command). */
+		if (imapc_list_try_get_root_sep(list, &sep) < 0) {
+			imapc_storage_client_unref(&list->client);
+			*error_r = "Failed to access imapc backend";
+			return -1;
+		}
+	}
 	return 0;
 }
 
@@ -76,21 +103,43 @@
 	mailbox_tree_deinit(&list->mailboxes);
 	if (list->tmp_subscriptions != NULL)
 		mailbox_tree_deinit(&list->tmp_subscriptions);
+	imapc_storage_client_unref(&list->client);
 	pool_unref(&list->list.pool);
 }
 
+static void
+imapc_list_copy_error_from_reply(struct imapc_mailbox_list *list,
+				 enum mail_error default_error,
+				 const struct imapc_command_reply *reply)
+{
+	enum mail_error error;
+
+	if (imap_resp_text_code_parse(reply->resp_text_key, &error)) {
+		mailbox_list_set_error(&list->list, error,
+				       reply->text_without_resp);
+	} else {
+		mailbox_list_set_error(&list->list, default_error,
+				       reply->text_without_resp);
+	}
+}
+
 static void imapc_list_simple_callback(const struct imapc_command_reply *reply,
 				       void *context)
 {
 	struct imapc_simple_context *ctx = context;
-	const char *str;
-	enum mail_error error;
 
-	imapc_simple_callback(reply, context);
-	if (ctx->ret < 0) {
-		str = mail_storage_get_last_error(&ctx->storage->storage, &error);
-		mailbox_list_set_error(&ctx->storage->list->list, error, str);
+	if (reply->state == IMAPC_COMMAND_STATE_OK)
+		ctx->ret = 0;
+	else if (reply->state == IMAPC_COMMAND_STATE_NO) {
+		imapc_list_copy_error_from_reply(ctx->client->_list,
+						 MAIL_ERROR_PARAMS, reply);
+		ctx->ret = -1;
+	} else {
+		mailbox_list_set_critical(&ctx->client->_list->list,
+			"imapc: Command failed: %s", reply->text_full);
+		ctx->ret = -1;
 	}
+	imapc_client_stop(ctx->client->client);
 }
 
 static bool
@@ -144,13 +193,13 @@
 }
 
 static void imapc_untagged_list(const struct imapc_untagged_reply *reply,
-				struct imapc_storage *storage)
+				struct imapc_storage_client *client)
 {
-	struct imapc_mailbox_list *list = storage->list;
+	struct imapc_mailbox_list *list = client->_list;
 	const struct imap_arg *args = reply->args;
 	const char *sep, *name;
 
-	if (storage->root_sep == '\0') {
+	if (list->root_sep == '\0') {
 		/* we haven't asked for the separator yet.
 		   lets see if this is the reply for its request. */
 		if (args[0].type == IMAP_ARG_EOL ||
@@ -159,21 +208,21 @@
 			return;
 
 		/* we can't handle NIL separator yet */
-		storage->root_sep = sep == NULL ? '/' : sep[0];
-		mailbox_tree_set_separator(list->mailboxes, storage->root_sep);
+		list->root_sep = sep == NULL ? '/' : sep[0];
+		mailbox_tree_set_separator(list->mailboxes, list->root_sep);
 	} else {
 		(void)imapc_list_update_tree(list, list->mailboxes, args);
 	}
 }
 
 static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply,
-				struct imapc_storage *storage)
+				struct imapc_storage_client *client)
 {
-	struct imapc_mailbox_list *list = storage->list;
+	struct imapc_mailbox_list *list = client->_list;
 	const struct imap_arg *args = reply->args;
 	struct mailbox_node *node;
 
-	if (storage->root_sep == '\0') {
+	if (list->root_sep == '\0') {
 		/* we haven't asked for the separator yet */
 		return;
 	}
@@ -191,12 +240,61 @@
 	}
 }
 
-void imapc_list_register_callbacks(struct imapc_mailbox_list *list)
+static void imapc_list_sep_verify(struct imapc_mailbox_list *list)
+{
+	const char *imapc_list_prefix = list->set->imapc_list_prefix;
+
+	if (list->root_sep == '\0') {
+		mailbox_list_set_critical(&list->list,
+			"imapc: LIST didn't return hierarchy separator");
+	} else if (imapc_list_prefix[0] != '\0' &&
+		   imapc_list_prefix[strlen(imapc_list_prefix)-1] == list->root_sep) {
+		mailbox_list_set_critical(&list->list,
+			"imapc_list_prefix must not end with hierarchy separator");
+	}
+}
+
+static void imapc_storage_sep_callback(const struct imapc_command_reply *reply,
+				       void *context)
 {
-	imapc_storage_register_untagged(list->storage, "LIST",
-					imapc_untagged_list);
-	imapc_storage_register_untagged(list->storage, "LSUB",
-					imapc_untagged_lsub);
+	struct imapc_mailbox_list *list = context;
+
+	list->root_sep_pending = FALSE;
+	if (reply->state == IMAPC_COMMAND_STATE_OK)
+		imapc_list_sep_verify(list);
+	else if (reply->state == IMAPC_COMMAND_STATE_NO)
+		imapc_list_copy_error_from_reply(list, MAIL_ERROR_PARAMS, reply);
+	else {
+		mailbox_list_set_critical(&list->list,
+			"imapc: Command failed: %s", reply->text_full);
+	}
+	imapc_client_stop(list->client->client);
+}
+
+static void imapc_list_send_hierarcy_sep_lookup(struct imapc_mailbox_list *list)
+{
+	struct imapc_command *cmd;
+
+	if (list->root_sep_pending)
+		return;
+	list->root_sep_pending = TRUE;
+
+	cmd = imapc_client_cmd(list->client->client,
+			       imapc_storage_sep_callback, list);
+	imapc_command_send(cmd, "LIST \"\" \"\"");
+}
+
+int imapc_list_try_get_root_sep(struct imapc_mailbox_list *list, char *sep_r)
+{
+	if (list->root_sep == '\0') {
+		imapc_list_send_hierarcy_sep_lookup(list);
+		while (list->root_sep_pending)
+			imapc_client_run(list->client->client);
+		if (list->root_sep == '\0')
+			return -1;
+	}
+	*sep_r = list->root_sep;
+	return 0;
 }
 
 static char imapc_list_get_hierarchy_sep(struct mailbox_list *_list)
@@ -204,7 +302,7 @@
 	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
 	char sep;
 
-	if (imapc_storage_try_get_root_sep(list->storage, &sep) < 0) {
+	if (imapc_list_try_get_root_sep(list, &sep) < 0) {
 		/* we can't really fail here. just return a common separator
 		   and keep failing all list commands until it succeeds. */
 		return '/';
@@ -367,8 +465,8 @@
 imapc_list_simple_context_init(struct imapc_simple_context *ctx,
 			       struct imapc_mailbox_list *list)
 {
-	imapc_simple_context_init(ctx, list->storage);
-	return imapc_client_cmd(list->storage->client,
+	imapc_simple_context_init(ctx, list->client);
+	return imapc_client_cmd(list->client->client,
 				imapc_list_simple_callback, ctx);
 }
 
@@ -690,7 +788,7 @@
 	struct imapc_command *cmd;
 	struct imapc_simple_context ctx;
 
-	capa = imapc_client_get_capabilities(list->storage->client);
+	capa = imapc_client_get_capabilities(list->client->client);
 
 	cmd = imapc_list_simple_context_init(&ctx, list);
 	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT);
--- a/src/lib-storage/index/imapc/imapc-list.h	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-list.h	Wed Jul 10 06:54:57 2013 +0300
@@ -10,21 +10,22 @@
 struct imapc_mailbox_list {
 	struct mailbox_list list;
 	const struct imapc_settings *set;
-	struct imapc_storage *storage;
+	struct imapc_storage_client *client;
 	struct mailbox_list *index_list;
 
 	struct mailbox_tree_context *mailboxes, *tmp_subscriptions;
+	char root_sep;
 
 	unsigned int iter_count;
 
 	unsigned int refreshed_subscriptions:1;
 	unsigned int refreshed_mailboxes:1;
 	unsigned int index_list_failed:1;
+	unsigned int root_sep_pending:1;
 };
 
 int imapc_list_get_mailbox_flags(struct mailbox_list *list, const char *name,
 				 enum mailbox_info_flags *flags_r);
-
-void imapc_list_register_callbacks(struct imapc_mailbox_list *list);
+int imapc_list_try_get_root_sep(struct imapc_mailbox_list *list, char *sep_r);
 
 #endif
--- a/src/lib-storage/index/imapc/imapc-mail-fetch.c	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail-fetch.c	Wed Jul 10 06:54:57 2013 +0300
@@ -48,7 +48,7 @@
 			"imapc: Mail prefetch failed: %s", reply->text_full);
 	}
 	pool_unref(&mail->imail.mail.pool);
-	imapc_client_stop(mbox->storage->client);
+	imapc_client_stop(mbox->storage->client->client);
 }
 
 static int
@@ -407,6 +407,6 @@
 	if (!match) {
 		/* this is only a FETCH FLAGS update for the wanted mail */
 	} else {
-		imapc_client_stop(mbox->storage->client);
+		imapc_client_stop(mbox->storage->client->client);
 	}
 }
--- a/src/lib-storage/index/imapc/imapc-save.c	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-save.c	Wed Jul 10 06:54:57 2013 +0300
@@ -66,7 +66,8 @@
 
 	i_assert(ctx->fd == -1);
 
-	ctx->fd = imapc_client_create_temp_fd(ctx->mbox->storage->client, &path);
+	ctx->fd = imapc_client_create_temp_fd(ctx->mbox->storage->client->client,
+					      &path);
 	if (ctx->fd == -1) {
 		mail_storage_set_critical(storage,
 					  "Couldn't create temp file %s", path);
@@ -180,7 +181,7 @@
 			"imapc: COPY failed: %s", reply->text_full);
 		ctx->ret = -1;
 	}
-	imapc_client_stop(ctx->ctx->mbox->storage->client);
+	imapc_client_stop(ctx->ctx->mbox->storage->client->client);
 }
 
 static void
@@ -191,7 +192,7 @@
 
 	/* we don't really care about the reply */
 	ctx->ret = 0;
-	imapc_client_stop(ctx->ctx->mbox->storage->client);
+	imapc_client_stop(ctx->ctx->mbox->storage->client->client);
 }
 
 static void
@@ -239,7 +240,7 @@
 	input = i_stream_create_fd(ctx->fd, IO_BLOCK_SIZE, FALSE);
 	sctx.ctx = ctx;
 	sctx.ret = -2;
-	cmd = imapc_client_cmd(ctx->mbox->storage->client,
+	cmd = imapc_client_cmd(ctx->mbox->storage->client->client,
 			       imapc_save_callback, &sctx);
 	imapc_command_sendf(cmd, "APPEND %s%1s%1s %p",
 			    ctx->mbox->box.name, flags, internaldate, input);
@@ -254,7 +255,7 @@
 		   but it makes the behavior better. See if NOOP finds
 		   the mail. */
 		sctx.ret = -2;
-		cmd = imapc_client_cmd(ctx->mbox->storage->client,
+		cmd = imapc_client_cmd(ctx->mbox->storage->client->client,
 				       imapc_save_noop_callback, &sctx);
 		imapc_command_send(cmd, "NOOP");
 		while (sctx.ret == -2)
@@ -398,7 +399,7 @@
 			"imapc: COPY failed: %s", reply->text_full);
 		ctx->ret = -1;
 	}
-	imapc_client_stop(ctx->ctx->mbox->storage->client);
+	imapc_client_stop(ctx->ctx->mbox->storage->client->client);
 }
 
 int imapc_copy(struct mail_save_context *_ctx, struct mail *mail)
--- a/src/lib-storage/index/imapc/imapc-storage.c	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Wed Jul 10 06:54:57 2013 +0300
@@ -51,12 +51,11 @@
 };
 
 static void imapc_untagged_status(const struct imapc_untagged_reply *reply,
-				  struct imapc_storage *storage);
+				  struct imapc_storage_client *client);
 static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply,
-				     struct imapc_storage *storage);
+				     struct imapc_storage_client *client);
 
-static bool
-imap_resp_text_code_parse(const char *str, enum mail_error *error_r)
+bool imap_resp_text_code_parse(const char *str, enum mail_error *error_r)
 {
 	unsigned int i;
 
@@ -101,23 +100,23 @@
 }
 
 void imapc_simple_context_init(struct imapc_simple_context *sctx,
-			       struct imapc_storage *storage)
+			       struct imapc_storage_client *client)
 {
 	memset(sctx, 0, sizeof(*sctx));
-	sctx->storage = storage;
+	sctx->client = client;
 	sctx->ret = -2;
 }
 
 void imapc_simple_run(struct imapc_simple_context *sctx)
 {
 	while (sctx->ret == -2)
-		imapc_storage_run(sctx->storage);
+		imapc_client_run(sctx->client->client);
 }
 
 void imapc_storage_run(struct imapc_storage *storage)
 {
 	do {
-		imapc_client_run(storage->client);
+		imapc_client_run(storage->client->client);
 	} while (storage->reopen_count > 0);
 }
 
@@ -129,14 +128,15 @@
 	if (reply->state == IMAPC_COMMAND_STATE_OK)
 		ctx->ret = 0;
 	else if (reply->state == IMAPC_COMMAND_STATE_NO) {
-		imapc_copy_error_from_reply(ctx->storage, MAIL_ERROR_PARAMS, reply);
+		imapc_copy_error_from_reply(ctx->client->_storage,
+					    MAIL_ERROR_PARAMS, reply);
 		ctx->ret = -1;
 	} else {
-		mail_storage_set_critical(&ctx->storage->storage,
+		mail_storage_set_critical(&ctx->client->_storage->storage,
 			"imapc: Command failed: %s", reply->text_full);
 		ctx->ret = -1;
 	}
-	imapc_client_stop(ctx->storage->client);
+	imapc_client_stop(ctx->client->client);
 }
 
 void imapc_mailbox_noop(struct imapc_mailbox *mbox)
@@ -144,24 +144,25 @@
 	struct imapc_command *cmd;
 	struct imapc_simple_context sctx;
 
-	imapc_simple_context_init(&sctx, mbox->storage);
+	imapc_simple_context_init(&sctx, mbox->storage->client);
 	cmd = imapc_client_mailbox_cmd(mbox->client_box,
 				       imapc_simple_callback, &sctx);
 	imapc_command_send(cmd, "NOOP");
 	imapc_simple_run(&sctx);
 }
 
-static void imapc_storage_untagged_cb(const struct imapc_untagged_reply *reply,
-				      void *context)
+static void
+imapc_storage_client_untagged_cb(const struct imapc_untagged_reply *reply,
+				 void *context)
 {
-	struct imapc_storage *storage = context;
+	struct imapc_storage_client *client = context;
 	struct imapc_mailbox *mbox = reply->untagged_box_context;
 	const struct imapc_storage_event_callback *cb;
 	const struct imapc_mailbox_event_callback *mcb;
 
-	array_foreach(&storage->untagged_callbacks, cb) {
+	array_foreach(&client->untagged_callbacks, cb) {
 		if (strcasecmp(reply->name, cb->name) == 0)
-			cb->callback(reply, storage);
+			cb->callback(reply, client);
 	}
 
 	if (mbox == NULL)
@@ -180,71 +181,13 @@
 	}
 }
 
-static void imapc_storage_sep_verify(struct imapc_storage *storage)
-{
-	const char *imapc_list_prefix = storage->set->imapc_list_prefix;
-
-	if (storage->root_sep == '\0') {
-		mail_storage_set_critical(&storage->storage,
-			"imapc: LIST didn't return hierarchy separator");
-	} else if (imapc_list_prefix[0] != '\0' &&
-		   imapc_list_prefix[strlen(imapc_list_prefix)-1] == storage->root_sep) {
-		mail_storage_set_critical(&storage->storage,
-			"imapc_list_prefix must not end with hierarchy separator");
-	}
-}
-
-static void imapc_storage_sep_callback(const struct imapc_command_reply *reply,
-				       void *context)
-{
-	struct imapc_storage *storage = context;
-
-	storage->root_sep_pending = FALSE;
-	if (reply->state == IMAPC_COMMAND_STATE_OK)
-		imapc_storage_sep_verify(storage);
-	else if (reply->state == IMAPC_COMMAND_STATE_NO)
-		imapc_copy_error_from_reply(storage, MAIL_ERROR_PARAMS, reply);
-	else {
-		mail_storage_set_critical(&storage->storage,
-			"imapc: Command failed: %s", reply->text_full);
-	}
-	imapc_client_stop(storage->client);
-}
-
-static void imapc_storage_send_hierarcy_sep_lookup(struct imapc_storage *storage)
+int imapc_storage_client_create(struct mail_namespace *ns,
+				const struct imapc_settings *imapc_set,
+				const struct mail_storage_settings *mail_set,
+				struct imapc_storage_client **client_r,
+				const char **error_r)
 {
-	struct imapc_command *cmd;
-
-	if (storage->root_sep_pending)
-		return;
-	storage->root_sep_pending = TRUE;
-
-	cmd = imapc_client_cmd(storage->client,
-			       imapc_storage_sep_callback, storage);
-	imapc_command_send(cmd, "LIST \"\" \"\"");
-}
-
-int imapc_storage_try_get_root_sep(struct imapc_storage *storage, char *sep_r)
-{
-	i_assert(storage->list != NULL);
-
-	if (storage->root_sep == '\0') {
-		imapc_storage_send_hierarcy_sep_lookup(storage);
-		while (storage->root_sep_pending)
-			imapc_client_run(storage->client);
-		if (storage->root_sep == '\0')
-			return -1;
-	}
-	*sep_r = storage->root_sep;
-	return 0;
-}
-
-static int
-imapc_client_create(struct mail_namespace *ns,
-		    const struct imapc_settings *imapc_set,
-		    const struct mail_storage_settings *mail_set,
-		    struct imapc_client **client_r, const char **error_r)
-{
+	struct imapc_storage_client *client;
 	struct imapc_client_settings set;
 	string_t *str;
 
@@ -290,53 +233,61 @@
 		set.ssl_mode = IMAPC_CLIENT_SSL_MODE_NONE;
 	set.ssl_crypto_device = mail_set->ssl_crypto_device;
 
-	*client_r = imapc_client_init(&set);
+	client = i_new(struct imapc_storage_client, 1);
+	client->refcount = 1;
+	i_array_init(&client->untagged_callbacks, 16);
+	client->client = imapc_client_init(&set);
+	imapc_client_register_untagged(client->client,
+				       imapc_storage_client_untagged_cb, client);
+	/* start logging in immediately */
+	imapc_client_login(client->client, NULL, NULL);
+
+	*client_r = client;
 	return 0;
 }
 
+void imapc_storage_client_unref(struct imapc_storage_client **_client)
+{
+	struct imapc_storage_client *client = *_client;
+	struct imapc_storage_event_callback *cb;
+
+	*_client = NULL;
+
+	i_assert(client->refcount > 0);
+	if (--client->refcount > 0)
+		return;
+	imapc_client_deinit(&client->client);
+	array_foreach_modifiable(&client->untagged_callbacks, cb)
+		i_free(cb->name);
+	array_free(&client->untagged_callbacks);
+	i_free(client);
+}
+
 static int
 imapc_storage_create(struct mail_storage *_storage,
 		     struct mail_namespace *ns,
 		     const char **error_r)
 {
 	struct imapc_storage *storage = (struct imapc_storage *)_storage;
-	char sep;
+	struct imapc_mailbox_list *imapc_list = NULL;
 
 	storage->set = mail_storage_get_driver_settings(_storage);
-	if (imapc_client_create(ns, storage->set, _storage->set,
-				&storage->client, error_r) < 0)
-		return -1;
-
+	if (strcmp(ns->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) {
+		imapc_list = (struct imapc_mailbox_list *)ns->list;
+		storage->client = imapc_list->client;
+		storage->client->refcount++;
+	} else {
+		if (imapc_storage_client_create(ns, storage->set, _storage->set,
+						&storage->client, error_r) < 0)
+			return -1;
+	}
+	storage->client->_storage = storage;
 	p_array_init(&storage->remote_namespaces, _storage->pool, 4);
-	p_array_init(&storage->untagged_callbacks, _storage->pool, 16);
-	if (strcmp(ns->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) {
-		storage->list = (struct imapc_mailbox_list *)ns->list;
-		storage->list->storage = storage;
-		imapc_list_register_callbacks(storage->list);
-	}
 
-	imapc_client_register_untagged(storage->client,
-				       imapc_storage_untagged_cb, storage);
-	imapc_storage_register_untagged(storage, "STATUS",
-					imapc_untagged_status);
-	imapc_storage_register_untagged(storage, "NAMESPACE",
-					imapc_untagged_namespace);
-	/* start connecting to imap server and get the hierarchy separator. */
-	imapc_client_login(storage->client, NULL, NULL);
-	if (storage->list != NULL)
-		imapc_storage_send_hierarcy_sep_lookup(storage);
-	if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
-	    storage->list != NULL) {
-		/* we're using imapc for the INBOX namespace. wait and make
-		   sure we can successfully access the IMAP server (so if the
-		   username is invalid we don't just keep failing every
-		   command). */
-		if (imapc_storage_try_get_root_sep(storage, &sep) < 0) {
-			imapc_client_deinit(&storage->client);
-			*error_r = "Failed to access imapc backend";
-			return -1;
-		}
-	}
+	imapc_storage_client_register_untagged(storage->client, "STATUS",
+					       imapc_untagged_status);
+	imapc_storage_client_register_untagged(storage->client, "NAMESPACE",
+					       imapc_untagged_namespace);
 	return 0;
 }
 
@@ -344,30 +295,18 @@
 {
 	struct imapc_storage *storage = (struct imapc_storage *)_storage;
 
-	imapc_client_deinit(&storage->client);
+	imapc_storage_client_unref(&storage->client);
 	index_storage_destroy(_storage);
 }
 
-static void imapc_storage_add_list(struct mail_storage *_storage,
-				   struct mailbox_list *_list)
-{
-	struct imapc_storage *storage = (struct imapc_storage *)_storage;
-	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
-
-	if (strcmp(_list->name, MAILBOX_LIST_NAME_IMAPC) == 0) {
-		i_assert(storage->list != NULL);
-		list->storage = storage;
-	}
-}
-
-void imapc_storage_register_untagged(struct imapc_storage *storage,
-				     const char *name,
-				     imapc_storage_callback_t *callback)
+void imapc_storage_client_register_untagged(struct imapc_storage_client *client,
+					    const char *name,
+					    imapc_storage_callback_t *callback)
 {
 	struct imapc_storage_event_callback *cb;
 
-	cb = array_append_space(&storage->untagged_callbacks);
-	cb->name = p_strdup(storage->storage.pool, name);
+	cb = array_append_space(&client->untagged_callbacks);
+	cb->name = i_strdup(name);
 	cb->callback = callback;
 }
 
@@ -446,7 +385,7 @@
 			mbox->box.name, reply->text_full);
 		imapc_client_mailbox_reconnect(mbox->client_box);
 	}
-	imapc_client_stop(mbox->storage->client);
+	imapc_client_stop(mbox->storage->client->client);
 }
 
 static void imapc_mailbox_reopen(void *context)
@@ -494,13 +433,13 @@
 			ctx->mbox->box.name, reply->text_full);
 		ctx->ret = -1;
 	}
-	imapc_client_stop(ctx->mbox->storage->client);
+	imapc_client_stop(ctx->mbox->storage->client->client);
 }
 
 static void imapc_mailbox_get_extensions(struct imapc_mailbox *mbox)
 {
 	enum imapc_capability capa =
-		imapc_client_get_capabilities(mbox->storage->client);
+		imapc_client_get_capabilities(mbox->storage->client->client);
 
 	if (mbox->guid_fetch_field_name == NULL) {
 		/* see if we can get message GUIDs somehow */
@@ -519,7 +458,7 @@
 	i_assert(mbox->client_box == NULL);
 
 	mbox->client_box =
-		imapc_client_mailbox_open(mbox->storage->client, mbox);
+		imapc_client_mailbox_open(mbox->storage->client->client, mbox);
 	imapc_client_mailbox_set_reopen_cb(mbox->client_box,
 					   imapc_mailbox_reopen, mbox);
 
@@ -618,8 +557,8 @@
 		name = t_strdup_printf("%s%c", name,
 				mailbox_list_get_hierarchy_sep(box->list));
 	}
-	imapc_simple_context_init(&sctx, mbox->storage);
-	cmd = imapc_client_cmd(mbox->storage->client,
+	imapc_simple_context_init(&sctx, mbox->storage->client);
+	cmd = imapc_client_cmd(mbox->storage->client->client,
 			       imapc_simple_callback, &sctx);
 	imapc_command_sendf(cmd, "CREATE %s", name);
 	imapc_simple_run(&sctx);
@@ -639,8 +578,9 @@
 }
 
 static void imapc_untagged_status(const struct imapc_untagged_reply *reply,
-				  struct imapc_storage *storage)
+				  struct imapc_storage_client *client)
 {
+	struct imapc_storage *storage = client->_storage;
 	struct mailbox_status *status;
 	const struct imap_arg *list;
 	const char *name, *key, *value;
@@ -676,8 +616,9 @@
 }
 
 static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply,
-				     struct imapc_storage *storage)
+				     struct imapc_storage_client *client)
 {
+	struct imapc_storage *storage = client->_storage;
 	static enum mail_namespace_type ns_types[] = {
 		MAIL_NAMESPACE_TYPE_PRIVATE,
 		MAIL_NAMESPACE_TYPE_SHARED,
@@ -769,10 +710,10 @@
 		return 0;
 	}
 
-	imapc_simple_context_init(&sctx, mbox->storage);
+	imapc_simple_context_init(&sctx, mbox->storage->client);
 	mbox->storage->cur_status_box = mbox;
 	mbox->storage->cur_status = status_r;
-	cmd = imapc_client_cmd(mbox->storage->client,
+	cmd = imapc_client_cmd(mbox->storage->client->client,
 			       imapc_simple_callback, &sctx);
 	imapc_command_sendf(cmd, "STATUS %s (%1s)", box->name, str_c(str)+1);
 	imapc_simple_run(&sctx);
@@ -790,14 +731,14 @@
 	if (storage->namespaces_requested)
 		return 0;
 
-	capa = imapc_client_get_capabilities(storage->client);
+	capa = imapc_client_get_capabilities(storage->client->client);
 	if ((capa & IMAPC_CAPABILITY_NAMESPACE) == 0) {
 		/* NAMESPACE capability not supported */
 		return 0;
 	}
 
-	imapc_simple_context_init(&sctx, storage);
-	cmd = imapc_client_cmd(storage->client,
+	imapc_simple_context_init(&sctx, storage->client);
+	cmd = imapc_client_cmd(storage->client->client,
 			       imapc_simple_callback, &sctx);
 	imapc_command_send(cmd, "NAMESPACE");
 	imapc_simple_run(&sctx);
@@ -908,7 +849,7 @@
 		return;
 	}
 
-	capa = imapc_client_get_capabilities(mbox->storage->client);
+	capa = imapc_client_get_capabilities(mbox->storage->client->client);
 	if ((capa & IMAPC_CAPABILITY_IDLE) != 0) {
 		/* remote server is already in IDLE. but since some servers
 		   don't notice changes immediately, we'll force them to check
@@ -920,7 +861,7 @@
 	} else {
 		/* remote server doesn't support IDLE.
 		   check for changes with NOOP every once in a while. */
-		i_assert(!imapc_client_is_running(mbox->storage->client));
+		i_assert(!imapc_client_is_running(mbox->storage->client->client));
 		mbox->to_idle_check =
 			timeout_add(set->mailbox_idle_check_interval * 1000,
 				    imapc_idle_timeout, mbox);
@@ -946,7 +887,7 @@
 		imapc_storage_alloc,
 		imapc_storage_create,
 		imapc_storage_destroy,
-		imapc_storage_add_list,
+		NULL,
 		imapc_storage_get_list_settings,
 		NULL,
 		imapc_mailbox_alloc,
--- a/src/lib-storage/index/imapc/imapc-storage.h	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-storage.h	Wed Jul 10 06:54:57 2013 +0300
@@ -12,15 +12,15 @@
 struct imapc_untagged_reply;
 struct imapc_command_reply;
 struct imapc_mailbox;
-struct imapc_storage;
+struct imapc_storage_client;
 
 typedef void imapc_storage_callback_t(const struct imapc_untagged_reply *reply,
-				      struct imapc_storage *storage);
+				      struct imapc_storage_client *client);
 typedef void imapc_mailbox_callback_t(const struct imapc_untagged_reply *reply,
 				      struct imapc_mailbox *mbox);
 
 struct imapc_storage_event_callback {
-	const char *name;
+	char *name;
 	imapc_storage_callback_t *callback;
 };
 
@@ -40,24 +40,32 @@
 	enum mail_namespace_type type;
 };
 
+struct imapc_storage_client {
+	int refcount;
+
+	/* either one of these may not be available: */
+	struct imapc_storage *_storage;
+	struct imapc_mailbox_list *_list;
+
+	struct imapc_client *client;
+
+	ARRAY(struct imapc_storage_event_callback) untagged_callbacks;
+};
+
 struct imapc_storage {
 	struct mail_storage storage;
 	const struct imapc_settings *set;
 
 	struct ioloop *root_ioloop;
-	struct imapc_mailbox_list *list;
-	struct imapc_client *client;
-	char root_sep;
+	struct imapc_storage_client *client;
 
 	struct imapc_mailbox *cur_status_box;
 	struct mailbox_status *cur_status;
 	unsigned int reopen_count;
 
 	ARRAY(struct imapc_namespace) remote_namespaces;
-	ARRAY(struct imapc_storage_event_callback) untagged_callbacks;
 
 	unsigned int namespaces_requested:1;
-	unsigned int root_sep_pending:1;
 };
 
 struct imapc_mail_cache {
@@ -110,10 +118,17 @@
 };
 
 struct imapc_simple_context {
-	struct imapc_storage *storage;
+	struct imapc_storage_client *client;
 	int ret;
 };
 
+int imapc_storage_client_create(struct mail_namespace *ns,
+				const struct imapc_settings *imapc_set,
+				const struct mail_storage_settings *mail_set,
+				struct imapc_storage_client **client_r,
+				const char **error_r);
+void imapc_storage_client_unref(struct imapc_storage_client **client);
+
 struct mail_save_context *
 imapc_save_alloc(struct mailbox_transaction_context *_t);
 int imapc_save_begin(struct mail_save_context *ctx, struct istream *input);
@@ -130,13 +145,13 @@
 void imapc_storage_run(struct imapc_storage *storage);
 void imapc_mail_cache_free(struct imapc_mail_cache *cache);
 int imapc_mailbox_select(struct imapc_mailbox *mbox);
-int imapc_storage_try_get_root_sep(struct imapc_storage *storage, char *sep_r);
 
+bool imap_resp_text_code_parse(const char *str, enum mail_error *error_r);
 void imapc_copy_error_from_reply(struct imapc_storage *storage,
 				 enum mail_error default_error,
 				 const struct imapc_command_reply *reply);
 void imapc_simple_context_init(struct imapc_simple_context *sctx,
-			       struct imapc_storage *storage);
+			       struct imapc_storage_client *client);
 void imapc_simple_run(struct imapc_simple_context *sctx);
 void imapc_simple_callback(const struct imapc_command_reply *reply,
 			   void *context);
@@ -146,9 +161,9 @@
 void imapc_mailbox_set_corrupted(struct imapc_mailbox *mbox,
 				 const char *reason, ...) ATTR_FORMAT(2, 3);
 
-void imapc_storage_register_untagged(struct imapc_storage *storage,
-				     const char *name,
-				     imapc_storage_callback_t *callback);
+void imapc_storage_client_register_untagged(struct imapc_storage_client *client,
+					    const char *name,
+					    imapc_storage_callback_t *callback);
 void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox,
 				     const char *name,
 				     imapc_mailbox_callback_t *callback);
--- a/src/lib-storage/index/imapc/imapc-sync.c	Wed Jul 10 06:05:20 2013 +0300
+++ b/src/lib-storage/index/imapc/imapc-sync.c	Wed Jul 10 06:54:57 2013 +0300
@@ -34,7 +34,7 @@
 	}
 	
 	if (--ctx->sync_command_count == 0)
-		imapc_client_stop(ctx->mbox->storage->client);
+		imapc_client_stop(ctx->mbox->storage->client->client);
 }
 
 static void imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd_str)
@@ -136,7 +136,7 @@
 	if (array_count(&ctx->expunged_uids) == 0)
 		return;
 
-	caps = imapc_client_get_capabilities(ctx->mbox->storage->client);
+	caps = imapc_client_get_capabilities(ctx->mbox->storage->client->client);
 	if ((caps & IMAPC_CAPABILITY_UIDPLUS) == 0) {
 		/* just expunge everything */
 		imapc_sync_cmd(ctx, "EXPUNGE");
@@ -457,7 +457,7 @@
 			ret = -1;
 	}
 
-	capabilities = imapc_client_get_capabilities(mbox->storage->client);
+	capabilities = imapc_client_get_capabilities(mbox->storage->client->client);
 	if ((capabilities & IMAPC_CAPABILITY_IDLE) == 0) {
 		/* IDLE not supported. do NOOP to get latest changes
 		   before starting sync. */