changeset 12590:9e471f267fb4

imapc: Code cleanups and fixes.
author Timo Sirainen <tss@iki.fi>
date Fri, 21 Jan 2011 17:39:24 +0200
parents 2fd54529e7d6
children 868f2de3898b
files src/lib-storage/index/imapc/Makefile.am src/lib-storage/index/imapc/imapc-client.h src/lib-storage/index/imapc/imapc-connection.c src/lib-storage/index/imapc/imapc-list.c src/lib-storage/index/imapc/imapc-list.h src/lib-storage/index/imapc/imapc-mailbox.c src/lib-storage/index/imapc/imapc-storage.c src/lib-storage/index/imapc/imapc-storage.h
diffstat 8 files changed, 346 insertions(+), 188 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/Makefile.am	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/Makefile.am	Fri Jan 21 17:39:24 2011 +0200
@@ -14,6 +14,7 @@
 	imapc-connection.c \
 	imapc-list.c \
 	imapc-mail.c \
+	imapc-mailbox.c \
 	imapc-save.c \
 	imapc-search.c \
 	imapc-seqmap.c \
--- a/src/lib-storage/index/imapc/imapc-client.h	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.h	Fri Jan 21 17:39:24 2011 +0200
@@ -34,10 +34,14 @@
 
 struct imapc_command_reply {
 	enum imapc_command_state state;
-	/* "RESP TEXT" when the reply contains [RESP TEXT], otherwise NULL */
-	const char *resp_text;
+	/* "[RESP TEXT]" produces key=RESP, value=TEXT.
+	   "[RESP]" produces key=RESP, value=NULL
+	   otherwise both are NULL */
+	const char *resp_text_key, *resp_text_value;
 	/* The full tagged reply, including [RESP TEXT]. */
-	const char *text;
+	const char *text_full;
+	/* Tagged reply text without [RESP TEXT] */
+	const char *text_without_resp;
 };
 
 struct imapc_untagged_reply {
@@ -49,8 +53,10 @@
 	/* the rest of the reply can be read from these args. */
 	const struct imap_arg *args;
 
-	/* "RESP TEXT" when the reply is "* OK [RESP TEXT]", otherwise NULL */
-	const char *resp_text;
+	/* "* OK [RESP TEXT]" produces key=RESP, value=TEXT.
+	   "* OK [RESP]" produces key=RESP, value=NULL
+	   otherwise both are NULL */
+	const char *resp_text_key, *resp_text_value;
 
 	/* If this reply occurred while a mailbox was selected, this contains
 	   the mailbox's untagged_context. */
--- a/src/lib-storage/index/imapc/imapc-connection.c	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-connection.c	Fri Jan 21 17:39:24 2011 +0200
@@ -126,7 +126,8 @@
 
 		memset(&reply, 0, sizeof(reply));
 		reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
-		reply.text = "Disconnected from server";
+		reply.text_without_resp = reply.text_full =
+			"Disconnected from server";
 
 		while (array_count(&conn->cmd_wait_list) > 0) {
 			cmdp = array_idx(&conn->cmd_wait_list, 0);
@@ -241,17 +242,8 @@
 
 static int
 imapc_connection_handle_resp_text_code(struct imapc_connection *conn,
-				       const char *text)
+				       const char *key, const char *value)
 {
-	const char *key, *value;
-
-	value = strchr(text, ' ');
-	if (value != NULL)
-		key = t_strdup_until(text, value++);
-	else {
-		key = text;
-		value = "";
-	}
 	if (strcasecmp(key, "CAPABILITY") == 0) {
 		if (imapc_connection_parse_capability(conn, value) < 0)
 			return -1;
@@ -268,10 +260,36 @@
 
 static int
 imapc_connection_handle_resp_text(struct imapc_connection *conn,
-				  const struct imap_arg *args,
-				  const char **text_r)
+				  const char *text,
+				  const char **key_r, const char **value_r)
 {
-	const char *text, *p;
+	const char *p, *value;
+
+	i_assert(text[0] == '[');
+
+	p = strchr(text, ']');
+	if (p == NULL) {
+		imapc_connection_input_error(conn, "Missing ']' in resp-text");
+		return -1;
+	}
+	text = t_strdup_until(text + 1, p);
+	value = strchr(text, ' ');
+	if (value != NULL) {
+		*key_r = t_strdup_until(text, value);
+		*value_r = value + 1;
+	} else {
+		*key_r = text;
+		*value_r = NULL;
+	}
+	return 0;
+}
+
+static int
+imapc_connection_handle_imap_resp_text(struct imapc_connection *conn,
+				       const struct imap_arg *args,
+				       const char **key_r, const char **value_r)
+{
+	const char *text;
 
 	if (args->type != IMAP_ARG_ATOM)
 		return 0;
@@ -285,17 +303,10 @@
 		}
 		return 0;
 	}
-	p = strchr(text, ']');
-	if (p == NULL) {
-		imapc_connection_input_error(conn, "Missing ']' in resp-text");
+	if (imapc_connection_handle_resp_text(conn, text, key_r, value_r) < 0)
 		return -1;
-	}
-	if (p[1] == '\0' || p[1] != ' ' || p[2] == '\0') {
-		imapc_connection_input_error(conn, "Missing text in resp-text");
-		return -1;
-	}
-	*text_r = text = t_strdup_until(text + 1, p);
-	return imapc_connection_handle_resp_text_code(conn, text);
+
+	return imapc_connection_handle_resp_text_code(conn, *key_r, *value_r);
 }
 
 static bool need_literal(const char *str)
