changeset 13554:743ebecc1224

Moved imapc-client into its own lib-imap-client library.
author Timo Sirainen <tss@iki.fi>
date Mon, 26 Sep 2011 15:34:58 +0300
parents af13ef62d083
children fe89e95867a4
files configure.in src/Makefile.am src/lib-imap-client/Makefile.am src/lib-imap-client/imapc-client-private.h src/lib-imap-client/imapc-client.c src/lib-imap-client/imapc-client.h src/lib-imap-client/imapc-connection.c src/lib-imap-client/imapc-connection.h src/lib-imap-client/imapc-msgmap.c src/lib-imap-client/imapc-msgmap.h src/lib-storage/index/imapc/Makefile.am src/lib-storage/index/imapc/imapc-client-private.h src/lib-storage/index/imapc/imapc-client.c src/lib-storage/index/imapc/imapc-client.h src/lib-storage/index/imapc/imapc-connection.c src/lib-storage/index/imapc/imapc-connection.h src/lib-storage/index/imapc/imapc-msgmap.c src/lib-storage/index/imapc/imapc-msgmap.h
diffstat 18 files changed, 2584 insertions(+), 2461 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Sun Sep 25 22:32:04 2011 +0000
+++ b/configure.in	Mon Sep 26 15:34:58 2011 +0300
@@ -2435,7 +2435,7 @@
 sdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-single/libstorage_dbox_single.la'
 mdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-multi/libstorage_dbox_multi.la'
 cydir_libs='$(top_builddir)/src/lib-storage/index/cydir/libstorage_cydir.la'
-imapc_libs='$(top_builddir)/src/lib-storage/index/imapc/libstorage_imapc.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
+imapc_libs='$(top_builddir)/src/lib-storage/index/imapc/libstorage_imapc.la $(top_builddir)/src/lib-imap-client/libimap_client.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
 raw_libs='$(top_builddir)/src/lib-storage/index/raw/libstorage_raw.la'
 shared_libs='$(top_builddir)/src/lib-storage/index/shared/libstorage_shared.la'
 
@@ -2719,6 +2719,7 @@
 src/lib-dns/Makefile
 src/lib-fs/Makefile
 src/lib-imap/Makefile
+src/lib-imap-client/Makefile
 src/lib-index/Makefile
 src/lib-lda/Makefile
 src/lib-mail/Makefile
--- a/src/Makefile.am	Sun Sep 25 22:32:04 2011 +0000
+++ b/src/Makefile.am	Mon Sep 26 15:34:58 2011 +0300
@@ -14,6 +14,7 @@
 	lib-test \
 	$(LIBDOVECOT_SUBDIRS) \
 	lib-ssl-iostream \
+	lib-imap-client \
 	lib-dovecot \
 	lib-index \
 	lib-storage \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/Makefile.am	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libimap_client.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-dns \