@@ -346,7 +357,7 @@
 
 	if (reply->state != IMAPC_COMMAND_STATE_OK) {
 		imapc_connection_input_error(conn,
-			"Failed to get capabilities: %s", reply->text);
+			"Failed to get capabilities: %s", reply->text_full);
 	} else if (conn->capabilities == 0) {
 		imapc_connection_input_error(conn,
 			"Capabilities not returned by server");
@@ -363,7 +374,7 @@
 
 	if (reply->state != IMAPC_COMMAND_STATE_OK) {
 		imapc_connection_input_error(conn, "Authentication failed: %s",
-					     reply->text);
+					     reply->text_full);
 		return;
 	}
 
@@ -406,13 +417,14 @@
 {
 	const struct imapc_client_settings *set = &conn->client->set;
 	const struct imap_arg *imap_args;
-	const char *cmd, *text;
+	const char *cmd, *key, *value;
 	int ret;
 
 	if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
 		return ret;
 
-	if (imapc_connection_handle_resp_text(conn, imap_args, &text) < 0)
+	if (imapc_connection_handle_imap_resp_text(conn, imap_args,
+						   &key, &value) < 0)
 		return -1;
 	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_AUTHENTICATING);
 
@@ -482,8 +494,9 @@
 	memset(&reply, 0, sizeof(reply));
 
 	if (strcasecmp(name, "OK") == 0) {
-		if (imapc_connection_handle_resp_text(conn, imap_args,
-						      &reply.resp_text) < 0)
+		if (imapc_connection_handle_imap_resp_text(conn, imap_args,
+						&reply.resp_text_key,
+						&reply.resp_text_value) < 0)
 			return -1;
 	}
 
@@ -535,10 +548,10 @@
 
 	linep = strchr(line, ' ');
 	if (linep == NULL)
-		reply.text = "";
+		reply.text_full = "";
 	else {
 		*linep = '\0';
-		reply.text = linep + 1;
+		reply.text_full = linep + 1;
 	}
 
 	if (strcasecmp(line, "ok") == 0)
@@ -554,19 +567,20 @@
 		return -1;
 	}
 
-	if (reply.text[0] == '[') {
+	if (reply.text_full[0] == '[') {
 		/* get resp-text */
-		p = strchr(reply.text, ']');
-		if (p == NULL) {
-			imapc_connection_input_error(conn,
-				"Missing ']' from resp-text: %u %s",
-				conn->cur_tag, line);
+		if (imapc_connection_handle_resp_text(conn, reply.text_full,
+					&reply.resp_text_key,
+					&reply.resp_text_value) < 0)
 			return -1;
-		}
-		reply.resp_text = t_strndup(reply.text + 1, p - reply.text - 1);
-		if (imapc_connection_handle_resp_text_code(conn,
-							   reply.resp_text) < 0)
-			return -1;
+
+		p = strchr(reply.text_full, ']');
+		i_assert(p != NULL);
+		reply.text_without_resp = p + 1;
+		if (reply.text_without_resp[0] == ' ')
+			reply.text_without_resp++;
+	} else {
+		reply.text_without_resp = reply.text_full;
 	}
 
 	/* find the command. it's either the first command in send queue
--- a/src/lib-storage/index/imapc/imapc-list.c	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-list.c	Fri Jan 21 17:39:24 2011 +0200
@@ -77,9 +77,11 @@
 	return node;
 }
 
-void imapc_list_update_mailbox(struct imapc_mailbox_list *list,
-			       const struct imap_arg *args)
+static void imapc_untagged_list(const struct imapc_untagged_reply *reply,
+				struct imapc_storage *storage)
 {
+	struct imapc_mailbox_list *list = storage->list;
+	const struct imap_arg *args = reply->args;
 	const char *sep, *name;
 
 	if (list->sep == '\0') {
@@ -99,9 +101,11 @@
 	(void)imapc_list_update_tree(list->mailboxes, args);
 }
 
-void imapc_list_update_subscription(struct imapc_mailbox_list *list,
-				    const struct imap_arg *args)
+static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply,
+				struct imapc_storage *storage)
 {
+	struct imapc_mailbox_list *list = storage->list;
+	const struct imap_arg *args = reply->args;
 	struct mailbox_node *node;
 
 	if (list->sep == '\0') {
@@ -115,6 +119,14 @@
 		node->flags |= MAILBOX_SUBSCRIBED;
 }
 
+void imapc_list_register_callbacks(struct imapc_mailbox_list *list)
+{
+	imapc_storage_register_untagged(list->storage, "LIST",
+					imapc_untagged_list);
+	imapc_storage_register_untagged(list->storage, "LSUB",
+					imapc_untagged_lsub);
+}
+
 static int imapc_list_refresh(struct imapc_mailbox_list *list,
 			      enum mailbox_list_iter_flags flags)
 {
@@ -279,10 +291,13 @@
 }
 
 static int
-imapc_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
-			      enum mailbox_dir_create_type type)
+imapc_list_create_mailbox_dir(struct mailbox_list *list ATTR_UNUSED,
+			      const char *name ATTR_UNUSED,
+			      enum mailbox_dir_create_type type ATTR_UNUSED)
 {
-	return -1;
+	/* this gets called just before mailbox.create().
+	   we don't need to do anything. */
+	return 0;
 }
 
 static int
--- a/src/lib-storage/index/imapc/imapc-list.h	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-list.h	Fri Jan 21 17:39:24 2011 +0200
@@ -19,9 +19,6 @@
 	unsigned int broken:1;
 };
 