+	-I$(top_srcdir)/src/lib-ssl-iostream \
+	-I$(top_srcdir)/src/lib-mail \
+	-I$(top_srcdir)/src/lib-imap
+
+libimap_client_la_SOURCES = \
+	imapc-client.c \
+	imapc-connection.c \
+	imapc-msgmap.c
+
+headers = \
+	imapc-client.h \
+	imapc-client-private.h \
+	imapc-connection.h \
+	imapc-msgmap.h
+
+pkginc_libdir=$(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-client-private.h	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,40 @@
+#ifndef IMAPC_CLIENT_PRIVATE_H
+#define IMAPC_CLIENT_PRIVATE_H
+
+#include "imapc-client.h"
+
+struct imapc_client_connection {
+	struct imapc_connection *conn;
+	struct imapc_client_mailbox *box;
+};
+
+struct imapc_client {
+	pool_t pool;
+	int refcount;
+
+	struct imapc_client_settings set;
+	struct ssl_iostream_context *ssl_ctx;
+
+	imapc_untagged_callback_t *untagged_callback;
+	void *untagged_context;
+
+	ARRAY_DEFINE(conns, struct imapc_client_connection *);
+
+	struct ioloop *ioloop;
+
+	unsigned int stop_now:1;
+};
+
+struct imapc_client_mailbox {
+	struct imapc_client *client;
+	struct imapc_connection *conn;
+	struct imapc_msgmap *msgmap;
+
+	void *untagged_box_context;
+	unsigned int pending_box_command_count;
+};
+
+void imapc_client_ref(struct imapc_client *client);
+void imapc_client_unref(struct imapc_client **client);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-client.c	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,444 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "safe-mkstemp.h"
+#include "iostream-ssl.h"
+#include "imapc-msgmap.h"
+#include "imapc-connection.h"
+#include "imapc-client-private.h"
+
+#include <unistd.h>
+
+struct imapc_client_command_context {
+	struct imapc_client_mailbox *box;
+
+	imapc_command_callback_t *callback;
+	void *context;
+};
+
+const struct imapc_capability_name imapc_capability_names[] = {
+	{ "SASL-IR", IMAPC_CAPABILITY_SASL_IR },
+	{ "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
+	{ "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
+	{ "IDLE", IMAPC_CAPABILITY_IDLE },
+	{ "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS },
+	{ "AUTH=PLAIN", IMAPC_CAPABILITY_AUTH_PLAIN },
+	{ "STARTTLS", IMAPC_CAPABILITY_STARTTLS },
+
+	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
+	{ NULL, 0 }
+};
+
+static void
+default_untagged_callback(const struct imapc_untagged_reply *reply ATTR_UNUSED,
+			  void *context ATTR_UNUSED)
+{
+}
+
+struct imapc_client *
+imapc_client_init(const struct imapc_client_settings *set)
+{
+	struct imapc_client *client;
+	struct ssl_iostream_settings ssl_set;
+	const char *source;
+	pool_t pool;
+
+	pool = pool_alloconly_create("imapc client", 1024);
+	client = p_new(pool, struct imapc_client, 1);
+	client->pool = pool;
+	client->refcount = 1;
+
+	client->set.debug = set->debug;
+	client->set.host = p_strdup(pool, set->host);
+	client->set.port = set->port;
+	client->set.master_user = p_strdup(pool, set->master_user);
+	client->set.username = p_strdup(pool, set->username);
+	client->set.password = p_strdup(pool, set->password);
+	client->set.dns_client_socket_path =
+		p_strdup(pool, set->dns_client_socket_path);
+	client->set.temp_path_prefix =
+		p_strdup(pool, set->temp_path_prefix);
+	client->set.rawlog_dir = p_strdup(pool, set->rawlog_dir);
+
+	if (set->ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE) {
+		client->set.ssl_mode = set->ssl_mode;
+		client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);
+
+		memset(&ssl_set, 0, sizeof(ssl_set));
+		ssl_set.ca_dir = set->ssl_ca_dir;
+		ssl_set.verify_remote_cert = TRUE;
+
+		source = t_strdup_printf("%s:%u", set->host, set->port);
+		if (ssl_iostream_context_init_client(source, &ssl_set,
+						     &client->ssl_ctx) < 0) {
+			i_error("imapc(%s): Couldn't initialize SSL context",
+				source);
+		}
+	}
+	client->untagged_callback = default_untagged_callback;
+
+	p_array_init(&client->conns, pool, 8);
+	return client;
+}
+
+void imapc_client_ref(struct imapc_client *client)
+{
+	i_assert(client->refcount > 0);
+
+	client->refcount++;
+}
+
+void imapc_client_unref(struct imapc_client **_client)
+{
+	struct imapc_client *client = *_client;
+
+	i_assert(client->refcount > 0);
+	if (--client->refcount > 0)
+		return;
+
+	if (client->ssl_ctx != NULL)
+		ssl_iostream_context_deinit(&client->ssl_ctx);
+	pool_unref(&client->pool);
+}
+
+void imapc_client_deinit(struct imapc_client **_client)
+{
+	struct imapc_client *client = *_client;
+	struct imapc_client_connection **connp;
+
+	array_foreach_modifiable(&client->conns, connp) {
+		imapc_connection_deinit(&(*connp)->conn);
+		i_free(*connp);
+	}
+	array_clear(&client->conns);
+	imapc_client_unref(_client);
+}
+
+void imapc_client_register_untagged(struct imapc_client *client,
+				    imapc_untagged_callback_t *callback,
+				    void *context)
+{
+	client->untagged_callback = callback;
+	client->untagged_context = context;
+}
+
+void imapc_client_run_pre(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *connp;
+	struct ioloop *prev_ioloop = current_ioloop;
+	bool handle_pending = client->stop_now;
+
+	i_assert(client->ioloop == NULL);
+
+	client->stop_now = FALSE;
+
+	client->ioloop = io_loop_create();
+	io_loop_set_running(client->ioloop);
+
+	array_foreach(&client->conns, connp) {
+		imapc_connection_ioloop_changed((*connp)->conn);
+		imapc_connection_connect((*connp)->conn, NULL, NULL);
+		if (handle_pending)
+			imapc_connection_input_pending((*connp)->conn);
+	}
+
+	if (io_loop_is_running(client->ioloop))
+		io_loop_run(client->ioloop);
+	current_ioloop = prev_ioloop;
+}
+
+void imapc_client_run_post(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *connp;
+	struct ioloop *ioloop = client->ioloop;
+
+	client->ioloop = NULL;
+	array_foreach(&client->conns, connp)
+		imapc_connection_ioloop_changed((*connp)->conn);
+
+	current_ioloop = ioloop;
+	io_loop_destroy(&ioloop);
+}
+
+void imapc_client_stop(struct imapc_client *client)
+{
+	if (client->ioloop != NULL)
+		io_loop_stop(client->ioloop);
+}
+
+bool imapc_client_is_running(struct imapc_client *client)
+{
+	return client->ioloop != NULL;
+}
+
+void imapc_client_stop_now(struct imapc_client *client)
+{
+	client->stop_now = TRUE;
+	imapc_client_stop(client);
+}
+
+static struct imapc_client_connection *
+imapc_client_add_connection(struct imapc_client *client)
+{
+	struct imapc_client_connection *conn;
+
+	conn = i_new(struct imapc_client_connection, 1);
+	conn->conn = imapc_connection_init(client);
+	array_append(&client->conns, &conn, 1);
+	return conn;
+}
+
+static struct imapc_connection *
+imapc_client_find_connection(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *connp;
+
+	/* FIXME: stupid algorithm */
+	if (array_count(&client->conns) == 0)
+		return imapc_client_add_connection(client)->conn;
+	connp = array_idx(&client->conns, 0);
+	return (*connp)->conn;
+}
+
+void imapc_client_cmdf(struct imapc_client *client,
+		       imapc_command_callback_t *callback, void *context,
+		       const char *cmd_fmt, ...)
+{
+	struct imapc_connection *conn;
+	va_list args;
+
+	conn = imapc_client_find_connection(client);
+
+	va_start(args, cmd_fmt);
+	imapc_connection_cmdvf(conn, FALSE, callback, context, cmd_fmt, args);
+	va_end(args);
+}
+
+static struct imapc_client_connection *
+imapc_client_get_unboxed_connection(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *conns;
+	unsigned int i, count;
+
+	conns = array_get(&client->conns, &count);
+	for (i = 0; i < count; i++) {
+		if (conns[i]->box == NULL)
+			return conns[i];
+	}
+	return imapc_client_add_connection(client);
+}
+
+
+void imapc_client_login(struct imapc_client *client,
+			imapc_command_callback_t *callback, void *context)
+{
+	struct imapc_client_connection *conn;
+
+	i_assert(array_count(&client->conns) == 0);
+
+	conn = imapc_client_add_connection(client);
+	imapc_connection_connect(conn->conn, callback, context);
+}
+
+struct imapc_client_mailbox *
+imapc_client_mailbox_open(struct imapc_client *client,
+			  const char *name, bool examine,
+			  imapc_command_callback_t *callback, void *context,
+			  void *untagged_box_context)
+{
+	struct imapc_client_mailbox *box;
+	struct imapc_client_connection *conn;
+
+	box = i_new(struct imapc_client_mailbox, 1);
+	box->client = client;
+	box->untagged_box_context = untagged_box_context;
+	conn = imapc_client_get_unboxed_connection(client);
+	conn->box = box;
+	box->conn = conn->conn;
+	box->msgmap = imapc_msgmap_init();
+
+	imapc_connection_select(box, name, examine, callback, context);
+	return box;
+}
+
+void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box)
+{
+	if (box->conn != NULL)
+		imapc_connection_disconnect(box->conn);
+}
+
+void imapc_client_mailbox_close(struct imapc_client_mailbox **_box)
+{
+	struct imapc_client_mailbox *box = *_box;
+	struct imapc_client_connection *const *connp;
+
+	array_foreach(&box->client->conns, connp) {
+		if ((*connp)->box == box) {
+			(*connp)->box = NULL;
+			break;
+		}
+	}
+
+	if (box->conn != NULL)
+		imapc_connection_unselect(box);
+	imapc_msgmap_deinit(&box->msgmap);
+	i_free(box);
+
+	/* set this only after unselect, which may cancel some commands that
+	   reference this box */
+	*_box = NULL;
+}
+
+static void imapc_client_mailbox_cmd_cb(const struct imapc_command_reply *reply,
+					void *context)
+{
+	struct imapc_client_command_context *ctx = context;
+
+	ctx->box->pending_box_command_count--;
+
+	ctx->callback(reply, ctx->context);
+	i_free(ctx);
+}
+
+static struct imapc_client_command_context *
+imapc_client_mailbox_cmd_common(struct imapc_client_mailbox *box,
+				imapc_command_callback_t *callback,
+				void *context)
+{
+	struct imapc_client_command_context *ctx;
+
+	ctx = i_new(struct imapc_client_command_context, 1);
+	ctx->box = box;
+	ctx->callback = callback;
+	ctx->context = context;
+
+	box->pending_box_command_count++;
+	return ctx;
+}
+
+static bool
+imapc_client_mailbox_is_selected(struct imapc_client_mailbox *box,
+				 struct imapc_command_reply *reply_r)
+{
+	struct imapc_client_mailbox *selected_box;
+
+	selected_box = box->conn == NULL ? NULL :
+		imapc_connection_get_mailbox(box->conn);
+	if (selected_box == box)
+		return TRUE;
+
+	memset(reply_r, 0, sizeof(*reply_r));
+	reply_r->state = IMAPC_COMMAND_STATE_DISCONNECTED;
+	if (selected_box == NULL) {
+		reply_r->text_full = "Disconnected from server";
+	} else {
+		i_error("imapc: Selected mailbox changed unexpectedly");
+		reply_r->text_full = "Internal error";
+	}
+	reply_r->text_without_resp = reply_r->text_full;
+
+	box->conn = NULL;
+	return FALSE;
+}
+
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+			      const char *cmd,
+			      imapc_command_callback_t *callback,
+			      void *context)
+{
+	struct imapc_client_command_context *ctx;
+	struct imapc_command_reply reply;
+
+	if (!imapc_client_mailbox_is_selected(box, &reply)) {
+		callback(&reply, context);
+		return;
+	}
+
+	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
+	imapc_connection_cmd(box->conn, TRUE, cmd,
+			     imapc_client_mailbox_cmd_cb, ctx);
+}
+
+void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
+			       imapc_command_callback_t *callback,
+			       void *context, const char *cmd_fmt, ...)
+{
+	struct imapc_client_command_context *ctx;
+	va_list args;
+	struct imapc_command_reply reply;
+
+	if (!imapc_client_mailbox_is_selected(box, &reply)) {
+		callback(&reply, context);
+		return;
+	}
+
+	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
+	va_start(args, cmd_fmt);
+	imapc_connection_cmdvf(box->conn, TRUE, imapc_client_mailbox_cmd_cb,
+			       ctx, cmd_fmt, args);
+	va_end(args);
+}
+
+struct imapc_msgmap *
+imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box)
+{
+	return box->msgmap;
+}
+
+void imapc_client_mailbox_idle(struct imapc_client_mailbox *box)
+{
+	struct imapc_command_reply reply;
+
+	if (imapc_client_mailbox_is_selected(box, &reply))
+		imapc_connection_idle(box->conn);
+}
+
+bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box)
+{
+	struct imapc_command_reply reply;
+
+	return imapc_client_mailbox_is_selected(box, &reply);
+}
+
+enum imapc_capability
+imapc_client_get_capabilities(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *connp;
+
+	connp = array_idx(&client->conns, 0);
+	return imapc_connection_get_capabilities((*connp)->conn);
+}
+
+int imapc_client_create_temp_fd(struct imapc_client *client,
+				const char **path_r)
+{
+	string_t *path;
+	int fd;
+
+	if (client->set.temp_path_prefix == NULL) {
+		i_error("imapc: temp_path_prefix not set, "
+			"can't create temp file");
+		return -1;
+	}
+
+	path = t_str_new(128);
+	str_append(path, client->set.temp_path_prefix);
+	fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
+	if (fd == -1) {
+		i_error("safe_mkstemp(%s) failed: %m", str_c(path));
+		return -1;
+	}
+
+	/* we just want the fd, unlink it */
+	if (unlink(str_c(path)) < 0) {
+		/* shouldn't happen.. */
+		i_error("unlink(%s) failed: %m", str_c(path));
+		(void)close(fd);
+		return -1;
+	}
+	*path_r = str_c(path);
+	return fd;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-client.h	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,155 @@
+#ifndef IMAPC_CLIENT_H
+#define IMAPC_CLIENT_H
+
+enum imapc_command_state {
+	IMAPC_COMMAND_STATE_OK,
+	IMAPC_COMMAND_STATE_NO,
+	IMAPC_COMMAND_STATE_BAD,
+	IMAPC_COMMAND_STATE_DISCONNECTED
+};
+
+enum imapc_capability {
+	IMAPC_CAPABILITY_SASL_IR	= 0x01,
+	IMAPC_CAPABILITY_LITERALPLUS	= 0x02,
+	IMAPC_CAPABILITY_QRESYNC	= 0x04,
+	IMAPC_CAPABILITY_IDLE		= 0x08,
+	IMAPC_CAPABILITY_UIDPLUS	= 0x10,
+	IMAPC_CAPABILITY_AUTH_PLAIN	= 0x20,
+	IMAPC_CAPABILITY_STARTTLS	= 0x40,
+
+	IMAPC_CAPABILITY_IMAP4REV1	= 0x400000000
+};
+struct imapc_capability_name {
+	const char *name;
+	enum imapc_capability capability;
+};
+extern const struct imapc_capability_name imapc_capability_names[];
+
+enum imapc_client_ssl_mode {
+	IMAPC_CLIENT_SSL_MODE_NONE,
+	IMAPC_CLIENT_SSL_MODE_IMMEDIATE,
+	IMAPC_CLIENT_SSL_MODE_STARTTLS
+};
+
+struct imapc_client_settings {
+	const char *host;
+	unsigned int port;
+
+	const char *master_user;
+	const char *username;
+	const char *password;
+
+	const char *dns_client_socket_path;
+	const char *temp_path_prefix;
+
+	enum imapc_client_ssl_mode ssl_mode;
+	const char *ssl_ca_dir;
+
+	const char *rawlog_dir;
+	bool debug;
+};
+
+struct imapc_command_reply {
+	enum imapc_command_state state;
+	/* "[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_full;
+	/* Tagged reply text without [RESP TEXT] */
+	const char *text_without_resp;
+};
+
+struct imapc_arg_file {
+	/* file descriptor containing the value */
+	int fd;
+
+	/* parent_arg.list[list_idx] points to the IMAP_ARG_LITERAL_SIZE
+	   argument */
+	const struct imap_arg *parent_arg;
+	unsigned int list_idx;
+};
+
+struct imapc_untagged_reply {
+	/* name of the untagged reply, e.g. EXISTS */
+	const char *name;
+	/* number at the beginning of the reply, or 0 if there wasn't any.
+	   Set for EXISTS, EXPUNGE, etc. */
+	uint32_t num;
+	/* the rest of the reply can be read from these args. */
+	const struct imap_arg *args;
+	/* arguments whose contents are stored into files. only
+	   "FETCH (BODY[" arguments can be here. */
+	const struct imapc_arg_file *file_args;
+	unsigned int file_args_count;
+
+	/* "* 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. */
+	void *untagged_box_context;
+};
+
+/* Called when tagged reply is received for command. */
+typedef void imapc_command_callback_t(const struct imapc_command_reply *reply,
+				      void *context);
+/* Called each time untagged input is received. */
+typedef void imapc_untagged_callback_t(const struct imapc_untagged_reply *reply,
+				       void *context);
+
+struct imapc_client *
+imapc_client_init(const struct imapc_client_settings *set);
+void imapc_client_deinit(struct imapc_client **client);
+
+/* Explicitly login to server (also done automatically). */
+void imapc_client_login(struct imapc_client *client,
+			imapc_command_callback_t *callback, void *context);
+
+void imapc_client_cmdf(struct imapc_client *client,
+		       imapc_command_callback_t *callback, void *context,
+		       const char *cmd_fmt, ...) ATTR_FORMAT(4, 5);
+
+void imapc_client_register_untagged(struct imapc_client *client,
+				    imapc_untagged_callback_t *callback,
+				    void *context);
+
+void imapc_client_run_pre(struct imapc_client *client);
+void imapc_client_run_post(struct imapc_client *client);
+void imapc_client_stop(struct imapc_client *client);
+/* Stop immediately, don't finish even any already read pending replies.
+   They'll be finished when imapc_client_run() is again called. */
+void imapc_client_stop_now(struct imapc_client *client);
+bool imapc_client_is_running(struct imapc_client *client);
+
+struct imapc_client_mailbox *
+imapc_client_mailbox_open(struct imapc_client *client,
+			  const char *name, bool examine,
+			  imapc_command_callback_t *callback, void *context,
+			  void *untagged_box_context);
+void imapc_client_mailbox_close(struct imapc_client_mailbox **box);
+void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box);
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+			      const char *cmd,
+			      imapc_command_callback_t *callback,
+			      void *context);
+void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
+			       imapc_command_callback_t *callback,
+			       void *context, const char *cmd_fmt, ...)
+	ATTR_FORMAT(4, 5);
+struct imapc_msgmap *
+imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box);
+
+void imapc_client_mailbox_idle(struct imapc_client_mailbox *box);
+bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box);
+
+enum imapc_capability
+imapc_client_get_capabilities(struct imapc_client *client);
+
+int imapc_client_create_temp_fd(struct imapc_client *client,
+				const char **path_r);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-connection.c	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,1758 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "base64.h"
+#include "write-full.h"
+#include "str.h"
+#include "dns-lookup.h"
+#include "iostream-rawlog.h"
+#include "iostream-ssl.h"
+#include "imap-quote.h"
+#include "imap-util.h"
+#include "imap-parser.h"
+#include "imapc-client-private.h"
+#include "imapc-connection.h"
+
+#include <unistd.h>
+#include <ctype.h>
+
+#define IMAPC_DNS_LOOKUP_TIMEOUT_MSECS (1000*30)
+#define IMAPC_CONNECT_TIMEOUT_MSECS (1000*30)
+#define IMAPC_COMMAND_TIMEOUT_MSECS (1000*60*5)
+#define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
+/* IMAP protocol requires activity at least every 30 minutes */
+#define IMAPC_MAX_IDLE_MSECS (1000*60*29)
+
+enum imapc_input_state {
+	IMAPC_INPUT_STATE_NONE = 0,
+	IMAPC_INPUT_STATE_PLUS,
+	IMAPC_INPUT_STATE_UNTAGGED,
+	IMAPC_INPUT_STATE_UNTAGGED_NUM,
+	IMAPC_INPUT_STATE_TAGGED
+};
+
+struct imapc_command_stream {
+	unsigned int pos;
+	uoff_t size;
+	struct istream *input;
+};
+
+struct imapc_command {
+	pool_t pool;
+	buffer_t *data;
+	unsigned int send_pos;
+	unsigned int tag;
+
+	ARRAY_DEFINE(streams, struct imapc_command_stream);
+
+	imapc_command_callback_t *callback;
+	void *context;
+
+	unsigned int idle:1;
+	unsigned int mailboxcmd:1;
+	unsigned int wait_for_literal:1;
+};
+
+struct imapc_connection_literal {
+	char *temp_path;
+	int fd;
+	uoff_t bytes_left;
+
+	const struct imap_arg *parent_arg;
+	unsigned int list_idx;
+};
+
+struct imapc_connection {
+	struct imapc_client *client;
+	char *name;
+	int refcount;
+
+	int fd;
+	struct io *io;
+	struct istream *input, *raw_input;
+	struct ostream *output, *raw_output;
+	struct imap_parser *parser;
+	struct timeout *to;
+	struct timeout *to_output;
+
+	struct ssl_iostream *ssl_iostream;
+
+	int (*input_callback)(struct imapc_connection *conn);
+	enum imapc_input_state input_state;
+	unsigned int cur_tag;
+	uint32_t cur_num;
+
+	struct imapc_client_mailbox *selecting_box, *selected_box;
+	enum imapc_connection_state state;
+
+	enum imapc_capability capabilities;
+	char **capabilities_list;
+
+	imapc_command_callback_t *login_callback;
+	void *login_context;
+
+	/* commands pending in queue to be sent */
+	ARRAY_DEFINE(cmd_send_queue, struct imapc_command *);
+	/* commands that have been sent, waiting for their tagged reply */
+	ARRAY_DEFINE(cmd_wait_list, struct imapc_command *);
+
+	unsigned int ips_count, prev_connect_idx;
+	struct ip_addr *ips;
+
+	struct imapc_connection_literal literal;
+	ARRAY_DEFINE(literal_files, struct imapc_arg_file);
+
+	unsigned int idling:1;
+	unsigned int idle_stopping:1;
+	unsigned int idle_plus_waiting:1;
+	unsigned int handshake_failed:1;
+};
+
+static int imapc_connection_output(struct imapc_connection *conn);
+static int imapc_connection_ssl_init(struct imapc_connection *conn);
+static void imapc_command_free(struct imapc_command *cmd);
+static void imapc_command_send_more(struct imapc_connection *conn,
+				    struct imapc_command *cmd);
+
+struct imapc_connection *
+imapc_connection_init(struct imapc_client *client)
+{
+	struct imapc_connection *conn;
+
+	conn = i_new(struct imapc_connection, 1);
+	conn->refcount = 1;
+	conn->client = client;
+	conn->fd = -1;
+	conn->name = i_strdup_printf("%s:%u", client->set.host,
+				     client->set.port);
+	conn->literal.fd = -1;
+	i_array_init(&conn->cmd_send_queue, 8);
+	i_array_init(&conn->cmd_wait_list, 32);
+	i_array_init(&conn->literal_files, 4);
+
+	imapc_client_ref(client);
+	return conn;
+}
+
+static void imapc_connection_ref(struct imapc_connection *conn)
+{
+	i_assert(conn->refcount > 0);
+
+	conn->refcount++;
+}
+
+static void imapc_connection_unref(struct imapc_connection **_conn)
+{
+	struct imapc_connection *conn = *_conn;
+
+	i_assert(conn->refcount > 0);
+
+	*_conn = NULL;
+	if (--conn->refcount > 0)
+		return;
+
+	if (conn->capabilities_list != NULL)
+		p_strsplit_free(default_pool, conn->capabilities_list);
+	array_free(&conn->cmd_send_queue);
+	array_free(&conn->cmd_wait_list);
+	array_free(&conn->literal_files);
+	imapc_client_unref(&conn->client);
+	i_free(conn->ips);
+	i_free(conn->name);
+	i_free(conn);
+}
+
+void imapc_connection_deinit(struct imapc_connection **_conn)
+{
+	imapc_connection_disconnect(*_conn);
+	imapc_connection_unref(_conn);
+}
+
+void imapc_connection_ioloop_changed(struct imapc_connection *conn)
+{
+	if (conn->io != NULL)
+		conn->io = io_loop_move_io(&conn->io);
+	if (conn->to != NULL)
+		conn->to = io_loop_move_timeout(&conn->to);
+	if (conn->output != NULL)
+		o_stream_switch_ioloop(conn->output);
+
+	if (conn->client->ioloop == NULL && conn->to_output != NULL) {
+		/* we're only once moving the to_output to the main ioloop,
+		   since timeout moves currently also reset the timeout.
+		   (the rest of the times this is a no-op) */
+		conn->to_output = io_loop_move_timeout(&conn->to_output);
+	}
+}
+
+static const char *imapc_command_get_readable(struct imapc_command *cmd)
+{
+	string_t *str = t_str_new(256);
+	const unsigned char *data = cmd->data->data;
+	unsigned int i;
+
+	for (i = 0; i < cmd->data->used; i++) {
+		if (data[i] != '\r' && data[i] != '\n')
+			str_append_c(str, data[i]);
+	}
+	return str_c(str);
+}
+
+static void
+imapc_connection_abort_pending_commands(struct imapc_connection *conn,
+					const struct imapc_command_reply *reply)
+{
+	struct imapc_command *const *cmdp, *cmd;
+
+	while (array_count(&conn->cmd_wait_list) > 0) {
+		cmdp = array_idx(&conn->cmd_wait_list, 0);
+		cmd = *cmdp;
+		array_delete(&conn->cmd_wait_list, 0, 1);
+
+		if (cmd->callback != NULL)
+			cmd->callback(reply, cmd->context);
+		imapc_command_free(cmd);
+	}
+	while (array_count(&conn->cmd_send_queue) > 0) {
+		cmdp = array_idx(&conn->cmd_send_queue, 0);
+		cmd = *cmdp;
+		array_delete(&conn->cmd_send_queue, 0, 1);
+
+		if (cmd->callback != NULL)
+			cmd->callback(reply, cmd->context);
+		imapc_command_free(cmd);
+	}
+}
+
+static void
+imapc_login_callback(struct imapc_connection *conn,
+		     const struct imapc_command_reply *reply)
+{
+	imapc_command_callback_t *login_callback = conn->login_callback;
+	void *login_context = conn->login_context;
+
+	if (login_callback == NULL)
+		return;
+
+	conn->login_callback = NULL;
+	conn->login_context = NULL;
+	login_callback(reply, login_context);
+}
+
+static void imapc_connection_set_state(struct imapc_connection *conn,
+				       enum imapc_connection_state state)
+{
+	if (state == IMAPC_CONNECTION_STATE_DISCONNECTED) {
+		struct imapc_command_reply reply;
+
+		memset(&reply, 0, sizeof(reply));
+		reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
+		reply.text_without_resp = reply.text_full =
+			"Disconnected from server";
+		imapc_connection_abort_pending_commands(conn, &reply);
+		imapc_login_callback(conn, &reply);
+
+		conn->idling = FALSE;
+		conn->idle_plus_waiting = FALSE;
+		conn->idle_stopping = FALSE;
+
+		conn->selecting_box = NULL;
+		conn->selected_box = NULL;
+	}
+	if (state == IMAPC_CONNECTION_STATE_DONE) {
+		if (array_count(&conn->cmd_send_queue) > 0) {
+			struct imapc_command *const *cmd_p =
+				array_idx(&conn->cmd_send_queue, 0);
+			imapc_command_send_more(conn, *cmd_p);
+		}
+	}
+	conn->state = state;
+}
+
+static void imapc_connection_lfiles_free(struct imapc_connection *conn)
+{
+	struct imapc_arg_file *lfile;
+
+	array_foreach_modifiable(&conn->literal_files, lfile) {
+		if (close(lfile->fd) < 0)
+			i_error("imapc: close(literal file) failed: %m");
+	}
+	array_clear(&conn->literal_files);
+}
+
+static void
+imapc_connection_literal_reset(struct imapc_connection_literal *literal)
+{
+	if (literal->fd != -1) {
+		if (close(literal->fd) < 0)
+			i_error("close(%s) failed: %m", literal->temp_path);
+	}
+	i_free_and_null(literal->temp_path);
+
+	memset(literal, 0, sizeof(*literal));
+	literal->fd = -1;
+}
+
+void imapc_connection_disconnect(struct imapc_connection *conn)
+{
+	if (conn->fd == -1)
+		return;
+
+	if (conn->client->set.debug)
+		i_debug("imapc(%s): Disconnected", conn->name);
+
+	imapc_connection_lfiles_free(conn);
+	imapc_connection_literal_reset(&conn->literal);
+	if (conn->to != NULL)
+		timeout_remove(&conn->to);
+	if (conn->to_output != NULL)
+		timeout_remove(&conn->to_output);
+	imap_parser_destroy(&conn->parser);
+	io_remove(&conn->io);
+	if (conn->ssl_iostream != NULL)
+		ssl_iostream_unref(&conn->ssl_iostream);
+	i_stream_destroy(&conn->input);
+	o_stream_destroy(&conn->output);
+	net_disconnect(conn->fd);
+	conn->fd = -1;
+
+	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
+}
+
+static void ATTR_FORMAT(2, 3)
+imapc_connection_input_error(struct imapc_connection *conn,
+			     const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	i_error("imapc(%s): Server sent invalid input: %s",
+		conn->name, t_strdup_vprintf(fmt, va));
+	sleep(3600);
+	imapc_connection_disconnect(conn);
+	va_end(va);
+}
+
+static bool last_arg_is_fetch_body(const struct imap_arg *args,
+				   const struct imap_arg **parent_arg_r,
+				   unsigned int *idx_r)
+{
+	const struct imap_arg *list;
+	const char *name;
+	unsigned int count;
+
+	if (args[0].type == IMAP_ARG_ATOM &&
+	    imap_arg_atom_equals(&args[1], "FETCH") &&
+	    imap_arg_get_list_full(&args[2], &list, &count) && count >= 2 &&
+	    list[count].type == IMAP_ARG_LITERAL_SIZE &&
+	    imap_arg_get_atom(&list[count-1], &name) &&
+	    strncasecmp(name, "BODY[", 5) == 0) {
+		*parent_arg_r = &args[2];
+		*idx_r = count;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static int
+imapc_connection_read_literal_init(struct imapc_connection *conn, uoff_t size,
+				   const struct imap_arg *args)
+{
+	const char *path;
+	const struct imap_arg *parent_arg;
+	unsigned int idx;
+
+	i_assert(size > 0);
+	i_assert(conn->literal.fd == -1);
+
+	if (size <= IMAPC_MAX_INLINE_LITERAL_SIZE ||
+	    !last_arg_is_fetch_body(args, &parent_arg, &idx)) {
+		/* read the literal directly into parser */
+		return 0;
+	}
+
+	conn->literal.fd = imapc_client_create_temp_fd(conn->client, &path);
+	if (conn->literal.fd == -1)
+		return -1;
+	conn->literal.temp_path = i_strdup(path);
+	conn->literal.bytes_left = size;
+	conn->literal.parent_arg = parent_arg;
+	conn->literal.list_idx = idx;
+	return 1;
+}
+
+static int imapc_connection_read_literal(struct imapc_connection *conn)
+{
+	struct imapc_arg_file *lfile;
+	const unsigned char *data;
+	size_t size;
+
+	if (conn->literal.bytes_left == 0)
+		return 1;
+
+	data = i_stream_get_data(conn->input, &size);
+	if (size > conn->literal.bytes_left)
+		size = conn->literal.bytes_left;
+	if (size > 0) {
+		if (write_full(conn->literal.fd, data, size) < 0) {
+			i_error("imapc(%s): write(%s) failed: %m",
+				conn->name, conn->literal.temp_path);
+			imapc_connection_disconnect(conn);
+			return -1;
+		}
+		i_stream_skip(conn->input, size);
+		conn->literal.bytes_left -= size;
+	}
+	if (conn->literal.bytes_left > 0)
+		return 0;
+
+	/* finished */
+	lfile = array_append_space(&conn->literal_files);
+	lfile->fd = conn->literal.fd;
+	lfile->parent_arg = conn->literal.parent_arg;
+	lfile->list_idx = conn->literal.list_idx;
+
+	conn->literal.fd = -1;
+	imapc_connection_literal_reset(&conn->literal);
+	return 1;
+}
+
+static int
+imapc_connection_read_line_more(struct imapc_connection *conn,
+				const struct imap_arg **imap_args_r)
+{
+	uoff_t literal_size;
+	bool fatal;
+	int ret;
+
+	if ((ret = imapc_connection_read_literal(conn)) <= 0)
+		return ret;
+
+	ret = imap_parser_read_args(conn->parser, 0,
+				    IMAP_PARSE_FLAG_LITERAL_SIZE |
+				    IMAP_PARSE_FLAG_ATOM_ALLCHARS, imap_args_r);
+	if (ret == -2) {
+		/* need more data */
+		return 0;
+	}
+	if (ret < 0) {
+		imapc_connection_input_error(conn, "Error parsing input: %s",
+			imap_parser_get_error(conn->parser, &fatal));
+		return -1;
+	}
+
+	if (imap_parser_get_literal_size(conn->parser, &literal_size)) {
+		if (imapc_connection_read_literal_init(conn, literal_size,
+						       *imap_args_r) <= 0) {
+			imap_parser_read_last_literal(conn->parser);
+			return 2;
+		}
+		return imapc_connection_read_line_more(conn, imap_args_r);
+	}
+	return 1;
+}
+
+static int
+imapc_connection_read_line(struct imapc_connection *conn,
+			   const struct imap_arg **imap_args_r)
+{
+	const unsigned char *data;
+	size_t size;
+	int ret;
+
+	while ((ret = imapc_connection_read_line_more(conn, imap_args_r)) == 2)
+		;
+
+	if (ret > 0) {
+		data = i_stream_get_data(conn->input, &size);
+		if (size >= 2 && data[0] == '\r' && data[1] == '\n')
+			i_stream_skip(conn->input, 2);
+		else if (size >= 1 && data[0] == '\n')
+			i_stream_skip(conn->input, 1);
+		else
+			i_panic("imapc: Missing LF from input line");
+	}
+	return ret;
+}
+
+static int
+imapc_connection_parse_capability(struct imapc_connection *conn,
+				  const char *value)
+{
+	const char *const *tmp;
+	unsigned int i;
+
+	if (conn->client->set.debug) {
+		i_debug("imapc(%s): Server capabilities: %s",
+			conn->name, value);
+	}
+
+	conn->capabilities = 0;
+	if (conn->capabilities_list != NULL)
+		p_strsplit_free(default_pool, conn->capabilities_list);
+	conn->capabilities_list = p_strsplit(default_pool, value, " ");
+
+	for (tmp = t_strsplit(value, " "); *tmp != NULL; tmp++) {
+		for (i = 0; imapc_capability_names[i].name != NULL; i++) {
+			const struct imapc_capability_name *cap =
+				&imapc_capability_names[i];
+
+			if (strcasecmp(*tmp, cap->name) == 0) {
+				conn->capabilities |= cap->capability;
+				break;
+			}
+		}
+	}
+
+	if ((conn->capabilities & IMAPC_CAPABILITY_IMAP4REV1) == 0) {
+		imapc_connection_input_error(conn,
+			"CAPABILITY list is missing IMAP4REV1");
+		return -1;
+	}
+	return 0;
+}
+
+static int
+imapc_connection_handle_resp_text_code(struct imapc_connection *conn,
+				       const char *key, const char *value)
+{
+	if (strcasecmp(key, "CAPABILITY") == 0) {
+		if (imapc_connection_parse_capability(conn, value) < 0)
+			return -1;
+	}
+	if (strcasecmp(key, "CLOSED") == 0) {
+		/* QRESYNC: SELECTing another mailbox */
+		if (conn->selecting_box != NULL) {
+			conn->selected_box = conn->selecting_box;
+			conn->selecting_box = NULL;
+		}
+	}
+	return 0;
+}
+
+static int
+imapc_connection_handle_resp_text(struct imapc_connection *conn,
+				  const char *text,
+				  const char **key_r, const char **value_r)
+{
+	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;
+
+	text = imap_args_to_str(args);
+	if (*text != '[') {
+		if (*text == '\0') {
+			imapc_connection_input_error(conn,
+				"Missing text in resp-text");
+			return -1;
+		}
+		return 0;
+	}
+	if (imapc_connection_handle_resp_text(conn, text, key_r, value_r) < 0)
+		return -1;
+
+	return imapc_connection_handle_resp_text_code(conn, *key_r, *value_r);
+}
+
+static bool need_literal(const char *str)
+{
+	unsigned int i;
+
+	for (i = 0; str[i] != '\0'; i++) {
+		unsigned char c = str[i];
+
+		if ((c & 0x80) != 0 || c == '\r' || c == '\n')
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void imapc_connection_input_reset(struct imapc_connection *conn)
+{
+	conn->input_state = IMAPC_INPUT_STATE_NONE;
+	conn->cur_tag = 0;
+	conn->cur_num = 0;
+	if (conn->parser != NULL)
+		imap_parser_reset(conn->parser);
+	imapc_connection_lfiles_free(conn);
+}
+
+static void imapc_connection_login_cb(const struct imapc_command_reply *reply,
+				      void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (reply->state != IMAPC_COMMAND_STATE_OK) {
+		if (conn->login_callback != NULL)
+			imapc_login_callback(conn, reply);
+		else {
+			i_error("imapc(%s): Authentication failed: %s",
+				conn->name, reply->text_full);
+		}
+		imapc_connection_disconnect(conn);
+		return;
+	}
+
+	if (conn->client->set.debug)
+		i_debug("imapc(%s): Authenticated successfully", conn->name);
+
+	timeout_remove(&conn->to);
+	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE);
+	imapc_login_callback(conn, reply);
+}
+
+static const char *
+imapc_connection_get_sasl_plain_request(struct imapc_connection *conn)
+{
+	const struct imapc_client_settings *set = &conn->client->set;
+	string_t *in, *out;
+
+	in = t_str_new(128);
+	if (set->master_user != NULL) {
+		str_append(in, set->username);
+		str_append_c(in, '\0');
+		str_append(in, set->master_user);
+	} else {
+		str_append_c(in, '\0');
+		str_append(in, set->username);
+	}
+	str_append_c(in, '\0');
+	str_append(in, set->password);
+
+	out = t_str_new(128);
+	base64_encode(in->data, in->used, out);
+	return str_c(out);
+}
+
+static void imapc_connection_authenticate(struct imapc_connection *conn)
+{
+	const struct imapc_client_settings *set = &conn->client->set;
+	const char *cmd;
+
+	if (conn->client->set.debug) {
+		if (set->master_user == NULL) {
+			i_debug("imapc(%s): Authenticating as %s",
+				conn->name, set->username);
+		} else {
+			i_debug("imapc(%s): Authenticating as %s for user %s",
+				conn->name, set->master_user, set->username);
+		}
+	}
+
+	if ((set->master_user == NULL &&
+	     need_literal(set->username) && need_literal(set->password)) ||
+	    (conn->capabilities & IMAPC_CAPABILITY_AUTH_PLAIN) == 0) {
+		/* We can use LOGIN command */
+		imapc_connection_cmdf(conn, FALSE, imapc_connection_login_cb,
+				      conn, "LOGIN %s %s",
+				      set->username, set->password);
+	} else if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) {
+		cmd = t_strdup_printf("AUTHENTICATE PLAIN %s",
+			imapc_connection_get_sasl_plain_request(conn));
+		imapc_connection_cmd(conn, FALSE, cmd,
+				     imapc_connection_login_cb, conn);
+	} else {
+		cmd = t_strdup_printf("AUTHENTICATE PLAIN\r\n%s",
+			imapc_connection_get_sasl_plain_request(conn));
+		imapc_connection_cmd(conn, FALSE, cmd,
+				     imapc_connection_login_cb, conn);
+	}
+}
+
+static void
+imapc_connection_starttls_cb(const struct imapc_command_reply *reply,
+			     void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (reply->state != IMAPC_COMMAND_STATE_OK) {
+		imapc_connection_input_error(conn, "STARTTLS failed: %s",
+					     reply->text_full);
+		return;
+	}
+
+	if (imapc_connection_ssl_init(conn) < 0)
+		imapc_connection_disconnect(conn);
+	else
+		imapc_connection_authenticate(conn);
+}
+
+static void imapc_connection_starttls(struct imapc_connection *conn)
+{
+	if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_STARTTLS &&
+	    conn->ssl_iostream == NULL) {
+		if ((conn->capabilities & IMAPC_CAPABILITY_STARTTLS) == 0) {
+			i_error("imapc(%s): Requested STARTTLS, "
+				"but server doesn't support it",
+				conn->name);
+			imapc_connection_disconnect(conn);
+			return;
+		}
+		imapc_connection_cmd(conn, FALSE, "STARTTLS",
+				     imapc_connection_starttls_cb, conn);
+		return;
+	}
+	imapc_connection_authenticate(conn);
+}
+
+static void
+imapc_connection_capability_cb(const struct imapc_command_reply *reply,
+			       void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (reply->state != IMAPC_COMMAND_STATE_OK) {
+		imapc_connection_input_error(conn,
+			"Failed to get capabilities: %s", reply->text_full);
+	} else if (conn->capabilities == 0) {
+		imapc_connection_input_error(conn,
+			"Capabilities not returned by server");
+	} else {
+		imapc_connection_starttls(conn);
+	}
+}
+
+static int imapc_connection_input_banner(struct imapc_connection *conn)
+{
+	const struct imap_arg *imap_args;
+	const char *key, *value;
+	int ret;
+
+	if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
+		return ret;
+
+	if (imapc_connection_handle_imap_resp_text(conn, imap_args,
+						   &key, &value) < 0)
+		return -1;
+	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_AUTHENTICATING);
+
+	if (conn->capabilities == 0) {
+		/* capabilities weren't sent in the banner. ask for them. */
+		imapc_connection_cmd(conn, FALSE, "CAPABILITY",
+				     imapc_connection_capability_cb, conn);
+	} else {
+		imapc_connection_starttls(conn);
+	}
+	conn->input_callback = NULL;
+	imapc_connection_input_reset(conn);
+	return 1;
+}
+
+static int imapc_connection_input_untagged(struct imapc_connection *conn)
+{
+	const struct imap_arg *imap_args;
+	const char *name, *value;
+	struct imapc_untagged_reply reply;
+	int ret;
+
+	if (conn->state == IMAPC_CONNECTION_STATE_CONNECTING) {
+		/* input banner */
+		name = imap_parser_read_word(conn->parser);
+		if (name == NULL)
+			return 0;
+
+		if (strcasecmp(name, "OK") != 0) {
+			imapc_connection_input_error(conn,
+				"Banner doesn't begin with OK: %s", name);
+			return -1;
+		}
+		conn->input_callback = imapc_connection_input_banner;
+		return 1;
+	}
+
+	if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
+		return ret;
+	if (!imap_arg_get_atom(&imap_args[0], &name)) {
+		imapc_connection_input_error(conn, "Invalid untagged reply");
+		return -1;
+	}
+	imap_args++;
+
+	if (conn->input_state == IMAPC_INPUT_STATE_UNTAGGED &&
+	    str_to_uint32(name, &conn->cur_num) == 0) {
+		/* <seq> <event> */
+		conn->input_state = IMAPC_INPUT_STATE_UNTAGGED_NUM;
+		if (!imap_arg_get_atom(&imap_args[0], &name)) {
+			imapc_connection_input_error(conn,
+						     "Invalid untagged reply");
+			return -1;
+		}
+		imap_args++;
+	}
+	memset(&reply, 0, sizeof(reply));
+
+	if (strcasecmp(name, "OK") == 0) {
+		if (imapc_connection_handle_imap_resp_text(conn, imap_args,
+						&reply.resp_text_key,
+						&reply.resp_text_value) < 0)
+			return -1;
+	} else if (strcasecmp(name, "CAPABILITY") == 0) {
+		value = imap_args_to_str(imap_args);
+		if (imapc_connection_parse_capability(conn, value) < 0)
+			return -1;
+	}
+
+	reply.name = name;
+	reply.num = conn->cur_num;
+	reply.args = imap_args;
+	reply.file_args = array_get(&conn->literal_files,
+				    &reply.file_args_count);
+
+	if (conn->selected_box != NULL) {
+		reply.untagged_box_context =
+			conn->selected_box->untagged_box_context;
+	}
+	conn->client->untagged_callback(&reply, conn->client->untagged_context);
+	imapc_connection_input_reset(conn);
+	return 1;
+}
+
+static int imapc_connection_input_plus(struct imapc_connection *conn)
+{
+	struct imapc_command *const *cmds;
+	unsigned int cmds_count;
+	const char *line;
+
+	if ((line = i_stream_next_line(conn->input)) == NULL)
+		return 0;
+
+	cmds = array_get(&conn->cmd_send_queue, &cmds_count);
+	if (conn->idle_plus_waiting) {
+		/* "+ idling" reply for IDLE command */
+		conn->idle_plus_waiting = FALSE;
+		conn->idling = TRUE;
+	} else if (cmds_count > 0 && cmds[0]->wait_for_literal) {
+		/* reply for literal */
+		cmds[0]->wait_for_literal = FALSE;
+		imapc_command_send_more(conn, cmds[0]);
+	} else {
+		imapc_connection_input_error(conn, "Unexpected '+': %s", line);
+		return -1;
+	}
+
+	imapc_connection_input_reset(conn);
+	return 1;
+}
+
+static int imapc_connection_input_tagged(struct imapc_connection *conn)
+{
+	struct imapc_command *const *cmds, *cmd = NULL;
+	unsigned int i, count;
+	char *line, *linep;
+	const char *p;
+	struct imapc_command_reply reply;
+
+	line = i_stream_next_line(conn->input);
+	if (line == NULL)
+		return 0;
+
+	memset(&reply, 0, sizeof(reply));
+
+	linep = strchr(line, ' ');
+	if (linep == NULL)
+		reply.text_full = "";
+	else {
+		*linep = '\0';
+		reply.text_full = linep + 1;
+	}
+
+	if (strcasecmp(line, "ok") == 0)
+		reply.state = IMAPC_COMMAND_STATE_OK;
+	else if (strcasecmp(line, "no") == 0)
+		reply.state = IMAPC_COMMAND_STATE_NO;
+	else if (strcasecmp(line, "bad") == 0)
+		reply.state = IMAPC_COMMAND_STATE_BAD;
+	else {
+		imapc_connection_input_error(conn,
+			"Invalid state in tagged reply: %u %s %s",
+			conn->cur_tag, line, reply.text_full);
+		return -1;
+	}
+
+	if (reply.text_full[0] == '[') {
+		/* get resp-text */
+		if (imapc_connection_handle_resp_text(conn, reply.text_full,
+					&reply.resp_text_key,
+					&reply.resp_text_value) < 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
+	   (literal failed) or somewhere in wait list. */
+	cmds = array_get(&conn->cmd_send_queue, &count);
+	if (count > 0 && cmds[0]->tag == conn->cur_tag) {
+		cmd = cmds[0];
+		array_delete(&conn->cmd_send_queue, 0, 1);
+	} else {
+		cmds = array_get(&conn->cmd_wait_list, &count);
+		for (i = 0; i < count; i++) {
+			if (cmds[i]->tag == conn->cur_tag) {
+				cmd = cmds[i];
+				array_delete(&conn->cmd_wait_list, i, 1);
+				break;
+			}
+		}
+	}
+	if (array_count(&conn->cmd_wait_list) == 0 &&
+	    array_count(&conn->cmd_send_queue) == 0 &&
+	    conn->state == IMAPC_CONNECTION_STATE_DONE && conn->to != NULL)
+		timeout_remove(&conn->to);
+
+	if (cmd == NULL) {
+		imapc_connection_input_error(conn,
+			"Unknown tag in a reply: %u %s %s",
+			conn->cur_tag, line, reply.text_full);
+		return -1;
+	}
+
+	if (reply.state == IMAPC_COMMAND_STATE_BAD) {
+		i_error("imapc(%s): Command '%s' failed with BAD: %u %s",
+			conn->name, imapc_command_get_readable(cmd),
+			conn->cur_tag, reply.text_full);
+		imapc_connection_disconnect(conn);
+	}
+
+	imapc_connection_input_reset(conn);
+	if (cmd->callback != NULL)
+		cmd->callback(&reply, cmd->context);
+	imapc_command_free(cmd);
+	return 1;
+}
+
+static int imapc_connection_input_one(struct imapc_connection *conn)
+{
+	const char *tag;
+	int ret = -1;
+
+	if (conn->input_callback != NULL)
+		return conn->input_callback(conn);
+
+	switch (conn->input_state) {
+	case IMAPC_INPUT_STATE_NONE:
+		tag = imap_parser_read_word(conn->parser);
+		if (tag == NULL)
+			return 0;
+
+		if (strcmp(tag, "*") == 0) {
+			conn->input_state = IMAPC_INPUT_STATE_UNTAGGED;
+			conn->cur_num = 0;
+			ret = imapc_connection_input_untagged(conn);
+		} else if (strcmp(tag, "+") == 0) {
+			conn->input_state = IMAPC_INPUT_STATE_PLUS;
+			ret = imapc_connection_input_plus(conn);
+		} else {
+			conn->input_state = IMAPC_INPUT_STATE_TAGGED;
+			if (str_to_uint(tag, &conn->cur_tag) < 0 ||
+			    conn->cur_tag == 0) {
+				imapc_connection_input_error(conn,
+					"Invalid command tag: %s", tag);
+				ret = -1;
+			} else {
+				ret = imapc_connection_input_tagged(conn);
+			}
+		}
+		break;
+	case IMAPC_INPUT_STATE_PLUS:
+		ret = imapc_connection_input_plus(conn);
+		break;
+	case IMAPC_INPUT_STATE_UNTAGGED:
+	case IMAPC_INPUT_STATE_UNTAGGED_NUM:
+		ret = imapc_connection_input_untagged(conn);
+		break;
+	case IMAPC_INPUT_STATE_TAGGED:
+		ret = imapc_connection_input_tagged(conn);
+		break;
+	}
+	return ret;
+}
+
+static void imapc_connection_input(struct imapc_connection *conn)
+{
+	const char *errstr;
+	ssize_t ret = 0;
+
+	/* we need to read as much as we can with SSL streams to avoid
+	   hanging */
+	imapc_connection_ref(conn);
+	while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0)
+		imapc_connection_input_pending(conn);
+
+	if (ret < 0) {
+		/* disconnected */
+		if (conn->ssl_iostream == NULL) {
+			i_error("imapc(%s): Server disconnected unexpectedly",
+				conn->name);
+		} else if (!conn->handshake_failed) {
+			errstr = ssl_iostream_get_last_error(conn->ssl_iostream);
+			i_error("imapc(%s): Server disconnected: %s",
+				conn->name, errstr != NULL ? errstr : "");
+		}
+		imapc_connection_disconnect(conn);
+	}
+	imapc_connection_unref(&conn);
+}
+
+static int imapc_connection_ssl_handshaked(void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (!ssl_iostream_has_valid_client_cert(conn->ssl_iostream)) {
+		if (!ssl_iostream_has_broken_client_cert(conn->ssl_iostream)) {
+			i_error("imapc(%s): SSL certificate not received",
+				conn->name);
+		} else {
+			i_error("imapc(%s): Received invalid SSL certificate",
+				conn->name);
+		}
+	} else if (ssl_iostream_cert_match_name(conn->ssl_iostream,
+						conn->client->set.host) < 0) {
+		i_error("imapc(%s): SSL certificate doesn't match host name",
+			conn->name);
+	} else {
+		if (conn->client->set.debug) {
+			i_debug("imapc(%s): SSL handshake successful",
+				conn->name);
+		}
+		return 0;
+	}
+	conn->handshake_failed = TRUE;
+	i_stream_close(conn->input);
+	return -1;
+}
+
+static int imapc_connection_ssl_init(struct imapc_connection *conn)
+{
+	struct ssl_iostream_settings ssl_set;
+	const char *source;
+
+	if (conn->client->ssl_ctx == NULL) {
+		i_error("imapc(%s): No SSL context", conn->name);
+		return -1;
+	}
+
+	memset(&ssl_set, 0, sizeof(ssl_set));
+	ssl_set.verbose_invalid_cert = TRUE;
+	ssl_set.verify_remote_cert = TRUE;
+	ssl_set.require_valid_cert = TRUE;
+
+	if (conn->client->set.debug)
+		i_debug("imapc(%s): Starting SSL handshake", conn->name);
+
+	if (conn->raw_input != conn->input) {
+		/* recreate rawlog after STARTTLS */
+		i_stream_ref(conn->raw_input);
+		o_stream_ref(conn->raw_output);
+		i_stream_destroy(&conn->input);
+		o_stream_destroy(&conn->output);
+		conn->input = conn->raw_input;
+		conn->output = conn->raw_output;
+	}
+
+	source = t_strdup_printf("imapc(%s): ", conn->name);
+	if (io_stream_create_ssl(conn->client->ssl_ctx, source, &ssl_set,
+				 &conn->input, &conn->output,
+				 &conn->ssl_iostream) < 0) {
+		i_error("imapc(%s): Couldn't initialize SSL client",
+			conn->name);
+		return -1;
+	}
+	ssl_iostream_set_handshake_callback(conn->ssl_iostream,
+					    imapc_connection_ssl_handshaked,
+					    conn);
+	if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
+		i_error("imapc(%s): SSL handshake failed: %s", conn->name,
+			ssl_iostream_get_last_error(conn->ssl_iostream));
+		return -1;
+	}
+
+	if (*conn->client->set.rawlog_dir != '\0') {
+		(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
+					     &conn->input, &conn->output);
+	}
+
+	imap_parser_set_streams(conn->parser, conn->input, NULL);
+	return 0;
+}
+
+static void imapc_connection_connected(struct imapc_connection *conn)
+{
+	const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
+	int err;
+
+	err = net_geterror(conn->fd);
+	if (err != 0) {
+		i_error("imapc(%s): connect(%s, %u) failed: %s",
+			conn->name, net_ip2addr(ip), conn->client->set.port,
+			strerror(err));
+		imapc_connection_disconnect(conn);
+		return;
+	}
+	io_remove(&conn->io);
+	conn->io = io_add(conn->fd, IO_READ, imapc_connection_input, conn);
+
+	if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
+		if (imapc_connection_ssl_init(conn) < 0)
+			imapc_connection_disconnect(conn);
+	}
+}
+
+static void imapc_connection_timeout(struct imapc_connection *conn)
+{
+	const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
+
+	switch (conn->state) {
+	case IMAPC_CONNECTION_STATE_CONNECTING:
+		i_error("imapc(%s): connect(%s, %u) timed out after %u seconds",
+			conn->name, net_ip2addr(ip), conn->client->set.port,
+			IMAPC_CONNECT_TIMEOUT_MSECS/1000);
+		break;
+	case IMAPC_CONNECTION_STATE_AUTHENTICATING:
+		i_error("imapc(%s): Authentication timed out after %u seconds",
+			conn->name, IMAPC_CONNECT_TIMEOUT_MSECS/1000);
+		break;
+	default:
+		i_unreached();
+	}
+	imapc_connection_disconnect(conn);
+}
+
+static void
+imapc_reidle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
+		      void *context)
+{
+	struct imapc_connection *conn = context;
+
+	imapc_connection_idle(conn);
+}
+
+static void imapc_connection_reset_idle(struct imapc_connection *conn)
+{
+	if (!conn->idling)
+		imapc_connection_cmd(conn, FALSE, "NOOP", NULL, NULL);
+	else {
+		imapc_connection_cmd(conn, FALSE, "NOOP",
+				     imapc_reidle_callback, conn);
+	}
+}
+
+static void imapc_connection_connect_next_ip(struct imapc_connection *conn)
+{
+	const struct ip_addr *ip;
+	int fd;
+
+	conn->prev_connect_idx = (conn->prev_connect_idx+1) % conn->ips_count;
+	ip = &conn->ips[conn->prev_connect_idx];
+	fd = net_connect_ip(ip, conn->client->set.port, NULL);
+	if (fd == -1) {
+		imapc_connection_set_state(conn,
+			IMAPC_CONNECTION_STATE_DISCONNECTED);
+		return;
+	}
+	conn->fd = fd;
+	conn->input = conn->raw_input = i_stream_create_fd(fd, (size_t)-1, FALSE);
+	conn->output = conn->raw_output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+
+	if (*conn->client->set.rawlog_dir != '\0' &&
+	    conn->client->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
+		(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
+					     &conn->input, &conn->output);
+	}
+
+	o_stream_set_flush_callback(conn->output, imapc_connection_output,
+				    conn);
+	conn->io = io_add(fd, IO_WRITE, imapc_connection_connected, conn);
+	conn->parser = imap_parser_create(conn->input, NULL, (size_t)-1);
+	conn->to = timeout_add(IMAPC_CONNECT_TIMEOUT_MSECS,
+			       imapc_connection_timeout, conn);
+	conn->to_output = timeout_add(IMAPC_MAX_IDLE_MSECS,
+				      imapc_connection_reset_idle, conn);
+	if (conn->client->set.debug) {
+		i_debug("imapc(%s): Connecting to %s:%u", conn->name,
+			net_ip2addr(ip), conn->client->set.port);
+	}
+}
+
+static void
+imapc_connection_dns_callback(const struct dns_lookup_result *result,
+			      void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (result->ret != 0) {
+		i_error("imapc(%s): dns_lookup(%s) failed: %s",
+			conn->name, conn->client->set.host, result->error);
+		imapc_connection_set_state(conn,
+			IMAPC_CONNECTION_STATE_DISCONNECTED);
+		return;
+	}
+
+	i_assert(result->ips_count > 0);
+	conn->ips_count = result->ips_count;
+	conn->ips = i_new(struct ip_addr, conn->ips_count);
+	memcpy(conn->ips, result->ips, sizeof(*conn->ips) * conn->ips_count);
+	conn->prev_connect_idx = conn->ips_count - 1;
+
+	imapc_connection_connect_next_ip(conn);
+}
+
+void imapc_connection_connect(struct imapc_connection *conn,
+			      imapc_command_callback_t *callback, void *context)
+{
+	struct dns_lookup_settings dns_set;
+
+	i_assert(conn->login_callback == NULL);
+	if (conn->fd != -1) {
+		i_assert(callback == NULL);
+		return;
+	}
+	conn->login_callback = callback;
+	conn->login_context = context;
+
+	imapc_connection_input_reset(conn);
+
+	if (conn->client->set.debug)
+		i_debug("imapc(%s): Looking up IP address", conn->name);
+
+	memset(&dns_set, 0, sizeof(dns_set));
+	dns_set.dns_client_socket_path =
+		conn->client->set.dns_client_socket_path;
+	dns_set.timeout_msecs = IMAPC_DNS_LOOKUP_TIMEOUT_MSECS;
+
+	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_CONNECTING);
+	if (conn->ips_count == 0) {
+		(void)dns_lookup(conn->client->set.host, &dns_set,
+				 imapc_connection_dns_callback, conn);
+	} else {
+		imapc_connection_connect_next_ip(conn);
+	}
+}
+
+void imapc_connection_input_pending(struct imapc_connection *conn)
+{
+	int ret = 1;
+
+	if (conn->input == NULL)
+		return;
+
+	if (conn->to != NULL)
+		timeout_reset(conn->to);
+
+	o_stream_cork(conn->output);
+	while (ret > 0 && !conn->client->stop_now && conn->input != NULL) {
+		T_BEGIN {
+			ret = imapc_connection_input_one(conn);
+		} T_END;
+	}
+
+	if (conn->output != NULL)
+		o_stream_uncork(conn->output);
+}
+
+static struct imapc_command *
+imapc_command_begin(imapc_command_callback_t *callback, void *context)
+{
+	static unsigned int cmd_tag_counter = 0;
+	struct imapc_command *cmd;
+	pool_t pool;
+
+	pool = pool_alloconly_create("imapc command", 2048);
+	cmd = p_new(pool, struct imapc_command, 1);
+	cmd->pool = pool;
+	cmd->callback = callback;
+	cmd->context = context;
+
+	if (++cmd_tag_counter == 0)
+		cmd_tag_counter++;
+	cmd->tag = cmd_tag_counter;
+	return cmd;
+}
+
+static void imapc_command_free(struct imapc_command *cmd)
+{
+	struct imapc_command_stream *stream;
+
+	if (array_is_created(&cmd->streams)) {
+		array_foreach_modifiable(&cmd->streams, stream)
+			i_stream_unref(&stream->input);
+	}
+	pool_unref(&cmd->pool);
+}
+
+static bool
+parse_sync_literal(const unsigned char *data, unsigned int pos,
+		   unsigned int *value_r)
+{
+	unsigned int value = 0, mul = 1;
+
+	/* data should contain "{size}\r\n" and pos points after \n */
+	if (pos <= 4 || data[pos-1] != '\n' || data[pos-2] != '\r' ||
+	    data[pos-3] != '}' || !i_isdigit(data[pos-4]))
+		return FALSE;
+	pos -= 4;
+
+	do {
+		value += (data[pos] - '0') * mul;
+		mul = mul*10;
+		pos--;
+	} while (pos > 0 && i_isdigit(data[pos]));
+
+	if (pos == 0 || data[pos] != '{')
+		return FALSE;
+
+	*value_r = value;
+	return TRUE;
+}
+
+static void imapc_command_send_done(struct imapc_connection *conn,
+				    struct imapc_command *cmd)
+{
+	if (cmd->idle)
+		conn->idle_plus_waiting = TRUE;
+
+	/* everything sent. move command to wait list. */
+	i_assert(*array_idx(&conn->cmd_send_queue, 0) == cmd);
+	array_delete(&conn->cmd_send_queue, 0, 1);
+	array_append(&conn->cmd_wait_list, &cmd, 1);
+
+	if (array_count(&conn->cmd_send_queue) > 0 &&
+	    conn->state == IMAPC_CONNECTION_STATE_DONE) {
+		/* send the next command in queue */
+		struct imapc_command *const *cmd2_p =
+			array_idx(&conn->cmd_send_queue, 0);
+		imapc_command_send_more(conn, *cmd2_p);
+	}
+}
+
+static struct imapc_command_stream *
+imapc_command_get_sending_stream(struct imapc_command *cmd)
+{
+	struct imapc_command_stream *stream;
+
+	if (!array_is_created(&cmd->streams) || array_count(&cmd->streams) == 0)
+		return NULL;
+
+	stream = array_idx_modifiable(&cmd->streams, 0);
+	if (stream->pos != cmd->send_pos)
+		return NULL;
+	return stream;
+}
+
+static int imapc_command_try_send_stream(struct imapc_connection *conn,
+					 struct imapc_command *cmd)
+{
+	struct imapc_command_stream *stream;
+
+	stream = imapc_command_get_sending_stream(cmd);
+	if (stream == NULL)
+		return -1;
+
+	/* we're sending the stream now */
+	o_stream_set_max_buffer_size(conn->output, 0);
+	(void)o_stream_send_istream(conn->output, stream->input);
+	o_stream_set_max_buffer_size(conn->output, (size_t)-1);
+
+	if (!i_stream_is_eof(stream->input)) {
+		o_stream_set_flush_pending(conn->output, TRUE);
+		i_assert(stream->input->v_offset < stream->size);
+		return 0;
+	}
+	i_assert(stream->input->v_offset == stream->size);
+
+	/* finished with the stream */
+	i_stream_unref(&stream->input);
+	array_delete(&cmd->streams, 0, 1);
+
+	i_assert(cmd->send_pos != cmd->data->used);
+	return 1;
+}
+
+static void imapc_command_send_more(struct imapc_connection *conn,
+				    struct imapc_command *cmd)
+{
+	const unsigned char *p, *data;
+	unsigned int seek_pos, start_pos, end_pos, size;
+	int ret;
+
+	i_assert(!cmd->wait_for_literal);
+	i_assert(cmd->send_pos < cmd->data->used);
+
+	timeout_reset(conn->to_output);
+	if ((ret = imapc_command_try_send_stream(conn, cmd)) == 0)
+		return;
+
+	seek_pos = cmd->send_pos;
+	if (seek_pos != 0 && ret < 0) {
+		/* skip over the literal. we can also get here from
+		   AUTHENTICATE command, which doesn't use a literal */
+		if (parse_sync_literal(cmd->data->data, seek_pos, &size)) {
+			seek_pos += size;
+			i_assert(seek_pos <= cmd->data->used);
+		}
+	}
+
+	do {
+		start_pos = seek_pos;
+		p = memchr(CONST_PTR_OFFSET(cmd->data->data, seek_pos), '\n',
+			   cmd->data->used - seek_pos);
+		i_assert(p != NULL);
+
+		seek_pos = p - (const unsigned char *)cmd->data->data + 1;
+		/* keep going for LITERAL+ command */
+	} while (start_pos + 3 < seek_pos &&
+		 p[-1] == '\r' && p[-2] == '}' && p[-3] == '+');
+	end_pos = seek_pos;
+
+	data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos);
+	size = end_pos - cmd->send_pos;
+	o_stream_send(conn->output, data, size);
+	cmd->send_pos = end_pos;
+
+	if (cmd->send_pos == cmd->data->used) {
+		i_assert(!array_is_created(&cmd->streams) ||
+			 array_count(&cmd->streams) == 0);
+		imapc_command_send_done(conn, cmd);
+	} else {
+		cmd->wait_for_literal = TRUE;
+	}
+}
+
+static void imapc_command_timeout(struct imapc_connection *conn)
+{
+	struct imapc_command *const *cmds;
+	unsigned int count;
+
+	cmds = array_get(&conn->cmd_wait_list, &count);
+	i_assert(count > 0);
+
+	i_error("imapc(%s): Command '%s' timed out, disconnecting",
+		conn->name, imapc_command_get_readable(cmds[0]));
+	imapc_connection_disconnect(conn);
+}
+
+static void imapc_connection_send_idle_done(struct imapc_connection *conn)
+{
+	if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) {
+		conn->idle_stopping = TRUE;
+		o_stream_send_str(conn->output, "DONE\r\n");
+	}
+}
+
+static void imapc_command_send(struct imapc_connection *conn,
+			       struct imapc_command *cmd)
+{
+	imapc_connection_send_idle_done(conn);
+	switch (conn->state) {
+	case IMAPC_CONNECTION_STATE_AUTHENTICATING:
+		array_insert(&conn->cmd_send_queue, 0, &cmd, 1);
+		imapc_command_send_more(conn, cmd);
+		break;
+	case IMAPC_CONNECTION_STATE_DONE:
+		if (cmd->idle) {
+			if (conn->to != NULL)
+				timeout_remove(&conn->to);
+		} else if (conn->to == NULL) {
+			conn->to = timeout_add(IMAPC_COMMAND_TIMEOUT_MSECS,
+					       imapc_command_timeout, conn);
+		}
+
+		array_append(&conn->cmd_send_queue, &cmd, 1);
+		if (array_count(&conn->cmd_send_queue) == 1)
+			imapc_command_send_more(conn, cmd);
+		break;
+	default:
+		array_append(&conn->cmd_send_queue, &cmd, 1);
+		break;
+	}
+}
+
+static int imapc_connection_output(struct imapc_connection *conn)
+{
+	struct imapc_command *const *cmds;
+	unsigned int count;
+	int ret;
+
+	if (conn->to != NULL)
+		timeout_reset(conn->to);
+
+	o_stream_cork(conn->output);
+	if ((ret = o_stream_flush(conn->output)) < 0)
+		return 1;
+
+	imapc_connection_ref(conn);
+	cmds = array_get(&conn->cmd_send_queue, &count);
+	if (count > 0) {
+		if (imapc_command_get_sending_stream(cmds[0]) != NULL &&
+		    !cmds[0]->wait_for_literal) {
+			/* we're sending a stream. send more. */
+			imapc_command_send_more(conn, cmds[0]);
+		}
+	}
+	o_stream_uncork(conn->output);
+	imapc_connection_unref(&conn);
+	return ret;
+}
+
+static struct imapc_command *
+imapc_connection_cmd_build(const char *cmdline,
+			   imapc_command_callback_t *callback, void *context)
+{
+	struct imapc_command *cmd;
+	unsigned int len = strlen(cmdline);
+
+	cmd = imapc_command_begin(callback, context);
+	cmd->data = str_new(cmd->pool, 6 + len + 2);
+	str_printfa(cmd->data, "%u %s\r\n", cmd->tag, cmdline);
+	return cmd;
+}
+
+void imapc_connection_cmd(struct imapc_connection *conn, bool mailboxcmd,
+			  const char *cmdline,
+			  imapc_command_callback_t *callback, void *context)
+{
+	struct imapc_command *cmd;
+
+	cmd = imapc_connection_cmd_build(cmdline, callback, context);
+	cmd->mailboxcmd = mailboxcmd;
+	imapc_command_send(conn, cmd);
+}
+
+void imapc_connection_cmdf(struct imapc_connection *conn, bool mailboxcmd,
+			   imapc_command_callback_t *callback, void *context,
+			   const char *cmd_fmt, ...)
+{
+	va_list args;
+
+	va_start(args, cmd_fmt);
+	imapc_connection_cmdvf(conn, mailboxcmd, callback, context,
+			       cmd_fmt, args);
+	va_end(args);
+}
+
+void imapc_connection_cmdvf(struct imapc_connection *conn, bool mailboxcmd,
+			   imapc_command_callback_t *callback, void *context,
+			   const char *cmd_fmt, va_list args)
+{
+	struct imapc_command *cmd;
+	unsigned int i;
+
+	cmd = imapc_command_begin(callback, context);
+	cmd->mailboxcmd = mailboxcmd;
+	cmd->data = str_new(cmd->pool, 128);
+	str_printfa(cmd->data, "%u ", cmd->tag);
+
+	for (i = 0; cmd_fmt[i] != '\0'; i++) {
+		if (cmd_fmt[i] != '%') {
+			str_append_c(cmd->data, cmd_fmt[i]);
+			continue;
+		}
+
+		switch (cmd_fmt[++i]) {
+		case '\0':
+			i_unreached();
+		case 'u': {
+			unsigned int arg = va_arg(args, unsigned int);
+
+			str_printfa(cmd->data, "%u", arg);
+			break;
+		}
+		case 'p': {
+			struct istream *input = va_arg(args, struct istream *);
+			struct imapc_command_stream *s;
+			uoff_t size;
+
+			if (!array_is_created(&cmd->streams))
+				p_array_init(&cmd->streams, cmd->pool, 2);
+			if (i_stream_get_size(input, TRUE, &size) < 0)
+				size = 0;
+			str_printfa(cmd->data, "{%"PRIuUOFF_T"}\r\n", size);
+			s = array_append_space(&cmd->streams);
+			s->pos = str_len(cmd->data);
+			s->size = size;
+			s->input = input;
+			i_stream_ref(input);
+			break;
+		}
+		case 's': {
+			const char *arg = va_arg(args, const char *);
+
+			if (!need_literal(arg))
+				imap_dquote_append(cmd->data, arg);
+			else if ((conn->capabilities &
+				  IMAPC_CAPABILITY_LITERALPLUS) != 0) {
+				str_printfa(cmd->data, "{%"PRIuSIZE_T"+}\r\n%s",
+					    strlen(arg), arg);
+			} else {
+				str_printfa(cmd->data, "{%"PRIuSIZE_T"}\r\n%s",
+					    strlen(arg), arg);
+			}
+			break;
+		}
+		case '1': {
+			/* %1s - no quoting */
+			const char *arg = va_arg(args, const char *);
+
+			i_assert(cmd_fmt[++i] == 's');
+			str_append(cmd->data, arg);
+			break;
+		}
+		}
+	}
+	str_append(cmd->data, "\r\n");
+
+	imapc_command_send(conn, cmd);
+}
+
+enum imapc_connection_state
+imapc_connection_get_state(struct imapc_connection *conn)
+{
+	return conn->state;
+}
+
+enum imapc_capability
+imapc_connection_get_capabilities(struct imapc_connection *conn)
+{
+	return conn->capabilities;
+}
+
+void imapc_connection_select(struct imapc_client_mailbox *box,
+			     const char *name, bool examine,
+			     imapc_command_callback_t *callback, void *context)
+{
+	struct imapc_connection *conn = box->conn;
+
+	i_assert(conn->selecting_box == NULL);
+
+	if (conn->selected_box != NULL &&
+	    (conn->capabilities & IMAPC_CAPABILITY_QRESYNC) != 0) {
+		/* server will send a [CLOSED] once selected mailbox is
+		   closed */
+		conn->selecting_box = box;
+	} else {
+		/* we'll have to assume that all the future untagged messages
+		   are for the mailbox we're selecting */
+		conn->selected_box = box;
+	}
+
+	imapc_connection_cmdf(conn, FALSE, callback, context,
+			      examine ? "EXAMINE %s" : "SELECT %s", name);
+}
+
+void imapc_connection_unselect(struct imapc_client_mailbox *box)
+{
+	struct imapc_connection *conn = box->conn;
+	struct imapc_command *const *cmdp, *cmd;
+	struct imapc_command_reply reply;
+	unsigned int i;
+
+	/* mailbox is being closed. if there are any pending commands, we must
+	   finish them immediately so callbacks don't access any freed
+	   contexts */
+	memset(&reply, 0, sizeof(reply));
+	reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
+	reply.text_without_resp = reply.text_full = "Closing mailbox";
+
+	imapc_connection_send_idle_done(conn);
+
+	array_foreach(&conn->cmd_wait_list, cmdp) {
+		if ((*cmdp)->callback != NULL && (*cmdp)->mailboxcmd) {
+			(*cmdp)->callback(&reply, (*cmdp)->context);
+			(*cmdp)->callback = NULL;
+		}
+	}
+	for (i = 0; i < array_count(&conn->cmd_send_queue); ) {
+		cmdp = array_idx(&conn->cmd_send_queue, i);
+		cmd = *cmdp;
+		if (!cmd->mailboxcmd)
+			i++;
+		else {
+			array_delete(&conn->cmd_send_queue, i, 1);
+			if (cmd->callback != NULL)
+				cmd->callback(&reply, cmd->context);
+			imapc_command_free(cmd);
+		}
+	}
+
+	if (conn->selected_box == NULL && conn->selecting_box == NULL) {
+		i_assert(conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED);
+	} else {
+		i_assert(conn->selected_box == box ||
+			 conn->selecting_box == box);
+
+		conn->selected_box = NULL;
+		conn->selecting_box = NULL;
+	}
+}
+
+struct imapc_client_mailbox *
+imapc_connection_get_mailbox(struct imapc_connection *conn)
+{
+	if (conn->selecting_box != NULL)
+		return conn->selecting_box;
+	return conn->selected_box;
+}
+
+static void
+imapc_connection_idle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
+			       void *context)
+{
+	struct imapc_connection *conn = context;
+
+	conn->idling = FALSE;
+	conn->idle_plus_waiting = FALSE;
+	conn->idle_stopping = FALSE;
+}
+
+void imapc_connection_idle(struct imapc_connection *conn)
+{
+	struct imapc_command *cmd;
+
+	if (array_count(&conn->cmd_send_queue) != 0 ||
+	    array_count(&conn->cmd_wait_list) != 0 ||
+	    conn->idling || conn->idle_plus_waiting ||
+	    (conn->capabilities & IMAPC_CAPABILITY_IDLE) == 0)
+		return;
+
+	cmd = imapc_connection_cmd_build("IDLE", imapc_connection_idle_callback,
+					 conn);
+	cmd->idle = TRUE;
+	imapc_command_send(conn, cmd);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-connection.h	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,56 @@
+#ifndef IMAPC_CONNECTION_H
+#define IMAPC_CONNECTION_H
+
+#include "imapc-client.h"
+
+struct imapc_client;
+struct imapc_connection;
+
+enum imapc_connection_state {
+	/* No connection */
+	IMAPC_CONNECTION_STATE_DISCONNECTED = 0,
+	/* Trying to connect */
+	IMAPC_CONNECTION_STATE_CONNECTING,
+	/* Connected, trying to authenticate */
+	IMAPC_CONNECTION_STATE_AUTHENTICATING,
+	/* Authenticated, ready to accept commands */
+	IMAPC_CONNECTION_STATE_DONE
+};
+
+struct imapc_connection *
+imapc_connection_init(struct imapc_client *client);
+void imapc_connection_deinit(struct imapc_connection **conn);
+
+void imapc_connection_connect(struct imapc_connection *conn,
+			      imapc_command_callback_t *callback,
+			      void *context);
+void imapc_connection_disconnect(struct imapc_connection *conn);
+void imapc_connection_ioloop_changed(struct imapc_connection *conn);
+void imapc_connection_input_pending(struct imapc_connection *conn);
+
+void imapc_connection_cmd(struct imapc_connection *conn, bool mailboxcmd,
+			  const char *cmdline,
+			  imapc_command_callback_t *callback, void *context);
+void imapc_connection_cmdf(struct imapc_connection *conn, bool mailboxcmd,
+			   imapc_command_callback_t *callback, void *context,
+			   const char *cmd_fmt, ...) ATTR_FORMAT(5, 6);
+void imapc_connection_cmdvf(struct imapc_connection *conn, bool mailboxcmd,
+			    imapc_command_callback_t *callback, void *context,
+			    const char *cmd_fmt, va_list args)
+	ATTR_FORMAT(5, 0);
+void imapc_connection_select(struct imapc_client_mailbox *box,
+			     const char *name, bool examine,
+			     imapc_command_callback_t *callback, void *context);
+void imapc_connection_unselect(struct imapc_client_mailbox *box);
+
+enum imapc_connection_state
+imapc_connection_get_state(struct imapc_connection *conn);
+enum imapc_capability
+imapc_connection_get_capabilities(struct imapc_connection *conn);
+
+struct imapc_client_mailbox *
+imapc_connection_get_mailbox(struct imapc_connection *conn);
+
+void imapc_connection_idle(struct imapc_connection *conn);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-msgmap.c	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,88 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "imapc-msgmap.h"
+
+struct imapc_msgmap {
+	ARRAY_TYPE(uint32_t) uids;
+	uint32_t uid_next;
+};
+
+struct imapc_msgmap *imapc_msgmap_init(void)
+{
+	struct imapc_msgmap *msgmap;
+
+	msgmap = i_new(struct imapc_msgmap, 1);
+	i_array_init(&msgmap->uids, 128);
+	msgmap->uid_next = 1;
+	return msgmap;
+}
+
+void imapc_msgmap_deinit(struct imapc_msgmap **_msgmap)
+{
+	struct imapc_msgmap *msgmap = *_msgmap;
+
+	*_msgmap = NULL;
+
+	array_free(&msgmap->uids);
+	i_free(msgmap);
+}
+
+uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap)
+{
+	return array_count(&msgmap->uids);
+}
+
+uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap)
+{
+	return msgmap->uid_next;
+}
+
+uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq)
+{
+	const uint32_t *uidp;
+
+	uidp = array_idx(&msgmap->uids, rseq-1);
+	return *uidp;
+}
+
+static int uint32_cmp(const uint32_t *p1, const uint32_t *p2)
+{
+	return *p1 < *p2 ? -1 :
+		(*p1 > *p2 ? 1 : 0);
+}
+
+bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap,
+			      uint32_t uid, uint32_t *rseq_r)
+{
+	const uint32_t *p, *first;
+
+	p = array_bsearch(&msgmap->uids, &uid, uint32_cmp);
+	if (p == NULL) {
+		*rseq_r = 0;
+		return FALSE;
+	}
+
+	first = array_idx(&msgmap->uids, 0);
+	*rseq_r = (p - first) + 1;
+	return TRUE;
+}
+
+void imapc_msgmap_append(struct imapc_msgmap *msgmap,
+			 uint32_t rseq, uint32_t uid)
+{
+	i_assert(rseq == imapc_msgmap_count(msgmap) + 1);
+	i_assert(uid >= msgmap->uid_next);
+
+	msgmap->uid_next = uid + 1;
+	array_append(&msgmap->uids, &uid, 1);
+}
+
+void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq)
+{
+	i_assert(rseq > 0);
+	i_assert(rseq <= imapc_msgmap_count(msgmap));
+
+	array_delete(&msgmap->uids, rseq-1, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-client/imapc-msgmap.h	Mon Sep 26 15:34:58 2011 +0300
@@ -0,0 +1,17 @@
+#ifndef IMAPC_MSGMAP_H
+#define IMAPC_MSGMAP_H
+
+struct imapc_msgmap *imapc_msgmap_init(void);
+void imapc_msgmap_deinit(struct imapc_msgmap **msgmap);
+
+uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap);
+uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap);
+uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq);
+bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap,
+			      uint32_t uid, uint32_t *rseq_r);
+
+void imapc_msgmap_append(struct imapc_msgmap *msgmap,
+			 uint32_t rseq, uint32_t uid);
+void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq);
+
+#endif
--- a/src/lib-storage/index/imapc/Makefile.am	Sun Sep 25 22:32:04 2011 +0000
+++ b/src/lib-storage/index/imapc/Makefile.am	Mon Sep 26 15:34:58 2011 +0300
@@ -4,35 +4,27 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-settings \
-	-I$(top_srcdir)/src/lib-dns \
-	-I$(top_srcdir)/src/lib-ssl-iostream \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
+	-I$(top_srcdir)/src/lib-imap-client \
 	-I$(top_srcdir)/src/lib-index \
 	-I$(top_srcdir)/src/lib-storage \
 	-I$(top_srcdir)/src/lib-storage/list \
 	-I$(top_srcdir)/src/lib-storage/index
 
 libstorage_imapc_la_SOURCES = \