-void imapc_list_update_mailbox(struct imapc_mailbox_list *list,
-			       const struct imap_arg *args);
-void imapc_list_update_subscription(struct imapc_mailbox_list *list,
-				    const struct imap_arg *args);
+void imapc_list_register_callbacks(struct imapc_mailbox_list *list);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/imapc/imapc-mailbox.c	Fri Jan 21 17:39:24 2011 +0200
@@ -0,0 +1,176 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "imap-arg.h"
+#include "imap-util.h"
+#include "imapc-client.h"
+#include "imapc-seqmap.h"
+#include "imapc-storage.h"
+
+static void imapc_untagged_exists(const struct imapc_untagged_reply *reply,
+				  struct imapc_mailbox *mbox)
+{
+	uint32_t rcount = reply->num;
+	const struct mail_index_header *hdr;
+	struct imapc_seqmap *seqmap;
+	uint32_t next_lseq, next_rseq;
+
+	if (mbox == NULL)
+		return;
+
+	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
+	next_lseq = mail_index_view_get_messages_count(mbox->box.view) + 1;
+	next_rseq = imapc_seqmap_lseq_to_rseq(seqmap, next_lseq);
+	if (next_rseq > rcount)
+		return;
+
+	hdr = mail_index_get_header(mbox->box.view);
+
+	mbox->new_msgs = TRUE;
+	imapc_client_mailbox_cmdf(mbox->client_box, imapc_async_stop_callback,
+				  mbox->storage, "UID FETCH %u:* FLAGS",
+				  hdr->next_uid);
+}
+
+
+static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
+				 struct imapc_mailbox *mbox)
+{
+	uint32_t seq = reply->num;
+	struct imapc_seqmap *seqmap;
+	const struct imap_arg *list, *flags_list;
+	const char *atom;
+	const struct mail_index_record *rec;
+	enum mail_flags flags;
+	uint32_t uid, old_count;
+	unsigned int i, j;
+	bool seen_flags = FALSE;
+
+	if (mbox == NULL || seq == 0 || !imap_arg_get_list(reply->args, &list))
+		return;
+
+	uid = 0; flags = 0;
+	for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) {
+		if (!imap_arg_get_atom(&list[i], &atom))
+			return;
+
+		if (strcasecmp(atom, "UID") == 0) {
+			if (!imap_arg_get_atom(&list[i+1], &atom) ||
+			    str_to_uint32(atom, &uid) < 0)
+				return;
+		} else if (strcasecmp(atom, "FLAGS") == 0) {
+			if (!imap_arg_get_list(&list[i+1], &flags_list))
+				return;
+
+			seen_flags = TRUE;
+			for (j = 0; flags_list[j].type != IMAP_ARG_EOL; j++) {
+				if (!imap_arg_get_atom(&flags_list[j], &atom))
+					return;
+				if (atom[0] == '\\')
+					flags |= imap_parse_system_flag(atom);
+			}
+		}
+	}
+
+	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
+	seq = imapc_seqmap_rseq_to_lseq(seqmap, seq);
+
+	if (mbox->cur_fetch_mail != NULL && mbox->cur_fetch_mail->seq == seq) {
+		i_assert(uid == 0 || mbox->cur_fetch_mail->uid == uid);
+		imapc_fetch_mail_update(mbox->cur_fetch_mail, list);
+	}
+
+	old_count = mail_index_view_get_messages_count(mbox->delayed_sync_view);
+	if (seq > old_count) {
+		if (uid == 0)
+			return;
+		i_assert(seq == old_count + 1);
+		mail_index_append(mbox->delayed_sync_trans, uid, &seq);
+	}
+	rec = mail_index_lookup(mbox->delayed_sync_view, seq);
+	if (seen_flags && rec->flags != flags) {
+		mail_index_update_flags(mbox->delayed_sync_trans, seq,
+					MODIFY_REPLACE, flags);
+	}
+}
+
+static void imapc_untagged_expunge(const struct imapc_untagged_reply *reply,
+				   struct imapc_mailbox *mbox)
+{
+	struct imapc_seqmap *seqmap;
+	uint32_t lseq, rseq = reply->num;
+
+	if (mbox == NULL || rseq == 0)
+		return;
+
+	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
+	lseq = imapc_seqmap_rseq_to_lseq(seqmap, rseq);
+	mail_index_expunge(mbox->delayed_sync_trans, lseq);
+}
+
+static void
+imapc_resp_text_uidvalidity(const struct imapc_untagged_reply *reply,
+			    struct imapc_mailbox *mbox)
+{
+	uint32_t uid_validity;
+
+	if (mbox == NULL || reply->resp_text_value == NULL ||
+	    str_to_uint32(reply->resp_text_value, &uid_validity) < 0)
+		return;
+
+	mail_index_update_header(mbox->delayed_sync_trans,
+		offsetof(struct mail_index_header, uid_validity),
+		&uid_validity, sizeof(uid_validity), TRUE);
+}
+
+static void
+imapc_resp_text_uidnext(const struct imapc_untagged_reply *reply,
+			struct imapc_mailbox *mbox)
+{
+	uint32_t uid_next;
+
+	if (mbox == NULL || reply->resp_text_value == NULL ||
+	    str_to_uint32(reply->resp_text_value, &uid_next) < 0)
+		return;
+
+	mail_index_update_header(mbox->delayed_sync_trans,
+				 offsetof(struct mail_index_header, next_uid),
+				 &uid_next, sizeof(uid_next), FALSE);
+}
+
+
+void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox,
+				     const char *key,
+				     imapc_mailbox_callback_t *callback)
+{
+	struct imapc_mailbox_event_callback *cb;
+
+	cb = array_append_space(&mbox->untagged_callbacks);
+	cb->name = p_strdup(mbox->box.pool, key);
+	cb->callback = callback;
+}
+
+void imapc_mailbox_register_resp_text(struct imapc_mailbox *mbox,
+				      const char *key,
+				      imapc_mailbox_callback_t *callback)
+{
+	struct imapc_mailbox_event_callback *cb;
+
+	cb = array_append_space(&mbox->resp_text_callbacks);
+	cb->name = p_strdup(mbox->box.pool, key);
+	cb->callback = callback;
+}
+
+void imapc_mailbox_register_callbacks(struct imapc_mailbox *mbox)
+{
+	imapc_mailbox_register_untagged(mbox, "EXISTS",
+					imapc_untagged_exists);
+	imapc_mailbox_register_untagged(mbox, "FETCH",
+					imapc_untagged_fetch);
+	imapc_mailbox_register_untagged(mbox, "EXPUNGE",
+					imapc_untagged_expunge);
+	imapc_mailbox_register_resp_text(mbox, "UIDVALIDITY",
+					 imapc_resp_text_uidvalidity);
+	imapc_mailbox_register_resp_text(mbox, "UIDNEXT",
+					 imapc_resp_text_uidnext);
+}
--- a/src/lib-storage/index/imapc/imapc-storage.c	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Fri Jan 21 17:39:24 2011 +0200
@@ -2,20 +2,14 @@
 
 #include "lib.h"
 #include "str.h"
-#include "imap-util.h"
-#include "imap-arg.h"
 #include "imap-resp-code.h"
 #include "mail-copy.h"
 #include "index-mail.h"
-#include "mailbox-list-private.h"
 #include "imapc-client.h"
 #include "imapc-list.h"
-#include "imapc-seqmap.h"
 #include "imapc-sync.h"
 #include "imapc-storage.h"
 
-#include <sys/stat.h>
-
 #define DNS_CLIENT_SOCKET_NAME "dns-client"
 
 struct imapc_open_context {
@@ -92,15 +86,13 @@
 			    const struct imapc_command_reply *reply)
 {
 	enum mail_error error;
-	const char *p;
 
-	if (imap_resp_text_code_parse(reply->resp_text, &error)) {
-		p = strchr(reply->text, ']');
-		i_assert(p != NULL);
-		mail_storage_set_error(&storage->storage, error, p + 1);
+	if (imap_resp_text_code_parse(reply->resp_text_key, &error)) {
+		mail_storage_set_error(&storage->storage, error,
+				       reply->text_without_resp);
 	} else {
 		mail_storage_set_error(&storage->storage, default_error,
-				       reply->text);
+				       reply->text_without_resp);
 	}
 }
 
@@ -116,7 +108,7 @@
 		ctx->ret = -1;
 	} else {
 		mail_storage_set_critical(&ctx->storage->storage,
-			"imapc: Command failed: %s", reply->text);
+			"imapc: Command failed: %s", reply->text_full);
 		ctx->ret = -1;
 	}
 	imapc_client_stop(ctx->storage->client);