-	imapc-client.c \
-	imapc-connection.c \
 	imapc-list.c \
 	imapc-mail.c \
 	imapc-mail-fetch.c \
 	imapc-mailbox.c \
-	imapc-msgmap.c \
 	imapc-save.c \
 	imapc-settings.c \
 	imapc-sync.c \
 	imapc-storage.c
 
 headers = \
-	imapc-client.h \
-	imapc-client-private.h \
-	imapc-connection.h \
 	imapc-list.h \
 	imapc-mail.h \
-	imapc-msgmap.h \
 	imapc-settings.h \
 	imapc-storage.h \
 	imapc-sync.h
--- a/src/lib-storage/index/imapc/imapc-client-private.h	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#ifndef IMAPC_CLIENT_PRIVATE_H
-#define IMAPC_CLIENT_PRIVATE_H
-
-#include "imapc-client.h"
-
-struct imapc_client_connection {
-	struct imapc_connection *conn;
-	struct imapc_client_mailbox *box;
-};
-
-struct imapc_client {
-	pool_t pool;
-	struct imapc_client_settings set;
-	struct ssl_iostream_context *ssl_ctx;
-
-	imapc_untagged_callback_t *untagged_callback;
-	void *untagged_context;
-
-	ARRAY_DEFINE(conns, struct imapc_client_connection *);
-
-	struct ioloop *ioloop;
-
-	unsigned int stop_now:1;
-};
-
-struct imapc_client_mailbox {
-	struct imapc_client *client;
-	struct imapc_connection *conn;
-	struct imapc_msgmap *msgmap;
-
-	void *untagged_box_context;
-	unsigned int pending_box_command_count;
-};
-
-#endif
--- a/src/lib-storage/index/imapc/imapc-client.c	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +0,0 @@
-/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "ioloop.h"
-#include "safe-mkstemp.h"
-#include "iostream-ssl.h"
-#include "imapc-msgmap.h"
-#include "imapc-connection.h"
-#include "imapc-client-private.h"
-
-#include <unistd.h>
-
-struct imapc_client_command_context {
-	struct imapc_client_mailbox *box;
-
-	imapc_command_callback_t *callback;
-	void *context;
-};
-
-const struct imapc_capability_name imapc_capability_names[] = {
-	{ "SASL-IR", IMAPC_CAPABILITY_SASL_IR },
-	{ "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
-	{ "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
-	{ "IDLE", IMAPC_CAPABILITY_IDLE },
-	{ "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS },
-	{ "AUTH=PLAIN", IMAPC_CAPABILITY_AUTH_PLAIN },
-	{ "STARTTLS", IMAPC_CAPABILITY_STARTTLS },
-
-	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
-	{ NULL, 0 }
-};
-
-struct imapc_client *
-imapc_client_init(const struct imapc_client_settings *set)
-{
-	struct imapc_client *client;
-	struct ssl_iostream_settings ssl_set;
-	const char *source;
-	pool_t pool;
-
-	pool = pool_alloconly_create("imapc client", 1024);
-	client = p_new(pool, struct imapc_client, 1);
-	client->pool = pool;
-
-	client->set.debug = set->debug;
-	client->set.host = p_strdup(pool, set->host);
-	client->set.port = set->port;
-	client->set.master_user = p_strdup(pool, set->master_user);
-	client->set.username = p_strdup(pool, set->username);
-	client->set.password = p_strdup(pool, set->password);
-	client->set.dns_client_socket_path =
-		p_strdup(pool, set->dns_client_socket_path);
-	client->set.temp_path_prefix =
-		p_strdup(pool, set->temp_path_prefix);
-	client->set.rawlog_dir = p_strdup(pool, set->rawlog_dir);
-
-	if (set->ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE) {
-		client->set.ssl_mode = set->ssl_mode;
-		client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);
-
-		memset(&ssl_set, 0, sizeof(ssl_set));
-		ssl_set.ca_dir = set->ssl_ca_dir;
-		ssl_set.verify_remote_cert = TRUE;
-
-		source = t_strdup_printf("%s:%u", set->host, set->port);
-		if (ssl_iostream_context_init_client(source, &ssl_set,
-						     &client->ssl_ctx) < 0) {
-			i_error("imapc(%s): Couldn't initialize SSL context",
-				source);
-		}
-	}
-
-	p_array_init(&client->conns, pool, 8);
-	return client;
-}
-
-void imapc_client_deinit(struct imapc_client **_client)
-{
-	struct imapc_client *client = *_client;
-	struct imapc_client_connection **connp;
-
-	*_client = NULL;
-
-	if (client->ssl_ctx != NULL)
-		ssl_iostream_context_deinit(&client->ssl_ctx);
-	array_foreach_modifiable(&client->conns, connp) {
-		imapc_connection_deinit(&(*connp)->conn);
-		i_free(*connp);
-	}
-	pool_unref(&client->pool);
-}
-
-void imapc_client_register_untagged(struct imapc_client *client,
-				    imapc_untagged_callback_t *callback,
-				    void *context)
-{
-	client->untagged_callback = callback;
-	client->untagged_context = context;
-}
-
-void imapc_client_run_pre(struct imapc_client *client)
-{
-	struct imapc_client_connection *const *connp;
-	struct ioloop *prev_ioloop = current_ioloop;
-	bool handle_pending = client->stop_now;
-
-	i_assert(client->ioloop == NULL);
-
-	client->stop_now = FALSE;
-
-	client->ioloop = io_loop_create();
-	io_loop_set_running(client->ioloop);
-
-	array_foreach(&client->conns, connp) {
-		imapc_connection_ioloop_changed((*connp)->conn);
-		imapc_connection_connect((*connp)->conn);
-		if (handle_pending)
-			imapc_connection_input_pending((*connp)->conn);
-	}
-
-	if (io_loop_is_running(client->ioloop))
-		io_loop_run(client->ioloop);
-	current_ioloop = prev_ioloop;
-}
-
-void imapc_client_run_post(struct imapc_client *client)
-{
-	struct imapc_client_connection *const *connp;
-	struct ioloop *ioloop = client->ioloop;
-
-	client->ioloop = NULL;
-	array_foreach(&client->conns, connp)
-		imapc_connection_ioloop_changed((*connp)->conn);
-
-	current_ioloop = ioloop;
-	io_loop_destroy(&ioloop);
-}
-
-void imapc_client_stop(struct imapc_client *client)
-{
-	if (client->ioloop != NULL)
-		io_loop_stop(client->ioloop);
-}
-
-bool imapc_client_is_running(struct imapc_client *client)
-{
-	return client->ioloop != NULL;
-}
-
-void imapc_client_stop_now(struct imapc_client *client)
-{
-	client->stop_now = TRUE;
-	imapc_client_stop(client);
-}
-
-static struct imapc_client_connection *
-imapc_client_add_connection(struct imapc_client *client)
-{
-	struct imapc_client_connection *conn;
-
-	conn = i_new(struct imapc_client_connection, 1);
-	conn->conn = imapc_connection_init(client);
-	array_append(&client->conns, &conn, 1);
-	return conn;
-}
-
-static struct imapc_connection *
-imapc_client_find_connection(struct imapc_client *client)
-{
-	struct imapc_client_connection *const *connp;
-
-	/* FIXME: stupid algorithm */
-	if (array_count(&client->conns) == 0)
-		return imapc_client_add_connection(client)->conn;
-	connp = array_idx(&client->conns, 0);
-	return (*connp)->conn;
-}
-
-void imapc_client_cmdf(struct imapc_client *client,
-		       imapc_command_callback_t *callback, void *context,
-		       const char *cmd_fmt, ...)
-{
-	struct imapc_connection *conn;
-	va_list args;
-
-	conn = imapc_client_find_connection(client);
-
-	va_start(args, cmd_fmt);
-	imapc_connection_cmdvf(conn, FALSE, callback, context, cmd_fmt, args);
-	va_end(args);
-}
-
-static struct imapc_client_connection *
-imapc_client_get_unboxed_connection(struct imapc_client *client)
-{
-	struct imapc_client_connection *const *conns;
-	unsigned int i, count;
-
-	conns = array_get(&client->conns, &count);
-	for (i = 0; i < count; i++) {
-		if (conns[i]->box == NULL)
-			return conns[i];
-	}
-	return imapc_client_add_connection(client);
-}
-
-
-struct imapc_client_mailbox *
-imapc_client_mailbox_open(struct imapc_client *client,
-			  const char *name, bool examine,
-			  imapc_command_callback_t *callback, void *context,
-			  void *untagged_box_context)
-{
-	struct imapc_client_mailbox *box;
-	struct imapc_client_connection *conn;
-
-	box = i_new(struct imapc_client_mailbox, 1);
-	box->client = client;
-	box->untagged_box_context = untagged_box_context;
-	conn = imapc_client_get_unboxed_connection(client);
-	conn->box = box;
-	box->conn = conn->conn;
-	box->msgmap = imapc_msgmap_init();
-
-	imapc_connection_select(box, name, examine, callback, context);
-	return box;
-}
-
-void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box)
-{
-	if (box->conn != NULL)
-		imapc_connection_disconnect(box->conn);
-}
-
-void imapc_client_mailbox_close(struct imapc_client_mailbox **_box)
-{
-	struct imapc_client_mailbox *box = *_box;
-	struct imapc_client_connection *const *connp;
-
-	array_foreach(&box->client->conns, connp) {
-		if ((*connp)->box == box) {
-			(*connp)->box = NULL;
-			break;
-		}
-	}
-
-	if (box->conn != NULL)
-		imapc_connection_unselect(box);
-	imapc_msgmap_deinit(&box->msgmap);
-	i_free(box);
-
-	/* set this only after unselect, which may cancel some commands that
-	   reference this box */
-	*_box = NULL;
-}
-
-static void imapc_client_mailbox_cmd_cb(const struct imapc_command_reply *reply,
-					void *context)
-{
-	struct imapc_client_command_context *ctx = context;
-
-	ctx->box->pending_box_command_count--;
-
-	ctx->callback(reply, ctx->context);
-	i_free(ctx);
-}
-
-static struct imapc_client_command_context *
-imapc_client_mailbox_cmd_common(struct imapc_client_mailbox *box,
-				imapc_command_callback_t *callback,
-				void *context)
-{
-	struct imapc_client_command_context *ctx;
-
-	ctx = i_new(struct imapc_client_command_context, 1);
-	ctx->box = box;
-	ctx->callback = callback;
-	ctx->context = context;
-
-	box->pending_box_command_count++;
-	return ctx;
-}
-
-static bool
-imapc_client_mailbox_is_selected(struct imapc_client_mailbox *box,
-				 struct imapc_command_reply *reply_r)
-{
-	struct imapc_client_mailbox *selected_box;
-
-	selected_box = box->conn == NULL ? NULL :
-		imapc_connection_get_mailbox(box->conn);
-	if (selected_box == box)
-		return TRUE;
-
-	memset(reply_r, 0, sizeof(*reply_r));
-	reply_r->state = IMAPC_COMMAND_STATE_DISCONNECTED;
-	if (selected_box == NULL) {
-		reply_r->text_full = "Disconnected from server";
-	} else {
-		i_error("imapc: Selected mailbox changed unexpectedly");
-		reply_r->text_full = "Internal error";
-	}
-	reply_r->text_without_resp = reply_r->text_full;
-
-	box->conn = NULL;
-	return FALSE;
-}
-
-void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
-			      const char *cmd,
-			      imapc_command_callback_t *callback,
-			      void *context)
-{
-	struct imapc_client_command_context *ctx;
-	struct imapc_command_reply reply;
-
-	if (!imapc_client_mailbox_is_selected(box, &reply)) {
-		callback(&reply, context);
-		return;
-	}
-
-	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
-	imapc_connection_cmd(box->conn, TRUE, cmd,
-			     imapc_client_mailbox_cmd_cb, ctx);
-}
-
-void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
-			       imapc_command_callback_t *callback,
-			       void *context, const char *cmd_fmt, ...)
-{
-	struct imapc_client_command_context *ctx;
-	va_list args;
-	struct imapc_command_reply reply;
-
-	if (!imapc_client_mailbox_is_selected(box, &reply)) {
-		callback(&reply, context);
-		return;
-	}
-
-	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
-	va_start(args, cmd_fmt);
-	imapc_connection_cmdvf(box->conn, TRUE, imapc_client_mailbox_cmd_cb,
-			       ctx, cmd_fmt, args);
-	va_end(args);
-}
-
-struct imapc_msgmap *
-imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box)
-{
-	return box->msgmap;
-}
-
-void imapc_client_mailbox_idle(struct imapc_client_mailbox *box)
-{
-	struct imapc_command_reply reply;
-
-	if (imapc_client_mailbox_is_selected(box, &reply))
-		imapc_connection_idle(box->conn);
-}
-
-bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box)
-{
-	struct imapc_command_reply reply;
-
-	return imapc_client_mailbox_is_selected(box, &reply);
-}
-
-enum imapc_capability
-imapc_client_get_capabilities(struct imapc_client *client)
-{
-	struct imapc_client_connection *const *connp;
-
-	connp = array_idx(&client->conns, 0);
-	return imapc_connection_get_capabilities((*connp)->conn);
-}
-
-int imapc_client_create_temp_fd(struct imapc_client *client,
-				const char **path_r)
-{
-	string_t *path;
-	int fd;
-
-	path = t_str_new(128);
-	str_append(path, client->set.temp_path_prefix);
-	fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
-	if (fd == -1) {
-		i_error("safe_mkstemp(%s) failed: %m", str_c(path));
-		return -1;
-	}
-
-	/* we just want the fd, unlink it */
-	if (unlink(str_c(path)) < 0) {
-		/* shouldn't happen.. */
-		i_error("unlink(%s) failed: %m", str_c(path));
-		(void)close(fd);
-		return -1;
-	}
-	*path_r = str_c(path);
-	return fd;
-}
--- a/src/lib-storage/index/imapc/imapc-client.h	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-#ifndef IMAPC_CLIENT_H
-#define IMAPC_CLIENT_H
-
-enum imapc_command_state {
-	IMAPC_COMMAND_STATE_OK,
-	IMAPC_COMMAND_STATE_NO,
-	IMAPC_COMMAND_STATE_BAD,
-	IMAPC_COMMAND_STATE_DISCONNECTED
-};
-
-enum imapc_capability {
-	IMAPC_CAPABILITY_SASL_IR	= 0x01,
-	IMAPC_CAPABILITY_LITERALPLUS	= 0x02,
-	IMAPC_CAPABILITY_QRESYNC	= 0x04,
-	IMAPC_CAPABILITY_IDLE		= 0x08,
-	IMAPC_CAPABILITY_UIDPLUS	= 0x10,
-	IMAPC_CAPABILITY_AUTH_PLAIN	= 0x20,
-	IMAPC_CAPABILITY_STARTTLS	= 0x40,
-
-	IMAPC_CAPABILITY_IMAP4REV1	= 0x400000000
-};
-struct imapc_capability_name {
-	const char *name;
-	enum imapc_capability capability;
-};
-extern const struct imapc_capability_name imapc_capability_names[];
-
-enum imapc_client_ssl_mode {
-	IMAPC_CLIENT_SSL_MODE_NONE,
-	IMAPC_CLIENT_SSL_MODE_IMMEDIATE,
-	IMAPC_CLIENT_SSL_MODE_STARTTLS
-};
-
-struct imapc_client_settings {
-	const char *host;
-	unsigned int port;
-
-	const char *master_user;
-	const char *username;
-	const char *password;
-
-	const char *dns_client_socket_path;
-	const char *temp_path_prefix;
-
-	enum imapc_client_ssl_mode ssl_mode;
-	const char *ssl_ca_dir;
-
-	const char *rawlog_dir;
-	bool debug;
-};
-
-struct imapc_command_reply {
-	enum imapc_command_state state;
-	/* "[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_full;
-	/* Tagged reply text without [RESP TEXT] */
-	const char *text_without_resp;
-};
-
-struct imapc_arg_file {
-	/* file descriptor containing the value */
-	int fd;
-
-	/* parent_arg.list[list_idx] points to the IMAP_ARG_LITERAL_SIZE
-	   argument */
-	const struct imap_arg *parent_arg;
-	unsigned int list_idx;
-};
-
-struct imapc_untagged_reply {
-	/* name of the untagged reply, e.g. EXISTS */
-	const char *name;
-	/* number at the beginning of the reply, or 0 if there wasn't any.
-	   Set for EXISTS, EXPUNGE, etc. */
-	uint32_t num;
-	/* the rest of the reply can be read from these args. */
-	const struct imap_arg *args;
-	/* arguments whose contents are stored into files. only
-	   "FETCH (BODY[" arguments can be here. */
-	const struct imapc_arg_file *file_args;
-	unsigned int file_args_count;
-
-	/* "* 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. */
-	void *untagged_box_context;
-};
-
-/* Called when tagged reply is received for command. */
-typedef void imapc_command_callback_t(const struct imapc_command_reply *reply,
-				      void *context);
-/* Called each time untagged input is received. */
-typedef void imapc_untagged_callback_t(const struct imapc_untagged_reply *reply,
-				       void *context);
-
-struct imapc_client *
-imapc_client_init(const struct imapc_client_settings *set);
-void imapc_client_deinit(struct imapc_client **client);
-
-void imapc_client_cmdf(struct imapc_client *client,
-		       imapc_command_callback_t *callback, void *context,
-		       const char *cmd_fmt, ...) ATTR_FORMAT(4, 5);
-
-void imapc_client_register_untagged(struct imapc_client *client,
-				    imapc_untagged_callback_t *callback,
-				    void *context);
-
-void imapc_client_run_pre(struct imapc_client *client);
-void imapc_client_run_post(struct imapc_client *client);
-void imapc_client_stop(struct imapc_client *client);
-/* Stop immediately, don't finish even any already read pending replies.
-   They'll be finished when imapc_client_run() is again called. */
-void imapc_client_stop_now(struct imapc_client *client);
-bool imapc_client_is_running(struct imapc_client *client);
-
-struct imapc_client_mailbox *
-imapc_client_mailbox_open(struct imapc_client *client,
-			  const char *name, bool examine,
-			  imapc_command_callback_t *callback, void *context,
-			  void *untagged_box_context);
-void imapc_client_mailbox_close(struct imapc_client_mailbox **box);
-void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box);
-void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
-			      const char *cmd,
-			      imapc_command_callback_t *callback,
-			      void *context);
-void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
-			       imapc_command_callback_t *callback,
-			       void *context, const char *cmd_fmt, ...)
-	ATTR_FORMAT(4, 5);
-struct imapc_msgmap *
-imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box);
-
-void imapc_client_mailbox_idle(struct imapc_client_mailbox *box);
-bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box);
-
-enum imapc_capability
-imapc_client_get_capabilities(struct imapc_client *client);
-
-int imapc_client_create_temp_fd(struct imapc_client *client,
-				const char **path_r);
-
-#endif
--- a/src/lib-storage/index/imapc/imapc-connection.c	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1704 +0,0 @@
-/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "ioloop.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "base64.h"
-#include "write-full.h"
-#include "str.h"
-#include "dns-lookup.h"
-#include "iostream-rawlog.h"
-#include "iostream-ssl.h"
-#include "imap-quote.h"
-#include "imap-util.h"
-#include "imap-parser.h"
-#include "imapc-client-private.h"
-#include "imapc-connection.h"
-
-#include <unistd.h>
-#include <ctype.h>
-
-#define IMAPC_DNS_LOOKUP_TIMEOUT_MSECS (1000*30)
-#define IMAPC_CONNECT_TIMEOUT_MSECS (1000*30)
-#define IMAPC_COMMAND_TIMEOUT_MSECS (1000*60*5)
-#define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
-/* IMAP protocol requires activity at least every 30 minutes */
-#define IMAPC_MAX_IDLE_MSECS (1000*60*29)
-
-enum imapc_input_state {
-	IMAPC_INPUT_STATE_NONE = 0,
-	IMAPC_INPUT_STATE_PLUS,
-	IMAPC_INPUT_STATE_UNTAGGED,
-	IMAPC_INPUT_STATE_UNTAGGED_NUM,
-	IMAPC_INPUT_STATE_TAGGED
-};
-
-struct imapc_command_stream {
-	unsigned int pos;
-	uoff_t size;
-	struct istream *input;
-};
-
-struct imapc_command {
-	pool_t pool;
-	buffer_t *data;
-	unsigned int send_pos;
-	unsigned int tag;
-
-	ARRAY_DEFINE(streams, struct imapc_command_stream);
-
-	imapc_command_callback_t *callback;
-	void *context;
-
-	unsigned int idle:1;
-	unsigned int mailboxcmd:1;
-	unsigned int wait_for_literal:1;
-};
-
-struct imapc_connection_literal {
-	char *temp_path;
-	int fd;
-	uoff_t bytes_left;
-
-	const struct imap_arg *parent_arg;
-	unsigned int list_idx;
-};
-
-struct imapc_connection {
-	struct imapc_client *client;
-	char *name;
-
-	int fd;
-	struct io *io;
-	struct istream *input, *raw_input;
-	struct ostream *output, *raw_output;
-	struct imap_parser *parser;
-	struct timeout *to;
-	struct timeout *to_output;
-
-	struct ssl_iostream *ssl_iostream;
-
-	int (*input_callback)(struct imapc_connection *conn);
-	enum imapc_input_state input_state;
-	unsigned int cur_tag;
-	uint32_t cur_num;
-
-	struct imapc_client_mailbox *selecting_box, *selected_box;
-	enum imapc_connection_state state;
-
-	enum imapc_capability capabilities;
-	char **capabilities_list;
-
-	/* commands pending in queue to be sent */
-	ARRAY_DEFINE(cmd_send_queue, struct imapc_command *);
-	/* commands that have been sent, waiting for their tagged reply */
-	ARRAY_DEFINE(cmd_wait_list, struct imapc_command *);
-
-	unsigned int ips_count, prev_connect_idx;
-	struct ip_addr *ips;
-
-	struct imapc_connection_literal literal;
-	ARRAY_DEFINE(literal_files, struct imapc_arg_file);
-
-	unsigned int idling:1;
-	unsigned int idle_stopping:1;
-	unsigned int idle_plus_waiting:1;
-	unsigned int handshake_failed:1;
-};
-
-static int imapc_connection_output(struct imapc_connection *conn);
-static int imapc_connection_ssl_init(struct imapc_connection *conn);
-static void imapc_command_free(struct imapc_command *cmd);
-static void imapc_command_send_more(struct imapc_connection *conn,
-				    struct imapc_command *cmd);
-
-struct imapc_connection *
-imapc_connection_init(struct imapc_client *client)
-{
-	struct imapc_connection *conn;
-
-	conn = i_new(struct imapc_connection, 1);
-	conn->client = client;
-	conn->fd = -1;
-	conn->name = i_strdup_printf("%s:%u", client->set.host,
-				     client->set.port);
-	conn->literal.fd = -1;
-	i_array_init(&conn->cmd_send_queue, 8);
-	i_array_init(&conn->cmd_wait_list, 32);
-	i_array_init(&conn->literal_files, 4);
-	return conn;
-}
-
-void imapc_connection_deinit(struct imapc_connection **_conn)
-{
-	struct imapc_connection *conn = *_conn;
-
-	*_conn = NULL;
-
-	imapc_connection_disconnect(conn);
-	if (conn->capabilities_list != NULL)
-		p_strsplit_free(default_pool, conn->capabilities_list);
-	array_free(&conn->cmd_send_queue);
-	array_free(&conn->cmd_wait_list);
-	array_free(&conn->literal_files);
-	i_free(conn->ips);
-	i_free(conn->name);
-	i_free(conn);
-}
-
-void imapc_connection_ioloop_changed(struct imapc_connection *conn)
-{
-	if (conn->io != NULL)
-		conn->io = io_loop_move_io(&conn->io);
-	if (conn->to != NULL)
-		conn->to = io_loop_move_timeout(&conn->to);
-	if (conn->output != NULL)
-		o_stream_switch_ioloop(conn->output);
-
-	if (conn->client->ioloop == NULL && conn->to_output != NULL) {
-		/* we're only once moving the to_output to the main ioloop,
-		   since timeout moves currently also reset the timeout.
-		   (the rest of the times this is a no-op) */
-		conn->to_output = io_loop_move_timeout(&conn->to_output);
-	}
-}
-
-static const char *imapc_command_get_readable(struct imapc_command *cmd)
-{
-	string_t *str = t_str_new(256);
-	const unsigned char *data = cmd->data->data;
-	unsigned int i;
-
-	for (i = 0; i < cmd->data->used; i++) {
-		if (data[i] != '\r' && data[i] != '\n')
-			str_append_c(str, data[i]);
-	}
-	return str_c(str);
-}
-
-static void
-imapc_connection_abort_pending_commands(struct imapc_connection *conn,
-					const struct imapc_command_reply *reply)
-{
-	struct imapc_command *const *cmdp, *cmd;
-
-	while (array_count(&conn->cmd_wait_list) > 0) {
-		cmdp = array_idx(&conn->cmd_wait_list, 0);
-		cmd = *cmdp;
-		array_delete(&conn->cmd_wait_list, 0, 1);
-
-		if (cmd->callback != NULL)
-			cmd->callback(reply, cmd->context);
-		imapc_command_free(cmd);
-	}
-	while (array_count(&conn->cmd_send_queue) > 0) {
-		cmdp = array_idx(&conn->cmd_send_queue, 0);
-		cmd = *cmdp;
-		array_delete(&conn->cmd_send_queue, 0, 1);
-
-		if (cmd->callback != NULL)
-			cmd->callback(reply, cmd->context);
-		imapc_command_free(cmd);
-	}
-}
-
-static void imapc_connection_set_state(struct imapc_connection *conn,
-				       enum imapc_connection_state state)
-{
-	if (state == IMAPC_CONNECTION_STATE_DISCONNECTED) {
-		struct imapc_command_reply reply;
-
-		memset(&reply, 0, sizeof(reply));
-		reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
-		reply.text_without_resp = reply.text_full =
-			"Disconnected from server";
-		imapc_connection_abort_pending_commands(conn, &reply);
-
-		conn->idling = FALSE;
-		conn->idle_plus_waiting = FALSE;
-		conn->idle_stopping = FALSE;
-
-		conn->selecting_box = NULL;
-		conn->selected_box = NULL;
-	}
-	if (state == IMAPC_CONNECTION_STATE_DONE) {
-		if (array_count(&conn->cmd_send_queue) > 0) {
-			struct imapc_command *const *cmd_p =
-				array_idx(&conn->cmd_send_queue, 0);
-			imapc_command_send_more(conn, *cmd_p);
-		}
-	}
-	conn->state = state;
-}
-
-static void imapc_connection_lfiles_free(struct imapc_connection *conn)
-{
-	struct imapc_arg_file *lfile;
-
-	array_foreach_modifiable(&conn->literal_files, lfile) {
-		if (close(lfile->fd) < 0)
-			i_error("imapc: close(literal file) failed: %m");
-	}
-	array_clear(&conn->literal_files);
-}
-
-static void
-imapc_connection_literal_reset(struct imapc_connection_literal *literal)
-{
-	if (literal->fd != -1) {
-		if (close(literal->fd) < 0)
-			i_error("close(%s) failed: %m", literal->temp_path);
-	}
-	i_free_and_null(literal->temp_path);
-
-	memset(literal, 0, sizeof(*literal));
-	literal->fd = -1;
-}
-
-void imapc_connection_disconnect(struct imapc_connection *conn)
-{
-	if (conn->fd == -1)
-		return;
-
-	if (conn->client->set.debug)
-		i_debug("imapc(%s): Disconnected", conn->name);
-
-	imapc_connection_lfiles_free(conn);
-	imapc_connection_literal_reset(&conn->literal);
-	if (conn->to != NULL)
-		timeout_remove(&conn->to);
-	if (conn->to_output != NULL)
-		timeout_remove(&conn->to_output);
-	imap_parser_destroy(&conn->parser);
-	io_remove(&conn->io);
-	if (conn->ssl_iostream != NULL)
-		ssl_iostream_unref(&conn->ssl_iostream);
-	i_stream_destroy(&conn->input);
-	o_stream_destroy(&conn->output);
-	net_disconnect(conn->fd);
-	conn->fd = -1;
-
-	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
-}
-
-static void ATTR_FORMAT(2, 3)
-imapc_connection_input_error(struct imapc_connection *conn,
-			     const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	i_error("imapc(%s): Server sent invalid input: %s",
-		conn->name, t_strdup_vprintf(fmt, va));
-	sleep(3600);
-	imapc_connection_disconnect(conn);
-	va_end(va);
-}
-
-static bool last_arg_is_fetch_body(const struct imap_arg *args,
-				   const struct imap_arg **parent_arg_r,
-				   unsigned int *idx_r)
-{
-	const struct imap_arg *list;
-	const char *name;
-	unsigned int count;
-
-	if (args[0].type == IMAP_ARG_ATOM &&
-	    imap_arg_atom_equals(&args[1], "FETCH") &&
-	    imap_arg_get_list_full(&args[2], &list, &count) && count >= 2 &&
-	    list[count].type == IMAP_ARG_LITERAL_SIZE &&
-	    imap_arg_get_atom(&list[count-1], &name) &&
-	    strncasecmp(name, "BODY[", 5) == 0) {
-		*parent_arg_r = &args[2];
-		*idx_r = count;
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static int
-imapc_connection_read_literal_init(struct imapc_connection *conn, uoff_t size,
-				   const struct imap_arg *args)
-{
-	const char *path;
-	const struct imap_arg *parent_arg;
-	unsigned int idx;
-
-	i_assert(size > 0);
-	i_assert(conn->literal.fd == -1);
-
-	if (size <= IMAPC_MAX_INLINE_LITERAL_SIZE ||
-	    !last_arg_is_fetch_body(args, &parent_arg, &idx)) {
-		/* read the literal directly into parser */
-		return 0;
-	}
-
-	conn->literal.fd = imapc_client_create_temp_fd(conn->client, &path);
-	if (conn->literal.fd == -1)
-		return -1;
-	conn->literal.temp_path = i_strdup(path);
-	conn->literal.bytes_left = size;
-	conn->literal.parent_arg = parent_arg;
-	conn->literal.list_idx = idx;
-	return 1;
-}
-
-static int imapc_connection_read_literal(struct imapc_connection *conn)
-{
-	struct imapc_arg_file *lfile;
-	const unsigned char *data;
-	size_t size;
-
-	if (conn->literal.bytes_left == 0)
-		return 1;
-
-	data = i_stream_get_data(conn->input, &size);
-	if (size > conn->literal.bytes_left)
-		size = conn->literal.bytes_left;
-	if (size > 0) {
-		if (write_full(conn->literal.fd, data, size) < 0) {
-			i_error("imapc(%s): write(%s) failed: %m",
-				conn->name, conn->literal.temp_path);
-			imapc_connection_disconnect(conn);
-			return -1;
-		}
-		i_stream_skip(conn->input, size);
-		conn->literal.bytes_left -= size;
-	}
-	if (conn->literal.bytes_left > 0)
-		return 0;
-
-	/* finished */
-	lfile = array_append_space(&conn->literal_files);
-	lfile->fd = conn->literal.fd;
-	lfile->parent_arg = conn->literal.parent_arg;
-	lfile->list_idx = conn->literal.list_idx;
-
-	conn->literal.fd = -1;
-	imapc_connection_literal_reset(&conn->literal);
-	return 1;
-}
-
-static int
-imapc_connection_read_line_more(struct imapc_connection *conn,
-				const struct imap_arg **imap_args_r)
-{
-	uoff_t literal_size;
-	bool fatal;
-	int ret;
-
-	if ((ret = imapc_connection_read_literal(conn)) <= 0)
-		return ret;
-
-	ret = imap_parser_read_args(conn->parser, 0,
-				    IMAP_PARSE_FLAG_LITERAL_SIZE |
-				    IMAP_PARSE_FLAG_ATOM_ALLCHARS, imap_args_r);
-	if (ret == -2) {
-		/* need more data */
-		return 0;
-	}
-	if (ret < 0) {
-		imapc_connection_input_error(conn, "Error parsing input: %s",
-			imap_parser_get_error(conn->parser, &fatal));
-		return -1;
-	}
-
-	if (imap_parser_get_literal_size(conn->parser, &literal_size)) {
-		if (imapc_connection_read_literal_init(conn, literal_size,
-						       *imap_args_r) <= 0) {
-			imap_parser_read_last_literal(conn->parser);
-			return 2;
-		}
-		return imapc_connection_read_line_more(conn, imap_args_r);
-	}
-	return 1;
-}
-
-static int
-imapc_connection_read_line(struct imapc_connection *conn,
-			   const struct imap_arg **imap_args_r)
-{
-	const unsigned char *data;
-	size_t size;
-	int ret;
-
-	while ((ret = imapc_connection_read_line_more(conn, imap_args_r)) == 2)
-		;
-
-	if (ret > 0) {
-		data = i_stream_get_data(conn->input, &size);
-		if (size >= 2 && data[0] == '\r' && data[1] == '\n')
-			i_stream_skip(conn->input, 2);
-		else if (size >= 1 && data[0] == '\n')
-			i_stream_skip(conn->input, 1);
-		else
-			i_panic("imapc: Missing LF from input line");
-	}
-	return ret;
-}
-
-static int
-imapc_connection_parse_capability(struct imapc_connection *conn,
-				  const char *value)
-{
-	const char *const *tmp;
-	unsigned int i;
-
-	if (conn->client->set.debug) {
-		i_debug("imapc(%s): Server capabilities: %s",
-			conn->name, value);
-	}
-
-	conn->capabilities = 0;
-	if (conn->capabilities_list != NULL)
-		p_strsplit_free(default_pool, conn->capabilities_list);
-	conn->capabilities_list = p_strsplit(default_pool, value, " ");
-
-	for (tmp = t_strsplit(value, " "); *tmp != NULL; tmp++) {
-		for (i = 0; imapc_capability_names[i].name != NULL; i++) {
-			const struct imapc_capability_name *cap =
-				&imapc_capability_names[i];
-
-			if (strcasecmp(*tmp, cap->name) == 0) {
-				conn->capabilities |= cap->capability;
-				break;
-			}
-		}
-	}
-
-	if ((conn->capabilities & IMAPC_CAPABILITY_IMAP4REV1) == 0) {
-		imapc_connection_input_error(conn,
-			"CAPABILITY list is missing IMAP4REV1");
-		return -1;
-	}
-	return 0;
-}
-
-static int
-imapc_connection_handle_resp_text_code(struct imapc_connection *conn,
-				       const char *key, const char *value)
-{
-	if (strcasecmp(key, "CAPABILITY") == 0) {
-		if (imapc_connection_parse_capability(conn, value) < 0)
-			return -1;
-	}
-	if (strcasecmp(key, "CLOSED") == 0) {
-		/* QRESYNC: SELECTing another mailbox */
-		if (conn->selecting_box != NULL) {
-			conn->selected_box = conn->selecting_box;
-			conn->selecting_box = NULL;
-		}
-	}
-	return 0;
-}
-
-static int
-imapc_connection_handle_resp_text(struct imapc_connection *conn,
-				  const char *text,
-				  const char **key_r, const char **value_r)
-{
-	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;
-
-	text = imap_args_to_str(args);
-	if (*text != '[') {
-		if (*text == '\0') {
-			imapc_connection_input_error(conn,
-				"Missing text in resp-text");
-			return -1;
-		}
-		return 0;
-	}
-	if (imapc_connection_handle_resp_text(conn, text, key_r, value_r) < 0)
-		return -1;
-
-	return imapc_connection_handle_resp_text_code(conn, *key_r, *value_r);
-}
-
-static bool need_literal(const char *str)
-{
-	unsigned int i;
-
-	for (i = 0; str[i] != '\0'; i++) {
-		unsigned char c = str[i];
-
-		if ((c & 0x80) != 0 || c == '\r' || c == '\n')
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static void imapc_connection_input_reset(struct imapc_connection *conn)
-{
-	conn->input_state = IMAPC_INPUT_STATE_NONE;
-	conn->cur_tag = 0;
-	conn->cur_num = 0;
-	if (conn->parser != NULL)
-		imap_parser_reset(conn->parser);
-	imapc_connection_lfiles_free(conn);
-}
-
-static void imapc_connection_login_cb(const struct imapc_command_reply *reply,
-				      void *context)
-{
-	struct imapc_connection *conn = context;
-
-	if (reply->state != IMAPC_COMMAND_STATE_OK) {
-		i_error("imapc(%s): Authentication failed: %s",
-			conn->name, reply->text_full);
-		imapc_connection_disconnect(conn);
-		return;
-	}
-
-	if (conn->client->set.debug)
-		i_debug("imapc(%s): Authenticated successfully", conn->name);
-
-	timeout_remove(&conn->to);
-	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE);
-}
-
-static const char *
-imapc_connection_get_sasl_plain_request(struct imapc_connection *conn)
-{
-	const struct imapc_client_settings *set = &conn->client->set;
-	string_t *in, *out;
-
-	in = t_str_new(128);
-	if (set->master_user != NULL) {
-		str_append(in, set->username);
-		str_append_c(in, '\0');
-		str_append(in, set->master_user);
-	} else {
-		str_append_c(in, '\0');
-		str_append(in, set->username);
-	}
-	str_append_c(in, '\0');
-	str_append(in, set->password);
-
-	out = t_str_new(128);
-	base64_encode(in->data, in->used, out);
-	return str_c(out);
-}
-
-static void imapc_connection_authenticate(struct imapc_connection *conn)
-{
-	const struct imapc_client_settings *set = &conn->client->set;
-	const char *cmd;
-
-	if (conn->client->set.debug) {
-		if (set->master_user == NULL) {
-			i_debug("imapc(%s): Authenticating as %s",
-				conn->name, set->username);
-		} else {
-			i_debug("imapc(%s): Authenticating as %s for user %s",
-				conn->name, set->master_user, set->username);
-		}
-	}
-
-	if ((set->master_user == NULL &&
-	     need_literal(set->username) && need_literal(set->password)) ||
-	    (conn->capabilities & IMAPC_CAPABILITY_AUTH_PLAIN) == 0) {
-		/* We can use LOGIN command */
-		imapc_connection_cmdf(conn, FALSE, imapc_connection_login_cb,
-				      conn, "LOGIN %s %s",
-				      set->username, set->password);
-	} else if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) {
-		cmd = t_strdup_printf("AUTHENTICATE PLAIN %s",
-			imapc_connection_get_sasl_plain_request(conn));
-		imapc_connection_cmd(conn, FALSE, cmd,
-				     imapc_connection_login_cb, conn);
-	} else {
-		cmd = t_strdup_printf("AUTHENTICATE PLAIN\r\n%s",
-			imapc_connection_get_sasl_plain_request(conn));
-		imapc_connection_cmd(conn, FALSE, cmd,
-				     imapc_connection_login_cb, conn);
-	}
-}
-
-static void
-imapc_connection_starttls_cb(const struct imapc_command_reply *reply,
-			     void *context)
-{
-	struct imapc_connection *conn = context;
-
-	if (reply->state != IMAPC_COMMAND_STATE_OK) {
-		imapc_connection_input_error(conn, "STARTTLS failed: %s",
-					     reply->text_full);
-		return;
-	}
-
-	if (imapc_connection_ssl_init(conn) < 0)
-		imapc_connection_disconnect(conn);
-	else
-		imapc_connection_authenticate(conn);
-}
-
-static void imapc_connection_starttls(struct imapc_connection *conn)
-{
-	if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_STARTTLS &&
-	    conn->ssl_iostream == NULL) {
-		if ((conn->capabilities & IMAPC_CAPABILITY_STARTTLS) == 0) {
-			i_error("imapc(%s): Requested STARTTLS, "
-				"but server doesn't support it",
-				conn->name);
-			imapc_connection_disconnect(conn);
-			return;
-		}
-		imapc_connection_cmd(conn, FALSE, "STARTTLS",
-				     imapc_connection_starttls_cb, conn);
-		return;
-	}
-	imapc_connection_authenticate(conn);
-}
-
-static void
-imapc_connection_capability_cb(const struct imapc_command_reply *reply,
-			       void *context)
-{
-	struct imapc_connection *conn = context;
-
-	if (reply->state != IMAPC_COMMAND_STATE_OK) {
-		imapc_connection_input_error(conn,
-			"Failed to get capabilities: %s", reply->text_full);
-	} else if (conn->capabilities == 0) {
-		imapc_connection_input_error(conn,
-			"Capabilities not returned by server");
-	} else {
-		imapc_connection_starttls(conn);
-	}
-}
-
-static int imapc_connection_input_banner(struct imapc_connection *conn)
-{
-	const struct imap_arg *imap_args;
-	const char *key, *value;
-	int ret;
-
-	if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
-		return ret;
-
-	if (imapc_connection_handle_imap_resp_text(conn, imap_args,
-						   &key, &value) < 0)
-		return -1;
-	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_AUTHENTICATING);
-
-	if (conn->capabilities == 0) {
-		/* capabilities weren't sent in the banner. ask for them. */
-		imapc_connection_cmd(conn, FALSE, "CAPABILITY",
-				     imapc_connection_capability_cb, conn);
-	} else {
-		imapc_connection_starttls(conn);
-	}
-	conn->input_callback = NULL;
-	imapc_connection_input_reset(conn);
-	return 1;
-}
-
-static int imapc_connection_input_untagged(struct imapc_connection *conn)
-{
-	const struct imap_arg *imap_args;
-	const char *name, *value;
-	struct imapc_untagged_reply reply;
-	int ret;
-
-	if (conn->state == IMAPC_CONNECTION_STATE_CONNECTING) {
-		/* input banner */
-		name = imap_parser_read_word(conn->parser);
-		if (name == NULL)
-			return 0;
-
-		if (strcasecmp(name, "OK") != 0) {
-			imapc_connection_input_error(conn,
-				"Banner doesn't begin with OK: %s", name);
-			return -1;
-		}
-		conn->input_callback = imapc_connection_input_banner;
-		return 1;
-	}
-
-	if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
-		return ret;
-	if (!imap_arg_get_atom(&imap_args[0], &name)) {
-		imapc_connection_input_error(conn, "Invalid untagged reply");
-		return -1;
-	}
-	imap_args++;
-
-	if (conn->input_state == IMAPC_INPUT_STATE_UNTAGGED &&
-	    str_to_uint32(name, &conn->cur_num) == 0) {
-		/* <seq> <event> */
-		conn->input_state = IMAPC_INPUT_STATE_UNTAGGED_NUM;
-		if (!imap_arg_get_atom(&imap_args[0], &name)) {
-			imapc_connection_input_error(conn,
-						     "Invalid untagged reply");
-			return -1;
-		}
-		imap_args++;
-	}
-	memset(&reply, 0, sizeof(reply));
-
-	if (strcasecmp(name, "OK") == 0) {
-		if (imapc_connection_handle_imap_resp_text(conn, imap_args,
-						&reply.resp_text_key,
-						&reply.resp_text_value) < 0)
-			return -1;
-	} else if (strcasecmp(name, "CAPABILITY") == 0) {
-		value = imap_args_to_str(imap_args);
-		if (imapc_connection_parse_capability(conn, value) < 0)
-			return -1;
-	}
-
-	reply.name = name;
-	reply.num = conn->cur_num;
-	reply.args = imap_args;
-	reply.file_args = array_get(&conn->literal_files,
-				    &reply.file_args_count);
-
-	if (conn->selected_box != NULL) {
-		reply.untagged_box_context =
-			conn->selected_box->untagged_box_context;
-	}
-	conn->client->untagged_callback(&reply, conn->client->untagged_context);
-	imapc_connection_input_reset(conn);
-	return 1;
-}
-
-static int imapc_connection_input_plus(struct imapc_connection *conn)
-{
-	struct imapc_command *const *cmds;
-	unsigned int cmds_count;
-	const char *line;
-
-	if ((line = i_stream_next_line(conn->input)) == NULL)
-		return 0;
-
-	cmds = array_get(&conn->cmd_send_queue, &cmds_count);
-	if (conn->idle_plus_waiting) {
-		/* "+ idling" reply for IDLE command */
-		conn->idle_plus_waiting = FALSE;
-		conn->idling = TRUE;
-	} else if (cmds_count > 0 && cmds[0]->wait_for_literal) {
-		/* reply for literal */
-		cmds[0]->wait_for_literal = FALSE;
-		imapc_command_send_more(conn, cmds[0]);
-	} else {
-		imapc_connection_input_error(conn, "Unexpected '+': %s", line);
-		return -1;
-	}
-
-	imapc_connection_input_reset(conn);
-	return 1;
-}
-
-static int imapc_connection_input_tagged(struct imapc_connection *conn)
-{
-	struct imapc_command *const *cmds, *cmd = NULL;
-	unsigned int i, count;
-	char *line, *linep;
-	const char *p;
-	struct imapc_command_reply reply;
-
-	line = i_stream_next_line(conn->input);
-	if (line == NULL)
-		return 0;
-
-	memset(&reply, 0, sizeof(reply));
-
-	linep = strchr(line, ' ');
-	if (linep == NULL)
-		reply.text_full = "";
-	else {
-		*linep = '\0';
-		reply.text_full = linep + 1;
-	}
-
-	if (strcasecmp(line, "ok") == 0)
-		reply.state = IMAPC_COMMAND_STATE_OK;
-	else if (strcasecmp(line, "no") == 0)
-		reply.state = IMAPC_COMMAND_STATE_NO;
-	else if (strcasecmp(line, "bad") == 0)
-		reply.state = IMAPC_COMMAND_STATE_BAD;
-	else {
-		imapc_connection_input_error(conn,
-			"Invalid state in tagged reply: %u %s %s",
-			conn->cur_tag, line, reply.text_full);
-		return -1;
-	}
-
-	if (reply.text_full[0] == '[') {
-		/* get resp-text */
-		if (imapc_connection_handle_resp_text(conn, reply.text_full,
-					&reply.resp_text_key,
-					&reply.resp_text_value) < 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
-	   (literal failed) or somewhere in wait list. */
-	cmds = array_get(&conn->cmd_send_queue, &count);
-	if (count > 0 && cmds[0]->tag == conn->cur_tag) {
-		cmd = cmds[0];
-		array_delete(&conn->cmd_send_queue, 0, 1);
-	} else {
-		cmds = array_get(&conn->cmd_wait_list, &count);
-		for (i = 0; i < count; i++) {
-			if (cmds[i]->tag == conn->cur_tag) {
-				cmd = cmds[i];
-				array_delete(&conn->cmd_wait_list, i, 1);
-				break;
-			}
-		}
-	}
-	if (array_count(&conn->cmd_wait_list) == 0 &&
-	    array_count(&conn->cmd_send_queue) == 0 &&
-	    conn->state == IMAPC_CONNECTION_STATE_DONE && conn->to != NULL)
-		timeout_remove(&conn->to);
-
-	if (cmd == NULL) {
-		imapc_connection_input_error(conn,
-			"Unknown tag in a reply: %u %s %s",
-			conn->cur_tag, line, reply.text_full);
-		return -1;
-	}
-
-	if (reply.state == IMAPC_COMMAND_STATE_BAD) {
-		i_error("imapc(%s): Command '%s' failed with BAD: %u %s",
-			conn->name, imapc_command_get_readable(cmd),
-			conn->cur_tag, reply.text_full);
-		imapc_connection_disconnect(conn);
-	}
-
-	imapc_connection_input_reset(conn);
-	if (cmd->callback != NULL)
-		cmd->callback(&reply, cmd->context);
-	imapc_command_free(cmd);
-	return 1;
-}
-
-static int imapc_connection_input_one(struct imapc_connection *conn)
-{
-	const char *tag;
-	int ret = -1;
-
-	if (conn->input_callback != NULL)
-		return conn->input_callback(conn);
-
-	switch (conn->input_state) {
-	case IMAPC_INPUT_STATE_NONE:
-		tag = imap_parser_read_word(conn->parser);
-		if (tag == NULL)
-			return 0;
-
-		if (strcmp(tag, "*") == 0) {
-			conn->input_state = IMAPC_INPUT_STATE_UNTAGGED;
-			conn->cur_num = 0;
-			ret = imapc_connection_input_untagged(conn);
-		} else if (strcmp(tag, "+") == 0) {
-			conn->input_state = IMAPC_INPUT_STATE_PLUS;
-			ret = imapc_connection_input_plus(conn);
-		} else {
-			conn->input_state = IMAPC_INPUT_STATE_TAGGED;
-			if (str_to_uint(tag, &conn->cur_tag) < 0 ||
-			    conn->cur_tag == 0) {
-				imapc_connection_input_error(conn,
-					"Invalid command tag: %s", tag);
-				ret = -1;
-			} else {
-				ret = imapc_connection_input_tagged(conn);
-			}
-		}
-		break;
-	case IMAPC_INPUT_STATE_PLUS:
-		ret = imapc_connection_input_plus(conn);
-		break;
-	case IMAPC_INPUT_STATE_UNTAGGED:
-	case IMAPC_INPUT_STATE_UNTAGGED_NUM:
-		ret = imapc_connection_input_untagged(conn);
-		break;
-	case IMAPC_INPUT_STATE_TAGGED:
-		ret = imapc_connection_input_tagged(conn);
-		break;
-	}
-	return ret;
-}
-
-static void imapc_connection_input(struct imapc_connection *conn)
-{
-	const char *errstr;
-	ssize_t ret = 0;
-
-	/* we need to read as much as we can with SSL streams to avoid
-	   hanging */
-	while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0)
-		imapc_connection_input_pending(conn);
-
-	if (ret < 0) {
-		/* disconnected */
-		if (conn->ssl_iostream == NULL) {
-			i_error("imapc(%s): Server disconnected unexpectedly",
-				conn->name);
-		} else if (!conn->handshake_failed) {
-			errstr = ssl_iostream_get_last_error(conn->ssl_iostream);
-			i_error("imapc(%s): Server disconnected: %s",
-				conn->name, errstr != NULL ? errstr : "");
-		}
-		imapc_connection_disconnect(conn);
-		return;
-	}
-}
-
-static int imapc_connection_ssl_handshaked(void *context)
-{
-	struct imapc_connection *conn = context;
-
-	if (!ssl_iostream_has_valid_client_cert(conn->ssl_iostream)) {
-		if (!ssl_iostream_has_broken_client_cert(conn->ssl_iostream)) {
-			i_error("imapc(%s): SSL certificate not received",
-				conn->name);
-		} else {
-			i_error("imapc(%s): Received invalid SSL certificate",
-				conn->name);
-		}
-	} else if (ssl_iostream_cert_match_name(conn->ssl_iostream,
-						conn->client->set.host) < 0) {
-		i_error("imapc(%s): SSL certificate doesn't match host name",
-			conn->name);
-	} else {
-		if (conn->client->set.debug) {
-			i_debug("imapc(%s): SSL handshake successful",
-				conn->name);
-		}
-		return 0;
-	}
-	conn->handshake_failed = TRUE;
-	i_stream_close(conn->input);
-	return -1;
-}
-
-static int imapc_connection_ssl_init(struct imapc_connection *conn)
-{
-	struct ssl_iostream_settings ssl_set;
-	const char *source;
-
-	if (conn->client->ssl_ctx == NULL) {
-		i_error("imapc(%s): No SSL context", conn->name);
-		return -1;
-	}
-
-	memset(&ssl_set, 0, sizeof(ssl_set));
-	ssl_set.verbose_invalid_cert = TRUE;
-	ssl_set.verify_remote_cert = TRUE;
-	ssl_set.require_valid_cert = TRUE;
-
-	if (conn->client->set.debug)
-		i_debug("imapc(%s): Starting SSL handshake", conn->name);
-
-	if (conn->raw_input != conn->input) {
-		/* recreate rawlog after STARTTLS */
-		i_stream_ref(conn->raw_input);
-		o_stream_ref(conn->raw_output);
-		i_stream_destroy(&conn->input);
-		o_stream_destroy(&conn->output);
-		conn->input = conn->raw_input;
-		conn->output = conn->raw_output;
-	}
-
-	source = t_strdup_printf("imapc(%s): ", conn->name);
-	if (io_stream_create_ssl(conn->client->ssl_ctx, source, &ssl_set,
-				 &conn->input, &conn->output,
-				 &conn->ssl_iostream) < 0) {
-		i_error("imapc(%s): Couldn't initialize SSL client",
-			conn->name);
-		return -1;
-	}
-	ssl_iostream_set_handshake_callback(conn->ssl_iostream,
-					    imapc_connection_ssl_handshaked,
-					    conn);
-	if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
-		i_error("imapc(%s): SSL handshake failed: %s", conn->name,
-			ssl_iostream_get_last_error(conn->ssl_iostream));
-		return -1;
-	}
-
-	if (*conn->client->set.rawlog_dir != '\0') {
-		(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
-					     &conn->input, &conn->output);
-	}
-
-	imap_parser_set_streams(conn->parser, conn->input, NULL);
-	return 0;
-}
-
-static void imapc_connection_connected(struct imapc_connection *conn)
-{
-	const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
-	int err;
-
-	err = net_geterror(conn->fd);
-	if (err != 0) {
-		i_error("imapc(%s): connect(%s, %u) failed: %s",
-			conn->name, net_ip2addr(ip), conn->client->set.port,
-			strerror(err));
-		imapc_connection_disconnect(conn);
-		return;
-	}
-	io_remove(&conn->io);
-	conn->io = io_add(conn->fd, IO_READ, imapc_connection_input, conn);
-
-	if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
-		if (imapc_connection_ssl_init(conn) < 0)
-			imapc_connection_disconnect(conn);
-	}
-}
-
-static void imapc_connection_timeout(struct imapc_connection *conn)
-{
-	const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
-
-	switch (conn->state) {
-	case IMAPC_CONNECTION_STATE_CONNECTING:
-		i_error("imapc(%s): connect(%s, %u) timed out after %u seconds",
-			conn->name, net_ip2addr(ip), conn->client->set.port,
-			IMAPC_CONNECT_TIMEOUT_MSECS/1000);
-		break;
-	case IMAPC_CONNECTION_STATE_AUTHENTICATING:
-		i_error("imapc(%s): Authentication timed out after %u seconds",
-			conn->name, IMAPC_CONNECT_TIMEOUT_MSECS/1000);
-		break;
-	default:
-		i_unreached();
-	}
-	imapc_connection_disconnect(conn);
-}
-
-static void
-imapc_reidle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
-		      void *context)
-{
-	struct imapc_connection *conn = context;
-
-	imapc_connection_idle(conn);
-}
-
-static void imapc_connection_reset_idle(struct imapc_connection *conn)
-{
-	if (!conn->idling)
-		imapc_connection_cmd(conn, FALSE, "NOOP", NULL, NULL);
-	else {
-		imapc_connection_cmd(conn, FALSE, "NOOP",
-				     imapc_reidle_callback, conn);
-	}
-}
-
-static void imapc_connection_connect_next_ip(struct imapc_connection *conn)
-{
-	const struct ip_addr *ip;
-	int fd;
-
-	conn->prev_connect_idx = (conn->prev_connect_idx+1) % conn->ips_count;
-	ip = &conn->ips[conn->prev_connect_idx];
-	fd = net_connect_ip(ip, conn->client->set.port, NULL);
-	if (fd == -1) {
-		imapc_connection_set_state(conn,
-			IMAPC_CONNECTION_STATE_DISCONNECTED);
-		return;
-	}
-	conn->fd = fd;
-	conn->input = conn->raw_input = i_stream_create_fd(fd, (size_t)-1, FALSE);
-	conn->output = conn->raw_output = o_stream_create_fd(fd, (size_t)-1, FALSE);
-
-	if (*conn->client->set.rawlog_dir != '\0' &&
-	    conn->client->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
-		(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
-					     &conn->input, &conn->output);
-	}
-
-	o_stream_set_flush_callback(conn->output, imapc_connection_output,
-				    conn);
-	conn->io = io_add(fd, IO_WRITE, imapc_connection_connected, conn);
-	conn->parser = imap_parser_create(conn->input, NULL, (size_t)-1);
-	conn->to = timeout_add(IMAPC_CONNECT_TIMEOUT_MSECS,
-			       imapc_connection_timeout, conn);
-	conn->to_output = timeout_add(IMAPC_MAX_IDLE_MSECS,
-				      imapc_connection_reset_idle, conn);
-	if (conn->client->set.debug) {
-		i_debug("imapc(%s): Connecting to %s:%u", conn->name,
-			net_ip2addr(ip), conn->client->set.port);
-	}
-}
-
-static void
-imapc_connection_dns_callback(const struct dns_lookup_result *result,
-			      void *context)
-{
-	struct imapc_connection *conn = context;
-
-	if (result->ret != 0) {
-		i_error("imapc(%s): dns_lookup(%s) failed: %s",
-			conn->name, conn->client->set.host, result->error);
-		imapc_connection_set_state(conn,
-			IMAPC_CONNECTION_STATE_DISCONNECTED);
-		return;
-	}
-
-	i_assert(result->ips_count > 0);
-	conn->ips_count = result->ips_count;
-	conn->ips = i_new(struct ip_addr, conn->ips_count);
-	memcpy(conn->ips, result->ips, sizeof(*conn->ips) * conn->ips_count);
-	conn->prev_connect_idx = conn->ips_count - 1;
-
-	imapc_connection_connect_next_ip(conn);
-}
-
-void imapc_connection_connect(struct imapc_connection *conn)
-{
-	struct dns_lookup_settings dns_set;
-
-	if (conn->fd != -1)
-		return;
-
-	imapc_connection_input_reset(conn);
-
-	if (conn->client->set.debug)
-		i_debug("imapc(%s): Looking up IP address", conn->name);
-
-	memset(&dns_set, 0, sizeof(dns_set));
-	dns_set.dns_client_socket_path =
-		conn->client->set.dns_client_socket_path;
-	dns_set.timeout_msecs = IMAPC_DNS_LOOKUP_TIMEOUT_MSECS;
-
-	imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_CONNECTING);
-	if (conn->ips_count == 0) {
-		(void)dns_lookup(conn->client->set.host, &dns_set,
-				 imapc_connection_dns_callback, conn);
-	} else {
-		imapc_connection_connect_next_ip(conn);
-	}
-}
-
-void imapc_connection_input_pending(struct imapc_connection *conn)
-{
-	int ret = 1;
-
-	if (conn->input == NULL)
-		return;
-
-	if (conn->to != NULL)
-		timeout_reset(conn->to);
-
-	o_stream_cork(conn->output);
-	while (ret > 0 && !conn->client->stop_now && conn->input != NULL) {
-		T_BEGIN {
-			ret = imapc_connection_input_one(conn);
-		} T_END;
-	}
-
-	if (conn->output != NULL)
-		o_stream_uncork(conn->output);
-}
-
-static struct imapc_command *
-imapc_command_begin(imapc_command_callback_t *callback, void *context)
-{
-	static unsigned int cmd_tag_counter = 0;
-	struct imapc_command *cmd;
-	pool_t pool;
-
-	pool = pool_alloconly_create("imapc command", 2048);
-	cmd = p_new(pool, struct imapc_command, 1);
-	cmd->pool = pool;
-	cmd->callback = callback;
-	cmd->context = context;
-
-	if (++cmd_tag_counter == 0)
-		cmd_tag_counter++;
-	cmd->tag = cmd_tag_counter;
-	return cmd;
-}
-
-static void imapc_command_free(struct imapc_command *cmd)
-{
-	struct imapc_command_stream *stream;
-
-	if (array_is_created(&cmd->streams)) {
-		array_foreach_modifiable(&cmd->streams, stream)
-			i_stream_unref(&stream->input);
-	}
-	pool_unref(&cmd->pool);
-}
-
-static bool
-parse_sync_literal(const unsigned char *data, unsigned int pos,
-		   unsigned int *value_r)
-{
-	unsigned int value = 0, mul = 1;
-
-	/* data should contain "{size}\r\n" and pos points after \n */
-	if (pos <= 4 || data[pos-1] != '\n' || data[pos-2] != '\r' ||
-	    data[pos-3] != '}' || !i_isdigit(data[pos-4]))
-		return FALSE;
-	pos -= 4;
-
-	do {
-		value += (data[pos] - '0') * mul;
-		mul = mul*10;
-		pos--;
-	} while (pos > 0 && i_isdigit(data[pos]));
-
-	if (pos == 0 || data[pos] != '{')
-		return FALSE;
-
-	*value_r = value;
-	return TRUE;
-}
-
-static void imapc_command_send_done(struct imapc_connection *conn,
-				    struct imapc_command *cmd)
-{
-	if (cmd->idle)
-		conn->idle_plus_waiting = TRUE;
-
-	/* everything sent. move command to wait list. */
-	i_assert(*array_idx(&conn->cmd_send_queue, 0) == cmd);
-	array_delete(&conn->cmd_send_queue, 0, 1);
-	array_append(&conn->cmd_wait_list, &cmd, 1);
-
-	if (array_count(&conn->cmd_send_queue) > 0 &&
-	    conn->state == IMAPC_CONNECTION_STATE_DONE) {
-		/* send the next command in queue */
-		struct imapc_command *const *cmd2_p =
-			array_idx(&conn->cmd_send_queue, 0);
-		imapc_command_send_more(conn, *cmd2_p);
-	}
-}
-
-static struct imapc_command_stream *
-imapc_command_get_sending_stream(struct imapc_command *cmd)
-{
-	struct imapc_command_stream *stream;
-
-	if (!array_is_created(&cmd->streams) || array_count(&cmd->streams) == 0)
-		return NULL;
-
-	stream = array_idx_modifiable(&cmd->streams, 0);
-	if (stream->pos != cmd->send_pos)
-		return NULL;
-	return stream;
-}
-
-static int imapc_command_try_send_stream(struct imapc_connection *conn,
-					 struct imapc_command *cmd)
-{
-	struct imapc_command_stream *stream;
-
-	stream = imapc_command_get_sending_stream(cmd);
-	if (stream == NULL)
-		return -1;
-
-	/* we're sending the stream now */
-	o_stream_set_max_buffer_size(conn->output, 0);
-	(void)o_stream_send_istream(conn->output, stream->input);
-	o_stream_set_max_buffer_size(conn->output, (size_t)-1);
-
-	if (!i_stream_is_eof(stream->input)) {
-		o_stream_set_flush_pending(conn->output, TRUE);
-		i_assert(stream->input->v_offset < stream->size);
-		return 0;
-	}
-	i_assert(stream->input->v_offset == stream->size);
-
-	/* finished with the stream */
-	i_stream_unref(&stream->input);
-	array_delete(&cmd->streams, 0, 1);
-
-	i_assert(cmd->send_pos != cmd->data->used);
-	return 1;
-}
-
-static void imapc_command_send_more(struct imapc_connection *conn,
-				    struct imapc_command *cmd)
-{
-	const unsigned char *p, *data;
-	unsigned int seek_pos, start_pos, end_pos, size;
-	int ret;
-
-	i_assert(!cmd->wait_for_literal);
-	i_assert(cmd->send_pos < cmd->data->used);
-
-	timeout_reset(conn->to_output);
-	if ((ret = imapc_command_try_send_stream(conn, cmd)) == 0)
-		return;
-
-	seek_pos = cmd->send_pos;
-	if (seek_pos != 0 && ret < 0) {
-		/* skip over the literal. we can also get here from
-		   AUTHENTICATE command, which doesn't use a literal */
-		if (parse_sync_literal(cmd->data->data, seek_pos, &size)) {
-			seek_pos += size;
-			i_assert(seek_pos <= cmd->data->used);
-		}
-	}
-
-	do {
-		start_pos = seek_pos;
-		p = memchr(CONST_PTR_OFFSET(cmd->data->data, seek_pos), '\n',
-			   cmd->data->used - seek_pos);
-		i_assert(p != NULL);
-
-		seek_pos = p - (const unsigned char *)cmd->data->data + 1;
-		/* keep going for LITERAL+ command */
-	} while (start_pos + 3 < seek_pos &&
-		 p[-1] == '\r' && p[-2] == '}' && p[-3] == '+');
-	end_pos = seek_pos;
-
-	data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos);
-	size = end_pos - cmd->send_pos;
-	o_stream_send(conn->output, data, size);
-	cmd->send_pos = end_pos;
-
-	if (cmd->send_pos == cmd->data->used) {
-		i_assert(!array_is_created(&cmd->streams) ||
-			 array_count(&cmd->streams) == 0);
-		imapc_command_send_done(conn, cmd);
-	} else {
-		cmd->wait_for_literal = TRUE;
-	}
-}
-
-static void imapc_command_timeout(struct imapc_connection *conn)
-{
-	struct imapc_command *const *cmds;
-	unsigned int count;
-
-	cmds = array_get(&conn->cmd_wait_list, &count);
-	i_assert(count > 0);
-
-	i_error("imapc(%s): Command '%s' timed out, disconnecting",
-		conn->name, imapc_command_get_readable(cmds[0]));
-	imapc_connection_disconnect(conn);
-}
-
-static void imapc_connection_send_idle_done(struct imapc_connection *conn)
-{
-	if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) {
-		conn->idle_stopping = TRUE;
-		o_stream_send_str(conn->output, "DONE\r\n");
-	}
-}
-
-static void imapc_command_send(struct imapc_connection *conn,
-			       struct imapc_command *cmd)
-{
-	imapc_connection_send_idle_done(conn);
-	switch (conn->state) {
-	case IMAPC_CONNECTION_STATE_AUTHENTICATING:
-		array_insert(&conn->cmd_send_queue, 0, &cmd, 1);
-		imapc_command_send_more(conn, cmd);
-		break;
-	case IMAPC_CONNECTION_STATE_DONE:
-		if (cmd->idle) {
-			if (conn->to != NULL)
-				timeout_remove(&conn->to);
-		} else if (conn->to == NULL) {
-			conn->to = timeout_add(IMAPC_COMMAND_TIMEOUT_MSECS,
-					       imapc_command_timeout, conn);
-		}
-
-		array_append(&conn->cmd_send_queue, &cmd, 1);
-		if (array_count(&conn->cmd_send_queue) == 1)
-			imapc_command_send_more(conn, cmd);
-		break;
-	default:
-		array_append(&conn->cmd_send_queue, &cmd, 1);
-		break;
-	}
-}
-
-static int imapc_connection_output(struct imapc_connection *conn)
-{
-	struct imapc_command *const *cmds;
-	unsigned int count;
-	int ret;
-
-	if (conn->to != NULL)
-		timeout_reset(conn->to);
-
-	o_stream_cork(conn->output);
-	if ((ret = o_stream_flush(conn->output)) < 0)
-		return 1;
-
-	cmds = array_get(&conn->cmd_send_queue, &count);
-	if (count > 0) {
-		if (imapc_command_get_sending_stream(cmds[0]) != NULL &&
-		    !cmds[0]->wait_for_literal) {
-			/* we're sending a stream. send more. */
-			imapc_command_send_more(conn, cmds[0]);
-		}
-	}
-	o_stream_uncork(conn->output);
-	return ret;
-}
-
-static struct imapc_command *
-imapc_connection_cmd_build(const char *cmdline,
-			   imapc_command_callback_t *callback, void *context)
-{
-	struct imapc_command *cmd;
-	unsigned int len = strlen(cmdline);
-
-	cmd = imapc_command_begin(callback, context);
-	cmd->data = str_new(cmd->pool, 6 + len + 2);
-	str_printfa(cmd->data, "%u %s\r\n", cmd->tag, cmdline);
-	return cmd;
-}
-
-void imapc_connection_cmd(struct imapc_connection *conn, bool mailboxcmd,
-			  const char *cmdline,
-			  imapc_command_callback_t *callback, void *context)
-{
-	struct imapc_command *cmd;
-
-	cmd = imapc_connection_cmd_build(cmdline, callback, context);
-	cmd->mailboxcmd = mailboxcmd;
-	imapc_command_send(conn, cmd);
-}
-
-void imapc_connection_cmdf(struct imapc_connection *conn, bool mailboxcmd,
-			   imapc_command_callback_t *callback, void *context,
-			   const char *cmd_fmt, ...)
-{
-	va_list args;
-
-	va_start(args, cmd_fmt);
-	imapc_connection_cmdvf(conn, mailboxcmd, callback, context,
-			       cmd_fmt, args);
-	va_end(args);
-}
-
-void imapc_connection_cmdvf(struct imapc_connection *conn, bool mailboxcmd,
-			   imapc_command_callback_t *callback, void *context,
-			   const char *cmd_fmt, va_list args)
-{
-	struct imapc_command *cmd;
-	unsigned int i;
-
-	cmd = imapc_command_begin(callback, context);
-	cmd->mailboxcmd = mailboxcmd;
-	cmd->data = str_new(cmd->pool, 128);
-	str_printfa(cmd->data, "%u ", cmd->tag);
-
-	for (i = 0; cmd_fmt[i] != '\0'; i++) {
-		if (cmd_fmt[i] != '%') {
-			str_append_c(cmd->data, cmd_fmt[i]);
-			continue;
-		}
-
-		switch (cmd_fmt[++i]) {
-		case '\0':
-			i_unreached();
-		case 'u': {
-			unsigned int arg = va_arg(args, unsigned int);
-
-			str_printfa(cmd->data, "%u", arg);
-			break;
-		}
-		case 'p': {
-			struct istream *input = va_arg(args, struct istream *);
-			struct imapc_command_stream *s;
-			uoff_t size;
-
-			if (!array_is_created(&cmd->streams))
-				p_array_init(&cmd->streams, cmd->pool, 2);
-			if (i_stream_get_size(input, TRUE, &size) < 0)
-				size = 0;
-			str_printfa(cmd->data, "{%"PRIuUOFF_T"}\r\n", size);
-			s = array_append_space(&cmd->streams);
-			s->pos = str_len(cmd->data);
-			s->size = size;
-			s->input = input;
-			i_stream_ref(input);
-			break;
-		}
-		case 's': {
-			const char *arg = va_arg(args, const char *);
-
-			if (!need_literal(arg))
-				imap_dquote_append(cmd->data, arg);
-			else if ((conn->capabilities &
-				  IMAPC_CAPABILITY_LITERALPLUS) != 0) {
-				str_printfa(cmd->data, "{%"PRIuSIZE_T"+}\r\n%s",
-					    strlen(arg), arg);
-			} else {
-				str_printfa(cmd->data, "{%"PRIuSIZE_T"}\r\n%s",
-					    strlen(arg), arg);
-			}
-			break;
-		}
-		case '1': {
-			/* %1s - no quoting */
-			const char *arg = va_arg(args, const char *);
-
-			i_assert(cmd_fmt[++i] == 's');
-			str_append(cmd->data, arg);
-			break;
-		}
-		}
-	}
-	str_append(cmd->data, "\r\n");
-
-	imapc_command_send(conn, cmd);
-}
-
-enum imapc_connection_state
-imapc_connection_get_state(struct imapc_connection *conn)
-{
-	return conn->state;
-}
-
-enum imapc_capability
-imapc_connection_get_capabilities(struct imapc_connection *conn)
-{
-	return conn->capabilities;
-}
-
-void imapc_connection_select(struct imapc_client_mailbox *box,
-			     const char *name, bool examine,
-			     imapc_command_callback_t *callback, void *context)
-{
-	struct imapc_connection *conn = box->conn;
-
-	i_assert(conn->selecting_box == NULL);
-
-	if (conn->selected_box != NULL &&
-	    (conn->capabilities & IMAPC_CAPABILITY_QRESYNC) != 0) {
-		/* server will send a [CLOSED] once selected mailbox is
-		   closed */
-		conn->selecting_box = box;
-	} else {
-		/* we'll have to assume that all the future untagged messages
-		   are for the mailbox we're selecting */
-		conn->selected_box = box;
-	}
-
-	imapc_connection_cmdf(conn, FALSE, callback, context,
-			      examine ? "EXAMINE %s" : "SELECT %s", name);
-}
-
-void imapc_connection_unselect(struct imapc_client_mailbox *box)
-{
-	struct imapc_connection *conn = box->conn;
-	struct imapc_command *const *cmdp, *cmd;
-	struct imapc_command_reply reply;
-	unsigned int i;
-
-	/* mailbox is being closed. if there are any pending commands, we must
-	   finish them immediately so callbacks don't access any freed
-	   contexts */
-	memset(&reply, 0, sizeof(reply));
-	reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
-	reply.text_without_resp = reply.text_full = "Closing mailbox";
-
-	imapc_connection_send_idle_done(conn);
-
-	array_foreach(&conn->cmd_wait_list, cmdp) {
-		if ((*cmdp)->callback != NULL && (*cmdp)->mailboxcmd) {
-			(*cmdp)->callback(&reply, (*cmdp)->context);
-			(*cmdp)->callback = NULL;
-		}
-	}
-	for (i = 0; i < array_count(&conn->cmd_send_queue); ) {
-		cmdp = array_idx(&conn->cmd_send_queue, i);
-		cmd = *cmdp;
-		if (!cmd->mailboxcmd)
-			i++;
-		else {
-			array_delete(&conn->cmd_send_queue, i, 1);
-			if (cmd->callback != NULL)
-				cmd->callback(&reply, cmd->context);
-			imapc_command_free(cmd);
-		}
-	}
-
-	if (conn->selected_box == NULL && conn->selecting_box == NULL) {
-		i_assert(conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED);
-	} else {
-		i_assert(conn->selected_box == box ||
-			 conn->selecting_box == box);
-
-		conn->selected_box = NULL;
-		conn->selecting_box = NULL;
-	}
-}
-
-struct imapc_client_mailbox *
-imapc_connection_get_mailbox(struct imapc_connection *conn)
-{
-	if (conn->selecting_box != NULL)
-		return conn->selecting_box;
-	return conn->selected_box;
-}
-
-static void
-imapc_connection_idle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
-			       void *context)
-{
-	struct imapc_connection *conn = context;
-
-	conn->idling = FALSE;
-	conn->idle_plus_waiting = FALSE;
-	conn->idle_stopping = FALSE;
-}
-
-void imapc_connection_idle(struct imapc_connection *conn)
-{
-	struct imapc_command *cmd;
-
-	if (array_count(&conn->cmd_send_queue) != 0 ||
-	    array_count(&conn->cmd_wait_list) != 0 ||
-	    conn->idling || conn->idle_plus_waiting ||
-	    (conn->capabilities & IMAPC_CAPABILITY_IDLE) == 0)
-		return;
-
-	cmd = imapc_connection_cmd_build("IDLE", imapc_connection_idle_callback,
-					 conn);
-	cmd->idle = TRUE;
-	imapc_command_send(conn, cmd);
-}
--- a/src/lib-storage/index/imapc/imapc-connection.h	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#ifndef IMAPC_CONNECTION_H
-#define IMAPC_CONNECTION_H
-
-#include "imapc-client.h"
-
-struct imapc_client;
-struct imapc_connection;
-
-enum imapc_connection_state {
-	/* No connection */
-	IMAPC_CONNECTION_STATE_DISCONNECTED = 0,
-	/* Trying to connect */
-	IMAPC_CONNECTION_STATE_CONNECTING,
-	/* Connected, trying to authenticate */
-	IMAPC_CONNECTION_STATE_AUTHENTICATING,
-	/* Authenticated, ready to accept commands */
-	IMAPC_CONNECTION_STATE_DONE
-};
-
-struct imapc_connection *
-imapc_connection_init(struct imapc_client *client);
-void imapc_connection_deinit(struct imapc_connection **conn);
-
-void imapc_connection_connect(struct imapc_connection *conn);
-void imapc_connection_disconnect(struct imapc_connection *conn);
-void imapc_connection_ioloop_changed(struct imapc_connection *conn);
-void imapc_connection_input_pending(struct imapc_connection *conn);
-
-void imapc_connection_cmd(struct imapc_connection *conn, bool mailboxcmd,
-			  const char *cmdline,
-			  imapc_command_callback_t *callback, void *context);
-void imapc_connection_cmdf(struct imapc_connection *conn, bool mailboxcmd,
-			   imapc_command_callback_t *callback, void *context,
-			   const char *cmd_fmt, ...) ATTR_FORMAT(5, 6);
-void imapc_connection_cmdvf(struct imapc_connection *conn, bool mailboxcmd,
-			    imapc_command_callback_t *callback, void *context,
-			    const char *cmd_fmt, va_list args)
-	ATTR_FORMAT(5, 0);
-void imapc_connection_select(struct imapc_client_mailbox *box,
-			     const char *name, bool examine,
-			     imapc_command_callback_t *callback, void *context);
-void imapc_connection_unselect(struct imapc_client_mailbox *box);
-
-enum imapc_connection_state
-imapc_connection_get_state(struct imapc_connection *conn);
-enum imapc_capability
-imapc_connection_get_capabilities(struct imapc_connection *conn);
-
-struct imapc_client_mailbox *
-imapc_connection_get_mailbox(struct imapc_connection *conn);
-
-void imapc_connection_idle(struct imapc_connection *conn);
-
-#endif
--- a/src/lib-storage/index/imapc/imapc-msgmap.c	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "imapc-msgmap.h"
-
-struct imapc_msgmap {
-	ARRAY_TYPE(uint32_t) uids;
-	uint32_t uid_next;
-};
-
-struct imapc_msgmap *imapc_msgmap_init(void)
-{
-	struct imapc_msgmap *msgmap;
-
-	msgmap = i_new(struct imapc_msgmap, 1);
-	i_array_init(&msgmap->uids, 128);
-	msgmap->uid_next = 1;
-	return msgmap;
-}
-
-void imapc_msgmap_deinit(struct imapc_msgmap **_msgmap)
-{
-	struct imapc_msgmap *msgmap = *_msgmap;
-
-	*_msgmap = NULL;
-
-	array_free(&msgmap->uids);
-	i_free(msgmap);
-}
-
-uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap)
-{
-	return array_count(&msgmap->uids);
-}
-
-uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap)
-{
-	return msgmap->uid_next;
-}
-
-uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq)
-{
-	const uint32_t *uidp;
-
-	uidp = array_idx(&msgmap->uids, rseq-1);
-	return *uidp;
-}
-
-static int uint32_cmp(const uint32_t *p1, const uint32_t *p2)
-{
-	return *p1 < *p2 ? -1 :
-		(*p1 > *p2 ? 1 : 0);
-}
-
-bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap,
-			      uint32_t uid, uint32_t *rseq_r)
-{
-	const uint32_t *p, *first;
-
-	p = array_bsearch(&msgmap->uids, &uid, uint32_cmp);
-	if (p == NULL) {
-		*rseq_r = 0;
-		return FALSE;
-	}
-
-	first = array_idx(&msgmap->uids, 0);
-	*rseq_r = (p - first) + 1;
-	return TRUE;
-}
-
-void imapc_msgmap_append(struct imapc_msgmap *msgmap,
-			 uint32_t rseq, uint32_t uid)
-{
-	i_assert(rseq == imapc_msgmap_count(msgmap) + 1);
-	i_assert(uid >= msgmap->uid_next);
-
-	msgmap->uid_next = uid + 1;
-	array_append(&msgmap->uids, &uid, 1);
-}
-
-void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq)
-{
-	i_assert(rseq > 0);
-	i_assert(rseq <= imapc_msgmap_count(msgmap));
-
-	array_delete(&msgmap->uids, rseq-1, 1);
-}
--- a/src/lib-storage/index/imapc/imapc-msgmap.h	Sun Sep 25 22:32:04 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#ifndef IMAPC_MSGMAP_H
-#define IMAPC_MSGMAP_H
-
-struct imapc_msgmap *imapc_msgmap_init(void);
-void imapc_msgmap_deinit(struct imapc_msgmap **msgmap);
-
-uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap);
-uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap);
-uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq);
-bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap,
-			      uint32_t uid, uint32_t *rseq_r);
-
-void imapc_msgmap_append(struct imapc_msgmap *msgmap,
-			 uint32_t rseq, uint32_t uid);
-void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq);
-
-#endif