@@ -133,133 +125,37 @@
 		imapc_copy_error_from_reply(storage, MAIL_ERROR_PARAMS, reply);
 	} else {
 		mail_storage_set_critical(&storage->storage,
-			"imapc: Command failed: %s", reply->text);
+			"imapc: Command failed: %s", reply->text_full);
 	}
 	imapc_client_stop(storage->client);
 }
 
-static void
-imapc_mailbox_map_new_msgs(struct imapc_mailbox *mbox,
-			   struct imapc_seqmap *seqmap, uint32_t rcount)
-{
-	const struct mail_index_header *hdr;
-	uint32_t next_lseq, next_rseq;
-
-	next_lseq = mail_index_view_get_messages_count(mbox->box.view) + 1;
-	next_rseq = imapc_seqmap_lseq_to_rseq(seqmap, next_lseq);
-	if (next_rseq > rcount)
-		return;
-
-	hdr = mail_index_get_header(mbox->box.view);
-
-	mbox->new_msgs = TRUE;
-	imapc_client_mailbox_cmdf(mbox->client_box, imapc_async_stop_callback,
-				  mbox->storage, "UID FETCH %u:* FLAGS",
-				  hdr->next_uid);
-}
-
-static void
-imapc_mailbox_map_fetch_reply(struct imapc_mailbox *mbox,
-			      const struct imap_arg *args, uint32_t seq)
-{
-	struct imapc_seqmap *seqmap;
-	const struct imap_arg *list, *flags_list;
-	const char *atom;
-	const struct mail_index_record *rec;
-	enum mail_flags flags;
-	uint32_t uid, old_count;
-	unsigned int i, j;
-	bool seen_flags = FALSE;
-
-	if (seq == 0 || !imap_arg_get_list(args, &list))
-		return;
-
-	uid = 0; flags = 0;
-	for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) {
-		if (!imap_arg_get_atom(&list[i], &atom))
-			return;
-
-		if (strcasecmp(atom, "UID") == 0) {
-			if (!imap_arg_get_atom(&list[i+1], &atom) ||
-			    str_to_uint32(atom, &uid) < 0)
-				return;
-		} else if (strcasecmp(atom, "FLAGS") == 0) {
-			if (!imap_arg_get_list(&list[i+1], &flags_list))
-				return;
-
-			seen_flags = TRUE;
-			for (j = 0; flags_list[j].type != IMAP_ARG_EOL; j++) {
-				if (!imap_arg_get_atom(&flags_list[j], &atom))
-					return;
-				if (atom[0] == '\\')
-					flags |= imap_parse_system_flag(atom);
-			}
-		}
-	}
-
-	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
-	seq = imapc_seqmap_rseq_to_lseq(seqmap, seq);
-
-	if (mbox->cur_fetch_mail != NULL && mbox->cur_fetch_mail->seq == seq) {
-		i_assert(uid == 0 || mbox->cur_fetch_mail->uid == uid);
-		imapc_fetch_mail_update(mbox->cur_fetch_mail, list);
-	}
-
-	old_count = mail_index_view_get_messages_count(mbox->delayed_sync_view);
-	if (seq > old_count) {
-		if (uid == 0)
-			return;
-		i_assert(seq == old_count + 1);
-		mail_index_append(mbox->delayed_sync_trans, uid, &seq);
-	}
-	rec = mail_index_lookup(mbox->delayed_sync_view, seq);
-	if (seen_flags && rec->flags != flags) {
-		mail_index_update_flags(mbox->delayed_sync_trans, seq,
-					MODIFY_REPLACE, flags);
-	}
-}
-
 static void imapc_storage_untagged_cb(const struct imapc_untagged_reply *reply,
 				      void *context)
 {
 	struct imapc_storage *storage = context;
 	struct imapc_mailbox *mbox = reply->untagged_box_context;
-	struct imapc_seqmap *seqmap;
-	uint32_t lseq;
+	const struct imapc_storage_event_callback *cb;
+	const struct imapc_mailbox_event_callback *mcb;
 
-	if (strcasecmp(reply->name, "LIST") == 0)
-		imapc_list_update_mailbox(storage->list, reply->args);
-	else if (strcasecmp(reply->name, "LSUB") == 0)
-		imapc_list_update_subscription(storage->list, reply->args);
+	array_foreach(&storage->untagged_callbacks, cb) {
+		if (strcasecmp(reply->name, cb->name) == 0)
+			cb->callback(reply, storage);
+	}
 
 	if (mbox == NULL)
 		return;
 
-	if (reply->resp_text != NULL) {
-		uint32_t uid_validity, uid_next;
-
-		if (strncasecmp(reply->resp_text, "UIDVALIDITY ", 12) == 0 &&
-		    str_to_uint32(reply->resp_text + 12, &uid_validity) == 0) {
-			mail_index_update_header(mbox->delayed_sync_trans,
-				offsetof(struct mail_index_header, uid_validity),
-				&uid_validity, sizeof(uid_validity), TRUE);
-		}
-		if (strncasecmp(reply->resp_text, "UIDNEXT ", 8) == 0 &&
-		    str_to_uint32(reply->resp_text + 8, &uid_next) == 0) {
-			mail_index_update_header(mbox->delayed_sync_trans,
-				offsetof(struct mail_index_header, next_uid),
-				&uid_next, sizeof(uid_next), FALSE);
-		}
+	array_foreach(&mbox->untagged_callbacks, mcb) {
+		if (strcasecmp(reply->name, mcb->name) == 0)
+			mcb->callback(reply, mbox);
 	}
 
-	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
-	if (strcasecmp(reply->name, "EXISTS") == 0) {
-		imapc_mailbox_map_new_msgs(mbox, seqmap, reply->num);
-	} else if (strcasecmp(reply->name, "FETCH") == 0) {
-		imapc_mailbox_map_fetch_reply(mbox, reply->args, reply->num);
-	} else if (strcasecmp(reply->name, "EXPUNGE") == 0) {
-		lseq = imapc_seqmap_rseq_to_lseq(seqmap, reply->num);
-		mail_index_expunge(mbox->delayed_sync_trans, lseq);
+	if (reply->resp_text_key != NULL) {
+		array_foreach(&mbox->resp_text_callbacks, mcb) {
+			if (strcasecmp(reply->resp_text_key, mcb->name) == 0)
+				mcb->callback(reply, mbox);
+		}
 	}
 }
 
@@ -283,11 +179,14 @@
 	set.dns_client_socket_path =
 		t_strconcat(_storage->user->set->base_dir, "/",
 			    DNS_CLIENT_SOCKET_NAME, NULL);
-	storage->list = (struct imapc_list *)ns->list;
+	storage->list = (struct imapc_mailbox_list *)ns->list;
 	storage->list->storage = storage;
 	storage->client = imapc_client_init(&set);
+
+	p_array_init(&storage->untagged_callbacks, _storage->pool, 16);
 	imapc_client_register_untagged(storage->client,
 				       imapc_storage_untagged_cb, storage);
+	imapc_list_register_callbacks(storage->list);
 	return 0;
 }
 
@@ -298,6 +197,17 @@
 	imapc_client_deinit(&storage->client);
 }
 
+void imapc_storage_register_untagged(struct imapc_storage *storage,
+				     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->callback = callback;
+}
+
 static void
 imapc_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
 				struct mailbox_list_settings *set)
@@ -331,6 +241,10 @@
 	ibox->save_rollback = imapc_transaction_save_rollback;
 
 	mbox->storage = (struct imapc_storage *)storage;
+
+	p_array_init(&mbox->untagged_callbacks, pool, 16);
+	p_array_init(&mbox->resp_text_callbacks, pool, 16);
+	imapc_mailbox_register_callbacks(mbox);
 	return &mbox->box;
 }
 
@@ -349,7 +263,7 @@
 	} else {
 		mail_storage_set_critical(ctx->mbox->box.storage,
 			"imapc: Opening mailbox '%s' failed: %s",
-			ctx->mbox->box.name, reply->text);
+			ctx->mbox->box.name, reply->text_full);
 		ctx->ret = -1;
 	}
 	if (!ctx->mbox->new_msgs)
@@ -404,8 +318,8 @@
 	const char *name = box->name;
 
 	if (directory) {
-		/* FIXME: hardcoded separator.. */
-		name = t_strconcat(name, "/", NULL);
+		name = t_strdup_printf("%s%c", name,
+				mailbox_list_get_hierarchy_sep(box->list));
 	}
 	ctx.storage = mbox->storage;
 	imapc_client_cmdf(mbox->storage->client, imapc_simple_callback, &ctx,
@@ -443,7 +357,7 @@
 	} else {
 		mail_storage_set_critical(ctx->mbox->box.storage,
 			"imapc: STATUS for mailbox '%s' failed: %s",
-			ctx->mbox->box.name, reply->text);
+			ctx->mbox->box.name, reply->text_full);
 		ctx->ret = -1;
 	}
 	imapc_client_stop(ctx->mbox->storage->client);
--- a/src/lib-storage/index/imapc/imapc-storage.h	Fri Jan 21 01:05:00 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.h	Fri Jan 21 17:39:24 2011 +0200
@@ -6,12 +6,32 @@
 #define IMAPC_STORAGE_NAME "imapc"
 
 struct imap_arg;
+struct imapc_untagged_reply;
 struct imapc_command_reply;
+struct imapc_mailbox;
+struct imapc_storage;
+
+typedef void imapc_storage_callback_t(const struct imapc_untagged_reply *reply,
+				      struct imapc_storage *storage);
+typedef void imapc_mailbox_callback_t(const struct imapc_untagged_reply *reply,
+				      struct imapc_mailbox *mbox);
+
+struct imapc_storage_event_callback {
+	const char *name;
+	imapc_storage_callback_t *callback;
+};
+
+struct imapc_mailbox_event_callback {
+	const char *name;
+	imapc_mailbox_callback_t *callback;
+};
 
 struct imapc_storage {
 	struct mail_storage storage;
 	struct imapc_mailbox_list *list;
 	struct imapc_client *client;
+
+	ARRAY_DEFINE(untagged_callbacks, struct imapc_storage_event_callback);
 };
 
 struct imapc_mailbox {
@@ -24,6 +44,9 @@
 
 	struct mail *cur_fetch_mail;
 
+	ARRAY_DEFINE(untagged_callbacks, struct imapc_mailbox_event_callback);
+	ARRAY_DEFINE(resp_text_callbacks, struct imapc_mailbox_event_callback);
+
 	unsigned int new_msgs:1;
 };
 
@@ -59,4 +82,16 @@
 void imapc_async_stop_callback(const struct imapc_command_reply *reply,
 			       void *context);
 
+void imapc_storage_register_untagged(struct imapc_storage *storage,
+				     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);
+void imapc_mailbox_register_resp_text(struct imapc_mailbox *mbox,
+				      const char *key,
+				      imapc_mailbox_callback_t *callback);
+
+void imapc_mailbox_register_callbacks(struct imapc_mailbox *mbox);
+
 #endif