changeset 9219:97cdfeb57129 HEAD

Renamed headers to prevent collision if they were flattened on an install.
author Mark Washenberger
date Tue, 05 May 2009 11:57:04 -0400
parents 4a42f694b762
children 3cac418eeab5
files src/auth/Makefile.am src/auth/auth-cache.c src/auth/auth-client-connection.c src/auth/auth-common.h src/auth/auth-master-connection.c src/auth/auth-request-handler.c src/auth/auth-request.c src/auth/auth-stream.c src/auth/auth-worker-client.c src/auth/auth-worker-server.c src/auth/auth.c src/auth/common.h src/auth/db-checkpassword.c src/auth/db-ldap.c src/auth/db-passwd-file.c src/auth/db-sql.c src/auth/main.c src/auth/mech-anonymous.c src/auth/mech-apop.c src/auth/mech-cram-md5.c src/auth/mech-digest-md5.c src/auth/mech-gssapi.c src/auth/mech-login.c src/auth/mech-ntlm.c src/auth/mech-otp-skey-common.c src/auth/mech-otp-skey-common.h src/auth/mech-otp.c src/auth/mech-plain-common.c src/auth/mech-plain-common.h src/auth/mech-plain.c src/auth/mech-rpa.c src/auth/mech-skey.c src/auth/mech-winbind.c src/auth/mech.c src/auth/otp-skey-common.c src/auth/otp-skey-common.h src/auth/passdb-blocking.c src/auth/passdb-bsdauth.c src/auth/passdb-cache.c src/auth/passdb-checkpassword.c src/auth/passdb-ldap.c src/auth/passdb-pam.c src/auth/passdb-passwd-file.c src/auth/passdb-passwd.c src/auth/passdb-shadow.c src/auth/passdb-sia.c src/auth/passdb-sql.c src/auth/passdb-vpopmail.c src/auth/passdb.c src/auth/plain-common.c src/auth/plain-common.h src/auth/userdb-blocking.c src/auth/userdb-checkpassword.c src/auth/userdb-ldap.c src/auth/userdb-nss.c src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-prefetch.c src/auth/userdb-sql.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.c src/imap/Makefile.am src/imap/client.c src/imap/client.h src/imap/cmd-append.c src/imap/cmd-cancelupdate.c src/imap/cmd-capability.c src/imap/cmd-check.c src/imap/cmd-close.c src/imap/cmd-copy.c src/imap/cmd-create.c src/imap/cmd-delete.c src/imap/cmd-enable.c src/imap/cmd-examine.c src/imap/cmd-expunge.c src/imap/cmd-fetch.c src/imap/cmd-id.c src/imap/cmd-idle.c src/imap/cmd-list.c src/imap/cmd-logout.c src/imap/cmd-lsub.c src/imap/cmd-namespace.c src/imap/cmd-noop.c src/imap/cmd-rename.c src/imap/cmd-search.c src/imap/cmd-select.c src/imap/cmd-sort.c src/imap/cmd-status.c src/imap/cmd-store.c src/imap/cmd-subscribe.c src/imap/cmd-thread.c src/imap/cmd-uid.c src/imap/cmd-unselect.c src/imap/cmd-unsubscribe.c src/imap/cmd-x-cancel.c src/imap/commands-util.c src/imap/commands-util.h src/imap/commands.c src/imap/commands.h src/imap/common.h src/imap/imap-client.c src/imap/imap-client.h src/imap/imap-commands-util.c src/imap/imap-commands-util.h src/imap/imap-commands.c src/imap/imap-commands.h src/imap/imap-common.h src/imap/imap-expunge.c src/imap/imap-fetch-body.c src/imap/imap-fetch.c src/imap/imap-search-args.c src/imap/imap-search.c src/imap/imap-status.c src/imap/imap-sync.c src/imap/mail-storage-callbacks.c src/imap/main.c src/plugins/imap-acl/imap-acl-plugin.c src/plugins/imap-quota/imap-quota-plugin.c src/pop3-login/client-authenticate.c src/pop3/Makefile.am src/pop3/capability.h src/pop3/client.c src/pop3/client.h src/pop3/commands.c src/pop3/commands.h src/pop3/common.h src/pop3/main.c src/pop3/pop3-capability.h src/pop3/pop3-client.c src/pop3/pop3-client.h src/pop3/pop3-commands.c src/pop3/pop3-commands.h src/pop3/pop3-common.h
diffstat 134 files changed, 3625 insertions(+), 3625 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/Makefile.am	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/Makefile.am	Tue May 05 11:57:04 2009 -0400
@@ -56,6 +56,8 @@
 	auth-cache.c \
 	auth-client-connection.c \
 	auth-master-connection.c \
+	mech-otp-skey-common.c \
+	mech-plain-common.c \
 	auth-request.c \
 	auth-request-handler.c \
 	auth-settings.c \
@@ -79,8 +81,6 @@
 	mech-rpa.c \
 	mech-apop.c \
 	mech-winbind.c \
-	otp-skey-common.c \
-	plain-common.c \
 	passdb.c \
 	passdb-blocking.c \
 	passdb-bsdauth.c \
@@ -109,8 +109,11 @@
 	auth.h \
 	auth-cache.h \
 	auth-client-connection.h \
+	auth-common.h \
 	auth-master-interface.h \
 	auth-master-connection.h \
+	mech-otp-skey-common.h \
+	mech-plain-common.h \
 	auth-request.h \
 	auth-request-handler.h \
 	auth-settings.h \
@@ -120,12 +123,9 @@
 	db-ldap.h \
 	db-sql.h \
 	db-passwd-file.h \
-	common.h \
 	db-checkpassword.h \
 	mech.h \
 	mycrypt.h \
-	otp-skey-common.h \
-	plain-common.h \
 	passdb.h \
 	passdb-blocking.h \
 	passdb-cache.h \
--- a/src/auth/auth-cache.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-cache.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "lib-signals.h"
 #include "hash.h"
 #include "str.h"
--- a/src/auth/auth-client-connection.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-client-connection.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "array.h"
 #include "ioloop.h"
 #include "istream.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-common.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,11 @@
+#ifndef AUTH_COMMON_H
+#define AUTH_COMMON_H
+
+#include "lib.h"
+#include "auth.h"
+
+extern struct master_service *service;
+extern bool worker, shutdown_request;
+extern time_t process_start_time;
+
+#endif
--- a/src/auth/auth-master-connection.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-master-connection.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "array.h"
 #include "hash.h"
 #include "str.h"
--- a/src/auth/auth-request-handler.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-request-handler.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "ioloop.h"
 #include "array.h"
 #include "aqueue.h"
--- a/src/auth/auth-request.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-request.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "ioloop.h"
 #include "buffer.h"
 #include "hash.h"
--- a/src/auth/auth-stream.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-stream.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "str.h"
 #include "ostream.h"
 #include "auth-request.h"
--- a/src/auth/auth-worker-client.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-worker-client.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "base64.h"
 #include "ioloop.h"
 #include "network.h"
--- a/src/auth/auth-worker-server.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth-worker-server.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "ioloop.h"
 #include "array.h"
 #include "aqueue.h"
--- a/src/auth/auth.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/auth.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "network.h"
 #include "array.h"
 #include "str.h"
--- a/src/auth/common.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-#ifndef COMMON_H
-#define COMMON_H
-
-#include "lib.h"
-#include "auth.h"
-
-extern struct master_service *service;
-extern bool worker, shutdown_request;
-extern time_t process_start_time;
-
-#endif
--- a/src/auth/db-checkpassword.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/db-checkpassword.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 
 #if defined(PASSDB_CHECKPASSWORD) || defined(USERDB_CHECKPASSWORD)
 
--- a/src/auth/db-ldap.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/db-ldap.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 
 #if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)
 
--- a/src/auth/db-passwd-file.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/db-passwd-file.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 
 #if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE)
 
--- a/src/auth/db-sql.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/db-sql.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 
 #if defined(PASSDB_SQL) || defined(USERDB_SQL)
 
--- a/src/auth/main.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/main.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "array.h"
 #include "ioloop.h"
 #include "network.h"
--- a/src/auth/mech-anonymous.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-anonymous.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "mech.h"
 
 static void
--- a/src/auth/mech-apop.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-apop.c	Tue May 05 11:57:04 2009 -0400
@@ -6,7 +6,7 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "mech.h"
 #include "passdb.h"
 #include "md5.h"
--- a/src/auth/mech-cram-md5.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-cram-md5.c	Tue May 05 11:57:04 2009 -0400
@@ -3,7 +3,7 @@
 /* CRAM-MD5 SASL authentication, see RFC-2195
    Joshua Goodall <joshua@roughtrade.net> */
 
-#include "common.h"
+#include "auth-common.h"
 #include "ioloop.h"
 #include "buffer.h"
 #include "hex-binary.h"
--- a/src/auth/mech-digest-md5.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-digest-md5.c	Tue May 05 11:57:04 2009 -0400
@@ -2,7 +2,7 @@
 
 /* Digest-MD5 SASL authentication, see RFC-2831 */
 
-#include "common.h"
+#include "auth-common.h"
 #include "base64.h"
 #include "buffer.h"
 #include "hex-binary.h"
--- a/src/auth/mech-gssapi.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-gssapi.c	Tue May 05 11:57:04 2009 -0400
@@ -12,7 +12,7 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "env-util.h"
 #include "str.h"
 #include "str-sanitize.h"
--- a/src/auth/mech-login.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-login.c	Tue May 05 11:57:04 2009 -0400
@@ -6,11 +6,11 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "mech.h"
 #include "passdb.h"
 #include "safe-memset.h"
-#include "plain-common.h"
+#include "mech-plain-common.h"
 
 
 static void
--- a/src/auth/mech-ntlm.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-ntlm.c	Tue May 05 11:57:04 2009 -0400
@@ -6,7 +6,7 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "mech.h"
 #include "passdb.h"
 #include "str.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-otp-skey-common.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,68 @@
+/*
+ * Common code for OTP and SKEY authentication mechanisms.
+ *
+ * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "auth-common.h"
+#include "hash.h"
+#include "mech.h"
+
+#include "otp.h"
+#include "mech-otp-skey-common.h"
+
+static struct hash_table *otp_lock_table;
+
+void otp_lock_init(void)
+{
+	if (otp_lock_table != NULL)
+		return;
+
+	otp_lock_table = hash_table_create(system_pool, system_pool,
+					   128, strcase_hash,
+					   (hash_cmp_callback_t *)strcasecmp);
+}
+
+int otp_try_lock(struct auth_request *auth_request)
+{
+	if (hash_table_lookup(otp_lock_table, auth_request->user))
+		return FALSE;
+
+	hash_table_insert(otp_lock_table, auth_request->user, auth_request);
+
+	return TRUE;
+}
+
+void otp_unlock(struct auth_request *auth_request)
+{
+	struct otp_auth_request *request =
+		(struct otp_auth_request *)auth_request;
+
+	if (!request->lock)
+		return;
+
+	hash_table_remove(otp_lock_table, auth_request->user);
+	request->lock = FALSE;
+}
+
+void otp_set_credentials_callback(bool success,
+				  struct auth_request *auth_request)
+{
+	if (success)
+		auth_request_success(auth_request, NULL, 0);
+	else {
+		auth_request_internal_failure(auth_request);
+		otp_unlock(auth_request);
+	}
+
+	otp_unlock(auth_request);
+}
+
+void mech_otp_skey_auth_free(struct auth_request *auth_request)
+{
+	otp_unlock(auth_request);
+
+	pool_unref(&auth_request->pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-otp-skey-common.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,22 @@
+#ifndef MECH_OTP_SKEY_COMMON_H
+#define MECH_OTP_SKEY_COMMON_H
+
+struct otp_auth_request {
+	struct auth_request auth_request;
+
+	pool_t pool;
+
+	int lock;
+
+	struct otp_state state;
+};
+
+void otp_lock_init(void);
+int otp_try_lock(struct auth_request *auth_request);
+void otp_unlock(struct auth_request *auth_request);
+
+void otp_set_credentials_callback(bool success,
+				  struct auth_request *auth_request);
+void mech_otp_skey_auth_free(struct auth_request *auth_request);
+
+#endif
--- a/src/auth/mech-otp.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-otp.c	Tue May 05 11:57:04 2009 -0400
@@ -6,14 +6,14 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "safe-memset.h"
 #include "hash.h"
 #include "mech.h"
 #include "passdb.h"
 #include "hex-binary.h"
 #include "otp.h"
-#include "otp-skey-common.h"
+#include "mech-otp-skey-common.h"
 
 static void 
 otp_send_challenge(struct auth_request *auth_request,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-plain-common.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,20 @@
+#include "auth-common.h"
+#include "mech.h"
+#include "passdb.h"
+#include "mech-plain-common.h"
+
+void plain_verify_callback(enum passdb_result result,
+			   struct auth_request *request)
+{
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		auth_request_success(request, NULL, 0);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		auth_request_internal_failure(request);
+		break;
+	default:
+		auth_request_fail(request);
+		break;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-plain-common.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,7 @@
+#ifndef MECH_PLAIN_COMMON_H
+#define MECH_PLAIN_COMMON_H
+
+void plain_verify_callback(enum passdb_result result,
+			   struct auth_request *request);
+
+#endif
--- a/src/auth/mech-plain.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-plain.c	Tue May 05 11:57:04 2009 -0400
@@ -1,10 +1,10 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "safe-memset.h"
 #include "mech.h"
 #include "passdb.h"
-#include "plain-common.h"
+#include "mech-plain-common.h"
 
 static void
 mech_plain_auth_continue(struct auth_request *request,
--- a/src/auth/mech-rpa.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-rpa.c	Tue May 05 11:57:04 2009 -0400
@@ -6,7 +6,7 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "mech.h"
 #include "passdb.h"
 #include "str.h"
--- a/src/auth/mech-skey.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-skey.c	Tue May 05 11:57:04 2009 -0400
@@ -6,14 +6,14 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "safe-memset.h"
 #include "hash.h"
 #include "mech.h"
 #include "passdb.h"
 #include "hex-binary.h"
 #include "otp.h"
-#include "otp-skey-common.h"
+#include "mech-otp-skey-common.h"
 
 static void 
 skey_send_challenge(struct auth_request *auth_request,
--- a/src/auth/mech-winbind.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech-winbind.c	Tue May 05 11:57:04 2009 -0400
@@ -7,7 +7,7 @@
  * This software is released under the MIT license.
  */
 
-#include "common.h"
+#include "auth-common.h"
 #include "lib-signals.h"
 #include "mech.h"
 #include "str.h"
--- a/src/auth/mech.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/mech.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "ioloop.h"
 #include "mech.h"
 #include "str.h"
--- a/src/auth/otp-skey-common.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Common code for OTP and SKEY authentication mechanisms.
- *
- * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru>
- *
- * This software is released under the MIT license.
- */
-
-#include "common.h"
-#include "hash.h"
-#include "mech.h"
-
-#include "otp.h"
-#include "otp-skey-common.h"
-
-static struct hash_table *otp_lock_table;
-
-void otp_lock_init(void)
-{
-	if (otp_lock_table != NULL)
-		return;
-
-	otp_lock_table = hash_table_create(system_pool, system_pool,
-					   128, strcase_hash,
-					   (hash_cmp_callback_t *)strcasecmp);
-}
-
-int otp_try_lock(struct auth_request *auth_request)
-{
-	if (hash_table_lookup(otp_lock_table, auth_request->user))
-		return FALSE;
-
-	hash_table_insert(otp_lock_table, auth_request->user, auth_request);
-
-	return TRUE;
-}
-
-void otp_unlock(struct auth_request *auth_request)
-{
-	struct otp_auth_request *request =
-		(struct otp_auth_request *)auth_request;
-
-	if (!request->lock)
-		return;
-
-	hash_table_remove(otp_lock_table, auth_request->user);
-	request->lock = FALSE;
-}
-
-void otp_set_credentials_callback(bool success,
-				  struct auth_request *auth_request)
-{
-	if (success)
-		auth_request_success(auth_request, NULL, 0);
-	else {
-		auth_request_internal_failure(auth_request);
-		otp_unlock(auth_request);
-	}
-
-	otp_unlock(auth_request);
-}
-
-void mech_otp_skey_auth_free(struct auth_request *auth_request)
-{
-	otp_unlock(auth_request);
-
-	pool_unref(&auth_request->pool);
-}
--- a/src/auth/otp-skey-common.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#ifndef OTP_SKEY_COMMON_H
-#define OTP_SKEY_COMMON_H
-
-struct otp_auth_request {
-	struct auth_request auth_request;
-
-	pool_t pool;
-
-	int lock;
-
-	struct otp_state state;
-};
-
-void otp_lock_init(void);
-int otp_try_lock(struct auth_request *auth_request);
-void otp_unlock(struct auth_request *auth_request);
-
-void otp_set_credentials_callback(bool success,
-				  struct auth_request *auth_request);
-void mech_otp_skey_auth_free(struct auth_request *auth_request);
-
-#endif
--- a/src/auth/passdb-blocking.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-blocking.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "str.h"
 #include "auth-worker-server.h"
 #include "password-scheme.h"
--- a/src/auth/passdb-bsdauth.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-bsdauth.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_BSDAUTH
--- a/src/auth/passdb-cache.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-cache.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "password-scheme.h"
 #include "passdb.h"
 #include "passdb-cache.h"
--- a/src/auth/passdb-checkpassword.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-checkpassword.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_CHECKPASSWORD 
--- a/src/auth/passdb-ldap.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-ldap.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #if defined(PASSDB_LDAP) && (defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD))
--- a/src/auth/passdb-pam.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-pam.c	Tue May 05 11:57:04 2009 -0400
@@ -7,7 +7,7 @@
    modified versions are marked as such.  There's absolutely no warranty.
 */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_PAM
--- a/src/auth/passdb-passwd-file.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-passwd-file.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_PASSWD_FILE
--- a/src/auth/passdb-passwd.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-passwd.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_PASSWD
--- a/src/auth/passdb-shadow.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-shadow.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_SHADOW
--- a/src/auth/passdb-sia.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-sia.c	Tue May 05 11:57:04 2009 -0400
@@ -2,7 +2,7 @@
 
 /* Tru64 SIA support */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_SIA
--- a/src/auth/passdb-sql.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-sql.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_SQL
--- a/src/auth/passdb-vpopmail.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb-vpopmail.c	Tue May 05 11:57:04 2009 -0400
@@ -2,7 +2,7 @@
 
 /* Thanks to Courier-IMAP for showing how the vpopmail API should be used */
 
-#include "common.h"
+#include "auth-common.h"
 #include "passdb.h"
 
 #ifdef PASSDB_VPOPMAIL
--- a/src/auth/passdb.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/passdb.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "array.h"
 #include "password-scheme.h"
 #include "auth-worker-server.h"
--- a/src/auth/plain-common.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#include "common.h"
-#include "mech.h"
-#include "passdb.h"
-#include "plain-common.h"
-
-void plain_verify_callback(enum passdb_result result,
-			   struct auth_request *request)
-{
-	switch (result) {
-	case PASSDB_RESULT_OK:
-		auth_request_success(request, NULL, 0);
-		break;
-	case PASSDB_RESULT_INTERNAL_FAILURE:
-		auth_request_internal_failure(request);
-		break;
-	default:
-		auth_request_fail(request);
-		break;
-	}
-}
--- a/src/auth/plain-common.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#ifndef PLAIN_COMMON_H
-#define PLAIN_COMMON_H
-
-void plain_verify_callback(enum passdb_result result,
-			   struct auth_request *request);
-
-#endif
--- a/src/auth/userdb-blocking.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-blocking.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "str.h"
 #include "auth-worker-server.h"
 #include "userdb.h"
--- a/src/auth/userdb-checkpassword.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-checkpassword.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_CHECKPASSWORD
--- a/src/auth/userdb-ldap.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-ldap.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #if defined(USERDB_LDAP) && (defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD))
--- a/src/auth/userdb-nss.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-nss.c	Tue May 05 11:57:04 2009 -0400
@@ -2,7 +2,7 @@
 
 /* Currently supports only GLIBC-compatible NSS modules */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_NSS
--- a/src/auth/userdb-passwd-file.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-passwd-file.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_PASSWD_FILE
--- a/src/auth/userdb-passwd.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-passwd.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_PASSWD
--- a/src/auth/userdb-prefetch.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-prefetch.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_PREFETCH
--- a/src/auth/userdb-sql.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-sql.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #ifdef USERDB_SQL
--- a/src/auth/userdb-static.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-static.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 
 #include "array.h"
 #include "str.h"
--- a/src/auth/userdb-vpopmail.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb-vpopmail.c	Tue May 05 11:57:04 2009 -0400
@@ -2,7 +2,7 @@
 
 /* Thanks to Courier-IMAP for showing how the vpopmail API should be used */
 
-#include "common.h"
+#include "auth-common.h"
 #include "userdb.h"
 
 #if defined(PASSDB_VPOPMAIL) || defined(USERDB_VPOPMAIL)
--- a/src/auth/userdb.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/auth/userdb.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "auth-common.h"
 #include "array.h"
 #include "auth-worker-server.h"
 #include "userdb.h"
--- a/src/imap/Makefile.am	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/Makefile.am	Tue May 05 11:57:04 2009 -0400
@@ -62,9 +62,9 @@
 
 imap_SOURCES = \
 	$(cmds) \
-	client.c \
-	commands.c \
-	commands-util.c \
+	imap-client.c \
+	imap-commands.c \
+	imap-commands-util.c \
 	imap-expunge.c \
 	imap-fetch.c \
 	imap-fetch-body.c \
@@ -78,10 +78,10 @@
 
 
 headers = \
-	client.h \
-	commands.h \
-	commands-util.h \
-	common.h \
+	imap-client.h \
+	imap-commands.h \
+	imap-commands-util.h \
+	imap-common.h \
 	imap-expunge.h \
 	imap-fetch.h \
 	imap-search.h \
--- a/src/imap/client.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,938 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "ioloop.h"
-#include "llist.h"
-#include "str.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "var-expand.h"
-#include "master-service.h"
-#include "imap-resp-code.h"
-#include "imap-util.h"
-#include "mail-namespace.h"
-#include "commands.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-extern struct mail_storage_callbacks mail_storage_callbacks;
-struct imap_module_register imap_module_register = { 0 };
-
-static struct client *my_client; /* we don't need more than one currently */
-
-static void client_idle_timeout(struct client *client)
-{
-	if (client->output_lock == NULL)
-		client_send_line(client, "* BYE Disconnected for inactivity.");
-	client_destroy(client, "Disconnected for inactivity");
-}
-
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
-			     const struct imap_settings *set)
-{
-	struct client *client;
-	struct mail_namespace *ns;
-
-	/* always use nonblocking I/O */
-	net_set_nonblock(fd_in, TRUE);
-	net_set_nonblock(fd_out, TRUE);
-
-	client = i_new(struct client, 1);
-	client->set = set;
-	client->fd_in = fd_in;
-	client->fd_out = fd_out;
-	client->input = i_stream_create_fd(fd_in,
-					   set->imap_max_line_length, FALSE);
-	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
-
-	o_stream_set_flush_callback(client->output, client_output, client);
-
-	client->io = io_add(fd_in, IO_READ, client_input, client);
-        client->last_input = ioloop_time;
-	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
-				      client_idle_timeout, client);
-
-	client->command_pool =
-		pool_alloconly_create(MEMPOOL_GROWING"client command", 1024*12);
-	client->user = user;
-
-	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
-		mail_storage_set_callbacks(ns->storage,
-					   &mail_storage_callbacks, client);
-	}
-
-	client->capability_string =
-		str_new(default_pool, sizeof(CAPABILITY_STRING)+32);
-	str_append(client->capability_string, *set->imap_capability != '\0' ?
-		   set->imap_capability : CAPABILITY_STRING);
-
-	i_assert(my_client == NULL);
-	my_client = client;
-
-	if (hook_client_created != NULL)
-		hook_client_created(&client);
-	return client;
-}
-
-void client_command_cancel(struct client_command_context **_cmd)
-{
-	struct client_command_context *cmd = *_cmd;
-	bool cmd_ret;
-
-	switch (cmd->state) {
-	case CLIENT_COMMAND_STATE_WAIT_INPUT:
-		/* a bit kludgy check: cancel command only if it has context
-		   set. currently only append command matches this check. all
-		   other commands haven't even started the processing yet. */
-		if (cmd->context == NULL)
-			break;
-		/* fall through */
-	case CLIENT_COMMAND_STATE_WAIT_OUTPUT:
-		cmd->cancel = TRUE;
-		break;
-	case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY:
-	case CLIENT_COMMAND_STATE_WAIT_SYNC:
-		/* commands haven't started yet */
-		break;
-	case CLIENT_COMMAND_STATE_DONE:
-		i_unreached();
-	}
-
-	cmd_ret = !cmd->cancel || cmd->func == NULL ? TRUE : cmd->func(cmd);
-	if (!cmd_ret && cmd->state != CLIENT_COMMAND_STATE_DONE) {
-		if (cmd->client->output->closed)
-			i_panic("command didn't cancel itself: %s", cmd->name);
-	} else {
-		client_command_free(_cmd);
-	}
-}
-
-static const char *client_stats(struct client *client)
-{
-	static struct var_expand_table static_tab[] = {
-		{ 'i', NULL, "input" },
-		{ 'o', NULL, "output" },
-		{ '\0', NULL, NULL }
-	};
-	struct var_expand_table *tab;
-	string_t *str;
-
-	tab = t_malloc(sizeof(static_tab));
-	memcpy(tab, static_tab, sizeof(static_tab));
-
-	tab[0].value = dec2str(client->input->v_offset);
-	tab[1].value = dec2str(client->output->offset);
-
-	str = t_str_new(128);
-	var_expand(str, client->set->imap_logout_format, tab);
-	return str_c(str);
-}
-
-static const char *client_get_disconnect_reason(struct client *client)
-{
-	errno = client->input->stream_errno != 0 ?
-		client->input->stream_errno :
-		client->output->stream_errno;
-	return errno == 0 || errno == EPIPE ? "Connection closed" :
-		t_strdup_printf("Connection closed: %m");
-}
-
-void client_destroy(struct client *client, const char *reason)
-{
-	struct client_command_context *cmd;
-
-	i_assert(!client->destroyed);
-	client->destroyed = TRUE;
-
-	if (!client->disconnected) {
-		client->disconnected = TRUE;
-		if (reason == NULL)
-			reason = client_get_disconnect_reason(client);
-		i_info("%s %s", reason, client_stats(client));
-	}
-
-	i_stream_close(client->input);
-	o_stream_close(client->output);
-
-	/* finish off all the queued commands. */
-	if (client->output_lock != NULL)
-		client_command_cancel(&client->output_lock);
-	while (client->command_queue != NULL) {
-		cmd = client->command_queue;
-		client_command_cancel(&cmd);
-	}
-	/* handle the input_lock command last. it might have been waiting on
-	   other queued commands (although we probably should just drop the
-	   command at that point since it hasn't started running. but this may
-	   change in future). */
-	if (client->input_lock != NULL)
-		client_command_cancel(&client->input_lock);
-
-	if (client->mailbox != NULL) {
-		client_search_updates_free(client);
-		mailbox_close(&client->mailbox);
-	}
-	mail_user_unref(&client->user);
-
-	if (client->free_parser != NULL)
-		imap_parser_destroy(&client->free_parser);
-	if (client->io != NULL)
-		io_remove(&client->io);
-	if (client->to_idle_output != NULL)
-		timeout_remove(&client->to_idle_output);
-	timeout_remove(&client->to_idle);
-
-	i_stream_destroy(&client->input);
-	o_stream_destroy(&client->output);
-
-	if (close(client->fd_in) < 0)
-		i_error("close(client in) failed: %m");
-	if (client->fd_in != client->fd_out) {
-		if (close(client->fd_out) < 0)
-			i_error("close(client out) failed: %m");
-	}
-
-	if (array_is_created(&client->search_saved_uidset))
-		array_free(&client->search_saved_uidset);
-	if (array_is_created(&client->search_updates))
-		array_free(&client->search_updates);
-	str_free(&client->capability_string);
-	pool_unref(&client->command_pool);
-	i_free(client);
-
-	/* quit the program */
-	my_client = NULL;
-	master_service_stop(service);
-}
-
-void client_disconnect(struct client *client, const char *reason)
-{
-	i_assert(reason != NULL);
-
-	if (client->disconnected)
-		return;
-
-	i_info("Disconnected: %s %s", reason, client_stats(client));
-	client->disconnected = TRUE;
-	(void)o_stream_flush(client->output);
-
-	i_stream_close(client->input);
-	o_stream_close(client->output);
-}
-
-void client_disconnect_with_error(struct client *client, const char *msg)
-{
-	client_send_line(client, t_strconcat("* BYE ", msg, NULL));
-	client_disconnect(client, msg);
-}
-
-int client_send_line(struct client *client, const char *data)
-{
-	struct const_iovec iov[2];
-
-	if (client->output->closed)
-		return -1;
-
-	iov[0].iov_base = data;
-	iov[0].iov_len = strlen(data);
-	iov[1].iov_base = "\r\n";
-	iov[1].iov_len = 2;
-
-	if (o_stream_sendv(client->output, iov, 2) < 0)
-		return -1;
-	client->last_output = ioloop_time;
-
-	if (o_stream_get_buffer_used_size(client->output) >=
-	    CLIENT_OUTPUT_OPTIMAL_SIZE) {
-		/* buffer full, try flushing */
-		return o_stream_flush(client->output);
-	}
-	return 1;
-}
-
-void client_send_tagline(struct client_command_context *cmd, const char *data)
-{
-	struct client *client = cmd->client;
-	const char *tag = cmd->tag;
-
-	if (client->output->closed || cmd->cancel)
-		return;
-
-	if (tag == NULL || *tag == '\0')
-		tag = "*";
-
-	(void)o_stream_send_str(client->output, tag);
-	(void)o_stream_send(client->output, " ", 1);
-	(void)o_stream_send_str(client->output, data);
-	(void)o_stream_send(client->output, "\r\n", 2);
-
-	client->last_output = ioloop_time;
-}
-
-void client_send_command_error(struct client_command_context *cmd,
-			       const char *msg)
-{
-	struct client *client = cmd->client;
-	const char *error, *cmd_name;
-	bool fatal;
-
-	if (msg == NULL) {
-		msg = imap_parser_get_error(cmd->parser, &fatal);
-		if (fatal) {
-			client_disconnect_with_error(client, msg);
-			return;
-		}
-	}
-
-	if (cmd->tag == NULL)
-		error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL);
-	else if (cmd->name == NULL)
-		error = t_strconcat("BAD Error in IMAP command: ", msg, NULL);
-	else {
-		cmd_name = t_str_ucase(cmd->name);
-		error = t_strconcat("BAD Error in IMAP command ",
-				    cmd_name, ": ", msg, NULL);
-	}
-
-	client_send_tagline(cmd, error);
-
-	if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
-		client_disconnect_with_error(client,
-			"Too many invalid IMAP commands.");
-	}
-
-	cmd->param_error = TRUE;
-	/* client_read_args() failures rely on this being set, so that the
-	   command processing is stopped even while command function returns
-	   FALSE. */
-	cmd->state = CLIENT_COMMAND_STATE_DONE;
-}
-
-bool client_read_args(struct client_command_context *cmd, unsigned int count,
-		      unsigned int flags, const struct imap_arg **args_r)
-{
-	string_t *str;
-	int ret;
-
-	i_assert(count <= INT_MAX);
-
-	ret = imap_parser_read_args(cmd->parser, count, flags, args_r);
-	if (ret >= (int)count) {
-		/* all parameters read successfully */
-		i_assert(cmd->client->input_lock == NULL ||
-			 cmd->client->input_lock == cmd);
-
-		str = t_str_new(256);
-		imap_write_args(str, *args_r);
-		cmd->args = p_strdup(cmd->pool, str_c(str));
-
-		cmd->client->input_lock = NULL;
-		return TRUE;
-	} else if (ret == -2) {
-		/* need more data */
-		if (cmd->client->input->closed) {
-			/* disconnected */
-			cmd->state = CLIENT_COMMAND_STATE_DONE;
-		}
-		return FALSE;
-	} else {
-		/* error, or missing arguments */
-		client_send_command_error(cmd, ret < 0 ? NULL :
-					  "Missing arguments");
-		return FALSE;
-	}
-}
-
-bool client_read_string_args(struct client_command_context *cmd,
-			     unsigned int count, ...)
-{
-	const struct imap_arg *imap_args;
-	va_list va;
-	const char *str;
-	unsigned int i;
-
-	if (!client_read_args(cmd, count, 0, &imap_args))
-		return FALSE;
-
-	va_start(va, count);
-	for (i = 0; i < count; i++) {
-		const char **ret = va_arg(va, const char **);
-
-		if (imap_args[i].type == IMAP_ARG_EOL) {
-			client_send_command_error(cmd, "Missing arguments.");
-			break;
-		}
-
-		str = imap_arg_string(&imap_args[i]);
-		if (str == NULL) {
-			client_send_command_error(cmd, "Invalid arguments.");
-			break;
-		}
-
-		if (ret != NULL)
-			*ret = str;
-	}
-	va_end(va);
-
-	return i == count;
-}
-
-static struct client_command_context *
-client_command_find_with_flags(struct client_command_context *new_cmd,
-			       enum command_flags flags)
-{
-	struct client_command_context *cmd;
-
-	cmd = new_cmd->client->command_queue;
-	for (; cmd != NULL; cmd = cmd->next) {
-		if (cmd != new_cmd && (cmd->cmd_flags & flags) != 0)
-			return cmd;
-	}
-	return NULL;
-}
-
-static bool client_command_check_ambiguity(struct client_command_context *cmd)
-{
-	enum command_flags flags;
-	bool broken_client = FALSE;
-
-	if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_MAILBOX) ==
-	    COMMAND_FLAG_BREAKS_MAILBOX) {
-		/* there must be no other command running that uses the
-		   selected mailbox */
-		flags = COMMAND_FLAG_USES_MAILBOX;
-	} else if ((cmd->cmd_flags & COMMAND_FLAG_USES_SEQS) != 0) {
-		/* no existing command must be breaking sequences */
-		flags = COMMAND_FLAG_BREAKS_SEQS;
-		broken_client = TRUE;
-	} else if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_SEQS) != 0) {
-		/* if existing command uses sequences, we'll have to block */
-		flags = COMMAND_FLAG_USES_SEQS;
-	} else {
-		return FALSE;
-	}
-
-	if (client_command_find_with_flags(cmd, flags) == NULL) {
-		if (cmd->client->syncing) {
-			/* don't do anything until syncing is finished */
-			return TRUE;
-		}
-		if (cmd->client->mailbox_change_lock != NULL &&
-		    cmd->client->mailbox_change_lock != cmd) {
-			/* don't do anything until mailbox is fully
-			   opened/closed */
-			return TRUE;
-		}
-		return FALSE;
-	}
-
-	if (broken_client) {
-		client_send_line(cmd->client,
-				 "* BAD ["IMAP_RESP_CODE_CLIENTBUG"] "
-				 "Command pipelining results in ambiguity.");
-	}
-
-	return TRUE;
-}
-
-static struct client_command_context *
-client_command_new(struct client *client)
-{
-	struct client_command_context *cmd;
-
-	cmd = p_new(client->command_pool, struct client_command_context, 1);
-	cmd->client = client;
-	cmd->pool = client->command_pool;
-	p_array_init(&cmd->module_contexts, cmd->pool, 5);
-
-	if (client->free_parser != NULL) {
-		cmd->parser = client->free_parser;
-		client->free_parser = NULL;
-	} else {
-		cmd->parser =
-			imap_parser_create(client->input, client->output,
-					   client->set->imap_max_line_length);
-	}
-
-	DLLIST_PREPEND(&client->command_queue, cmd);
-	client->command_queue_size++;
-
-	return cmd;
-}
-
-void client_command_free(struct client_command_context **_cmd)
-{
-	struct client_command_context *cmd = *_cmd;
-	struct client *client = cmd->client;
-
-	*_cmd = NULL;
-
-	/* reset input idle time because command output might have taken a
-	   long time and we don't want to disconnect client immediately then */
-	client->last_input = ioloop_time;
-	timeout_reset(client->to_idle);
-
-	if (cmd->cancel) {
-		cmd->cancel = FALSE;
-		client_send_tagline(cmd, "NO Command cancelled.");
-	}
-
-	if (!cmd->param_error)
-		client->bad_counter = 0;
-
-	if (client->input_lock == cmd)
-		client->input_lock = NULL;
-	if (client->output_lock == cmd)
-		client->output_lock = NULL;
-	if (client->mailbox_change_lock == cmd)
-		client->mailbox_change_lock = NULL;
-
-	if (client->free_parser != NULL)
-		imap_parser_destroy(&cmd->parser);
-	else {
-		imap_parser_reset(cmd->parser);
-		client->free_parser = cmd->parser;
-	}
-
-	client->command_queue_size--;
-	DLLIST_REMOVE(&client->command_queue, cmd);
-	cmd = NULL;
-
-	if (client->command_queue == NULL) {
-		/* no commands left in the queue, we can clear the pool */
-		p_clear(client->command_pool);
-		if (client->to_idle_output != NULL)
-			timeout_remove(&client->to_idle_output);
-	}
-}
-
-static void client_add_missing_io(struct client *client)
-{
-	if (client->io == NULL && !client->disconnected) {
-		client->io = io_add(client->fd_in,
-				    IO_READ, client_input, client);
-	}
-}
-
-void client_continue_pending_input(struct client **_client)
-{
-	struct client *client = *_client;
-	size_t size;
-
-	i_assert(!client->handling_input);
-
-	if (client->disconnected) {
-		if (!client->destroyed)
-			client_destroy(client, NULL);
-		*_client = NULL;
-		return;
-	}
-
-	if (client->input_lock != NULL) {
-		/* there's a command that has locked the input */
-		struct client_command_context *cmd = client->input_lock;
-
-		if (cmd->state != CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY)
-			return;
-
-		/* the command is waiting for existing ambiguity causing
-		   commands to finish. */
-		if (client_command_check_ambiguity(cmd))
-			return;
-		cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT;
-	}
-
-	client_add_missing_io(client);
-
-	/* if there's unread data in buffer, handle it. */
-	(void)i_stream_get_data(client->input, &size);
-	if (size > 0)
-		(void)client_handle_input(client);
-}
-
-/* Skip incoming data until newline is found,
-   returns TRUE if newline was found. */
-static bool client_skip_line(struct client *client)
-{
-	const unsigned char *data;
-	size_t i, data_size;
-
-	data = i_stream_get_data(client->input, &data_size);
-
-	for (i = 0; i < data_size; i++) {
-		if (data[i] == '\n') {
-			client->input_skip_line = FALSE;
-			i++;
-			break;
-		}
-	}
-
-	i_stream_skip(client->input, i);
-	return !client->input_skip_line;
-}
-
-static void client_idle_output_timeout(struct client *client)
-{
-	client_destroy(client,
-		       "Disconnected for inactivity in reading our output");
-}
-
-bool client_handle_unfinished_cmd(struct client_command_context *cmd)
-{
-	if (cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT) {
-		/* need more input */
-		return FALSE;
-	}
-	if (cmd->state != CLIENT_COMMAND_STATE_WAIT_OUTPUT) {
-		/* waiting for something */
-		if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) {
-			/* this is mainly for APPEND. */
-			client_add_missing_io(cmd->client);
-		}
-		return TRUE;
-	}
-
-	/* output is blocking, we can execute more commands */
-	o_stream_set_flush_pending(cmd->client->output, TRUE);
-	if (cmd->client->to_idle_output == NULL) {
-		/* disconnect sooner if client isn't reading our output */
-		cmd->client->to_idle_output =
-			timeout_add(CLIENT_OUTPUT_TIMEOUT_MSECS,
-				    client_idle_output_timeout, cmd->client);
-	}
-	return TRUE;
-}
-
-static bool client_command_input(struct client_command_context *cmd)
-{
-	struct client *client = cmd->client;
-	struct command *command;
-
-        if (cmd->func != NULL) {
-		/* command is being executed - continue it */
-		if (cmd->func(cmd) || cmd->state == CLIENT_COMMAND_STATE_DONE) {
-			/* command execution was finished */
-			client_command_free(&cmd);
-			client_add_missing_io(client);
-			return TRUE;
-		}
-
-		return client_handle_unfinished_cmd(cmd);
-	}
-
-	if (cmd->tag == NULL) {
-                cmd->tag = imap_parser_read_word(cmd->parser);
-		if (cmd->tag == NULL)
-			return FALSE; /* need more data */
-		cmd->tag = p_strdup(cmd->pool, cmd->tag);
-	}
-
-	if (cmd->name == NULL) {
-		cmd->name = imap_parser_read_word(cmd->parser);
-		if (cmd->name == NULL)
-			return FALSE; /* need more data */
-		cmd->name = p_strdup(cmd->pool, cmd->name);
-	}
-
-	client->input_skip_line = TRUE;
-
-	if (cmd->name == '\0') {
-		/* command not given - cmd_func is already NULL. */
-	} else if ((command = command_find(cmd->name)) != NULL) {
-		cmd->func = command->func;
-		cmd->cmd_flags = command->flags;
-		if (client_command_check_ambiguity(cmd)) {
-			/* do nothing until existing commands are finished */
-			i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT);
-			cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
-			io_remove(&client->io);
-			return FALSE;
-		}
-	}
-
-	if (cmd->func == NULL) {
-		/* unknown command */
-		client_send_command_error(cmd, "Unknown command.");
-		cmd->param_error = TRUE;
-		client_command_free(&cmd);
-		return TRUE;
-	} else {
-		i_assert(!client->disconnected);
-
-		return client_command_input(cmd);
-	}
-}
-
-static bool client_handle_next_command(struct client *client, bool *remove_io_r)
-{
-	size_t size;
-
-	*remove_io_r = FALSE;
-
-	if (client->input_lock != NULL) {
-		if (client->input_lock->state ==
-		    CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) {
-			*remove_io_r = TRUE;
-			return FALSE;
-		}
-		return client_command_input(client->input_lock);
-	}
-
-	if (client->input_skip_line) {
-		/* first eat the previous command line */
-		if (!client_skip_line(client))
-			return FALSE;
-		client->input_skip_line = FALSE;
-	}
-
-	/* don't bother creating a new client command before there's at least
-	   some input */
-	(void)i_stream_get_data(client->input, &size);
-	if (size == 0)
-		return FALSE;
-
-	/* beginning a new command */
-	if (client->command_queue_size >= CLIENT_COMMAND_QUEUE_MAX_SIZE ||
-	    client->output_lock != NULL) {
-		/* wait for some of the commands to finish */
-		*remove_io_r = TRUE;
-		return FALSE;
-	}
-
-	client->input_lock = client_command_new(client);
-	return client_command_input(client->input_lock);
-}
-
-bool client_handle_input(struct client *client)
-{
-	bool ret, remove_io, handled_commands = FALSE;
-
-	client->handling_input = TRUE;
-	do {
-		T_BEGIN {
-			ret = client_handle_next_command(client, &remove_io);
-		} T_END;
-		if (ret)
-			handled_commands = TRUE;
-	} while (ret && !client->disconnected && client->io != NULL);
-	client->handling_input = FALSE;
-
-	if (client->output->closed) {
-		client_destroy(client, NULL);
-		return TRUE;
-	} else {
-		if (remove_io)
-			io_remove(&client->io);
-		else
-			client_add_missing_io(client);
-		if (!handled_commands)
-			return FALSE;
-
-		ret = cmd_sync_delayed(client);
-		if (ret)
-			client_continue_pending_input(&client);
-		return TRUE;
-	}
-}
-
-void client_input(struct client *client)
-{
-	struct client_command_context *cmd;
-	struct ostream *output = client->output;
-	ssize_t bytes;
-
-	i_assert(client->io != NULL);
-
-	client->last_input = ioloop_time;
-	timeout_reset(client->to_idle);
-
-	bytes = i_stream_read(client->input);
-	if (bytes == -1) {
-		/* disconnected */
-		client_destroy(client, NULL);
-		return;
-	}
-
-	o_stream_ref(output);
-	o_stream_cork(output);
-	if (!client_handle_input(client) && bytes == -2) {
-		/* parameter word is longer than max. input buffer size.
-		   this is most likely an error, so skip the new data
-		   until newline is found. */
-		client->input_skip_line = TRUE;
-
-		cmd = client->input_lock != NULL ? client->input_lock :
-			client_command_new(client);
-		cmd->param_error = TRUE;
-		client_send_command_error(cmd, "Too long argument.");
-		client_command_free(&cmd);
-	}
-	o_stream_uncork(output);
-	o_stream_unref(&output);
-}
-
-static void client_output_cmd(struct client_command_context *cmd)
-{
-	bool finished;
-
-	/* continue processing command */
-	finished = cmd->func(cmd) || cmd->state == CLIENT_COMMAND_STATE_DONE;
-
-	if (!finished)
-		(void)client_handle_unfinished_cmd(cmd);
-	else {
-		/* command execution was finished */
-		client_command_free(&cmd);
-	}
-}
-
-int client_output(struct client *client)
-{
-	struct client_command_context *cmd;
-	int ret;
-
-	i_assert(!client->destroyed);
-
-	client->last_output = ioloop_time;
-	timeout_reset(client->to_idle);
-	if (client->to_idle_output != NULL)
-		timeout_reset(client->to_idle_output);
-
-	if ((ret = o_stream_flush(client->output)) < 0) {
-		client_destroy(client, NULL);
-		return 1;
-	}
-
-	/* mark all commands non-executed */
-	for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next)
-		cmd->temp_executed = FALSE;
-
-	o_stream_cork(client->output);
-	if (client->output_lock != NULL) {
-		client->output_lock->temp_executed = TRUE;
-		client_output_cmd(client->output_lock);
-	}
-	while (client->output_lock == NULL) {
-		/* go through the entire commands list every time in case
-		   multiple commands were freed. temp_executed keeps track of
-		   which messages we've called so far */
-		cmd = client->command_queue;
-		for (; cmd != NULL; cmd = cmd->next) {
-			if (!cmd->temp_executed &&
-			    cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT) {
-				cmd->temp_executed = TRUE;
-				client_output_cmd(cmd);
-				break;
-			}
-		}
-		if (cmd == NULL) {
-			/* all commands executed */
-			break;
-		}
-	}
-
-	if (client->output->closed) {
-		client_destroy(client, NULL);
-		return 1;
-	} else {
-		(void)cmd_sync_delayed(client);
-		o_stream_uncork(client->output);
-		client_continue_pending_input(&client);
-		return ret;
-	}
-}
-
-bool client_handle_search_save_ambiguity(struct client_command_context *cmd)
-{
-	struct client_command_context *old_cmd = cmd->next;
-
-	/* search only commands that were added before this command
-	   (commands are prepended to the queue, so they're after ourself) */
-	for (; old_cmd != NULL; old_cmd = old_cmd->next) {
-		if (old_cmd->search_save_result)
-			break;
-	}
-	if (old_cmd == NULL)
-		return FALSE;
-
-	/* ambiguity, wait until it's over */
-	i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT);
-	cmd->client->input_lock = cmd;
-	cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
-	io_remove(&cmd->client->io);
-	return TRUE;
-}
-
-void client_enable(struct client *client, enum mailbox_feature features)
-{
-	struct mailbox_status status;
-
-	if ((client->enabled_features & features) == features)
-		return;
-
-	client->enabled_features |= features;
-	if (client->mailbox == NULL)
-		return;
-
-	mailbox_enable(client->mailbox, features);
-	if ((features & MAILBOX_FEATURE_CONDSTORE) != 0) {
-		/* CONDSTORE being enabled while mailbox is selected.
-		   Notify client of the latest HIGHESTMODSEQ. */
-		mailbox_get_status(client->mailbox,
-				   STATUS_HIGHESTMODSEQ, &status);
-		client_send_line(client, t_strdup_printf(
-			"* OK [HIGHESTMODSEQ %llu]",
-			(unsigned long long)status.highest_modseq));
-	}
-}
-
-struct imap_search_update *
-client_search_update_lookup(struct client *client, const char *tag,
-			    unsigned int *idx_r)
-{
-	struct imap_search_update *updates;
-	unsigned int i, count;
-
-	if (!array_is_created(&client->search_updates))
-		return NULL;
-
-	updates = array_get_modifiable(&client->search_updates, &count);
-	for (i = 0; i < count; i++) {
-		if (strcmp(updates[i].tag, tag) == 0) {
-			*idx_r = i;
-			return &updates[i];
-		}
-	}
-	return NULL;
-}
-
-void client_search_updates_free(struct client *client)
-{
-	struct imap_search_update *updates;
-	unsigned int i, count;
-
-	if (!array_is_created(&client->search_updates))
-		return;
-
-	updates = array_get_modifiable(&client->search_updates, &count);
-	for (i = 0; i < count; i++) {
-		i_free(updates[i].tag);
-		mailbox_search_result_free(&updates[i].result);
-	}
-	array_clear(&client->search_updates);
-}
-
-void clients_init(void)
-{
-	my_client = NULL;
-}
-
-void clients_deinit(void)
-{
-	if (my_client != NULL) {
-		client_send_line(my_client, "* BYE Server shutting down.");
-		client_destroy(my_client, "Server shutting down");
-	}
-}
--- a/src/imap/client.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-#ifndef CLIENT_H
-#define CLIENT_H
-
-#include "commands.h"
-
-#define CLIENT_COMMAND_QUEUE_MAX_SIZE 4
-/* Maximum number of CONTEXT=SEARCH UPDATEs. Clients probably won't need more
-   than a few, so this is mainly to avoid more or less accidental pointless
-   resource usage. */
-#define CLIENT_MAX_SEARCH_UPDATES 10
-
-struct client;
-struct mail_storage;
-struct imap_parser;
-struct imap_arg;
-
-struct mailbox_keywords {
-	/* All keyword names. The array itself exists in mail_index.
-	   Keywords are currently only appended, they're never removed. */
-	const ARRAY_TYPE(keywords) *names;
-	/* Number of keywords announced to client via FLAGS/PERMANENTFLAGS.
-	   This relies on keywords not being removed while mailbox is
-	   selected. */
-	unsigned int announce_count;
-};
-
-struct imap_search_update {
-	char *tag;
-	struct mail_search_result *result;
-	bool return_uids;
-};
-
-enum client_command_state {
-	/* Waiting for more input */
-	CLIENT_COMMAND_STATE_WAIT_INPUT,
-	/* Waiting to be able to send more output */
-	CLIENT_COMMAND_STATE_WAIT_OUTPUT,
-	/* Wait for other commands to finish execution */
-	CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY,
-	/* Waiting for other commands to finish so we can sync */
-	CLIENT_COMMAND_STATE_WAIT_SYNC,
-	/* Command is finished */
-	CLIENT_COMMAND_STATE_DONE
-};
-
-struct imap_module_register {
-	unsigned int id;
-};
-
-union imap_module_context {
-	struct imap_module_register *reg;
-};
-extern struct imap_module_register imap_module_register;
-
-struct client_command_context {
-	struct client_command_context *prev, *next;
-	struct client *client;
-
-	pool_t pool;
-	/* IMAP command tag */
-	const char *tag;
-	/* Name of this command */
-	const char *name;
-	/* Parameters for this command. These are generated from parsed IMAP
-	   arguments, so they may not be exactly the same as how client sent
-	   them. */
-	const char *args;
-	enum command_flags cmd_flags;
-
-	command_func_t *func;
-	void *context;
-
-	/* Module-specific contexts. */
-	ARRAY_DEFINE(module_contexts, union imap_module_context *);
-
-	struct imap_parser *parser;
-	enum client_command_state state;
-
-	struct client_sync_context *sync;
-
-	unsigned int uid:1; /* used UID command */
-	unsigned int cancel:1; /* command is wanted to be cancelled */
-	unsigned int param_error:1;
-	unsigned int search_save_result:1; /* search result is being updated */
-	unsigned int temp_executed:1; /* temporary execution state tracking */
-};
-
-struct client {
-	int fd_in, fd_out;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-	struct timeout *to_idle, *to_idle_output;
-
-        const struct imap_settings *set;
-	enum client_workarounds workarounds;
-	string_t *capability_string;
-
-        struct mail_user *user;
-	struct mailbox *mailbox;
-        struct mailbox_keywords keywords;
-	unsigned int select_counter; /* increased when mailbox is changed */
-	unsigned int sync_counter;
-	uint32_t messages_count, recent_count, uidvalidity;
-	enum mailbox_feature enabled_features;
-
-	time_t last_input, last_output;
-	unsigned int bad_counter;
-
-	/* one parser is kept here to be used for new commands */
-	struct imap_parser *free_parser;
-	/* command_pool is cleared when the command queue gets empty */
-	pool_t command_pool;
-	/* New commands are always prepended to the queue */
-	struct client_command_context *command_queue;
-	unsigned int command_queue_size;
-
-	uint64_t sync_last_full_modseq;
-	uint64_t highest_fetch_modseq;
-
-	/* SEARCHRES extension: Last saved SEARCH result */
-	ARRAY_TYPE(seq_range) search_saved_uidset;
-	/* SEARCH=CONTEXT extension: Searches that get updated */
-	ARRAY_DEFINE(search_updates, struct imap_search_update);
-
-	/* client input/output is locked by this command */
-	struct client_command_context *input_lock;
-	struct client_command_context *output_lock;
-	/* command changing the mailbox */
-	struct client_command_context *mailbox_change_lock;
-
-	/* syncing marks this TRUE when it sees \Deleted flags. this is by
-	   EXPUNGE for Outlook-workaround. */
-	unsigned int sync_seen_deletes:1;
-	unsigned int sync_seen_expunges:1;
-	unsigned int disconnected:1;
-	unsigned int destroyed:1;
-	unsigned int handling_input:1;
-	unsigned int syncing:1;
-	unsigned int id_logged:1;
-	unsigned int mailbox_examined:1;
-	unsigned int input_skip_line:1; /* skip all the data until we've
-					   found a new line */
-	unsigned int modseqs_sent_since_sync:1;
-};
-
-/* Create new client with specified input/output handles. socket specifies
-   if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
-			     const struct imap_settings *set);
-void client_destroy(struct client *client, const char *reason);
-
-/* Disconnect client connection */
-void client_disconnect(struct client *client, const char *reason);
-void client_disconnect_with_error(struct client *client, const char *msg);
-
-/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full,
-   -1 if error */
-int client_send_line(struct client *client, const char *data);
-/* Send line of data to client, prefixed with client->tag */
-void client_send_tagline(struct client_command_context *cmd, const char *data);
-
-/* Send BAD command error to client. msg can be NULL. */
-void client_send_command_error(struct client_command_context *cmd,
-			       const char *msg);
-
-/* Read a number of arguments. Returns TRUE if everything was read or
-   FALSE if either needs more data or error occurred. */
-bool client_read_args(struct client_command_context *cmd, unsigned int count,
-		      unsigned int flags, const struct imap_arg **args_r);
-/* Reads a number of string arguments. ... is a list of pointers where to
-   store the arguments. */
-bool client_read_string_args(struct client_command_context *cmd,
-			     unsigned int count, ...);
-
-/* SEARCHRES extension: Call if $ is being used/updated, returns TRUE if we
-   have to wait for an existing SEARCH SAVE to finish. */
-bool client_handle_search_save_ambiguity(struct client_command_context *cmd);
-
-void client_enable(struct client *client, enum mailbox_feature features);
-
-struct imap_search_update *
-client_search_update_lookup(struct client *client, const char *tag,
-			    unsigned int *idx_r);
-void client_search_updates_free(struct client *client);
-
-void clients_init(void);
-void clients_deinit(void);
-
-void client_command_cancel(struct client_command_context **cmd);
-void client_command_free(struct client_command_context **cmd);
-
-bool client_handle_unfinished_cmd(struct client_command_context *cmd);
-void client_continue_pending_input(struct client **_client);
-
-void client_input(struct client *client);
-bool client_handle_input(struct client *client);
-int client_output(struct client *client);
-
-#endif
--- a/src/imap/cmd-append.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-append.c	Tue May 05 11:57:04 2009 -0400
@@ -1,11 +1,11 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-parser.h"
 #include "imap-date.h"
 #include "mail-storage.h"
--- a/src/imap/cmd-cancelupdate.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-cancelupdate.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 static bool client_search_update_cancel(struct client *client, const char *tag)
 {
--- a/src/imap/cmd-capability.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-capability.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 #include "str.h"
 
 bool cmd_capability(struct client_command_context *cmd)
--- a/src/imap/cmd-check.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-check.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_check(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-close.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-close.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 #include "imap-expunge.h"
 
 bool cmd_close(struct client_command_context *cmd)
--- a/src/imap/cmd-copy.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-copy.c	Tue May 05 11:57:04 2009 -0400
@@ -1,10 +1,10 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "ostream.h"
 #include "imap-resp-code.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 
 #include <time.h>
--- a/src/imap/cmd-create.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-create.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "mail-namespace.h"
-#include "commands.h"
+#include "imap-commands.h"
 
 bool cmd_create(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-delete.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-delete.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_delete(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-enable.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-enable.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 
 bool cmd_enable(struct client_command_context *cmd)
--- a/src/imap/cmd-examine.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-examine.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_examine(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-expunge.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-expunge.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 #include "imap-expunge.h"
 
--- a/src/imap/cmd-fetch.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-fetch.c	Tue May 05 11:57:04 2009 -0400
@@ -1,9 +1,9 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ostream.h"
 #include "imap-resp-code.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-fetch.h"
 #include "imap-search-args.h"
 #include "mail-search.h"
--- a/src/imap/cmd-id.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-id.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "imap-id.h"
 
 bool cmd_id(struct client_command_context *cmd)
--- a/src/imap/cmd-idle.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-idle.c	Tue May 05 11:57:04 2009 -0400
@@ -1,11 +1,11 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
 #include "mail-storage-settings.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-sync.h"
 
 #include <stdlib.h>
--- a/src/imap/cmd-list.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-list.c	Tue May 05 11:57:04 2009 -0400
@@ -1,13 +1,13 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "array.h"
 #include "str.h"
 #include "strescape.h"
 #include "imap-quote.h"
 #include "imap-match.h"
 #include "imap-status.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "mail-namespace.h"
 
 struct cmd_list_context {
--- a/src/imap/cmd-logout.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-logout.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ostream.h"
-#include "commands.h"
+#include "imap-commands.h"
 
 bool cmd_logout(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-lsub.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-lsub.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_lsub(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-namespace.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-namespace.c	Tue May 05 11:57:04 2009 -0400
@@ -1,9 +1,9 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "imap-quote.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "mail-namespace.h"
 
 static void list_namespaces(struct mail_namespace *ns,
--- a/src/imap/cmd-noop.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-noop.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_noop(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-rename.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-rename.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "mail-namespace.h"
-#include "commands.h"
+#include "imap-commands.h"
 
 bool cmd_rename(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-search.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-search.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "imap-search-args.h"
 #include "imap-search.h"
 
--- a/src/imap/cmd-select.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-select.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "seq-range-array.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "mail-search-build.h"
 #include "imap-seqset.h"
 #include "imap-fetch.h"
--- a/src/imap/cmd-sort.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-sort.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "buffer.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 #include "imap-search.h"
 
--- a/src/imap/cmd-status.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-status.c	Tue May 05 11:57:04 2009 -0400
@@ -1,8 +1,8 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "imap-resp-code.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-sync.h"
 #include "imap-status.h"
 
--- a/src/imap/cmd-store.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-store.c	Tue May 05 11:57:04 2009 -0400
@@ -1,9 +1,9 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "seq-range-array.h"
 #include "str.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 #include "imap-util.h"
 
--- a/src/imap/cmd-subscribe.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-subscribe.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 #include "mail-namespace.h"
 
 static bool have_listable_namespace_prefix(struct mail_namespace *ns,
--- a/src/imap/cmd-thread.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-thread.c	Tue May 05 11:57:04 2009 -0400
@@ -1,9 +1,9 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "ostream.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 #include "mail-thread.h"
 
--- a/src/imap/cmd-uid.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-uid.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_uid(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-unselect.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-unselect.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_unselect(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-unsubscribe.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-unsubscribe.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_unsubscribe(struct client_command_context *cmd)
 {
--- a/src/imap/cmd-x-cancel.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/cmd-x-cancel.c	Tue May 05 11:57:04 2009 -0400
@@ -1,7 +1,7 @@
 /* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
-#include "commands.h"
+#include "imap-common.h"
+#include "imap-commands.h"
 
 bool cmd_x_cancel(struct client_command_context *cmd)
 {
--- a/src/imap/commands-util.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,411 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "buffer.h"
-#include "str.h"
-#include "str-sanitize.h"
-#include "imap-resp-code.h"
-#include "imap-parser.h"
-#include "imap-sync.h"
-#include "imap-util.h"
-#include "mail-storage.h"
-#include "mail-namespace.h"
-#include "commands-util.h"
-
-/* Maximum length for mailbox name, including it's path. This isn't fully
-   exact since the user can create folder hierarchy with small names, then
-   rename them to larger names. Mail storages should set more strict limits
-   to them, mbox/maildir currently allow paths only up to PATH_MAX. */
-#define MAILBOX_MAX_NAME_LEN 512
-
-struct mail_namespace *
-client_find_namespace(struct client_command_context *cmd, const char **mailbox)
-{
-	struct mail_namespace *ns;
-
-	ns = mail_namespace_find(cmd->client->user->namespaces, mailbox);
-	if (ns != NULL)
-		return ns;
-
-	client_send_tagline(cmd, "NO Unknown namespace.");
-	return NULL;
-}
-
-struct mail_storage *
-client_find_storage(struct client_command_context *cmd, const char **mailbox)
-{
-	struct mail_namespace *ns;
-
-	ns = client_find_namespace(cmd, mailbox);
-	return ns == NULL ? NULL : ns->storage;
-}
-
-bool client_verify_mailbox_name(struct client_command_context *cmd,
-				const char *mailbox,
-				bool should_exist, bool should_not_exist)
-{
-	struct mail_namespace *ns;
-	struct mailbox_list *list;
-	enum mailbox_name_status mailbox_status;
-	const char *orig_mailbox, *p;
-
-	orig_mailbox = mailbox;
-	ns = client_find_namespace(cmd, &mailbox);
-	if (ns == NULL)
-		return FALSE;
-
-	/* make sure it even looks valid */
-	if (*mailbox == '\0') {
-		client_send_tagline(cmd, "NO Empty mailbox name.");
-		return FALSE;
-	}
-
-	if (ns->real_sep != ns->sep && ns->prefix_len < strlen(orig_mailbox)) {
-		/* make sure there are no real separators used in the mailbox
-		   name. */
-		orig_mailbox += ns->prefix_len;
-		for (p = orig_mailbox; *p != '\0'; p++) {
-			if (*p == ns->real_sep) {
-				client_send_tagline(cmd, t_strdup_printf(
-					"NO Character not allowed "
-					"in mailbox name: '%c'",
-					ns->real_sep));
-				return FALSE;
-			}
-		}
-	}
-
-	/* make sure two hierarchy separators aren't next to each others */
-	for (p = mailbox+1; *p != '\0'; p++) {
-		if (p[0] == ns->real_sep && p[-1] == ns->real_sep) {
-			client_send_tagline(cmd, "NO Invalid mailbox name.");
-			return FALSE;
-		}
-	}
-
-	if (strlen(mailbox) > MAILBOX_MAX_NAME_LEN) {
-		client_send_tagline(cmd, "NO Mailbox name too long.");
-		return FALSE;
-	}
-
-	/* check what our storage thinks of it */
-	list = mail_storage_get_list(ns->storage);
-	if (mailbox_list_get_mailbox_name_status(list, mailbox,
-						 &mailbox_status) < 0) {
-		client_send_storage_error(cmd, ns->storage);
-		return FALSE;
-	}
-
-	switch (mailbox_status) {
-	case MAILBOX_NAME_EXISTS:
-		if (should_exist || !should_not_exist)
-			return TRUE;
-
-		client_send_tagline(cmd, "NO Mailbox exists.");
-		break;
-
-	case MAILBOX_NAME_VALID:
-		if (!should_exist)
-			return TRUE;
-
-		client_send_tagline(cmd, t_strconcat(
-			"NO [TRYCREATE] Mailbox doesn't exist: ",
-			str_sanitize(orig_mailbox, MAILBOX_MAX_NAME_LEN),
-			NULL));
-		break;
-
-	case MAILBOX_NAME_INVALID:
-		client_send_tagline(cmd, t_strconcat(
-			"NO Invalid mailbox name: ",
-			str_sanitize(orig_mailbox, MAILBOX_MAX_NAME_LEN),
-			NULL));
-		break;
-
-	case MAILBOX_NAME_NOINFERIORS:
-		client_send_tagline(cmd,
-			"NO Mailbox parent doesn't allow inferior mailboxes.");
-		break;
-
-	default:
-                i_unreached();
-	}
-
-	return FALSE;
-}
-
-bool client_verify_open_mailbox(struct client_command_context *cmd)
-{
-	if (cmd->client->mailbox != NULL)
-		return TRUE;
-	else {
-		client_send_tagline(cmd, "BAD No mailbox selected.");
-		return FALSE;
-	}
-}
-
-static const char *
-get_error_string(const char *error_string, enum mail_error error)
-{
-	const char *resp_code = NULL;
-
-	switch (error) {
-	case MAIL_ERROR_NONE:
-		break;
-	case MAIL_ERROR_TEMP:
-		resp_code = IMAP_RESP_CODE_SERVERBUG;
-		break;
-	case MAIL_ERROR_NOTPOSSIBLE:
-	case MAIL_ERROR_PARAMS:
-		resp_code = IMAP_RESP_CODE_CANNOT;
-		break;
-	case MAIL_ERROR_PERM:
-		resp_code = IMAP_RESP_CODE_NOPERM;
-		break;
-	case MAIL_ERROR_NOSPACE:
-		resp_code = IMAP_RESP_CODE_OVERQUOTA;
-		break;
-	case MAIL_ERROR_NOTFOUND:
-		resp_code = IMAP_RESP_CODE_NONEXISTENT;
-		break;
-	case MAIL_ERROR_EXISTS:
-		resp_code = IMAP_RESP_CODE_ALREADYEXISTS;
-		break;
-	case MAIL_ERROR_EXPUNGED:
-		resp_code = IMAP_RESP_CODE_EXPUNGEISSUED;
-		break;
-	case MAIL_ERROR_INUSE:
-		resp_code = IMAP_RESP_CODE_INUSE;
-		break;
-	}
-	if (resp_code == NULL || *error_string == '[')
-		return t_strconcat("NO ", error_string, NULL);
-	else
-		return t_strdup_printf("NO [%s] %s", resp_code, error_string);
-}
-
-void client_send_list_error(struct client_command_context *cmd,
-			    struct mailbox_list *list)
-{
-	const char *error_string;
-	enum mail_error error;
-
-	error_string = mailbox_list_get_last_error(list, &error);
-	client_send_tagline(cmd, get_error_string(error_string, error));
-}
-
-void client_send_storage_error(struct client_command_context *cmd,
-			       struct mail_storage *storage)
-{
-	const char *error_string;
-	enum mail_error error;
-
-	if (cmd->client->mailbox != NULL &&
-	    mailbox_is_inconsistent(cmd->client->mailbox)) {
-		/* we can't do forced CLOSE, so have to disconnect */
-		client_disconnect_with_error(cmd->client,
-			"IMAP session state is inconsistent, please relogin.");
-		return;
-	}
-
-	error_string = mail_storage_get_last_error(storage, &error);
-	client_send_tagline(cmd, get_error_string(error_string, error));
-}
-
-void client_send_untagged_storage_error(struct client *client,
-					struct mail_storage *storage)
-{
-	const char *error_string;
-	enum mail_error error;
-
-	if (client->mailbox != NULL &&
-	    mailbox_is_inconsistent(client->mailbox)) {
-		/* we can't do forced CLOSE, so have to disconnect */
-		client_disconnect_with_error(client,
-			"IMAP session state is inconsistent, please relogin.");
-		return;
-	}
-
-	error_string = mail_storage_get_last_error(storage, &error);
-	client_send_line(client, t_strconcat("* NO ", error_string, NULL));
-}
-
-bool client_parse_mail_flags(struct client_command_context *cmd,
-			     const struct imap_arg *args,
-			     enum mail_flags *flags_r,
-			     const char *const **keywords_r)
-{
-	const char *atom;
-	ARRAY_DEFINE(keywords, const char *);
-
-	*flags_r = 0;
-	*keywords_r = NULL;
-	p_array_init(&keywords, cmd->pool, 16);
-
-	while (args->type != IMAP_ARG_EOL) {
-		if (args->type != IMAP_ARG_ATOM) {
-			client_send_command_error(cmd,
-				"Flags list contains non-atoms.");
-			return FALSE;
-		}
-
-		atom = IMAP_ARG_STR(args);
-		if (*atom == '\\') {
-			/* system flag */
-			atom = t_str_ucase(atom);
-			if (strcmp(atom, "\\ANSWERED") == 0)
-				*flags_r |= MAIL_ANSWERED;
-			else if (strcmp(atom, "\\FLAGGED") == 0)
-				*flags_r |= MAIL_FLAGGED;
-			else if (strcmp(atom, "\\DELETED") == 0)
-				*flags_r |= MAIL_DELETED;
-			else if (strcmp(atom, "\\SEEN") == 0)
-				*flags_r |= MAIL_SEEN;
-			else if (strcmp(atom, "\\DRAFT") == 0)
-				*flags_r |= MAIL_DRAFT;
-			else {
-				client_send_tagline(cmd, t_strconcat(
-					"BAD Invalid system flag ",
-					atom, NULL));
-				return FALSE;
-			}
-		} else {
-			/* keyword validity checks are done by lib-storage */
-			array_append(&keywords, &atom, 1);
-		}
-
-		args++;
-	}
-
-	if (array_count(&keywords) == 0)
-		*keywords_r = NULL;
-	else {
-		(void)array_append_space(&keywords); /* NULL-terminate */
-		*keywords_r = array_idx(&keywords, 0);
-	}
-	return TRUE;
-}
-
-static const char *get_keywords_string(const ARRAY_TYPE(keywords) *keywords)
-{
-	string_t *str;
-	const char *const *names;
-	unsigned int i, count;
-
-	str = t_str_new(256);
-	names = array_get(keywords, &count);
-	for (i = 0; i < count; i++) {
-		str_append_c(str, ' ');
-		str_append(str, names[i]);
-	}
-	return str_c(str);
-}
-
-#define SYSTEM_FLAGS "\\Answered \\Flagged \\Deleted \\Seen \\Draft"
-
-void client_send_mailbox_flags(struct client *client, bool selecting)
-{
-	unsigned int count = array_count(client->keywords.names);
-	const char *str;
-
-	if (!selecting && count == client->keywords.announce_count) {
-		/* no changes to keywords and we're not selecting a mailbox */
-		return;
-	}
-
-	client->keywords.announce_count = count;
-	str = count == 0 ? "" : get_keywords_string(client->keywords.names);
-	client_send_line(client,
-		t_strconcat("* FLAGS ("SYSTEM_FLAGS, str, ")", NULL));
-
-	if (mailbox_is_readonly(client->mailbox)) {
-		client_send_line(client, "* OK [PERMANENTFLAGS ()] "
-				 "Read-only mailbox.");
-	} else {
-		bool star = mailbox_allow_new_keywords(client->mailbox);
-
-		client_send_line(client,
-			t_strconcat("* OK [PERMANENTFLAGS ("SYSTEM_FLAGS, str,
-				    star ? " \\*" : "",
-				    ")] Flags permitted.", NULL));
-	}
-}
-
-void client_update_mailbox_flags(struct client *client,
-				 const ARRAY_TYPE(keywords) *keywords)
-{
-	client->keywords.names = keywords;
-	client->keywords.announce_count = 0;
-}
-
-const char *const *
-client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
-			 const ARRAY_TYPE(keyword_indexes) *src)
-{
-	const unsigned int *kw_indexes;
-	const char *const *all_names;
-	unsigned int i, kw_count, all_count;
-
-	client_send_mailbox_flags(client, FALSE);
-
-	all_names = array_get(client->keywords.names, &all_count);
-	kw_indexes = array_get(src, &kw_count);
-
-	/* convert indexes to names */
-	array_clear(dest);
-	for (i = 0; i < kw_count; i++) {
-		i_assert(kw_indexes[i] < all_count);
-		array_append(dest, &all_names[kw_indexes[i]], 1);
-	}
-
-	(void)array_append_space(dest);
-	return array_idx(dest, 0);
-}
-
-bool mailbox_equals(const struct mailbox *box1,
-		    const struct mail_storage *storage2, const char *name2)
-{
-	struct mail_storage *storage1 = mailbox_get_storage(box1);
-	const char *name1;
-
-	if (storage1 != storage2)
-		return FALSE;
-
-        name1 = mailbox_get_name(box1);
-	if (strcmp(name1, name2) == 0)
-		return TRUE;
-
-	return strcasecmp(name1, "INBOX") == 0 &&
-		strcasecmp(name2, "INBOX") == 0;
-}
-
-void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str)
-{
-	memset(ctx, 0, sizeof(*ctx));
-	ctx->str = str;
-	ctx->last_uid = (uint32_t)-1;
-}
-
-void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid)
-{
-	if (uid != ctx->last_uid+1) {
-		if (ctx->first_uid == 0)
-			;
-		else if (ctx->first_uid == ctx->last_uid)
-			str_printfa(ctx->str, "%u,", ctx->first_uid);
-		else {
-			str_printfa(ctx->str, "%u:%u,",
-				    ctx->first_uid, ctx->last_uid);
-		}
-		ctx->first_uid = uid;
-	}
-	ctx->last_uid = uid;
-}
-
-void msgset_generator_finish(struct msgset_generator_context *ctx)
-{
-	if (ctx->first_uid == ctx->last_uid)
-		str_printfa(ctx->str, "%u", ctx->first_uid);
-	else
-		str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid);
-}
--- a/src/imap/commands-util.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#ifndef COMMANDS_UTIL_H
-#define COMMANDS_UTIL_H
-
-struct msgset_generator_context {
-	string_t *str;
-	uint32_t first_uid, last_uid;
-};
-
-struct mail_full_flags;
-struct mailbox_keywords;
-
-/* Finds namespace for given mailbox from namespaces. If not found,
-   sends "Unknown namespace" error message to client. */
-struct mail_namespace *
-client_find_namespace(struct client_command_context *cmd, const char **mailbox);
-/* Finds mail storage for given mailbox from namespaces. If not found,
-   sends "Unknown namespace" error message to client. */
-struct mail_storage *
-client_find_storage(struct client_command_context *cmd, const char **mailbox);
-
-/* If should_exist is TRUE, this function returns TRUE if the mailbox
-   exists. If it doesn't exist but would be a valid mailbox name, the
-   error message is prefixed with [TRYCREATE].
-
-   If should_exist is FALSE, the should_not_exist specifies if we should
-   return TRUE or FALSE if mailbox doesn't exist. */
-bool client_verify_mailbox_name(struct client_command_context *cmd,
-				const char *mailbox,
-				bool should_exist, bool should_not_exist);
-
-/* Returns TRUE if mailbox is selected. If not, sends "No mailbox selected"
-   error message to client. */
-bool client_verify_open_mailbox(struct client_command_context *cmd);
-
-/* Send last mailbox list error message to client. */
-void client_send_list_error(struct client_command_context *cmd,
-			    struct mailbox_list *list);
-/* Send last mail storage error message to client. */
-void client_send_storage_error(struct client_command_context *cmd,
-			       struct mail_storage *storage);
-
-/* Send untagged error message to client. */
-void client_send_untagged_storage_error(struct client *client,
-					struct mail_storage *storage);
-
-/* Parse flags. Returns TRUE if successful, if not sends an error message to
-   client. */
-bool client_parse_mail_flags(struct client_command_context *cmd,
-			     const struct imap_arg *args,
-			     enum mail_flags *flags_r,
-			     const char *const **keywords_r);
-
-/* Send FLAGS + PERMANENTFLAGS to client if they have changed,
-   or if selecting=TRUE. */
-void client_send_mailbox_flags(struct client *client, bool selecting);
-/* Update client->keywords array. Use keywords=NULL when unselecting. */
-void client_update_mailbox_flags(struct client *client,
-				 const ARRAY_TYPE(keywords) *keywords);
-/* Convert keyword indexes to keyword names in selected mailbox. */
-const char *const *
-client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
-			 const ARRAY_TYPE(keyword_indexes) *src);
-
-bool mailbox_equals(const struct mailbox *box1,
-		    const struct mail_storage *storage2,
-		    const char *name2) ATTR_PURE;
-
-void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str);
-void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid);
-void msgset_generator_finish(struct msgset_generator_context *ctx);
-
-#endif
--- a/src/imap/commands.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "buffer.h"
-#include "commands.h"
-
-#include <stdlib.h>
-
-static const struct command imap4rev1_commands[] = {
-	{ "CAPABILITY",		cmd_capability,  0 },
-	{ "LOGOUT",		cmd_logout,      COMMAND_FLAG_BREAKS_MAILBOX },
-	{ "NOOP",		cmd_noop,        COMMAND_FLAG_BREAKS_SEQS },
-
-	{ "APPEND",		cmd_append,      COMMAND_FLAG_BREAKS_SEQS },
-	{ "EXAMINE",		cmd_examine,     COMMAND_FLAG_BREAKS_MAILBOX },
-	{ "CREATE",		cmd_create,      0 },
-	{ "DELETE",		cmd_delete,      0 },
-	{ "RENAME",		cmd_rename,      0 },
-	{ "LIST",		cmd_list,        0 },
-	{ "LSUB",		cmd_lsub,        0 },
-	{ "SELECT",		cmd_select,      COMMAND_FLAG_BREAKS_MAILBOX },
-	{ "STATUS",		cmd_status,      0 },
-	{ "SUBSCRIBE",		cmd_subscribe,   0 },
-	{ "UNSUBSCRIBE",	cmd_unsubscribe, 0 },
-
-	{ "CHECK",		cmd_check,       COMMAND_FLAG_BREAKS_SEQS },
-	{ "CLOSE",		cmd_close,       COMMAND_FLAG_BREAKS_MAILBOX },
-	{ "COPY",		cmd_copy,        COMMAND_FLAG_USES_SEQS |
-						 COMMAND_FLAG_BREAKS_SEQS },
-	{ "EXPUNGE",		cmd_expunge,     COMMAND_FLAG_BREAKS_SEQS },
-	{ "FETCH",		cmd_fetch,       COMMAND_FLAG_USES_SEQS },
-	{ "SEARCH",		cmd_search,      COMMAND_FLAG_USES_SEQS },
-	{ "STORE",		cmd_store,       COMMAND_FLAG_USES_SEQS },
-	{ "UID",		cmd_uid,         0 },
-	{ "UID COPY",		cmd_copy,        COMMAND_FLAG_BREAKS_SEQS },
-	{ "UID FETCH",		cmd_fetch,       COMMAND_FLAG_BREAKS_SEQS },
-	{ "UID SEARCH",		cmd_search,      COMMAND_FLAG_BREAKS_SEQS },
-	{ "UID STORE",		cmd_store,       COMMAND_FLAG_BREAKS_SEQS }
-};
-#define IMAP4REV1_COMMANDS_COUNT N_ELEMENTS(imap4rev1_commands)
-
-static const struct command imap_ext_commands[] = {
-	{ "CANCELUPDATE",	cmd_cancelupdate,0 },
-	{ "ENABLE",		cmd_enable,      0 },
-	{ "ID",			cmd_id,          0 },
-	{ "IDLE",		cmd_idle,        COMMAND_FLAG_BREAKS_SEQS },
-	{ "NAMESPACE",		cmd_namespace,   0 },
-	{ "SORT",		cmd_sort,        COMMAND_FLAG_USES_SEQS },
-	{ "THREAD",		cmd_thread,      COMMAND_FLAG_USES_SEQS },
-	{ "UID EXPUNGE",	cmd_uid_expunge, COMMAND_FLAG_BREAKS_SEQS },
-	{ "UID SORT",		cmd_sort,        COMMAND_FLAG_BREAKS_SEQS },
-	{ "UID THREAD",		cmd_thread,      COMMAND_FLAG_BREAKS_SEQS },
-	{ "UNSELECT",		cmd_unselect,    COMMAND_FLAG_BREAKS_MAILBOX },
-	{ "X-CANCEL",		cmd_x_cancel,    0 }
-};
-#define IMAP_EXT_COMMANDS_COUNT N_ELEMENTS(imap_ext_commands)
-
-ARRAY_TYPE(command) imap_commands;
-static bool commands_unsorted;
-
-void command_register(const char *name, command_func_t *func,
-		      enum command_flags flags)
-{
-	struct command cmd;
-
-	memset(&cmd, 0, sizeof(cmd));
-	cmd.name = name;
-	cmd.func = func;
-	cmd.flags = flags;
-	array_append(&imap_commands, &cmd, 1);
-
-	commands_unsorted = TRUE;
-}
-
-void command_unregister(const char *name)
-{
-	const struct command *cmd;
-	unsigned int i, count;
-
-	cmd = array_get(&imap_commands, &count);
-	for (i = 0; i < count; i++) {
-		if (strcasecmp(cmd[i].name, name) == 0) {
-			array_delete(&imap_commands, i, 1);
-			return;
-		}
-	}
-
-	i_error("Trying to unregister unknown command '%s'", name);
-}
-
-void command_register_array(const struct command *cmdarr, unsigned int count)
-{
-	commands_unsorted = TRUE;
-	array_append(&imap_commands, cmdarr, count);
-}
-
-void command_unregister_array(const struct command *cmdarr, unsigned int count)
-{
-	while (count > 0) {
-		command_unregister(cmdarr->name);
-		count--; cmdarr++;
-	}
-}
-
-static int command_cmp(const void *p1, const void *p2)
-{
-        const struct command *c1 = p1, *c2 = p2;
-
-	return strcasecmp(c1->name, c2->name);
-}
-
-static int command_bsearch(const void *name, const void *cmd_p)
-{
-        const struct command *cmd = cmd_p;
-
-	return strcasecmp(name, cmd->name);
-}
-
-struct command *command_find(const char *name)
-{
-	void *base;
-	unsigned int count;
-
-	base = array_get_modifiable(&imap_commands, &count);
-	if (commands_unsorted) {
-		qsort(base, count, sizeof(struct command), command_cmp);
-                commands_unsorted = FALSE;
-	}
-
-	return bsearch(name, base, count, sizeof(struct command),
-		       command_bsearch);
-}
-
-void commands_init(void)
-{
-	i_array_init(&imap_commands, 64);
-	commands_unsorted = FALSE;
-
-        command_register_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT);
-        command_register_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT);
-}
-
-void commands_deinit(void)
-{
-        command_unregister_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT);
-        command_unregister_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT);
-	array_free(&imap_commands);
-}
--- a/src/imap/commands.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-#ifndef COMMANDS_H
-#define COMMANDS_H
-
-struct client_command_context;
-
-#include "mail-storage.h"
-#include "imap-parser.h"
-#include "imap-sync.h"
-#include "commands-util.h"
-
-typedef bool command_func_t(struct client_command_context *cmd);
-
-enum command_flags {
-	/* Command uses sequences as its input parameters */
-	COMMAND_FLAG_USES_SEQS		= 0x01,
-	/* Command may reply with EXPUNGE, causing sequences to break */
-	COMMAND_FLAG_BREAKS_SEQS	= 0x02,
-	/* Command changes the mailbox */
-	COMMAND_FLAG_BREAKS_MAILBOX	= 0x04 | COMMAND_FLAG_BREAKS_SEQS,
-
-	/* Command uses selected mailbox */
-	COMMAND_FLAG_USES_MAILBOX	= COMMAND_FLAG_BREAKS_MAILBOX |
-					  COMMAND_FLAG_USES_SEQS
-};
-
-struct command {
-	const char *name;
-	command_func_t *func;
-
-	enum command_flags flags;
-};
-ARRAY_DEFINE_TYPE(command, struct command);
-
-extern ARRAY_TYPE(command) imap_commands;
-
-/* Register command. Given name parameter must be permanently stored until
-   command is unregistered. */
-void command_register(const char *name, command_func_t *func,
-		      enum command_flags flags);
-void command_unregister(const char *name);
-
-/* Register array of commands. */
-void command_register_array(const struct command *cmdarr, unsigned int count);
-void command_unregister_array(const struct command *cmdarr, unsigned int count);
-
-struct command *command_find(const char *name);
-
-void commands_init(void);
-void commands_deinit(void);
-
-/* IMAP4rev1 commands: */
-
-/* Non-Authenticated State */
-bool cmd_logout(struct client_command_context *cmd);
-
-bool cmd_capability(struct client_command_context *cmd);
-bool cmd_noop(struct client_command_context *cmd);
-
-/* Authenticated State */
-bool cmd_select(struct client_command_context *cmd);
-bool cmd_examine(struct client_command_context *cmd);
-
-bool cmd_create(struct client_command_context *cmd);
-bool cmd_delete(struct client_command_context *cmd);
-bool cmd_rename(struct client_command_context *cmd);
-
-bool cmd_subscribe(struct client_command_context *cmd);
-bool cmd_unsubscribe(struct client_command_context *cmd);
-
-bool cmd_list(struct client_command_context *cmd);
-bool cmd_lsub(struct client_command_context *cmd);
-
-bool cmd_status(struct client_command_context *cmd);
-bool cmd_append(struct client_command_context *cmd);
-
-/* Selected state */
-bool cmd_check(struct client_command_context *cmd);
-bool cmd_close(struct client_command_context *cmd);
-bool cmd_expunge(struct client_command_context *cmd);
-bool cmd_search(struct client_command_context *cmd);
-bool cmd_fetch(struct client_command_context *cmd);
-bool cmd_store(struct client_command_context *cmd);
-bool cmd_copy(struct client_command_context *cmd);
-bool cmd_uid(struct client_command_context *cmd);
-
-/* IMAP extensions: */
-bool cmd_cancelupdate(struct client_command_context *cmd);
-bool cmd_enable(struct client_command_context *cmd);
-bool cmd_id(struct client_command_context *cmd);
-bool cmd_idle(struct client_command_context *cmd);
-bool cmd_namespace(struct client_command_context *cmd);
-bool cmd_sort(struct client_command_context *cmd);
-bool cmd_thread(struct client_command_context *cmd);
-bool cmd_uid_expunge(struct client_command_context *cmd);
-bool cmd_unselect(struct client_command_context *cmd);
-bool cmd_x_cancel(struct client_command_context *cmd);
-
-/* private: */
-bool cmd_list_full(struct client_command_context *cmd, bool lsub);
-bool cmd_select_full(struct client_command_context *cmd, bool readonly);
-bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe);
-
-#endif
--- a/src/imap/common.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#ifndef COMMON_H
-#define COMMON_H
-
-/* Disconnect client after idling this many milliseconds */
-#define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000)
-
-/* If we can't send anything to client for this long, disconnect the client */
-#define CLIENT_OUTPUT_TIMEOUT_MSECS (5*60*1000)
-
-/* Stop buffering more data into output stream after this many bytes */
-#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048
-
-/* Disconnect client when it sends too many bad commands in a row */
-#define CLIENT_MAX_BAD_COMMANDS 20
-
-enum client_workarounds {
-	WORKAROUND_DELAY_NEWMAIL		= 0x01,
-	WORKAROUND_NETSCAPE_EOH			= 0x04,
-	WORKAROUND_TB_EXTRA_MAILBOX_SEP		= 0x08
-};
-
-#include "lib.h"
-#include "client.h"
-#include "imap-settings.h"
-
-extern struct master_service *service;
-
-extern void (*hook_client_created)(struct client **client);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-client.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,938 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "imap-common.h"
+#include "ioloop.h"
+#include "llist.h"
+#include "str.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "var-expand.h"
+#include "master-service.h"
+#include "imap-resp-code.h"
+#include "imap-util.h"
+#include "mail-namespace.h"
+#include "imap-commands.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+extern struct mail_storage_callbacks mail_storage_callbacks;
+struct imap_module_register imap_module_register = { 0 };
+
+static struct client *my_client; /* we don't need more than one currently */
+
+static void client_idle_timeout(struct client *client)
+{
+	if (client->output_lock == NULL)
+		client_send_line(client, "* BYE Disconnected for inactivity.");
+	client_destroy(client, "Disconnected for inactivity");
+}
+
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct imap_settings *set)
+{
+	struct client *client;
+	struct mail_namespace *ns;
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd_in, TRUE);
+	net_set_nonblock(fd_out, TRUE);
+
+	client = i_new(struct client, 1);
+	client->set = set;
+	client->fd_in = fd_in;
+	client->fd_out = fd_out;
+	client->input = i_stream_create_fd(fd_in,
+					   set->imap_max_line_length, FALSE);
+	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
+
+	o_stream_set_flush_callback(client->output, client_output, client);
+
+	client->io = io_add(fd_in, IO_READ, client_input, client);
+        client->last_input = ioloop_time;
+	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
+				      client_idle_timeout, client);
+
+	client->command_pool =
+		pool_alloconly_create(MEMPOOL_GROWING"client command", 1024*12);
+	client->user = user;
+
+	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+		mail_storage_set_callbacks(ns->storage,
+					   &mail_storage_callbacks, client);
+	}
+
+	client->capability_string =
+		str_new(default_pool, sizeof(CAPABILITY_STRING)+32);
+	str_append(client->capability_string, *set->imap_capability != '\0' ?
+		   set->imap_capability : CAPABILITY_STRING);
+
+	i_assert(my_client == NULL);
+	my_client = client;
+
+	if (hook_client_created != NULL)
+		hook_client_created(&client);
+	return client;
+}
+
+void client_command_cancel(struct client_command_context **_cmd)
+{
+	struct client_command_context *cmd = *_cmd;
+	bool cmd_ret;
+
+	switch (cmd->state) {
+	case CLIENT_COMMAND_STATE_WAIT_INPUT:
+		/* a bit kludgy check: cancel command only if it has context
+		   set. currently only append command matches this check. all
+		   other commands haven't even started the processing yet. */
+		if (cmd->context == NULL)
+			break;
+		/* fall through */
+	case CLIENT_COMMAND_STATE_WAIT_OUTPUT:
+		cmd->cancel = TRUE;
+		break;
+	case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY:
+	case CLIENT_COMMAND_STATE_WAIT_SYNC:
+		/* commands haven't started yet */
+		break;
+	case CLIENT_COMMAND_STATE_DONE:
+		i_unreached();
+	}
+
+	cmd_ret = !cmd->cancel || cmd->func == NULL ? TRUE : cmd->func(cmd);
+	if (!cmd_ret && cmd->state != CLIENT_COMMAND_STATE_DONE) {
+		if (cmd->client->output->closed)
+			i_panic("command didn't cancel itself: %s", cmd->name);
+	} else {
+		client_command_free(_cmd);
+	}
+}
+
+static const char *client_stats(struct client *client)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'i', NULL, "input" },
+		{ 'o', NULL, "output" },
+		{ '\0', NULL, NULL }
+	};
+	struct var_expand_table *tab;
+	string_t *str;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	tab[0].value = dec2str(client->input->v_offset);
+	tab[1].value = dec2str(client->output->offset);
+
+	str = t_str_new(128);
+	var_expand(str, client->set->imap_logout_format, tab);
+	return str_c(str);
+}
+
+static const char *client_get_disconnect_reason(struct client *client)
+{
+	errno = client->input->stream_errno != 0 ?
+		client->input->stream_errno :
+		client->output->stream_errno;
+	return errno == 0 || errno == EPIPE ? "Connection closed" :
+		t_strdup_printf("Connection closed: %m");
+}
+
+void client_destroy(struct client *client, const char *reason)
+{
+	struct client_command_context *cmd;
+
+	i_assert(!client->destroyed);
+	client->destroyed = TRUE;
+
+	if (!client->disconnected) {
+		client->disconnected = TRUE;
+		if (reason == NULL)
+			reason = client_get_disconnect_reason(client);
+		i_info("%s %s", reason, client_stats(client));
+	}
+
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+
+	/* finish off all the queued commands. */
+	if (client->output_lock != NULL)
+		client_command_cancel(&client->output_lock);
+	while (client->command_queue != NULL) {
+		cmd = client->command_queue;
+		client_command_cancel(&cmd);
+	}
+	/* handle the input_lock command last. it might have been waiting on
+	   other queued commands (although we probably should just drop the
+	   command at that point since it hasn't started running. but this may
+	   change in future). */
+	if (client->input_lock != NULL)
+		client_command_cancel(&client->input_lock);
+
+	if (client->mailbox != NULL) {
+		client_search_updates_free(client);
+		mailbox_close(&client->mailbox);
+	}
+	mail_user_unref(&client->user);
+
+	if (client->free_parser != NULL)
+		imap_parser_destroy(&client->free_parser);
+	if (client->io != NULL)
+		io_remove(&client->io);
+	if (client->to_idle_output != NULL)
+		timeout_remove(&client->to_idle_output);
+	timeout_remove(&client->to_idle);
+
+	i_stream_destroy(&client->input);
+	o_stream_destroy(&client->output);
+
+	if (close(client->fd_in) < 0)
+		i_error("close(client in) failed: %m");
+	if (client->fd_in != client->fd_out) {
+		if (close(client->fd_out) < 0)
+			i_error("close(client out) failed: %m");
+	}
+
+	if (array_is_created(&client->search_saved_uidset))
+		array_free(&client->search_saved_uidset);
+	if (array_is_created(&client->search_updates))
+		array_free(&client->search_updates);
+	str_free(&client->capability_string);
+	pool_unref(&client->command_pool);
+	i_free(client);
+
+	/* quit the program */
+	my_client = NULL;
+	master_service_stop(service);
+}
+
+void client_disconnect(struct client *client, const char *reason)
+{
+	i_assert(reason != NULL);
+
+	if (client->disconnected)
+		return;
+
+	i_info("Disconnected: %s %s", reason, client_stats(client));
+	client->disconnected = TRUE;
+	(void)o_stream_flush(client->output);
+
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+}
+
+void client_disconnect_with_error(struct client *client, const char *msg)
+{
+	client_send_line(client, t_strconcat("* BYE ", msg, NULL));
+	client_disconnect(client, msg);
+}
+
+int client_send_line(struct client *client, const char *data)
+{
+	struct const_iovec iov[2];
+
+	if (client->output->closed)
+		return -1;
+
+	iov[0].iov_base = data;
+	iov[0].iov_len = strlen(data);
+	iov[1].iov_base = "\r\n";
+	iov[1].iov_len = 2;
+
+	if (o_stream_sendv(client->output, iov, 2) < 0)
+		return -1;
+	client->last_output = ioloop_time;
+
+	if (o_stream_get_buffer_used_size(client->output) >=
+	    CLIENT_OUTPUT_OPTIMAL_SIZE) {
+		/* buffer full, try flushing */
+		return o_stream_flush(client->output);
+	}
+	return 1;
+}
+
+void client_send_tagline(struct client_command_context *cmd, const char *data)
+{
+	struct client *client = cmd->client;
+	const char *tag = cmd->tag;
+
+	if (client->output->closed || cmd->cancel)
+		return;
+
+	if (tag == NULL || *tag == '\0')
+		tag = "*";
+
+	(void)o_stream_send_str(client->output, tag);
+	(void)o_stream_send(client->output, " ", 1);
+	(void)o_stream_send_str(client->output, data);
+	(void)o_stream_send(client->output, "\r\n", 2);
+
+	client->last_output = ioloop_time;
+}
+
+void client_send_command_error(struct client_command_context *cmd,
+			       const char *msg)
+{
+	struct client *client = cmd->client;
+	const char *error, *cmd_name;
+	bool fatal;
+
+	if (msg == NULL) {
+		msg = imap_parser_get_error(cmd->parser, &fatal);
+		if (fatal) {
+			client_disconnect_with_error(client, msg);
+			return;
+		}
+	}
+
+	if (cmd->tag == NULL)
+		error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL);
+	else if (cmd->name == NULL)
+		error = t_strconcat("BAD Error in IMAP command: ", msg, NULL);
+	else {
+		cmd_name = t_str_ucase(cmd->name);
+		error = t_strconcat("BAD Error in IMAP command ",
+				    cmd_name, ": ", msg, NULL);
+	}
+
+	client_send_tagline(cmd, error);
+
+	if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+		client_disconnect_with_error(client,
+			"Too many invalid IMAP commands.");
+	}
+
+	cmd->param_error = TRUE;
+	/* client_read_args() failures rely on this being set, so that the
+	   command processing is stopped even while command function returns
+	   FALSE. */
+	cmd->state = CLIENT_COMMAND_STATE_DONE;
+}
+
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
+		      unsigned int flags, const struct imap_arg **args_r)
+{
+	string_t *str;
+	int ret;
+
+	i_assert(count <= INT_MAX);
+
+	ret = imap_parser_read_args(cmd->parser, count, flags, args_r);
+	if (ret >= (int)count) {
+		/* all parameters read successfully */
+		i_assert(cmd->client->input_lock == NULL ||
+			 cmd->client->input_lock == cmd);
+
+		str = t_str_new(256);
+		imap_write_args(str, *args_r);
+		cmd->args = p_strdup(cmd->pool, str_c(str));
+
+		cmd->client->input_lock = NULL;
+		return TRUE;
+	} else if (ret == -2) {
+		/* need more data */
+		if (cmd->client->input->closed) {
+			/* disconnected */
+			cmd->state = CLIENT_COMMAND_STATE_DONE;
+		}
+		return FALSE;
+	} else {
+		/* error, or missing arguments */
+		client_send_command_error(cmd, ret < 0 ? NULL :
+					  "Missing arguments");
+		return FALSE;
+	}
+}
+
+bool client_read_string_args(struct client_command_context *cmd,
+			     unsigned int count, ...)
+{
+	const struct imap_arg *imap_args;
+	va_list va;
+	const char *str;
+	unsigned int i;
+
+	if (!client_read_args(cmd, count, 0, &imap_args))
+		return FALSE;
+
+	va_start(va, count);
+	for (i = 0; i < count; i++) {
+		const char **ret = va_arg(va, const char **);
+
+		if (imap_args[i].type == IMAP_ARG_EOL) {
+			client_send_command_error(cmd, "Missing arguments.");
+			break;
+		}
+
+		str = imap_arg_string(&imap_args[i]);
+		if (str == NULL) {
+			client_send_command_error(cmd, "Invalid arguments.");
+			break;
+		}
+
+		if (ret != NULL)
+			*ret = str;
+	}
+	va_end(va);
+
+	return i == count;
+}
+
+static struct client_command_context *
+client_command_find_with_flags(struct client_command_context *new_cmd,
+			       enum command_flags flags)
+{
+	struct client_command_context *cmd;
+
+	cmd = new_cmd->client->command_queue;
+	for (; cmd != NULL; cmd = cmd->next) {
+		if (cmd != new_cmd && (cmd->cmd_flags & flags) != 0)
+			return cmd;
+	}
+	return NULL;
+}
+
+static bool client_command_check_ambiguity(struct client_command_context *cmd)
+{
+	enum command_flags flags;
+	bool broken_client = FALSE;
+
+	if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_MAILBOX) ==
+	    COMMAND_FLAG_BREAKS_MAILBOX) {
+		/* there must be no other command running that uses the
+		   selected mailbox */
+		flags = COMMAND_FLAG_USES_MAILBOX;
+	} else if ((cmd->cmd_flags & COMMAND_FLAG_USES_SEQS) != 0) {
+		/* no existing command must be breaking sequences */
+		flags = COMMAND_FLAG_BREAKS_SEQS;
+		broken_client = TRUE;
+	} else if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_SEQS) != 0) {
+		/* if existing command uses sequences, we'll have to block */
+		flags = COMMAND_FLAG_USES_SEQS;
+	} else {
+		return FALSE;
+	}
+
+	if (client_command_find_with_flags(cmd, flags) == NULL) {
+		if (cmd->client->syncing) {
+			/* don't do anything until syncing is finished */
+			return TRUE;
+		}
+		if (cmd->client->mailbox_change_lock != NULL &&
+		    cmd->client->mailbox_change_lock != cmd) {
+			/* don't do anything until mailbox is fully
+			   opened/closed */
+			return TRUE;
+		}
+		return FALSE;
+	}
+
+	if (broken_client) {
+		client_send_line(cmd->client,
+				 "* BAD ["IMAP_RESP_CODE_CLIENTBUG"] "
+				 "Command pipelining results in ambiguity.");
+	}
+
+	return TRUE;
+}
+
+static struct client_command_context *
+client_command_new(struct client *client)
+{
+	struct client_command_context *cmd;
+
+	cmd = p_new(client->command_pool, struct client_command_context, 1);
+	cmd->client = client;
+	cmd->pool = client->command_pool;
+	p_array_init(&cmd->module_contexts, cmd->pool, 5);
+
+	if (client->free_parser != NULL) {
+		cmd->parser = client->free_parser;
+		client->free_parser = NULL;
+	} else {
+		cmd->parser =
+			imap_parser_create(client->input, client->output,
+					   client->set->imap_max_line_length);
+	}
+
+	DLLIST_PREPEND(&client->command_queue, cmd);
+	client->command_queue_size++;
+
+	return cmd;
+}
+
+void client_command_free(struct client_command_context **_cmd)
+{
+	struct client_command_context *cmd = *_cmd;
+	struct client *client = cmd->client;
+
+	*_cmd = NULL;
+
+	/* reset input idle time because command output might have taken a
+	   long time and we don't want to disconnect client immediately then */
+	client->last_input = ioloop_time;
+	timeout_reset(client->to_idle);
+
+	if (cmd->cancel) {
+		cmd->cancel = FALSE;
+		client_send_tagline(cmd, "NO Command cancelled.");
+	}
+
+	if (!cmd->param_error)
+		client->bad_counter = 0;
+
+	if (client->input_lock == cmd)
+		client->input_lock = NULL;
+	if (client->output_lock == cmd)
+		client->output_lock = NULL;
+	if (client->mailbox_change_lock == cmd)
+		client->mailbox_change_lock = NULL;
+
+	if (client->free_parser != NULL)
+		imap_parser_destroy(&cmd->parser);
+	else {
+		imap_parser_reset(cmd->parser);
+		client->free_parser = cmd->parser;
+	}
+
+	client->command_queue_size--;
+	DLLIST_REMOVE(&client->command_queue, cmd);
+	cmd = NULL;
+
+	if (client->command_queue == NULL) {
+		/* no commands left in the queue, we can clear the pool */
+		p_clear(client->command_pool);
+		if (client->to_idle_output != NULL)
+			timeout_remove(&client->to_idle_output);
+	}
+}
+
+static void client_add_missing_io(struct client *client)
+{
+	if (client->io == NULL && !client->disconnected) {
+		client->io = io_add(client->fd_in,
+				    IO_READ, client_input, client);
+	}
+}
+
+void client_continue_pending_input(struct client **_client)
+{
+	struct client *client = *_client;
+	size_t size;
+
+	i_assert(!client->handling_input);
+
+	if (client->disconnected) {
+		if (!client->destroyed)
+			client_destroy(client, NULL);
+		*_client = NULL;
+		return;
+	}
+
+	if (client->input_lock != NULL) {
+		/* there's a command that has locked the input */
+		struct client_command_context *cmd = client->input_lock;
+
+		if (cmd->state != CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY)
+			return;
+
+		/* the command is waiting for existing ambiguity causing
+		   commands to finish. */
+		if (client_command_check_ambiguity(cmd))
+			return;
+		cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT;
+	}
+
+	client_add_missing_io(client);
+
+	/* if there's unread data in buffer, handle it. */
+	(void)i_stream_get_data(client->input, &size);
+	if (size > 0)
+		(void)client_handle_input(client);
+}
+
+/* Skip incoming data until newline is found,
+   returns TRUE if newline was found. */
+static bool client_skip_line(struct client *client)
+{
+	const unsigned char *data;
+	size_t i, data_size;
+
+	data = i_stream_get_data(client->input, &data_size);
+
+	for (i = 0; i < data_size; i++) {
+		if (data[i] == '\n') {
+			client->input_skip_line = FALSE;
+			i++;
+			break;
+		}
+	}
+
+	i_stream_skip(client->input, i);
+	return !client->input_skip_line;
+}
+
+static void client_idle_output_timeout(struct client *client)
+{
+	client_destroy(client,
+		       "Disconnected for inactivity in reading our output");
+}
+
+bool client_handle_unfinished_cmd(struct client_command_context *cmd)
+{
+	if (cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT) {
+		/* need more input */
+		return FALSE;
+	}
+	if (cmd->state != CLIENT_COMMAND_STATE_WAIT_OUTPUT) {
+		/* waiting for something */
+		if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) {
+			/* this is mainly for APPEND. */
+			client_add_missing_io(cmd->client);
+		}
+		return TRUE;
+	}
+
+	/* output is blocking, we can execute more commands */
+	o_stream_set_flush_pending(cmd->client->output, TRUE);
+	if (cmd->client->to_idle_output == NULL) {
+		/* disconnect sooner if client isn't reading our output */
+		cmd->client->to_idle_output =
+			timeout_add(CLIENT_OUTPUT_TIMEOUT_MSECS,
+				    client_idle_output_timeout, cmd->client);
+	}
+	return TRUE;
+}
+
+static bool client_command_input(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct command *command;
+
+        if (cmd->func != NULL) {
+		/* command is being executed - continue it */
+		if (cmd->func(cmd) || cmd->state == CLIENT_COMMAND_STATE_DONE) {
+			/* command execution was finished */
+			client_command_free(&cmd);
+			client_add_missing_io(client);
+			return TRUE;
+		}
+
+		return client_handle_unfinished_cmd(cmd);
+	}
+
+	if (cmd->tag == NULL) {
+                cmd->tag = imap_parser_read_word(cmd->parser);
+		if (cmd->tag == NULL)
+			return FALSE; /* need more data */
+		cmd->tag = p_strdup(cmd->pool, cmd->tag);
+	}
+
+	if (cmd->name == NULL) {
+		cmd->name = imap_parser_read_word(cmd->parser);
+		if (cmd->name == NULL)
+			return FALSE; /* need more data */
+		cmd->name = p_strdup(cmd->pool, cmd->name);
+	}
+
+	client->input_skip_line = TRUE;
+
+	if (cmd->name == '\0') {
+		/* command not given - cmd_func is already NULL. */
+	} else if ((command = command_find(cmd->name)) != NULL) {
+		cmd->func = command->func;
+		cmd->cmd_flags = command->flags;
+		if (client_command_check_ambiguity(cmd)) {
+			/* do nothing until existing commands are finished */
+			i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT);
+			cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
+			io_remove(&client->io);
+			return FALSE;
+		}
+	}
+
+	if (cmd->func == NULL) {
+		/* unknown command */
+		client_send_command_error(cmd, "Unknown command.");
+		cmd->param_error = TRUE;
+		client_command_free(&cmd);
+		return TRUE;
+	} else {
+		i_assert(!client->disconnected);
+
+		return client_command_input(cmd);
+	}
+}
+
+static bool client_handle_next_command(struct client *client, bool *remove_io_r)
+{
+	size_t size;
+
+	*remove_io_r = FALSE;
+
+	if (client->input_lock != NULL) {
+		if (client->input_lock->state ==
+		    CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) {
+			*remove_io_r = TRUE;
+			return FALSE;
+		}
+		return client_command_input(client->input_lock);
+	}
+
+	if (client->input_skip_line) {
+		/* first eat the previous command line */
+		if (!client_skip_line(client))
+			return FALSE;
+		client->input_skip_line = FALSE;
+	}
+
+	/* don't bother creating a new client command before there's at least
+	   some input */
+	(void)i_stream_get_data(client->input, &size);
+	if (size == 0)
+		return FALSE;
+
+	/* beginning a new command */
+	if (client->command_queue_size >= CLIENT_COMMAND_QUEUE_MAX_SIZE ||
+	    client->output_lock != NULL) {
+		/* wait for some of the commands to finish */
+		*remove_io_r = TRUE;
+		return FALSE;
+	}
+
+	client->input_lock = client_command_new(client);
+	return client_command_input(client->input_lock);
+}
+
+bool client_handle_input(struct client *client)
+{
+	bool ret, remove_io, handled_commands = FALSE;
+
+	client->handling_input = TRUE;
+	do {
+		T_BEGIN {
+			ret = client_handle_next_command(client, &remove_io);
+		} T_END;
+		if (ret)
+			handled_commands = TRUE;
+	} while (ret && !client->disconnected && client->io != NULL);
+	client->handling_input = FALSE;
+
+	if (client->output->closed) {
+		client_destroy(client, NULL);
+		return TRUE;
+	} else {
+		if (remove_io)
+			io_remove(&client->io);
+		else
+			client_add_missing_io(client);
+		if (!handled_commands)
+			return FALSE;
+
+		ret = cmd_sync_delayed(client);
+		if (ret)
+			client_continue_pending_input(&client);
+		return TRUE;
+	}
+}
+
+void client_input(struct client *client)
+{
+	struct client_command_context *cmd;
+	struct ostream *output = client->output;
+	ssize_t bytes;
+
+	i_assert(client->io != NULL);
+
+	client->last_input = ioloop_time;
+	timeout_reset(client->to_idle);
+
+	bytes = i_stream_read(client->input);
+	if (bytes == -1) {
+		/* disconnected */
+		client_destroy(client, NULL);
+		return;
+	}
+
+	o_stream_ref(output);
+	o_stream_cork(output);
+	if (!client_handle_input(client) && bytes == -2) {
+		/* parameter word is longer than max. input buffer size.
+		   this is most likely an error, so skip the new data
+		   until newline is found. */
+		client->input_skip_line = TRUE;
+
+		cmd = client->input_lock != NULL ? client->input_lock :
+			client_command_new(client);
+		cmd->param_error = TRUE;
+		client_send_command_error(cmd, "Too long argument.");
+		client_command_free(&cmd);
+	}
+	o_stream_uncork(output);
+	o_stream_unref(&output);
+}
+
+static void client_output_cmd(struct client_command_context *cmd)
+{
+	bool finished;
+
+	/* continue processing command */
+	finished = cmd->func(cmd) || cmd->state == CLIENT_COMMAND_STATE_DONE;
+
+	if (!finished)
+		(void)client_handle_unfinished_cmd(cmd);
+	else {
+		/* command execution was finished */
+		client_command_free(&cmd);
+	}
+}
+
+int client_output(struct client *client)
+{
+	struct client_command_context *cmd;
+	int ret;
+
+	i_assert(!client->destroyed);
+
+	client->last_output = ioloop_time;
+	timeout_reset(client->to_idle);
+	if (client->to_idle_output != NULL)
+		timeout_reset(client->to_idle_output);
+
+	if ((ret = o_stream_flush(client->output)) < 0) {
+		client_destroy(client, NULL);
+		return 1;
+	}
+
+	/* mark all commands non-executed */
+	for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next)
+		cmd->temp_executed = FALSE;
+
+	o_stream_cork(client->output);
+	if (client->output_lock != NULL) {
+		client->output_lock->temp_executed = TRUE;
+		client_output_cmd(client->output_lock);
+	}
+	while (client->output_lock == NULL) {
+		/* go through the entire commands list every time in case
+		   multiple commands were freed. temp_executed keeps track of
+		   which messages we've called so far */
+		cmd = client->command_queue;
+		for (; cmd != NULL; cmd = cmd->next) {
+			if (!cmd->temp_executed &&
+			    cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT) {
+				cmd->temp_executed = TRUE;
+				client_output_cmd(cmd);
+				break;
+			}
+		}
+		if (cmd == NULL) {
+			/* all commands executed */
+			break;
+		}
+	}
+
+	if (client->output->closed) {
+		client_destroy(client, NULL);
+		return 1;
+	} else {
+		(void)cmd_sync_delayed(client);
+		o_stream_uncork(client->output);
+		client_continue_pending_input(&client);
+		return ret;
+	}
+}
+
+bool client_handle_search_save_ambiguity(struct client_command_context *cmd)
+{
+	struct client_command_context *old_cmd = cmd->next;
+
+	/* search only commands that were added before this command
+	   (commands are prepended to the queue, so they're after ourself) */
+	for (; old_cmd != NULL; old_cmd = old_cmd->next) {
+		if (old_cmd->search_save_result)
+			break;
+	}
+	if (old_cmd == NULL)
+		return FALSE;
+
+	/* ambiguity, wait until it's over */
+	i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT);
+	cmd->client->input_lock = cmd;
+	cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY;
+	io_remove(&cmd->client->io);
+	return TRUE;
+}
+
+void client_enable(struct client *client, enum mailbox_feature features)
+{
+	struct mailbox_status status;
+
+	if ((client->enabled_features & features) == features)
+		return;
+
+	client->enabled_features |= features;
+	if (client->mailbox == NULL)
+		return;
+
+	mailbox_enable(client->mailbox, features);
+	if ((features & MAILBOX_FEATURE_CONDSTORE) != 0) {
+		/* CONDSTORE being enabled while mailbox is selected.
+		   Notify client of the latest HIGHESTMODSEQ. */
+		mailbox_get_status(client->mailbox,
+				   STATUS_HIGHESTMODSEQ, &status);
+		client_send_line(client, t_strdup_printf(
+			"* OK [HIGHESTMODSEQ %llu]",
+			(unsigned long long)status.highest_modseq));
+	}
+}
+
+struct imap_search_update *
+client_search_update_lookup(struct client *client, const char *tag,
+			    unsigned int *idx_r)
+{
+	struct imap_search_update *updates;
+	unsigned int i, count;
+
+	if (!array_is_created(&client->search_updates))
+		return NULL;
+
+	updates = array_get_modifiable(&client->search_updates, &count);
+	for (i = 0; i < count; i++) {
+		if (strcmp(updates[i].tag, tag) == 0) {
+			*idx_r = i;
+			return &updates[i];
+		}
+	}
+	return NULL;
+}
+
+void client_search_updates_free(struct client *client)
+{
+	struct imap_search_update *updates;
+	unsigned int i, count;
+
+	if (!array_is_created(&client->search_updates))
+		return;
+
+	updates = array_get_modifiable(&client->search_updates, &count);
+	for (i = 0; i < count; i++) {
+		i_free(updates[i].tag);
+		mailbox_search_result_free(&updates[i].result);
+	}
+	array_clear(&client->search_updates);
+}
+
+void clients_init(void)
+{
+	my_client = NULL;
+}
+
+void clients_deinit(void)
+{
+	if (my_client != NULL) {
+		client_send_line(my_client, "* BYE Server shutting down.");
+		client_destroy(my_client, "Server shutting down");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-client.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,200 @@
+#ifndef IMAP_CLIENT_H
+#define IMAP_CLIENT_H
+
+#include "imap-commands.h"
+
+#define CLIENT_COMMAND_QUEUE_MAX_SIZE 4
+/* Maximum number of CONTEXT=SEARCH UPDATEs. Clients probably won't need more
+   than a few, so this is mainly to avoid more or less accidental pointless
+   resource usage. */
+#define CLIENT_MAX_SEARCH_UPDATES 10
+
+struct client;
+struct mail_storage;
+struct imap_parser;
+struct imap_arg;
+
+struct mailbox_keywords {
+	/* All keyword names. The array itself exists in mail_index.
+	   Keywords are currently only appended, they're never removed. */
+	const ARRAY_TYPE(keywords) *names;
+	/* Number of keywords announced to client via FLAGS/PERMANENTFLAGS.
+	   This relies on keywords not being removed while mailbox is
+	   selected. */
+	unsigned int announce_count;
+};
+
+struct imap_search_update {
+	char *tag;
+	struct mail_search_result *result;
+	bool return_uids;
+};
+
+enum client_command_state {
+	/* Waiting for more input */
+	CLIENT_COMMAND_STATE_WAIT_INPUT,
+	/* Waiting to be able to send more output */
+	CLIENT_COMMAND_STATE_WAIT_OUTPUT,
+	/* Wait for other commands to finish execution */
+	CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY,
+	/* Waiting for other commands to finish so we can sync */
+	CLIENT_COMMAND_STATE_WAIT_SYNC,
+	/* Command is finished */
+	CLIENT_COMMAND_STATE_DONE
+};
+
+struct imap_module_register {
+	unsigned int id;
+};
+
+union imap_module_context {
+	struct imap_module_register *reg;
+};
+extern struct imap_module_register imap_module_register;
+
+struct client_command_context {
+	struct client_command_context *prev, *next;
+	struct client *client;
+
+	pool_t pool;
+	/* IMAP command tag */
+	const char *tag;
+	/* Name of this command */
+	const char *name;
+	/* Parameters for this command. These are generated from parsed IMAP
+	   arguments, so they may not be exactly the same as how client sent
+	   them. */
+	const char *args;
+	enum command_flags cmd_flags;
+
+	command_func_t *func;
+	void *context;
+
+	/* Module-specific contexts. */
+	ARRAY_DEFINE(module_contexts, union imap_module_context *);
+
+	struct imap_parser *parser;
+	enum client_command_state state;
+
+	struct client_sync_context *sync;
+
+	unsigned int uid:1; /* used UID command */
+	unsigned int cancel:1; /* command is wanted to be cancelled */
+	unsigned int param_error:1;
+	unsigned int search_save_result:1; /* search result is being updated */
+	unsigned int temp_executed:1; /* temporary execution state tracking */
+};
+
+struct client {
+	int fd_in, fd_out;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+	struct timeout *to_idle, *to_idle_output;
+
+        const struct imap_settings *set;
+	enum client_workarounds workarounds;
+	string_t *capability_string;
+
+        struct mail_user *user;
+	struct mailbox *mailbox;
+        struct mailbox_keywords keywords;
+	unsigned int select_counter; /* increased when mailbox is changed */
+	unsigned int sync_counter;
+	uint32_t messages_count, recent_count, uidvalidity;
+	enum mailbox_feature enabled_features;
+
+	time_t last_input, last_output;
+	unsigned int bad_counter;
+
+	/* one parser is kept here to be used for new commands */
+	struct imap_parser *free_parser;
+	/* command_pool is cleared when the command queue gets empty */
+	pool_t command_pool;
+	/* New commands are always prepended to the queue */
+	struct client_command_context *command_queue;
+	unsigned int command_queue_size;
+
+	uint64_t sync_last_full_modseq;
+	uint64_t highest_fetch_modseq;
+
+	/* SEARCHRES extension: Last saved SEARCH result */
+	ARRAY_TYPE(seq_range) search_saved_uidset;
+	/* SEARCH=CONTEXT extension: Searches that get updated */
+	ARRAY_DEFINE(search_updates, struct imap_search_update);
+
+	/* client input/output is locked by this command */
+	struct client_command_context *input_lock;
+	struct client_command_context *output_lock;
+	/* command changing the mailbox */
+	struct client_command_context *mailbox_change_lock;
+
+	/* syncing marks this TRUE when it sees \Deleted flags. this is by
+	   EXPUNGE for Outlook-workaround. */
+	unsigned int sync_seen_deletes:1;
+	unsigned int sync_seen_expunges:1;
+	unsigned int disconnected:1;
+	unsigned int destroyed:1;
+	unsigned int handling_input:1;
+	unsigned int syncing:1;
+	unsigned int id_logged:1;
+	unsigned int mailbox_examined:1;
+	unsigned int input_skip_line:1; /* skip all the data until we've
+					   found a new line */
+	unsigned int modseqs_sent_since_sync:1;
+};
+
+/* Create new client with specified input/output handles. socket specifies
+   if the handle is a socket. */
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct imap_settings *set);
+void client_destroy(struct client *client, const char *reason);
+
+/* Disconnect client connection */
+void client_disconnect(struct client *client, const char *reason);
+void client_disconnect_with_error(struct client *client, const char *msg);
+
+/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full,
+   -1 if error */
+int client_send_line(struct client *client, const char *data);
+/* Send line of data to client, prefixed with client->tag */
+void client_send_tagline(struct client_command_context *cmd, const char *data);
+
+/* Send BAD command error to client. msg can be NULL. */
+void client_send_command_error(struct client_command_context *cmd,
+			       const char *msg);
+
+/* Read a number of arguments. Returns TRUE if everything was read or
+   FALSE if either needs more data or error occurred. */
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
+		      unsigned int flags, const struct imap_arg **args_r);
+/* Reads a number of string arguments. ... is a list of pointers where to
+   store the arguments. */
+bool client_read_string_args(struct client_command_context *cmd,
+			     unsigned int count, ...);
+
+/* SEARCHRES extension: Call if $ is being used/updated, returns TRUE if we
+   have to wait for an existing SEARCH SAVE to finish. */
+bool client_handle_search_save_ambiguity(struct client_command_context *cmd);
+
+void client_enable(struct client *client, enum mailbox_feature features);
+
+struct imap_search_update *
+client_search_update_lookup(struct client *client, const char *tag,
+			    unsigned int *idx_r);
+void client_search_updates_free(struct client *client);
+
+void clients_init(void);
+void clients_deinit(void);
+
+void client_command_cancel(struct client_command_context **cmd);
+void client_command_free(struct client_command_context **cmd);
+
+bool client_handle_unfinished_cmd(struct client_command_context *cmd);
+void client_continue_pending_input(struct client **_client);
+
+void client_input(struct client *client);
+bool client_handle_input(struct client *client);
+int client_output(struct client *client);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-commands-util.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,411 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "imap-common.h"
+#include "array.h"
+#include "buffer.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "imap-resp-code.h"
+#include "imap-parser.h"
+#include "imap-sync.h"
+#include "imap-util.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "imap-commands-util.h"
+
+/* Maximum length for mailbox name, including it's path. This isn't fully
+   exact since the user can create folder hierarchy with small names, then
+   rename them to larger names. Mail storages should set more strict limits
+   to them, mbox/maildir currently allow paths only up to PATH_MAX. */
+#define MAILBOX_MAX_NAME_LEN 512
+
+struct mail_namespace *
+client_find_namespace(struct client_command_context *cmd, const char **mailbox)
+{
+	struct mail_namespace *ns;
+
+	ns = mail_namespace_find(cmd->client->user->namespaces, mailbox);
+	if (ns != NULL)
+		return ns;
+
+	client_send_tagline(cmd, "NO Unknown namespace.");
+	return NULL;
+}
+
+struct mail_storage *
+client_find_storage(struct client_command_context *cmd, const char **mailbox)
+{
+	struct mail_namespace *ns;
+
+	ns = client_find_namespace(cmd, mailbox);
+	return ns == NULL ? NULL : ns->storage;
+}
+
+bool client_verify_mailbox_name(struct client_command_context *cmd,
+				const char *mailbox,
+				bool should_exist, bool should_not_exist)
+{
+	struct mail_namespace *ns;
+	struct mailbox_list *list;
+	enum mailbox_name_status mailbox_status;
+	const char *orig_mailbox, *p;
+
+	orig_mailbox = mailbox;
+	ns = client_find_namespace(cmd, &mailbox);
+	if (ns == NULL)
+		return FALSE;
+
+	/* make sure it even looks valid */
+	if (*mailbox == '\0') {
+		client_send_tagline(cmd, "NO Empty mailbox name.");
+		return FALSE;
+	}
+
+	if (ns->real_sep != ns->sep && ns->prefix_len < strlen(orig_mailbox)) {
+		/* make sure there are no real separators used in the mailbox
+		   name. */
+		orig_mailbox += ns->prefix_len;
+		for (p = orig_mailbox; *p != '\0'; p++) {
+			if (*p == ns->real_sep) {
+				client_send_tagline(cmd, t_strdup_printf(
+					"NO Character not allowed "
+					"in mailbox name: '%c'",
+					ns->real_sep));
+				return FALSE;
+			}
+		}
+	}
+
+	/* make sure two hierarchy separators aren't next to each others */
+	for (p = mailbox+1; *p != '\0'; p++) {
+		if (p[0] == ns->real_sep && p[-1] == ns->real_sep) {
+			client_send_tagline(cmd, "NO Invalid mailbox name.");
+			return FALSE;
+		}
+	}
+
+	if (strlen(mailbox) > MAILBOX_MAX_NAME_LEN) {
+		client_send_tagline(cmd, "NO Mailbox name too long.");
+		return FALSE;
+	}
+
+	/* check what our storage thinks of it */
+	list = mail_storage_get_list(ns->storage);
+	if (mailbox_list_get_mailbox_name_status(list, mailbox,
+						 &mailbox_status) < 0) {
+		client_send_storage_error(cmd, ns->storage);
+		return FALSE;
+	}
+
+	switch (mailbox_status) {
+	case MAILBOX_NAME_EXISTS:
+		if (should_exist || !should_not_exist)
+			return TRUE;
+
+		client_send_tagline(cmd, "NO Mailbox exists.");
+		break;
+
+	case MAILBOX_NAME_VALID:
+		if (!should_exist)
+			return TRUE;
+
+		client_send_tagline(cmd, t_strconcat(
+			"NO [TRYCREATE] Mailbox doesn't exist: ",
+			str_sanitize(orig_mailbox, MAILBOX_MAX_NAME_LEN),
+			NULL));
+		break;
+
+	case MAILBOX_NAME_INVALID:
+		client_send_tagline(cmd, t_strconcat(
+			"NO Invalid mailbox name: ",
+			str_sanitize(orig_mailbox, MAILBOX_MAX_NAME_LEN),
+			NULL));
+		break;
+
+	case MAILBOX_NAME_NOINFERIORS:
+		client_send_tagline(cmd,
+			"NO Mailbox parent doesn't allow inferior mailboxes.");
+		break;
+
+	default:
+                i_unreached();
+	}
+
+	return FALSE;
+}
+
+bool client_verify_open_mailbox(struct client_command_context *cmd)
+{
+	if (cmd->client->mailbox != NULL)
+		return TRUE;
+	else {
+		client_send_tagline(cmd, "BAD No mailbox selected.");
+		return FALSE;
+	}
+}
+
+static const char *
+get_error_string(const char *error_string, enum mail_error error)
+{
+	const char *resp_code = NULL;
+
+	switch (error) {
+	case MAIL_ERROR_NONE:
+		break;
+	case MAIL_ERROR_TEMP:
+		resp_code = IMAP_RESP_CODE_SERVERBUG;
+		break;
+	case MAIL_ERROR_NOTPOSSIBLE:
+	case MAIL_ERROR_PARAMS:
+		resp_code = IMAP_RESP_CODE_CANNOT;
+		break;
+	case MAIL_ERROR_PERM:
+		resp_code = IMAP_RESP_CODE_NOPERM;
+		break;
+	case MAIL_ERROR_NOSPACE:
+		resp_code = IMAP_RESP_CODE_OVERQUOTA;
+		break;
+	case MAIL_ERROR_NOTFOUND:
+		resp_code = IMAP_RESP_CODE_NONEXISTENT;
+		break;
+	case MAIL_ERROR_EXISTS:
+		resp_code = IMAP_RESP_CODE_ALREADYEXISTS;
+		break;
+	case MAIL_ERROR_EXPUNGED:
+		resp_code = IMAP_RESP_CODE_EXPUNGEISSUED;
+		break;
+	case MAIL_ERROR_INUSE:
+		resp_code = IMAP_RESP_CODE_INUSE;
+		break;
+	}
+	if (resp_code == NULL || *error_string == '[')
+		return t_strconcat("NO ", error_string, NULL);
+	else
+		return t_strdup_printf("NO [%s] %s", resp_code, error_string);
+}
+
+void client_send_list_error(struct client_command_context *cmd,
+			    struct mailbox_list *list)
+{
+	const char *error_string;
+	enum mail_error error;
+
+	error_string = mailbox_list_get_last_error(list, &error);
+	client_send_tagline(cmd, get_error_string(error_string, error));
+}
+
+void client_send_storage_error(struct client_command_context *cmd,
+			       struct mail_storage *storage)
+{
+	const char *error_string;
+	enum mail_error error;
+
+	if (cmd->client->mailbox != NULL &&
+	    mailbox_is_inconsistent(cmd->client->mailbox)) {
+		/* we can't do forced CLOSE, so have to disconnect */
+		client_disconnect_with_error(cmd->client,
+			"IMAP session state is inconsistent, please relogin.");
+		return;
+	}
+
+	error_string = mail_storage_get_last_error(storage, &error);
+	client_send_tagline(cmd, get_error_string(error_string, error));
+}
+
+void client_send_untagged_storage_error(struct client *client,
+					struct mail_storage *storage)
+{
+	const char *error_string;
+	enum mail_error error;
+
+	if (client->mailbox != NULL &&
+	    mailbox_is_inconsistent(client->mailbox)) {
+		/* we can't do forced CLOSE, so have to disconnect */
+		client_disconnect_with_error(client,
+			"IMAP session state is inconsistent, please relogin.");
+		return;
+	}
+
+	error_string = mail_storage_get_last_error(storage, &error);
+	client_send_line(client, t_strconcat("* NO ", error_string, NULL));
+}
+
+bool client_parse_mail_flags(struct client_command_context *cmd,
+			     const struct imap_arg *args,
+			     enum mail_flags *flags_r,
+			     const char *const **keywords_r)
+{
+	const char *atom;
+	ARRAY_DEFINE(keywords, const char *);
+
+	*flags_r = 0;
+	*keywords_r = NULL;
+	p_array_init(&keywords, cmd->pool, 16);
+
+	while (args->type != IMAP_ARG_EOL) {
+		if (args->type != IMAP_ARG_ATOM) {
+			client_send_command_error(cmd,
+				"Flags list contains non-atoms.");
+			return FALSE;
+		}
+
+		atom = IMAP_ARG_STR(args);
+		if (*atom == '\\') {
+			/* system flag */
+			atom = t_str_ucase(atom);
+			if (strcmp(atom, "\\ANSWERED") == 0)
+				*flags_r |= MAIL_ANSWERED;
+			else if (strcmp(atom, "\\FLAGGED") == 0)
+				*flags_r |= MAIL_FLAGGED;
+			else if (strcmp(atom, "\\DELETED") == 0)
+				*flags_r |= MAIL_DELETED;
+			else if (strcmp(atom, "\\SEEN") == 0)
+				*flags_r |= MAIL_SEEN;
+			else if (strcmp(atom, "\\DRAFT") == 0)
+				*flags_r |= MAIL_DRAFT;
+			else {
+				client_send_tagline(cmd, t_strconcat(
+					"BAD Invalid system flag ",
+					atom, NULL));
+				return FALSE;
+			}
+		} else {
+			/* keyword validity checks are done by lib-storage */
+			array_append(&keywords, &atom, 1);
+		}
+
+		args++;
+	}
+
+	if (array_count(&keywords) == 0)
+		*keywords_r = NULL;
+	else {
+		(void)array_append_space(&keywords); /* NULL-terminate */
+		*keywords_r = array_idx(&keywords, 0);
+	}
+	return TRUE;
+}
+
+static const char *get_keywords_string(const ARRAY_TYPE(keywords) *keywords)
+{
+	string_t *str;
+	const char *const *names;
+	unsigned int i, count;
+
+	str = t_str_new(256);
+	names = array_get(keywords, &count);
+	for (i = 0; i < count; i++) {
+		str_append_c(str, ' ');
+		str_append(str, names[i]);
+	}
+	return str_c(str);
+}
+
+#define SYSTEM_FLAGS "\\Answered \\Flagged \\Deleted \\Seen \\Draft"
+
+void client_send_mailbox_flags(struct client *client, bool selecting)
+{
+	unsigned int count = array_count(client->keywords.names);
+	const char *str;
+
+	if (!selecting && count == client->keywords.announce_count) {
+		/* no changes to keywords and we're not selecting a mailbox */
+		return;
+	}
+
+	client->keywords.announce_count = count;
+	str = count == 0 ? "" : get_keywords_string(client->keywords.names);
+	client_send_line(client,
+		t_strconcat("* FLAGS ("SYSTEM_FLAGS, str, ")", NULL));
+
+	if (mailbox_is_readonly(client->mailbox)) {
+		client_send_line(client, "* OK [PERMANENTFLAGS ()] "
+				 "Read-only mailbox.");
+	} else {
+		bool star = mailbox_allow_new_keywords(client->mailbox);
+
+		client_send_line(client,
+			t_strconcat("* OK [PERMANENTFLAGS ("SYSTEM_FLAGS, str,
+				    star ? " \\*" : "",
+				    ")] Flags permitted.", NULL));
+	}
+}
+
+void client_update_mailbox_flags(struct client *client,
+				 const ARRAY_TYPE(keywords) *keywords)
+{
+	client->keywords.names = keywords;
+	client->keywords.announce_count = 0;
+}
+
+const char *const *
+client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
+			 const ARRAY_TYPE(keyword_indexes) *src)
+{
+	const unsigned int *kw_indexes;
+	const char *const *all_names;
+	unsigned int i, kw_count, all_count;
+
+	client_send_mailbox_flags(client, FALSE);
+
+	all_names = array_get(client->keywords.names, &all_count);
+	kw_indexes = array_get(src, &kw_count);
+
+	/* convert indexes to names */
+	array_clear(dest);
+	for (i = 0; i < kw_count; i++) {
+		i_assert(kw_indexes[i] < all_count);
+		array_append(dest, &all_names[kw_indexes[i]], 1);
+	}
+
+	(void)array_append_space(dest);
+	return array_idx(dest, 0);
+}
+
+bool mailbox_equals(const struct mailbox *box1,
+		    const struct mail_storage *storage2, const char *name2)
+{
+	struct mail_storage *storage1 = mailbox_get_storage(box1);
+	const char *name1;
+
+	if (storage1 != storage2)
+		return FALSE;
+
+        name1 = mailbox_get_name(box1);
+	if (strcmp(name1, name2) == 0)
+		return TRUE;
+
+	return strcasecmp(name1, "INBOX") == 0 &&
+		strcasecmp(name2, "INBOX") == 0;
+}
+
+void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->str = str;
+	ctx->last_uid = (uint32_t)-1;
+}
+
+void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid)
+{
+	if (uid != ctx->last_uid+1) {
+		if (ctx->first_uid == 0)
+			;
+		else if (ctx->first_uid == ctx->last_uid)
+			str_printfa(ctx->str, "%u,", ctx->first_uid);
+		else {
+			str_printfa(ctx->str, "%u:%u,",
+				    ctx->first_uid, ctx->last_uid);
+		}
+		ctx->first_uid = uid;
+	}
+	ctx->last_uid = uid;
+}
+
+void msgset_generator_finish(struct msgset_generator_context *ctx)
+{
+	if (ctx->first_uid == ctx->last_uid)
+		str_printfa(ctx->str, "%u", ctx->first_uid);
+	else
+		str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-commands-util.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,72 @@
+#ifndef IMAP_COMMANDS_UTIL_H
+#define IMAP_COMMANDS_UTIL_H
+
+struct msgset_generator_context {
+	string_t *str;
+	uint32_t first_uid, last_uid;
+};
+
+struct mail_full_flags;
+struct mailbox_keywords;
+
+/* Finds namespace for given mailbox from namespaces. If not found,
+   sends "Unknown namespace" error message to client. */
+struct mail_namespace *
+client_find_namespace(struct client_command_context *cmd, const char **mailbox);
+/* Finds mail storage for given mailbox from namespaces. If not found,
+   sends "Unknown namespace" error message to client. */
+struct mail_storage *
+client_find_storage(struct client_command_context *cmd, const char **mailbox);
+
+/* If should_exist is TRUE, this function returns TRUE if the mailbox
+   exists. If it doesn't exist but would be a valid mailbox name, the
+   error message is prefixed with [TRYCREATE].
+
+   If should_exist is FALSE, the should_not_exist specifies if we should
+   return TRUE or FALSE if mailbox doesn't exist. */
+bool client_verify_mailbox_name(struct client_command_context *cmd,
+				const char *mailbox,
+				bool should_exist, bool should_not_exist);
+
+/* Returns TRUE if mailbox is selected. If not, sends "No mailbox selected"
+   error message to client. */
+bool client_verify_open_mailbox(struct client_command_context *cmd);
+
+/* Send last mailbox list error message to client. */
+void client_send_list_error(struct client_command_context *cmd,
+			    struct mailbox_list *list);
+/* Send last mail storage error message to client. */
+void client_send_storage_error(struct client_command_context *cmd,
+			       struct mail_storage *storage);
+
+/* Send untagged error message to client. */
+void client_send_untagged_storage_error(struct client *client,
+					struct mail_storage *storage);
+
+/* Parse flags. Returns TRUE if successful, if not sends an error message to
+   client. */
+bool client_parse_mail_flags(struct client_command_context *cmd,
+			     const struct imap_arg *args,
+			     enum mail_flags *flags_r,
+			     const char *const **keywords_r);
+
+/* Send FLAGS + PERMANENTFLAGS to client if they have changed,
+   or if selecting=TRUE. */
+void client_send_mailbox_flags(struct client *client, bool selecting);
+/* Update client->keywords array. Use keywords=NULL when unselecting. */
+void client_update_mailbox_flags(struct client *client,
+				 const ARRAY_TYPE(keywords) *keywords);
+/* Convert keyword indexes to keyword names in selected mailbox. */
+const char *const *
+client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
+			 const ARRAY_TYPE(keyword_indexes) *src);
+
+bool mailbox_equals(const struct mailbox *box1,
+		    const struct mail_storage *storage2,
+		    const char *name2) ATTR_PURE;
+
+void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str);
+void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid);
+void msgset_generator_finish(struct msgset_generator_context *ctx);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-commands.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,149 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "imap-common.h"
+#include "array.h"
+#include "buffer.h"
+#include "imap-commands.h"
+
+#include <stdlib.h>
+
+static const struct command imap4rev1_commands[] = {
+	{ "CAPABILITY",		cmd_capability,  0 },
+	{ "LOGOUT",		cmd_logout,      COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "NOOP",		cmd_noop,        COMMAND_FLAG_BREAKS_SEQS },
+
+	{ "APPEND",		cmd_append,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "EXAMINE",		cmd_examine,     COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "CREATE",		cmd_create,      0 },
+	{ "DELETE",		cmd_delete,      0 },
+	{ "RENAME",		cmd_rename,      0 },
+	{ "LIST",		cmd_list,        0 },
+	{ "LSUB",		cmd_lsub,        0 },
+	{ "SELECT",		cmd_select,      COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "STATUS",		cmd_status,      0 },
+	{ "SUBSCRIBE",		cmd_subscribe,   0 },
+	{ "UNSUBSCRIBE",	cmd_unsubscribe, 0 },
+
+	{ "CHECK",		cmd_check,       COMMAND_FLAG_BREAKS_SEQS },
+	{ "CLOSE",		cmd_close,       COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "COPY",		cmd_copy,        COMMAND_FLAG_USES_SEQS |
+						 COMMAND_FLAG_BREAKS_SEQS },
+	{ "EXPUNGE",		cmd_expunge,     COMMAND_FLAG_BREAKS_SEQS },
+	{ "FETCH",		cmd_fetch,       COMMAND_FLAG_USES_SEQS },
+	{ "SEARCH",		cmd_search,      COMMAND_FLAG_USES_SEQS },
+	{ "STORE",		cmd_store,       COMMAND_FLAG_USES_SEQS },
+	{ "UID",		cmd_uid,         0 },
+	{ "UID COPY",		cmd_copy,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID FETCH",		cmd_fetch,       COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID SEARCH",		cmd_search,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID STORE",		cmd_store,       COMMAND_FLAG_BREAKS_SEQS }
+};
+#define IMAP4REV1_COMMANDS_COUNT N_ELEMENTS(imap4rev1_commands)
+
+static const struct command imap_ext_commands[] = {
+	{ "CANCELUPDATE",	cmd_cancelupdate,0 },
+	{ "ENABLE",		cmd_enable,      0 },
+	{ "ID",			cmd_id,          0 },
+	{ "IDLE",		cmd_idle,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "NAMESPACE",		cmd_namespace,   0 },
+	{ "SORT",		cmd_sort,        COMMAND_FLAG_USES_SEQS },
+	{ "THREAD",		cmd_thread,      COMMAND_FLAG_USES_SEQS },
+	{ "UID EXPUNGE",	cmd_uid_expunge, COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID SORT",		cmd_sort,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID THREAD",		cmd_thread,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "UNSELECT",		cmd_unselect,    COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "X-CANCEL",		cmd_x_cancel,    0 }
+};
+#define IMAP_EXT_COMMANDS_COUNT N_ELEMENTS(imap_ext_commands)
+
+ARRAY_TYPE(command) imap_commands;
+static bool commands_unsorted;
+
+void command_register(const char *name, command_func_t *func,
+		      enum command_flags flags)
+{
+	struct command cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.name = name;
+	cmd.func = func;
+	cmd.flags = flags;
+	array_append(&imap_commands, &cmd, 1);
+
+	commands_unsorted = TRUE;
+}
+
+void command_unregister(const char *name)
+{
+	const struct command *cmd;
+	unsigned int i, count;
+
+	cmd = array_get(&imap_commands, &count);
+	for (i = 0; i < count; i++) {
+		if (strcasecmp(cmd[i].name, name) == 0) {
+			array_delete(&imap_commands, i, 1);
+			return;
+		}
+	}
+
+	i_error("Trying to unregister unknown command '%s'", name);
+}
+
+void command_register_array(const struct command *cmdarr, unsigned int count)
+{
+	commands_unsorted = TRUE;
+	array_append(&imap_commands, cmdarr, count);
+}
+
+void command_unregister_array(const struct command *cmdarr, unsigned int count)
+{
+	while (count > 0) {
+		command_unregister(cmdarr->name);
+		count--; cmdarr++;
+	}
+}
+
+static int command_cmp(const void *p1, const void *p2)
+{
+        const struct command *c1 = p1, *c2 = p2;
+
+	return strcasecmp(c1->name, c2->name);
+}
+
+static int command_bsearch(const void *name, const void *cmd_p)
+{
+        const struct command *cmd = cmd_p;
+
+	return strcasecmp(name, cmd->name);
+}
+
+struct command *command_find(const char *name)
+{
+	void *base;
+	unsigned int count;
+
+	base = array_get_modifiable(&imap_commands, &count);
+	if (commands_unsorted) {
+		qsort(base, count, sizeof(struct command), command_cmp);
+                commands_unsorted = FALSE;
+	}
+
+	return bsearch(name, base, count, sizeof(struct command),
+		       command_bsearch);
+}
+
+void commands_init(void)
+{
+	i_array_init(&imap_commands, 64);
+	commands_unsorted = FALSE;
+
+        command_register_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT);
+        command_register_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT);
+}
+
+void commands_deinit(void)
+{
+        command_unregister_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT);
+        command_unregister_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT);
+	array_free(&imap_commands);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-commands.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,103 @@
+#ifndef IMAP_COMMANDS_H
+#define IMAP_COMMANDS_H
+
+struct client_command_context;
+
+#include "mail-storage.h"
+#include "imap-parser.h"
+#include "imap-sync.h"
+#include "imap-commands-util.h"
+
+typedef bool command_func_t(struct client_command_context *cmd);
+
+enum command_flags {
+	/* Command uses sequences as its input parameters */
+	COMMAND_FLAG_USES_SEQS		= 0x01,
+	/* Command may reply with EXPUNGE, causing sequences to break */
+	COMMAND_FLAG_BREAKS_SEQS	= 0x02,
+	/* Command changes the mailbox */
+	COMMAND_FLAG_BREAKS_MAILBOX	= 0x04 | COMMAND_FLAG_BREAKS_SEQS,
+
+	/* Command uses selected mailbox */
+	COMMAND_FLAG_USES_MAILBOX	= COMMAND_FLAG_BREAKS_MAILBOX |
+					  COMMAND_FLAG_USES_SEQS
+};
+
+struct command {
+	const char *name;
+	command_func_t *func;
+
+	enum command_flags flags;
+};
+ARRAY_DEFINE_TYPE(command, struct command);
+
+extern ARRAY_TYPE(command) imap_commands;
+
+/* Register command. Given name parameter must be permanently stored until
+   command is unregistered. */
+void command_register(const char *name, command_func_t *func,
+		      enum command_flags flags);
+void command_unregister(const char *name);
+
+/* Register array of commands. */
+void command_register_array(const struct command *cmdarr, unsigned int count);
+void command_unregister_array(const struct command *cmdarr, unsigned int count);
+
+struct command *command_find(const char *name);
+
+void commands_init(void);
+void commands_deinit(void);
+
+/* IMAP4rev1 commands: */
+
+/* Non-Authenticated State */
+bool cmd_logout(struct client_command_context *cmd);
+
+bool cmd_capability(struct client_command_context *cmd);
+bool cmd_noop(struct client_command_context *cmd);
+
+/* Authenticated State */
+bool cmd_select(struct client_command_context *cmd);
+bool cmd_examine(struct client_command_context *cmd);
+
+bool cmd_create(struct client_command_context *cmd);
+bool cmd_delete(struct client_command_context *cmd);
+bool cmd_rename(struct client_command_context *cmd);
+
+bool cmd_subscribe(struct client_command_context *cmd);
+bool cmd_unsubscribe(struct client_command_context *cmd);
+
+bool cmd_list(struct client_command_context *cmd);
+bool cmd_lsub(struct client_command_context *cmd);
+
+bool cmd_status(struct client_command_context *cmd);
+bool cmd_append(struct client_command_context *cmd);
+
+/* Selected state */
+bool cmd_check(struct client_command_context *cmd);
+bool cmd_close(struct client_command_context *cmd);
+bool cmd_expunge(struct client_command_context *cmd);
+bool cmd_search(struct client_command_context *cmd);
+bool cmd_fetch(struct client_command_context *cmd);
+bool cmd_store(struct client_command_context *cmd);
+bool cmd_copy(struct client_command_context *cmd);
+bool cmd_uid(struct client_command_context *cmd);
+
+/* IMAP extensions: */
+bool cmd_cancelupdate(struct client_command_context *cmd);
+bool cmd_enable(struct client_command_context *cmd);
+bool cmd_id(struct client_command_context *cmd);
+bool cmd_idle(struct client_command_context *cmd);
+bool cmd_namespace(struct client_command_context *cmd);
+bool cmd_sort(struct client_command_context *cmd);
+bool cmd_thread(struct client_command_context *cmd);
+bool cmd_uid_expunge(struct client_command_context *cmd);
+bool cmd_unselect(struct client_command_context *cmd);
+bool cmd_x_cancel(struct client_command_context *cmd);
+
+/* private: */
+bool cmd_list_full(struct client_command_context *cmd, bool lsub);
+bool cmd_select_full(struct client_command_context *cmd, bool readonly);
+bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-common.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,30 @@
+#ifndef IMAP_COMMON_H
+#define IMAP_COMMON_H
+
+/* Disconnect client after idling this many milliseconds */
+#define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000)
+
+/* If we can't send anything to client for this long, disconnect the client */
+#define CLIENT_OUTPUT_TIMEOUT_MSECS (5*60*1000)
+
+/* Stop buffering more data into output stream after this many bytes */
+#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048
+
+/* Disconnect client when it sends too many bad commands in a row */
+#define CLIENT_MAX_BAD_COMMANDS 20
+
+enum client_workarounds {
+	WORKAROUND_DELAY_NEWMAIL		= 0x01,
+	WORKAROUND_NETSCAPE_EOH			= 0x04,
+	WORKAROUND_TB_EXTRA_MAILBOX_SEP		= 0x08
+};
+
+#include "lib.h"
+#include "imap-client.h"
+#include "imap-settings.h"
+
+extern struct master_service *service;
+
+extern void (*hook_client_created)(struct client **client);
+
+#endif
--- a/src/imap/imap-expunge.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-expunge.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "mail-storage.h"
 #include "mail-search-build.h"
 #include "imap-expunge.h"
--- a/src/imap/imap-fetch-body.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-fetch-body.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "buffer.h"
 #include "str.h"
 #include "strescape.h"
--- a/src/imap/imap-fetch.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-fetch.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "array.h"
 #include "buffer.h"
 #include "istream.h"
@@ -10,7 +10,7 @@
 #include "message-size.h"
 #include "imap-date.h"
 #include "mail-search-build.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-quote.h"
 #include "imap-fetch.h"
 #include "imap-util.h"
--- a/src/imap/imap-search-args.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-search-args.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "mail-storage.h"
 #include "mail-search-build.h"
 #include "imap-search-args.h"
--- a/src/imap/imap-search.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-search.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ostream.h"
 #include "str.h"
 #include "seq-range-array.h"
@@ -9,7 +9,7 @@
 #include "imap-seqset.h"
 #include "imap-util.h"
 #include "mail-search-build.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "imap-search-args.h"
 #include "imap-search.h"
 
--- a/src/imap/imap-status.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-status.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "imap-quote.h"
 #include "imap-status.h"
--- a/src/imap/imap-sync.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/imap-sync.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "ostream.h"
 #include "mail-storage.h"
@@ -8,7 +8,7 @@
 #include "imap-quote.h"
 #include "imap-util.h"
 #include "imap-sync.h"
-#include "commands.h"
+#include "imap-commands.h"
 
 struct client_sync_context {
 	/* if multiple commands are in progress, we may need to wait for them
--- a/src/imap/mail-storage-callbacks.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/mail-storage-callbacks.c	Tue May 05 11:57:04 2009 -0400
@@ -1,9 +1,9 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ostream.h"
 #include "mail-storage.h"
-#include "commands-util.h"
+#include "imap-commands-util.h"
 
 static void notify_ok(struct mailbox *mailbox ATTR_UNUSED,
 		      const char *text, void *context)
--- a/src/imap/main.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/imap/main.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
@@ -12,7 +12,7 @@
 #include "master-service.h"
 #include "mail-user.h"
 #include "mail-storage-service.h"
-#include "commands.h"
+#include "imap-commands.h"
 
 #include <stdio.h>
 #include <stdlib.h>
--- a/src/plugins/imap-acl/imap-acl-plugin.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/plugins/imap-acl/imap-acl-plugin.c	Tue May 05 11:57:04 2009 -0400
@@ -1,10 +1,10 @@
 /* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "imap-quote.h"
 #include "imap-resp-code.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "mail-storage.h"
 #include "mail-namespace.h"
 #include "acl-api.h"
--- a/src/plugins/imap-quota/imap-quota-plugin.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/plugins/imap-quota/imap-quota-plugin.c	Tue May 05 11:57:04 2009 -0400
@@ -1,10 +1,10 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "imap-common.h"
 #include "str.h"
 #include "imap-quote.h"
 #include "mail-namespace.h"
-#include "commands.h"
+#include "imap-commands.h"
 #include "quota.h"
 #include "quota-plugin.h"
 #include "imap-quota-plugin.h"
--- a/src/pop3-login/client-authenticate.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/pop3-login/client-authenticate.c	Tue May 05 11:57:04 2009 -0400
@@ -11,7 +11,7 @@
 #include "str.h"
 #include "str-sanitize.h"
 #include "auth-client.h"
-#include "../pop3/capability.h"
+#include "../pop3/pop3-capability.h"
 #include "ssl-proxy.h"
 #include "client.h"
 #include "client-authenticate.h"
--- a/src/pop3/Makefile.am	Mon May 04 20:50:13 2009 -0400
+++ b/src/pop3/Makefile.am	Tue May 05 11:57:04 2009 -0400
@@ -27,16 +27,16 @@
 pop3_DEPENDENCIES = $(libs)
 
 pop3_SOURCES = \
-	client.c \
-	commands.c \
 	main.c \
+	pop3-client.c \
+	pop3-commands.c \
 	pop3-settings.c
 
 headers = \
-	capability.h \
-	client.h \
-	commands.h \
-	common.h \
+	pop3-capability.h \
+	pop3-client.h \
+	pop3-commands.h \
+	pop3-common.h \
 	pop3-settings.h
 
 if INSTALL_HEADERS
--- a/src/pop3/capability.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef POP3_CAPABILITY_H
-#define POP3_CAPABILITY_H
-
-#define POP3_CAPABILITY_REPLY \
-	"CAPA\r\n" \
-	"TOP\r\n" \
-	"UIDL\r\n" \
-	"RESP-CODES\r\n" \
-	"PIPELINING\r\n"
-
-/* + SASL */
-
-#endif
--- a/src/pop3/client.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,586 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "var-expand.h"
-#include "master-service.h"
-#include "mail-storage.h"
-#include "commands.h"
-#include "mail-search-build.h"
-#include "mail-namespace.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-/* max. length of input command line (spec says 512) */
-#define MAX_INBUF_SIZE 2048
-
-/* Stop reading input when output buffer has this many bytes. Once the buffer
-   size has dropped to half of it, start reading input again. */
-#define OUTBUF_THROTTLE_SIZE 4096
-
-/* Disconnect client when it sends too many bad commands in a row */
-#define CLIENT_MAX_BAD_COMMANDS 20
-
-/* Disconnect client after idling this many milliseconds */
-#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
-/* If client starts idling for this many milliseconds, commit the current
-   transaction. This allows the mailbox to become unlocked. */
-#define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
-
-struct client_workaround_list {
-	const char *name;
-	enum client_workarounds num;
-};
-
-static struct client_workaround_list client_workaround_list[] = {
-	{ "outlook-no-nuls", WORKAROUND_OUTLOOK_NO_NULS },
-	{ "oe-ns-eoh", WORKAROUND_OE_NS_EOH },
-	{ NULL, 0 }
-};
-
-static struct client *my_client; /* we don't need more than one currently */
-
-static void client_input(struct client *client);
-static int client_output(struct client *client);
-
-static void client_commit_timeout(struct client *client)
-{
-	if (client->cmd != NULL) {
-		/* Can't commit while commands are running */
-		return;
-	}
-
-	(void)mailbox_transaction_commit(&client->trans);
-	client->trans = mailbox_transaction_begin(client->mailbox, 0);
-}
-
-static void client_idle_timeout(struct client *client)
-{
-	if (client->cmd != NULL) {
-		client_destroy(client,
-			"Disconnected for inactivity in reading our output");
-	} else {
-		client_send_line(client, "-ERR Disconnected for inactivity.");
-		client_destroy(client, "Disconnected for inactivity");
-	}
-}
-
-static bool init_mailbox(struct client *client, const char **error_r)
-{
-	struct mail_search_args *search_args;
-        struct mailbox_transaction_context *t;
-	struct mail_search_context *ctx;
-        struct mailbox_status status;
-	struct mail *mail;
-	buffer_t *message_sizes_buf;
-	uint32_t failed_uid = 0;
-	uoff_t size;
-	int i;
-	bool failed, expunged;
-
-	message_sizes_buf = buffer_create_dynamic(default_pool, 512);
-
-	search_args = mail_search_build_init();
-	mail_search_build_add_all(search_args);
-
-	for (i = 0; i < 2; i++) {
-		expunged = FALSE;
-		if (mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_READ,
-				 STATUS_UIDVALIDITY, &status) < 0) {
-			client_send_storage_error(client);
-			break;
-		}
-		client->uid_validity = status.uidvalidity;
-
-		t = mailbox_transaction_begin(client->mailbox, 0);
-		ctx = mailbox_search_init(t, search_args, NULL);
-
-		client->last_seen = 0;
-		client->total_size = 0;
-		buffer_set_used_size(message_sizes_buf, 0);
-
-		failed = FALSE;
-		mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL);
-		while (mailbox_search_next(ctx, mail) > 0) {
-			if (mail_get_virtual_size(mail, &size) < 0) {
-				expunged = mail->expunged;
-				failed = TRUE;
-				if (failed_uid == mail->uid) {
-					i_error("Getting size of message "
-						"UID=%u failed", mail->uid);
-					break;
-				}
-				failed_uid = mail->uid;
-				break;
-			}
-
-			if ((mail_get_flags(mail) & MAIL_SEEN) != 0)
-				client->last_seen = mail->seq;
-                        client->total_size += size;
-
-			buffer_append(message_sizes_buf, &size, sizeof(size));
-		}
-		client->messages_count =
-			message_sizes_buf->used / sizeof(uoff_t);
-
-		mail_free(&mail);
-		if (mailbox_search_deinit(&ctx) < 0 || (failed && !expunged)) {
-			client_send_storage_error(client);
-			(void)mailbox_transaction_commit(&t);
-			break;
-		}
-
-		if (!failed) {
-			client->trans = t;
-			client->message_sizes =
-				buffer_free_without_data(&message_sizes_buf);
-			mail_search_args_unref(&search_args);
-			return TRUE;
-		}
-
-		/* well, sync and try again. we might have cached virtual
-		   sizes, make sure they get committed. */
-		(void)mailbox_transaction_commit(&t);
-	}
-	mail_search_args_unref(&search_args);
-
-	if (expunged) {
-		client_send_line(client,
-				 "-ERR [IN-USE] Couldn't sync mailbox.");
-		*error_r = "Can't sync mailbox: Messages keep getting expunged";
-	} else {
-		struct mail_storage *storage = client->inbox_ns->storage;
-		enum mail_error error;
-
-		*error_r = mail_storage_get_last_error(storage, &error);
-	}
-	buffer_free(&message_sizes_buf);
-	return FALSE;
-}
-
-static enum client_workarounds
-parse_workarounds(const struct pop3_settings *set)
-{
-        enum client_workarounds client_workarounds = 0;
-	struct client_workaround_list *list;
-	const char *const *str;
-
-        str = t_strsplit_spaces(set->pop3_client_workarounds, " ,");
-	for (; *str != NULL; str++) {
-		list = client_workaround_list;
-		for (; list->name != NULL; list++) {
-			if (strcasecmp(*str, list->name) == 0) {
-				client_workarounds |= list->num;
-				break;
-			}
-		}
-		if (list->name == NULL)
-			i_fatal("Unknown client workaround: %s", *str);
-	}
-	return client_workarounds;
-}
-
-static enum uidl_keys parse_uidl_keymask(const char *format)
-{
-	enum uidl_keys mask = 0;
-
-	for (; *format != '\0'; format++) {
-		if (format[0] == '%' && format[1] != '\0') {
-			switch (var_get_key(++format)) {
-			case 'v':
-				mask |= UIDL_UIDVALIDITY;
-				break;
-			case 'u':
-				mask |= UIDL_UID;
-				break;
-			case 'm':
-				mask |= UIDL_MD5;
-				break;
-			case 'f':
-				mask |= UIDL_FILE_NAME;
-				break;
-			}
-		}
-	}
-	return mask;
-}
-
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
-			     const struct pop3_settings *set)
-{
-	struct mail_storage *storage;
-	const char *inbox;
-	struct client *client;
-        enum mailbox_open_flags flags;
-	const char *errmsg;
-	enum mail_error error;
-
-	/* always use nonblocking I/O */
-	net_set_nonblock(fd_in, TRUE);
-	net_set_nonblock(fd_out, TRUE);
-
-	client = i_new(struct client, 1);
-	client->set = set;
-	client->fd_in = fd_in;
-	client->fd_out = fd_out;
-	client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
-	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
-	o_stream_set_flush_callback(client->output, client_output, client);
-
-	client->io = io_add(fd_in, IO_READ, client_input, client);
-        client->last_input = ioloop_time;
-	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
-				      client_idle_timeout, client);
-	if (!set->pop3_lock_session) {
-		client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
-						client_commit_timeout, client);
-	}
-
-	client->user = user;
-
-	inbox = "INBOX";
-	client->inbox_ns = mail_namespace_find(user->namespaces, &inbox);
-	if (client->inbox_ns == NULL) {
-		client_send_line(client, "-ERR No INBOX namespace for user.");
-		client_destroy(client, "No INBOX namespace for user.");
-		return NULL;
-	}
-
-	storage = client->inbox_ns->storage;
-
-	client->mail_set = mail_storage_get_settings(storage);
-	flags = MAILBOX_OPEN_POP3_SESSION;
-	if (set->pop3_no_flag_updates)
-		flags |= MAILBOX_OPEN_KEEP_RECENT;
-	if (set->pop3_lock_session)
-		flags |= MAILBOX_OPEN_KEEP_LOCKED;
-	client->mailbox = mailbox_open(&storage, "INBOX", NULL, flags);
-	if (client->mailbox == NULL) {
-		errmsg = t_strdup_printf("Couldn't open INBOX: %s",
-				mail_storage_get_last_error(storage,
-							    &error));
-		i_error("%s", errmsg);
-		client_send_line(client, "-ERR [IN-USE] %s", errmsg);
-		client_destroy(client, "Couldn't open INBOX");
-		return NULL;
-	}
-
-	if (!init_mailbox(client, &errmsg)) {
-		i_error("Couldn't init INBOX: %s", errmsg);
-		client_destroy(client, "Mailbox init failed");
-		return NULL;
-	}
-
-	client->workarounds = parse_workarounds(set);
-	client->uidl_keymask =
-		parse_uidl_keymask(client->mail_set->pop3_uidl_format);
-	if (client->uidl_keymask == 0)
-		i_fatal("Invalid pop3_uidl_format");
-
-	if (!set->pop3_no_flag_updates && client->messages_count > 0)
-		client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
-
-	i_assert(my_client == NULL);
-	my_client = client;
-
-	if (hook_client_created != NULL)
-		hook_client_created(&client);
-	return client;
-}
-
-static const char *client_stats(struct client *client)
-{
-	static struct var_expand_table static_tab[] = {
-		{ 'p', NULL, "top_bytes" },
-		{ 't', NULL, "top_count" },
-		{ 'b', NULL, "retr_bytes" },
-		{ 'r', NULL, "retr_count" },
-		{ 'd', NULL, "deleted_count" },
-		{ 'm', NULL, "message_count" },
-		{ 's', NULL, "message_bytes" },
-		{ 'i', NULL, "input" },
-		{ 'o', NULL, "output" },
-		{ '\0', NULL, NULL }
-	};
-	struct var_expand_table *tab;
-	string_t *str;
-
-	tab = t_malloc(sizeof(static_tab));
-	memcpy(tab, static_tab, sizeof(static_tab));
-
-	tab[0].value = dec2str(client->top_bytes);
-	tab[1].value = dec2str(client->top_count);
-	tab[2].value = dec2str(client->retr_bytes);
-	tab[3].value = dec2str(client->retr_count);
-	tab[4].value = dec2str(client->expunged_count);
-	tab[5].value = dec2str(client->messages_count);
-	tab[6].value = dec2str(client->total_size);
-	tab[7].value = dec2str(client->input->v_offset);
-	tab[8].value = dec2str(client->output->offset);
-
-	str = t_str_new(128);
-	var_expand(str, client->set->pop3_logout_format, tab);
-	return str_c(str);
-}
-
-static const char *client_get_disconnect_reason(struct client *client)
-{
-	errno = client->input->stream_errno != 0 ?
-		client->input->stream_errno :
-		client->output->stream_errno;
-	return errno == 0 || errno == EPIPE ? "Connection closed" :
-		t_strdup_printf("Connection closed: %m");
-}
-
-void client_destroy(struct client *client, const char *reason)
-{
-	if (client->seen_change_count > 0)
-		client_update_mails(client);
-
-	if (!client->disconnected) {
-		if (reason == NULL)
-			reason = client_get_disconnect_reason(client);
-		i_info("%s %s", reason, client_stats(client));
-	}
-
-	if (client->cmd != NULL) {
-		/* deinitialize command */
-		i_stream_close(client->input);
-		o_stream_close(client->output);
-		client->cmd(client);
-		i_assert(client->cmd == NULL);
-	}
-	if (client->trans != NULL) {
-		/* client didn't QUIT, but we still want to save any changes
-		   done in this transaction. especially the cached virtual
-		   message sizes. */
-		(void)mailbox_transaction_commit(&client->trans);
-	}
-	if (client->mailbox != NULL)
-		mailbox_close(&client->mailbox);
-	mail_user_unref(&client->user);
-
-	i_free(client->message_sizes);
-	i_free(client->deleted_bitmask);
-	i_free(client->seen_bitmask);
-
-	if (client->io != NULL)
-		io_remove(&client->io);
-	timeout_remove(&client->to_idle);
-	if (client->to_commit != NULL)
-		timeout_remove(&client->to_commit);
-
-	i_stream_destroy(&client->input);
-	o_stream_destroy(&client->output);
-
-	if (close(client->fd_in) < 0)
-		i_error("close(client in) failed: %m");
-	if (client->fd_in != client->fd_out) {
-		if (close(client->fd_out) < 0)
-			i_error("close(client out) failed: %m");
-	}
-
-	i_free(client);
-
-	/* quit the program */
-	my_client = NULL;
-	master_service_stop(service);
-}
-
-void client_disconnect(struct client *client, const char *reason)
-{
-	if (client->disconnected)
-		return;
-
-	client->disconnected = TRUE;
-	i_info("Disconnected: %s %s", reason, client_stats(client));
-
-	(void)o_stream_flush(client->output);
-
-	i_stream_close(client->input);
-	o_stream_close(client->output);
-}
-
-int client_send_line(struct client *client, const char *fmt, ...)
-{
-	va_list va;
-	ssize_t ret;
-
-	if (client->output->closed)
-		return -1;
-
-	va_start(va, fmt);
-
-	T_BEGIN {
-		string_t *str;
-
-		str = t_str_new(256);
-		str_vprintfa(str, fmt, va);
-		str_append(str, "\r\n");
-
-		ret = o_stream_send(client->output,
-				    str_data(str), str_len(str));
-		i_assert(ret < 0 || (size_t)ret == str_len(str));
-	} T_END;
-	if (ret >= 0) {
-		if (o_stream_get_buffer_used_size(client->output) <
-		    OUTBUF_THROTTLE_SIZE) {
-			ret = 1;
-			client->last_output = ioloop_time;
-		} else {
-			ret = 0;
-			if (client->io != NULL) {
-				/* no more input until client has read
-				   our output */
-				io_remove(&client->io);
-
-				/* If someone happens to flush output,
-				   we want to get our IO handler back in
-				   flush callback */
-				o_stream_set_flush_pending(client->output,
-							   TRUE);
-			}
-		}
-	}
-
-	va_end(va);
-	return (int)ret;
-}
-
-void client_send_storage_error(struct client *client)
-{
-	enum mail_error error;
-
-	if (mailbox_is_inconsistent(client->mailbox)) {
-		client_send_line(client, "-ERR Mailbox is in inconsistent "
-				 "state, please relogin.");
-		client_disconnect(client, "Mailbox is in inconsistent state.");
-		return;
-	}
-
-	client_send_line(client, "-ERR %s",
-			 mail_storage_get_last_error(client->inbox_ns->storage,
-						     &error));
-}
-
-bool client_handle_input(struct client *client)
-{
-	char *line, *args;
-	int ret;
-
-	o_stream_cork(client->output);
-	while (!client->output->closed &&
-	       (line = i_stream_next_line(client->input)) != NULL) {
-		args = strchr(line, ' ');
-		if (args != NULL)
-			*args++ = '\0';
-
-		T_BEGIN {
-			ret = client_command_execute(client, line,
-						     args != NULL ? args : "");
-		} T_END;
-		if (ret >= 0) {
-			client->bad_counter = 0;
-			if (client->cmd != NULL) {
-				o_stream_set_flush_pending(client->output,
-							   TRUE);
-				client->waiting_input = TRUE;
-				break;
-			}
-		} else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
-			client_send_line(client, "-ERR Too many bad commands.");
-			client_disconnect(client, "Too many bad commands.");
-		}
-	}
-	o_stream_uncork(client->output);
-
-	if (client->output->closed) {
-		client_destroy(client, NULL);
-		return FALSE;
-	}
-	return TRUE;
-}
-
-static void client_input(struct client *client)
-{
-	if (client->cmd != NULL) {
-		/* we're still processing a command. wait until it's
-		   finished. */
-		io_remove(&client->io);
-		client->waiting_input = TRUE;
-		return;
-	}
-
-	client->waiting_input = FALSE;
-	client->last_input = ioloop_time;
-	timeout_reset(client->to_idle);
-	if (client->to_commit != NULL)
-		timeout_reset(client->to_commit);
-
-	switch (i_stream_read(client->input)) {
-	case -1:
-		/* disconnected */
-		client_destroy(client, NULL);
-		return;
-	case -2:
-		/* line too long, kill it */
-		client_send_line(client, "-ERR Input line too long.");
-		client_destroy(client, "Input line too long");
-		return;
-	}
-
-	(void)client_handle_input(client);
-}
-
-static int client_output(struct client *client)
-{
-	int ret;
-
-	if ((ret = o_stream_flush(client->output)) < 0) {
-		client_destroy(client, NULL);
-		return 1;
-	}
-
-	client->last_output = ioloop_time;
-	timeout_reset(client->to_idle);
-	if (client->to_commit != NULL)
-		timeout_reset(client->to_commit);
-
-	if (client->cmd != NULL) {
-		o_stream_cork(client->output);
-		client->cmd(client);
-		o_stream_uncork(client->output);
-	}
-
-	if (client->cmd == NULL) {
-		if (o_stream_get_buffer_used_size(client->output) <
-		    OUTBUF_THROTTLE_SIZE/2 && client->io == NULL) {
-			/* enable input again */
-			client->io = io_add(i_stream_get_fd(client->input),
-					    IO_READ, client_input, client);
-		}
-		if (client->io != NULL && client->waiting_input)
-			client_input(client);
-	}
-
-	return client->cmd == NULL;
-}
-
-void clients_init(void)
-{
-	my_client = NULL;
-}
-
-void clients_deinit(void)
-{
-	if (my_client != NULL) {
-		client_send_line(my_client, "-ERR Server shutting down.");
-		client_destroy(my_client, "Server shutting down");
-	}
-}
--- a/src/pop3/client.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-#ifndef CLIENT_H
-#define CLIENT_H
-
-struct client;
-struct mail_storage;
-
-typedef void command_func_t(struct client *client);
-
-#define MSGS_BITMASK_SIZE(client) \
-	(((client)->messages_count + (CHAR_BIT-1)) / CHAR_BIT)
-
-struct client {
-	int fd_in, fd_out;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-	struct timeout *to_idle, *to_commit;
-
-	command_func_t *cmd;
-	void *cmd_context;
-
-	struct mail_user *user;
-	struct mail_namespace *inbox_ns;
-	struct mailbox *mailbox;
-	struct mailbox_transaction_context *trans;
-
-	time_t last_input, last_output;
-	unsigned int bad_counter;
-	unsigned int highest_expunged_fetch_msgnum;
-
-	unsigned int uid_validity;
-	unsigned int messages_count;
-	unsigned int deleted_count, expunged_count, seen_change_count;
-	uoff_t *message_sizes;
-	uoff_t total_size;
-	uoff_t deleted_size;
-	uint32_t last_seen;
-
-	uoff_t top_bytes;
-	uoff_t retr_bytes;
-	unsigned int top_count;
-	unsigned int retr_count;
-
-	uoff_t *byte_counter;
-	uoff_t byte_counter_offset;
-
-	unsigned char *deleted_bitmask;
-	unsigned char *seen_bitmask;
-
-	/* settings: */
-	const struct pop3_settings *set;
-	const struct mail_storage_settings *mail_set;
-	enum client_workarounds workarounds;
-	enum uidl_keys uidl_keymask;
-
-	unsigned int disconnected:1;
-	unsigned int deleted:1;
-	unsigned int waiting_input:1;
-};
-
-/* Create new client with specified input/output handles. socket specifies
-   if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
-			     const struct pop3_settings *set);
-void client_destroy(struct client *client, const char *reason);
-
-/* Disconnect client connection */
-void client_disconnect(struct client *client, const char *reason);
-
-/* Send a line of data to client */
-int client_send_line(struct client *client, const char *fmt, ...)
-	ATTR_FORMAT(2, 3);
-void client_send_storage_error(struct client *client);
-
-bool client_handle_input(struct client *client);
-bool client_update_mails(struct client *client);
-
-void clients_init(void);
-void clients_deinit(void);
-
-#endif
--- a/src/pop3/commands.c	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,731 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "var-expand.h"
-#include "message-size.h"
-#include "mail-storage.h"
-#include "mail-storage-settings.h"
-#include "mail-search-build.h"
-#include "capability.h"
-#include "commands.h"
-
-static const char *get_msgnum(struct client *client, const char *args,
-			      unsigned int *msgnum)
-{
-	unsigned int num, last_num;
-
-	num = 0;
-	while (*args != '\0' && *args != ' ') {
-		if (*args < '0' || *args > '9') {
-			client_send_line(client,
-				"-ERR Invalid message number: %s", args);
-			return NULL;
-		}
-
-		last_num = num;
-		num = num*10 + (*args - '0');
-		if (num < last_num) {
-			client_send_line(client,
-				"-ERR Message number too large: %s", args);
-			return NULL;
-		}
-		args++;
-	}
-
-	if (num == 0 || num > client->messages_count) {
-		client_send_line(client,
-				 "-ERR There's no message %u.", num);
-		return NULL;
-	}
-	num--;
-
-	if (client->deleted) {
-		if (client->deleted_bitmask[num / CHAR_BIT] &
-		    (1 << (num % CHAR_BIT))) {
-			client_send_line(client, "-ERR Message is deleted.");
-			return NULL;
-		}
-	}
-
-	while (*args == ' ') args++;
-
-	*msgnum = num;
-	return args;
-}
-
-static const char *get_size(struct client *client, const char *args,
-			    uoff_t *size)
-{
-	uoff_t num, last_num;
-
-	num = 0;
-	while (*args != '\0' && *args != ' ') {
-		if (*args < '0' || *args > '9') {
-			client_send_line(client, "-ERR Invalid size: %s",
-					 args);
-			return NULL;
-		}
-
-		last_num = num;
-		num = num*10 + (*args - '0');
-		if (num < last_num) {
-			client_send_line(client, "-ERR Size too large: %s",
-					 args);
-			return NULL;
-		}
-		args++;
-	}
-
-	while (*args == ' ') args++;
-
-	*size = num;
-	return args;
-}
-
-static int cmd_capa(struct client *client, const char *args ATTR_UNUSED)
-{
-	client_send_line(client, "+OK\r\n"POP3_CAPABILITY_REPLY".");
-	return 1;
-}
-
-static int cmd_dele(struct client *client, const char *args)
-{
-	unsigned int msgnum;
-
-	if (get_msgnum(client, args, &msgnum) == NULL)
-		return -1;
-
-	if (!client->deleted) {
-		client->deleted_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
-		client->deleted = TRUE;
-	}
-
-	client->deleted_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT);
-	client->deleted_count++;
-	client->deleted_size += client->message_sizes[msgnum];
-	client_send_line(client, "+OK Marked to be deleted.");
-	return 1;
-}
-
-struct cmd_list_context {
-	unsigned int msgnum;
-};
-
-static void cmd_list_callback(struct client *client)
-{
-	struct cmd_list_context *ctx = client->cmd_context;
-	int ret = 1;
-
-	for (; ctx->msgnum != client->messages_count; ctx->msgnum++) {
-		if (ret == 0) {
-			/* buffer full */
-			return;
-		}
-
-		if (client->deleted) {
-			if (client->deleted_bitmask[ctx->msgnum / CHAR_BIT] &
-			    (1 << (ctx->msgnum % CHAR_BIT)))
-				continue;
-		}
-		ret = client_send_line(client, "%u %"PRIuUOFF_T,
-				       ctx->msgnum+1,
-				       client->message_sizes[ctx->msgnum]);
-		if (ret < 0)
-			break;
-	}
-
-	client_send_line(client, ".");
-
-	i_free(ctx);
-	client->cmd = NULL;
-}
-
-static int cmd_list(struct client *client, const char *args)
-{
-        struct cmd_list_context *ctx;
-
-	if (*args == '\0') {
-		ctx = i_new(struct cmd_list_context, 1);
-		client_send_line(client, "+OK %u messages:",
-				 client->messages_count - client->deleted_count);
-
-		client->cmd = cmd_list_callback;
-		client->cmd_context = ctx;
-		cmd_list_callback(client);
-	} else {
-		unsigned int msgnum;
-
-		if (get_msgnum(client, args, &msgnum) == NULL)
-			return -1;
-
-		client_send_line(client, "+OK %u %"PRIuUOFF_T, msgnum+1,
-				 client->message_sizes[msgnum]);
-	}
-
-	return 1;
-}
-
-static int cmd_last(struct client *client, const char *args ATTR_UNUSED)
-{
-	client_send_line(client, "+OK %u", client->last_seen);
-	return 1;
-}
-
-static int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
-{
-	client_send_line(client, "+OK");
-	return 1;
-}
-
-static struct mail_search_args *
-pop3_search_build(struct client *client, uint32_t seq)
-{
-	struct mail_search_args *search_args;
-
-	search_args = mail_search_build_init();
-	if (seq == 0) {
-		mail_search_build_add_seqset(search_args,
-					     1, client->messages_count);
-	} else {
-		mail_search_build_add_seqset(search_args, seq, seq);
-	}
-	return search_args;
-}
-
-bool client_update_mails(struct client *client)
-{
-	struct mail_search_args *search_args;
-	struct mail_search_context *ctx;
-	struct mail *mail;
-	uint32_t idx, bit;
-
-	if (mailbox_is_readonly(client->mailbox)) {
-		/* silently ignore */
-		return TRUE;
-	}
-
-	search_args = pop3_search_build(client, 0);
-	ctx = mailbox_search_init(client->trans, search_args, NULL);
-	mail_search_args_unref(&search_args);
-
-	mail = mail_alloc(client->trans, 0, NULL);
-	while (mailbox_search_next(ctx, mail) > 0) {
-		idx = mail->seq - 1;
-		bit = 1 << (idx % CHAR_BIT);
-		if (client->deleted_bitmask != NULL &&
-		    (client->deleted_bitmask[idx / CHAR_BIT] & bit) != 0) {
-			mail_expunge(mail);
-			client->expunged_count++;
-		} else if (client->seen_bitmask != NULL &&
-			   (client->seen_bitmask[idx / CHAR_BIT] & bit) != 0) {
-			mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN);
-		}
-	}
-	mail_free(&mail);
-
-	client->seen_change_count = 0;
-	return mailbox_search_deinit(&ctx) == 0;
-}
-
-static int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
-{
-	if (client->deleted || client->seen_bitmask != NULL) {
-		if (!client_update_mails(client)) {
-			client_send_storage_error(client);
-			client_disconnect(client,
-				"Storage error during logout.");
-			return 1;
-		}
-	}
-
-	if (mailbox_transaction_commit(&client->trans) < 0 ||
-	    mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_WRITE,
-			 0, NULL) < 0) {
-		client_send_storage_error(client);
-		client_disconnect(client, "Storage error during logout.");
-		return 1;
-	}
-
-	if (!client->deleted)
-		client_send_line(client, "+OK Logging out.");
-	else
-		client_send_line(client, "+OK Logging out, messages deleted.");
-
-	client_disconnect(client, "Logged out");
-	return 1;
-}
-
-struct fetch_context {
-	struct mail_search_context *search_ctx;
-	struct mail *mail;
-	struct istream *stream;
-	uoff_t body_lines;
-
-	unsigned char last;
-	bool cr_skipped, in_body;
-};
-
-static void fetch_deinit(struct fetch_context *ctx)
-{
-	(void)mailbox_search_deinit(&ctx->search_ctx);
-	mail_free(&ctx->mail);
-	i_free(ctx);
-}
-
-static void fetch_callback(struct client *client)
-{
-	struct fetch_context *ctx = client->cmd_context;
-	const unsigned char *data;
-	unsigned char add;
-	size_t i, size;
-	int ret;
-
-	while ((ctx->body_lines > 0 || !ctx->in_body) &&
-	       i_stream_read_data(ctx->stream, &data, &size, 0) > 0) {
-		if (size > 4096)
-			size = 4096;
-
-		add = '\0';
-		for (i = 0; i < size; i++) {
-			if ((data[i] == '\r' || data[i] == '\n') &&
-			    !ctx->in_body) {
-				if (i == 0 && (ctx->last == '\0' ||
-					       ctx->last == '\n'))
-					ctx->in_body = TRUE;
-				else if (i > 0 && data[i-1] == '\n')
-					ctx->in_body = TRUE;
-			}
-
-			if (data[i] == '\n') {
-				if ((i == 0 && ctx->last != '\r') ||
-				    (i > 0 && data[i-1] != '\r')) {
-					/* missing CR */
-					add = '\r';
-					break;
-				}
-
-				if (ctx->in_body) {
-					if (--ctx->body_lines == 0) {
-						i++;
-						break;
-					}
-				}
-			} else if (data[i] == '.' &&
-				   ((i == 0 && ctx->last == '\n') ||
-				    (i > 0 && data[i-1] == '\n'))) {
-				/* escape the dot */
-				add = '.';
-				break;
-			} else if (data[i] == '\0' &&
-				   (client->workarounds &
-				    WORKAROUND_OUTLOOK_NO_NULS) != 0) {
-				add = 0x80;
-				break;
-			}
-		}
-
-		if (i > 0) {
-			if (o_stream_send(client->output, data, i) < 0)
-				break;
-			ctx->last = data[i-1];
-			i_stream_skip(ctx->stream, i);
-		}
-
-		if (o_stream_get_buffer_used_size(client->output) >= 4096) {
-			if ((ret = o_stream_flush(client->output)) < 0)
-				break;
-			if (ret == 0) {
-				/* continue later */
-				return;
-			}
-		}
-
-		if (add != '\0') {
-			if (o_stream_send(client->output, &add, 1) < 0)
-				break;
-
-			ctx->last = add;
-			if (add == 0x80)
-				i_stream_skip(ctx->stream, 1);
-		}
-	}
-
-	if (ctx->last != '\n') {
-		/* didn't end with CRLF */
-		(void)o_stream_send(client->output, "\r\n", 2);
-	}
-
-	if (!ctx->in_body &&
-	    (client->workarounds & WORKAROUND_OE_NS_EOH) != 0) {
-		/* Add the missing end of headers line. */
-		(void)o_stream_send(client->output, "\r\n", 2);
-	}
-
-	*client->byte_counter +=
-		client->output->offset - client->byte_counter_offset;
-        client->byte_counter = NULL;
-
-	client_send_line(client, ".");
-	fetch_deinit(ctx);
-	client->cmd = NULL;
-}
-
-static int client_reply_msg_expunged(struct client *client, unsigned int msgnum)
-{
-	client_send_line(client, "-ERR Message %u expunged.", msgnum + 1);
-	if (msgnum <= client->highest_expunged_fetch_msgnum) {
-		/* client tried to fetch an expunged message again.
-		   treat this as error so we'll eventually disconnect the
-		   client instead of letting it loop forever. */
-		return -1;
-	}
-	client->highest_expunged_fetch_msgnum = msgnum;
-	return 1;
-}
-
-static int fetch(struct client *client, unsigned int msgnum, uoff_t body_lines)
-{
-        struct fetch_context *ctx;
-	struct mail_search_args *search_args;
-	int ret;
-
-	search_args = pop3_search_build(client, msgnum+1);
-
-	ctx = i_new(struct fetch_context, 1);
-	ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL);
-	mail_search_args_unref(&search_args);
-
-	ctx->mail = mail_alloc(client->trans, MAIL_FETCH_STREAM_HEADER |
-			       MAIL_FETCH_STREAM_BODY, NULL);
-
-	if (mailbox_search_next(ctx->search_ctx, ctx->mail) <= 0 ||
-	    mail_get_stream(ctx->mail, NULL, NULL, &ctx->stream) < 0) {
-		ret = client_reply_msg_expunged(client, msgnum);
-		fetch_deinit(ctx);
-		return ret;
-	}
-
-	if (body_lines == (uoff_t)-1 && client->seen_bitmask != NULL) {
-		if ((mail_get_flags(ctx->mail) & MAIL_SEEN) == 0) {
-			/* mark the message seen with RETR command */
-			client->seen_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT);
-			client->seen_change_count++;
-		}
-	}
-
-	ctx->body_lines = body_lines;
-	if (body_lines == (uoff_t)-1) {
-		client_send_line(client, "+OK %"PRIuUOFF_T" octets",
-				 client->message_sizes[msgnum]);
-	} else {
-		client_send_line(client, "+OK");
-		ctx->body_lines++; /* internally we count the empty line too */
-	}
-
-	client->cmd = fetch_callback;
-	client->cmd_context = ctx;
-	fetch_callback(client);
-	return 1;
-}
-
-static int cmd_retr(struct client *client, const char *args)
-{
-	unsigned int msgnum;
-
-	if (get_msgnum(client, args, &msgnum) == NULL)
-		return -1;
-
-	if (client->last_seen <= msgnum)
-		client->last_seen = msgnum+1;
-
-	client->retr_count++;
-	client->byte_counter = &client->retr_bytes;
-	client->byte_counter_offset = client->output->offset;
-
-	return fetch(client, msgnum, (uoff_t)-1);
-}
-
-static int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
-{
-	struct mail_search_context *search_ctx;
-	struct mail *mail;
-	struct mail_search_args *search_args;
-
-	client->last_seen = 0;
-
-	if (client->deleted) {
-		client->deleted = FALSE;
-		memset(client->deleted_bitmask, 0, MSGS_BITMASK_SIZE(client));
-		client->deleted_count = 0;
-		client->deleted_size = 0;
-	}
-	if (client->seen_change_count > 0) {
-		memset(client->seen_bitmask, 0, MSGS_BITMASK_SIZE(client));
-		client->seen_change_count = 0;
-	}
-
-	if (client->set->pop3_enable_last) {
-		/* remove all \Seen flags (as specified by RFC 1460) */
-		search_args = pop3_search_build(client, 0);
-		search_ctx = mailbox_search_init(client->trans,
-						 search_args, NULL);
-		mail_search_args_unref(&search_args);
-
-		mail = mail_alloc(client->trans, 0, NULL);
-		while (mailbox_search_next(search_ctx, mail) > 0)
-			mail_update_flags(mail, MODIFY_REMOVE, MAIL_SEEN);
-		mail_free(&mail);
-		(void)mailbox_search_deinit(&search_ctx);
-
-		mailbox_transaction_commit(&client->trans);
-		client->trans = mailbox_transaction_begin(client->mailbox, 0);
-	}
-
-	client_send_line(client, "+OK");
-	return 1;
-}
-
-static int cmd_stat(struct client *client, const char *args ATTR_UNUSED)
-{
-	client_send_line(client, "+OK %u %"PRIuUOFF_T, client->
-			 messages_count - client->deleted_count,
-			 client->total_size - client->deleted_size);
-	return 1;
-}
-
-static int cmd_top(struct client *client, const char *args)
-{
-	unsigned int msgnum;
-	uoff_t max_lines;
-
-	args = get_msgnum(client, args, &msgnum);
-	if (args == NULL)
-		return -1;
-	if (get_size(client, args, &max_lines) == NULL)
-		return -1;
-
-	client->top_count++;
-	client->byte_counter = &client->top_bytes;
-	client->byte_counter_offset = client->output->offset;
-
-	return fetch(client, msgnum, max_lines);
-}
-
-struct cmd_uidl_context {
-	struct mail_search_context *search_ctx;
-	struct mail *mail;
-	unsigned int message;
-};
-
-static void pop3_get_uid(struct client *client, struct cmd_uidl_context *ctx,
-			 struct var_expand_table *tab, string_t *str)
-{
-	char uid_str[MAX_INT_STRLEN];
-	const char *uidl;
-
-	if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_BACKEND, &uidl) == 0 &&
-	    *uidl != '\0') {
-		str_append(str, uidl);
-		return;
-	}
-
-	if (client->set->pop3_reuse_xuidl &&
-	    mail_get_first_header(ctx->mail, "X-UIDL", &uidl) > 0) {
-		str_append(str, uidl);
-		return;
-	}
-
-	if ((client->uidl_keymask & UIDL_UID) != 0) {
-		i_snprintf(uid_str, sizeof(uid_str), "%u",
-			   ctx->mail->uid);
-		tab[1].value = uid_str;
-	}
-	if ((client->uidl_keymask & UIDL_MD5) != 0) {
-		if (mail_get_special(ctx->mail, MAIL_FETCH_HEADER_MD5,
-				     &tab[2].value) < 0 ||
-		    *tab[2].value == '\0') {
-			/* broken */
-			i_fatal("UIDL: Header MD5 not found");
-		}
-	}
-	if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) {
-		if (mail_get_special(ctx->mail,
-				     MAIL_FETCH_UIDL_FILE_NAME,
-				     &tab[3].value) < 0 ||
-		    *tab[3].value == '\0') {
-			/* broken */
-			i_fatal("UIDL: File name not found");
-		}
-	}
-	var_expand(str, client->mail_set->pop3_uidl_format, tab);
-}
-
-static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx)
-{
-	static struct var_expand_table static_tab[] = {
-		{ 'v', NULL, "uidvalidity" },
-		{ 'u', NULL, "uid" },
-		{ 'm', NULL, "md5" },
-		{ 'f', NULL, "filename" },
-		{ '\0', NULL, NULL }
-	};
-	struct var_expand_table *tab;
-	string_t *str;
-	int ret;
-	bool found = FALSE;
-
-	tab = t_malloc(sizeof(static_tab));
-	memcpy(tab, static_tab, sizeof(static_tab));
-	tab[0].value = t_strdup_printf("%u", client->uid_validity);
-
-	str = t_str_new(128);
-	while (mailbox_search_next(ctx->search_ctx, ctx->mail) > 0) {
-		if (client->deleted) {
-			uint32_t idx = ctx->mail->seq - 1;
-			if (client->deleted_bitmask[idx / CHAR_BIT] &
-			    (1 << (idx % CHAR_BIT)))
-				continue;
-		}
-		found = TRUE;
-
-		str_truncate(str, 0);
-		str_printfa(str, ctx->message == 0 ? "%u " : "+OK %u ",
-			    ctx->mail->seq);
-		pop3_get_uid(client, ctx, tab, str);
-
-		ret = client_send_line(client, "%s", str_c(str));
-		if (ret < 0)
-			break;
-		if (ret == 0 && ctx->message == 0) {
-			/* output is being buffered, continue when there's
-			   more space */
-			return FALSE;
-		}
-	}
-
-	/* finished */
-	mail_free(&ctx->mail);
-	(void)mailbox_search_deinit(&ctx->search_ctx);
-
-	client->cmd = NULL;
-
-	if (ctx->message == 0)
-		client_send_line(client, ".");
-	i_free(ctx);
-	return found;
-}
-
-static void cmd_uidl_callback(struct client *client)
-{
-	struct cmd_uidl_context *ctx = client->cmd_context;
-
-        (void)list_uids_iter(client, ctx);
-}
-
-static struct cmd_uidl_context *
-cmd_uidl_init(struct client *client, unsigned int message)
-{
-        struct cmd_uidl_context *ctx;
-	struct mail_search_args *search_args;
-	enum mail_fetch_field wanted_fields;
-
-	search_args = pop3_search_build(client, message);
-
-	ctx = i_new(struct cmd_uidl_context, 1);
-	ctx->message = message;
-
-	wanted_fields = 0;
-	if ((client->uidl_keymask & UIDL_MD5) != 0)
-		wanted_fields |= MAIL_FETCH_HEADER_MD5;
-
-	ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL);
-	mail_search_args_unref(&search_args);
-
-	ctx->mail = mail_alloc(client->trans, wanted_fields, NULL);
-	if (message == 0) {
-		client->cmd = cmd_uidl_callback;
-		client->cmd_context = ctx;
-	}
-	return ctx;
-}
-
-static int cmd_uidl(struct client *client, const char *args)
-{
-        struct cmd_uidl_context *ctx;
-
-	if (*args == '\0') {
-		client_send_line(client, "+OK");
-		ctx = cmd_uidl_init(client, 0);
-		(void)list_uids_iter(client, ctx);
-	} else {
-		unsigned int msgnum;
-
-		if (get_msgnum(client, args, &msgnum) == NULL)
-			return -1;
-
-		ctx = cmd_uidl_init(client, msgnum+1);
-		if (!list_uids_iter(client, ctx))
-			return client_reply_msg_expunged(client, msgnum);
-	}
-
-	return 1;
-}
-
-int client_command_execute(struct client *client,
-			   const char *name, const char *args)
-{
-	/* keep the command uppercased */
-	name = t_str_ucase(name);
-
-	while (*args == ' ') args++;
-
-	switch (*name) {
-	case 'C':
-		if (strcmp(name, "CAPA") == 0)
-			return cmd_capa(client, args);
-		break;
-	case 'D':
-		if (strcmp(name, "DELE") == 0)
-			return cmd_dele(client, args);
-		break;
-	case 'L':
-		if (strcmp(name, "LIST") == 0)
-			return cmd_list(client, args);
-		if (strcmp(name, "LAST") == 0 && client->set->pop3_enable_last)
-			return cmd_last(client, args);
-		break;
-	case 'N':
-		if (strcmp(name, "NOOP") == 0)
-			return cmd_noop(client, args);
-		break;
-	case 'Q':
-		if (strcmp(name, "QUIT") == 0)
-			return cmd_quit(client, args);
-		break;
-	case 'R':
-		if (strcmp(name, "RETR") == 0)
-			return cmd_retr(client, args);
-		if (strcmp(name, "RSET") == 0)
-			return cmd_rset(client, args);
-		break;
-	case 'S':
-		if (strcmp(name, "STAT") == 0)
-			return cmd_stat(client, args);
-		break;
-	case 'T':
-		if (strcmp(name, "TOP") == 0)
-			return cmd_top(client, args);
-		break;
-	case 'U':
-		if (strcmp(name, "UIDL") == 0)
-			return cmd_uidl(client, args);
-		break;
-	}
-
-	client_send_line(client, "-ERR Unknown command: %s", name);
-	return -1;
-}
--- a/src/pop3/commands.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#ifndef COMMANDS_H
-#define COMMANDS_H
-
-int client_command_execute(struct client *client,
-			   const char *name, const char *args);
-
-#endif
--- a/src/pop3/common.h	Mon May 04 20:50:13 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#ifndef COMMON_H
-#define COMMON_H
-
-enum client_workarounds {
-	WORKAROUND_OUTLOOK_NO_NULS		= 0x01,
-	WORKAROUND_OE_NS_EOH			= 0x02
-};
-
-enum uidl_keys {
-	UIDL_UIDVALIDITY	= 0x01,
-	UIDL_UID		= 0x02,
-	UIDL_MD5		= 0x04,
-	UIDL_FILE_NAME		= 0x08
-};
-
-#include "lib.h"
-#include "client.h"
-#include "pop3-settings.h"
-
-extern struct master_service *service;
-
-extern void (*hook_client_created)(struct client **client);
-
-#endif
--- a/src/pop3/main.c	Mon May 04 20:50:13 2009 -0400
+++ b/src/pop3/main.c	Tue May 05 11:57:04 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "pop3-common.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "buffer.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-capability.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,13 @@
+#ifndef POP3_CAPABILITY_H
+#define POP3_CAPABILITY_H
+
+#define POP3_CAPABILITY_REPLY \
+	"CAPA\r\n" \
+	"TOP\r\n" \
+	"UIDL\r\n" \
+	"RESP-CODES\r\n" \
+	"PIPELINING\r\n"
+
+/* + SASL */
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-client.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,586 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "pop3-common.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "var-expand.h"
+#include "master-service.h"
+#include "mail-storage.h"
+#include "pop3-commands.h"
+#include "mail-search-build.h"
+#include "mail-namespace.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* max. length of input command line (spec says 512) */
+#define MAX_INBUF_SIZE 2048
+
+/* Stop reading input when output buffer has this many bytes. Once the buffer
+   size has dropped to half of it, start reading input again. */
+#define OUTBUF_THROTTLE_SIZE 4096
+
+/* Disconnect client when it sends too many bad commands in a row */
+#define CLIENT_MAX_BAD_COMMANDS 20
+
+/* Disconnect client after idling this many milliseconds */
+#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
+/* If client starts idling for this many milliseconds, commit the current
+   transaction. This allows the mailbox to become unlocked. */
+#define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
+
+struct client_workaround_list {
+	const char *name;
+	enum client_workarounds num;
+};
+
+static struct client_workaround_list client_workaround_list[] = {
+	{ "outlook-no-nuls", WORKAROUND_OUTLOOK_NO_NULS },
+	{ "oe-ns-eoh", WORKAROUND_OE_NS_EOH },
+	{ NULL, 0 }
+};
+
+static struct client *my_client; /* we don't need more than one currently */
+
+static void client_input(struct client *client);
+static int client_output(struct client *client);
+
+static void client_commit_timeout(struct client *client)
+{
+	if (client->cmd != NULL) {
+		/* Can't commit while commands are running */
+		return;
+	}
+
+	(void)mailbox_transaction_commit(&client->trans);
+	client->trans = mailbox_transaction_begin(client->mailbox, 0);
+}
+
+static void client_idle_timeout(struct client *client)
+{
+	if (client->cmd != NULL) {
+		client_destroy(client,
+			"Disconnected for inactivity in reading our output");
+	} else {
+		client_send_line(client, "-ERR Disconnected for inactivity.");
+		client_destroy(client, "Disconnected for inactivity");
+	}
+}
+
+static bool init_mailbox(struct client *client, const char **error_r)
+{
+	struct mail_search_args *search_args;
+        struct mailbox_transaction_context *t;
+	struct mail_search_context *ctx;
+        struct mailbox_status status;
+	struct mail *mail;
+	buffer_t *message_sizes_buf;
+	uint32_t failed_uid = 0;
+	uoff_t size;
+	int i;
+	bool failed, expunged;
+
+	message_sizes_buf = buffer_create_dynamic(default_pool, 512);
+
+	search_args = mail_search_build_init();
+	mail_search_build_add_all(search_args);
+
+	for (i = 0; i < 2; i++) {
+		expunged = FALSE;
+		if (mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_READ,
+				 STATUS_UIDVALIDITY, &status) < 0) {
+			client_send_storage_error(client);
+			break;
+		}
+		client->uid_validity = status.uidvalidity;
+
+		t = mailbox_transaction_begin(client->mailbox, 0);
+		ctx = mailbox_search_init(t, search_args, NULL);
+
+		client->last_seen = 0;
+		client->total_size = 0;
+		buffer_set_used_size(message_sizes_buf, 0);
+
+		failed = FALSE;
+		mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL);
+		while (mailbox_search_next(ctx, mail) > 0) {
+			if (mail_get_virtual_size(mail, &size) < 0) {
+				expunged = mail->expunged;
+				failed = TRUE;
+				if (failed_uid == mail->uid) {
+					i_error("Getting size of message "
+						"UID=%u failed", mail->uid);
+					break;
+				}
+				failed_uid = mail->uid;
+				break;
+			}
+
+			if ((mail_get_flags(mail) & MAIL_SEEN) != 0)
+				client->last_seen = mail->seq;
+                        client->total_size += size;
+
+			buffer_append(message_sizes_buf, &size, sizeof(size));
+		}
+		client->messages_count =
+			message_sizes_buf->used / sizeof(uoff_t);
+
+		mail_free(&mail);
+		if (mailbox_search_deinit(&ctx) < 0 || (failed && !expunged)) {
+			client_send_storage_error(client);
+			(void)mailbox_transaction_commit(&t);
+			break;
+		}
+
+		if (!failed) {
+			client->trans = t;
+			client->message_sizes =
+				buffer_free_without_data(&message_sizes_buf);
+			mail_search_args_unref(&search_args);
+			return TRUE;
+		}
+
+		/* well, sync and try again. we might have cached virtual
+		   sizes, make sure they get committed. */
+		(void)mailbox_transaction_commit(&t);
+	}
+	mail_search_args_unref(&search_args);
+
+	if (expunged) {
+		client_send_line(client,
+				 "-ERR [IN-USE] Couldn't sync mailbox.");
+		*error_r = "Can't sync mailbox: Messages keep getting expunged";
+	} else {
+		struct mail_storage *storage = client->inbox_ns->storage;
+		enum mail_error error;
+
+		*error_r = mail_storage_get_last_error(storage, &error);
+	}
+	buffer_free(&message_sizes_buf);
+	return FALSE;
+}
+
+static enum client_workarounds
+parse_workarounds(const struct pop3_settings *set)
+{
+        enum client_workarounds client_workarounds = 0;
+	struct client_workaround_list *list;
+	const char *const *str;
+
+        str = t_strsplit_spaces(set->pop3_client_workarounds, " ,");
+	for (; *str != NULL; str++) {
+		list = client_workaround_list;
+		for (; list->name != NULL; list++) {
+			if (strcasecmp(*str, list->name) == 0) {
+				client_workarounds |= list->num;
+				break;
+			}
+		}
+		if (list->name == NULL)
+			i_fatal("Unknown client workaround: %s", *str);
+	}
+	return client_workarounds;
+}
+
+static enum uidl_keys parse_uidl_keymask(const char *format)
+{
+	enum uidl_keys mask = 0;
+
+	for (; *format != '\0'; format++) {
+		if (format[0] == '%' && format[1] != '\0') {
+			switch (var_get_key(++format)) {
+			case 'v':
+				mask |= UIDL_UIDVALIDITY;
+				break;
+			case 'u':
+				mask |= UIDL_UID;
+				break;
+			case 'm':
+				mask |= UIDL_MD5;
+				break;
+			case 'f':
+				mask |= UIDL_FILE_NAME;
+				break;
+			}
+		}
+	}
+	return mask;
+}
+
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct pop3_settings *set)
+{
+	struct mail_storage *storage;
+	const char *inbox;
+	struct client *client;
+        enum mailbox_open_flags flags;
+	const char *errmsg;
+	enum mail_error error;
+
+	/* always use nonblocking I/O */
+	net_set_nonblock(fd_in, TRUE);
+	net_set_nonblock(fd_out, TRUE);
+
+	client = i_new(struct client, 1);
+	client->set = set;
+	client->fd_in = fd_in;
+	client->fd_out = fd_out;
+	client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
+	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
+	o_stream_set_flush_callback(client->output, client_output, client);
+
+	client->io = io_add(fd_in, IO_READ, client_input, client);
+        client->last_input = ioloop_time;
+	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
+				      client_idle_timeout, client);
+	if (!set->pop3_lock_session) {
+		client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
+						client_commit_timeout, client);
+	}
+
+	client->user = user;
+
+	inbox = "INBOX";
+	client->inbox_ns = mail_namespace_find(user->namespaces, &inbox);
+	if (client->inbox_ns == NULL) {
+		client_send_line(client, "-ERR No INBOX namespace for user.");
+		client_destroy(client, "No INBOX namespace for user.");
+		return NULL;
+	}
+
+	storage = client->inbox_ns->storage;
+
+	client->mail_set = mail_storage_get_settings(storage);
+	flags = MAILBOX_OPEN_POP3_SESSION;
+	if (set->pop3_no_flag_updates)
+		flags |= MAILBOX_OPEN_KEEP_RECENT;
+	if (set->pop3_lock_session)
+		flags |= MAILBOX_OPEN_KEEP_LOCKED;
+	client->mailbox = mailbox_open(&storage, "INBOX", NULL, flags);
+	if (client->mailbox == NULL) {
+		errmsg = t_strdup_printf("Couldn't open INBOX: %s",
+				mail_storage_get_last_error(storage,
+							    &error));
+		i_error("%s", errmsg);
+		client_send_line(client, "-ERR [IN-USE] %s", errmsg);
+		client_destroy(client, "Couldn't open INBOX");
+		return NULL;
+	}
+
+	if (!init_mailbox(client, &errmsg)) {
+		i_error("Couldn't init INBOX: %s", errmsg);
+		client_destroy(client, "Mailbox init failed");
+		return NULL;
+	}
+
+	client->workarounds = parse_workarounds(set);
+	client->uidl_keymask =
+		parse_uidl_keymask(client->mail_set->pop3_uidl_format);
+	if (client->uidl_keymask == 0)
+		i_fatal("Invalid pop3_uidl_format");
+
+	if (!set->pop3_no_flag_updates && client->messages_count > 0)
+		client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
+
+	i_assert(my_client == NULL);
+	my_client = client;
+
+	if (hook_client_created != NULL)
+		hook_client_created(&client);
+	return client;
+}
+
+static const char *client_stats(struct client *client)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'p', NULL, "top_bytes" },
+		{ 't', NULL, "top_count" },
+		{ 'b', NULL, "retr_bytes" },
+		{ 'r', NULL, "retr_count" },
+		{ 'd', NULL, "deleted_count" },
+		{ 'm', NULL, "message_count" },
+		{ 's', NULL, "message_bytes" },
+		{ 'i', NULL, "input" },
+		{ 'o', NULL, "output" },
+		{ '\0', NULL, NULL }
+	};
+	struct var_expand_table *tab;
+	string_t *str;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	tab[0].value = dec2str(client->top_bytes);
+	tab[1].value = dec2str(client->top_count);
+	tab[2].value = dec2str(client->retr_bytes);
+	tab[3].value = dec2str(client->retr_count);
+	tab[4].value = dec2str(client->expunged_count);
+	tab[5].value = dec2str(client->messages_count);
+	tab[6].value = dec2str(client->total_size);
+	tab[7].value = dec2str(client->input->v_offset);
+	tab[8].value = dec2str(client->output->offset);
+
+	str = t_str_new(128);
+	var_expand(str, client->set->pop3_logout_format, tab);
+	return str_c(str);
+}
+
+static const char *client_get_disconnect_reason(struct client *client)
+{
+	errno = client->input->stream_errno != 0 ?
+		client->input->stream_errno :
+		client->output->stream_errno;
+	return errno == 0 || errno == EPIPE ? "Connection closed" :
+		t_strdup_printf("Connection closed: %m");
+}
+
+void client_destroy(struct client *client, const char *reason)
+{
+	if (client->seen_change_count > 0)
+		client_update_mails(client);
+
+	if (!client->disconnected) {
+		if (reason == NULL)
+			reason = client_get_disconnect_reason(client);
+		i_info("%s %s", reason, client_stats(client));
+	}
+
+	if (client->cmd != NULL) {
+		/* deinitialize command */
+		i_stream_close(client->input);
+		o_stream_close(client->output);
+		client->cmd(client);
+		i_assert(client->cmd == NULL);
+	}
+	if (client->trans != NULL) {
+		/* client didn't QUIT, but we still want to save any changes
+		   done in this transaction. especially the cached virtual
+		   message sizes. */
+		(void)mailbox_transaction_commit(&client->trans);
+	}
+	if (client->mailbox != NULL)
+		mailbox_close(&client->mailbox);
+	mail_user_unref(&client->user);
+
+	i_free(client->message_sizes);
+	i_free(client->deleted_bitmask);
+	i_free(client->seen_bitmask);
+
+	if (client->io != NULL)
+		io_remove(&client->io);
+	timeout_remove(&client->to_idle);
+	if (client->to_commit != NULL)
+		timeout_remove(&client->to_commit);
+
+	i_stream_destroy(&client->input);
+	o_stream_destroy(&client->output);
+
+	if (close(client->fd_in) < 0)
+		i_error("close(client in) failed: %m");
+	if (client->fd_in != client->fd_out) {
+		if (close(client->fd_out) < 0)
+			i_error("close(client out) failed: %m");
+	}
+
+	i_free(client);
+
+	/* quit the program */
+	my_client = NULL;
+	master_service_stop(service);
+}
+
+void client_disconnect(struct client *client, const char *reason)
+{
+	if (client->disconnected)
+		return;
+
+	client->disconnected = TRUE;
+	i_info("Disconnected: %s %s", reason, client_stats(client));
+
+	(void)o_stream_flush(client->output);
+
+	i_stream_close(client->input);
+	o_stream_close(client->output);
+}
+
+int client_send_line(struct client *client, const char *fmt, ...)
+{
+	va_list va;
+	ssize_t ret;
+
+	if (client->output->closed)
+		return -1;
+
+	va_start(va, fmt);
+
+	T_BEGIN {
+		string_t *str;
+
+		str = t_str_new(256);
+		str_vprintfa(str, fmt, va);
+		str_append(str, "\r\n");
+
+		ret = o_stream_send(client->output,
+				    str_data(str), str_len(str));
+		i_assert(ret < 0 || (size_t)ret == str_len(str));
+	} T_END;
+	if (ret >= 0) {
+		if (o_stream_get_buffer_used_size(client->output) <
+		    OUTBUF_THROTTLE_SIZE) {
+			ret = 1;
+			client->last_output = ioloop_time;
+		} else {
+			ret = 0;
+			if (client->io != NULL) {
+				/* no more input until client has read
+				   our output */
+				io_remove(&client->io);
+
+				/* If someone happens to flush output,
+				   we want to get our IO handler back in
+				   flush callback */
+				o_stream_set_flush_pending(client->output,
+							   TRUE);
+			}
+		}
+	}
+
+	va_end(va);
+	return (int)ret;
+}
+
+void client_send_storage_error(struct client *client)
+{
+	enum mail_error error;
+
+	if (mailbox_is_inconsistent(client->mailbox)) {
+		client_send_line(client, "-ERR Mailbox is in inconsistent "
+				 "state, please relogin.");
+		client_disconnect(client, "Mailbox is in inconsistent state.");
+		return;
+	}
+
+	client_send_line(client, "-ERR %s",
+			 mail_storage_get_last_error(client->inbox_ns->storage,
+						     &error));
+}
+
+bool client_handle_input(struct client *client)
+{
+	char *line, *args;
+	int ret;
+
+	o_stream_cork(client->output);
+	while (!client->output->closed &&
+	       (line = i_stream_next_line(client->input)) != NULL) {
+		args = strchr(line, ' ');
+		if (args != NULL)
+			*args++ = '\0';
+
+		T_BEGIN {
+			ret = client_command_execute(client, line,
+						     args != NULL ? args : "");
+		} T_END;
+		if (ret >= 0) {
+			client->bad_counter = 0;
+			if (client->cmd != NULL) {
+				o_stream_set_flush_pending(client->output,
+							   TRUE);
+				client->waiting_input = TRUE;
+				break;
+			}
+		} else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
+			client_send_line(client, "-ERR Too many bad commands.");
+			client_disconnect(client, "Too many bad commands.");
+		}
+	}
+	o_stream_uncork(client->output);
+
+	if (client->output->closed) {
+		client_destroy(client, NULL);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void client_input(struct client *client)
+{
+	if (client->cmd != NULL) {
+		/* we're still processing a command. wait until it's
+		   finished. */
+		io_remove(&client->io);
+		client->waiting_input = TRUE;
+		return;
+	}
+
+	client->waiting_input = FALSE;
+	client->last_input = ioloop_time;
+	timeout_reset(client->to_idle);
+	if (client->to_commit != NULL)
+		timeout_reset(client->to_commit);
+
+	switch (i_stream_read(client->input)) {
+	case -1:
+		/* disconnected */
+		client_destroy(client, NULL);
+		return;
+	case -2:
+		/* line too long, kill it */
+		client_send_line(client, "-ERR Input line too long.");
+		client_destroy(client, "Input line too long");
+		return;
+	}
+
+	(void)client_handle_input(client);
+}
+
+static int client_output(struct client *client)
+{
+	int ret;
+
+	if ((ret = o_stream_flush(client->output)) < 0) {
+		client_destroy(client, NULL);
+		return 1;
+	}
+
+	client->last_output = ioloop_time;
+	timeout_reset(client->to_idle);
+	if (client->to_commit != NULL)
+		timeout_reset(client->to_commit);
+
+	if (client->cmd != NULL) {
+		o_stream_cork(client->output);
+		client->cmd(client);
+		o_stream_uncork(client->output);
+	}
+
+	if (client->cmd == NULL) {
+		if (o_stream_get_buffer_used_size(client->output) <
+		    OUTBUF_THROTTLE_SIZE/2 && client->io == NULL) {
+			/* enable input again */
+			client->io = io_add(i_stream_get_fd(client->input),
+					    IO_READ, client_input, client);
+		}
+		if (client->io != NULL && client->waiting_input)
+			client_input(client);
+	}
+
+	return client->cmd == NULL;
+}
+
+void clients_init(void)
+{
+	my_client = NULL;
+}
+
+void clients_deinit(void)
+{
+	if (my_client != NULL) {
+		client_send_line(my_client, "-ERR Server shutting down.");
+		client_destroy(my_client, "Server shutting down");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-client.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,81 @@
+#ifndef POP3_CLIENT_H
+#define POP3_CLIENT_H
+
+struct client;
+struct mail_storage;
+
+typedef void command_func_t(struct client *client);
+
+#define MSGS_BITMASK_SIZE(client) \
+	(((client)->messages_count + (CHAR_BIT-1)) / CHAR_BIT)
+
+struct client {
+	int fd_in, fd_out;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+	struct timeout *to_idle, *to_commit;
+
+	command_func_t *cmd;
+	void *cmd_context;
+
+	struct mail_user *user;
+	struct mail_namespace *inbox_ns;
+	struct mailbox *mailbox;
+	struct mailbox_transaction_context *trans;
+
+	time_t last_input, last_output;
+	unsigned int bad_counter;
+	unsigned int highest_expunged_fetch_msgnum;
+
+	unsigned int uid_validity;
+	unsigned int messages_count;
+	unsigned int deleted_count, expunged_count, seen_change_count;
+	uoff_t *message_sizes;
+	uoff_t total_size;
+	uoff_t deleted_size;
+	uint32_t last_seen;
+
+	uoff_t top_bytes;
+	uoff_t retr_bytes;
+	unsigned int top_count;
+	unsigned int retr_count;
+
+	uoff_t *byte_counter;
+	uoff_t byte_counter_offset;
+
+	unsigned char *deleted_bitmask;
+	unsigned char *seen_bitmask;
+
+	/* settings: */
+	const struct pop3_settings *set;
+	const struct mail_storage_settings *mail_set;
+	enum client_workarounds workarounds;
+	enum uidl_keys uidl_keymask;
+
+	unsigned int disconnected:1;
+	unsigned int deleted:1;
+	unsigned int waiting_input:1;
+};
+
+/* Create new client with specified input/output handles. socket specifies
+   if the handle is a socket. */
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct pop3_settings *set);
+void client_destroy(struct client *client, const char *reason);
+
+/* Disconnect client connection */
+void client_disconnect(struct client *client, const char *reason);
+
+/* Send a line of data to client */
+int client_send_line(struct client *client, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void client_send_storage_error(struct client *client);
+
+bool client_handle_input(struct client *client);
+bool client_update_mails(struct client *client);
+
+void clients_init(void);
+void clients_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-commands.c	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,731 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "pop3-common.h"
+#include "array.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "var-expand.h"
+#include "message-size.h"
+#include "mail-storage.h"
+#include "mail-storage-settings.h"
+#include "mail-search-build.h"
+#include "pop3-capability.h"
+#include "pop3-commands.h"
+
+static const char *get_msgnum(struct client *client, const char *args,
+			      unsigned int *msgnum)
+{
+	unsigned int num, last_num;
+
+	num = 0;
+	while (*args != '\0' && *args != ' ') {
+		if (*args < '0' || *args > '9') {
+			client_send_line(client,
+				"-ERR Invalid message number: %s", args);
+			return NULL;
+		}
+
+		last_num = num;
+		num = num*10 + (*args - '0');
+		if (num < last_num) {
+			client_send_line(client,
+				"-ERR Message number too large: %s", args);
+			return NULL;
+		}
+		args++;
+	}
+
+	if (num == 0 || num > client->messages_count) {
+		client_send_line(client,
+				 "-ERR There's no message %u.", num);
+		return NULL;
+	}
+	num--;
+
+	if (client->deleted) {
+		if (client->deleted_bitmask[num / CHAR_BIT] &
+		    (1 << (num % CHAR_BIT))) {
+			client_send_line(client, "-ERR Message is deleted.");
+			return NULL;
+		}
+	}
+
+	while (*args == ' ') args++;
+
+	*msgnum = num;
+	return args;
+}
+
+static const char *get_size(struct client *client, const char *args,
+			    uoff_t *size)
+{
+	uoff_t num, last_num;
+
+	num = 0;
+	while (*args != '\0' && *args != ' ') {
+		if (*args < '0' || *args > '9') {
+			client_send_line(client, "-ERR Invalid size: %s",
+					 args);
+			return NULL;
+		}
+
+		last_num = num;
+		num = num*10 + (*args - '0');
+		if (num < last_num) {
+			client_send_line(client, "-ERR Size too large: %s",
+					 args);
+			return NULL;
+		}
+		args++;
+	}
+
+	while (*args == ' ') args++;
+
+	*size = num;
+	return args;
+}
+
+static int cmd_capa(struct client *client, const char *args ATTR_UNUSED)
+{
+	client_send_line(client, "+OK\r\n"POP3_CAPABILITY_REPLY".");
+	return 1;
+}
+
+static int cmd_dele(struct client *client, const char *args)
+{
+	unsigned int msgnum;
+
+	if (get_msgnum(client, args, &msgnum) == NULL)
+		return -1;
+
+	if (!client->deleted) {
+		client->deleted_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
+		client->deleted = TRUE;
+	}
+
+	client->deleted_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT);
+	client->deleted_count++;
+	client->deleted_size += client->message_sizes[msgnum];
+	client_send_line(client, "+OK Marked to be deleted.");
+	return 1;
+}
+
+struct cmd_list_context {
+	unsigned int msgnum;
+};
+
+static void cmd_list_callback(struct client *client)
+{
+	struct cmd_list_context *ctx = client->cmd_context;
+	int ret = 1;
+
+	for (; ctx->msgnum != client->messages_count; ctx->msgnum++) {
+		if (ret == 0) {
+			/* buffer full */
+			return;
+		}
+
+		if (client->deleted) {
+			if (client->deleted_bitmask[ctx->msgnum / CHAR_BIT] &
+			    (1 << (ctx->msgnum % CHAR_BIT)))
+				continue;
+		}
+		ret = client_send_line(client, "%u %"PRIuUOFF_T,
+				       ctx->msgnum+1,
+				       client->message_sizes[ctx->msgnum]);
+		if (ret < 0)
+			break;
+	}
+
+	client_send_line(client, ".");
+
+	i_free(ctx);
+	client->cmd = NULL;
+}
+
+static int cmd_list(struct client *client, const char *args)
+{
+        struct cmd_list_context *ctx;
+
+	if (*args == '\0') {
+		ctx = i_new(struct cmd_list_context, 1);
+		client_send_line(client, "+OK %u messages:",
+				 client->messages_count - client->deleted_count);
+
+		client->cmd = cmd_list_callback;
+		client->cmd_context = ctx;
+		cmd_list_callback(client);
+	} else {
+		unsigned int msgnum;
+
+		if (get_msgnum(client, args, &msgnum) == NULL)
+			return -1;
+
+		client_send_line(client, "+OK %u %"PRIuUOFF_T, msgnum+1,
+				 client->message_sizes[msgnum]);
+	}
+
+	return 1;
+}
+
+static int cmd_last(struct client *client, const char *args ATTR_UNUSED)
+{
+	client_send_line(client, "+OK %u", client->last_seen);
+	return 1;
+}
+
+static int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
+{
+	client_send_line(client, "+OK");
+	return 1;
+}
+
+static struct mail_search_args *
+pop3_search_build(struct client *client, uint32_t seq)
+{
+	struct mail_search_args *search_args;
+
+	search_args = mail_search_build_init();
+	if (seq == 0) {
+		mail_search_build_add_seqset(search_args,
+					     1, client->messages_count);
+	} else {
+		mail_search_build_add_seqset(search_args, seq, seq);
+	}
+	return search_args;
+}
+
+bool client_update_mails(struct client *client)
+{
+	struct mail_search_args *search_args;
+	struct mail_search_context *ctx;
+	struct mail *mail;
+	uint32_t idx, bit;
+
+	if (mailbox_is_readonly(client->mailbox)) {
+		/* silently ignore */
+		return TRUE;
+	}
+
+	search_args = pop3_search_build(client, 0);
+	ctx = mailbox_search_init(client->trans, search_args, NULL);
+	mail_search_args_unref(&search_args);
+
+	mail = mail_alloc(client->trans, 0, NULL);
+	while (mailbox_search_next(ctx, mail) > 0) {
+		idx = mail->seq - 1;
+		bit = 1 << (idx % CHAR_BIT);
+		if (client->deleted_bitmask != NULL &&
+		    (client->deleted_bitmask[idx / CHAR_BIT] & bit) != 0) {
+			mail_expunge(mail);
+			client->expunged_count++;
+		} else if (client->seen_bitmask != NULL &&
+			   (client->seen_bitmask[idx / CHAR_BIT] & bit) != 0) {
+			mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN);
+		}
+	}
+	mail_free(&mail);
+
+	client->seen_change_count = 0;
+	return mailbox_search_deinit(&ctx) == 0;
+}
+
+static int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
+{
+	if (client->deleted || client->seen_bitmask != NULL) {
+		if (!client_update_mails(client)) {
+			client_send_storage_error(client);
+			client_disconnect(client,
+				"Storage error during logout.");
+			return 1;
+		}
+	}
+
+	if (mailbox_transaction_commit(&client->trans) < 0 ||
+	    mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_WRITE,
+			 0, NULL) < 0) {
+		client_send_storage_error(client);
+		client_disconnect(client, "Storage error during logout.");
+		return 1;
+	}
+
+	if (!client->deleted)
+		client_send_line(client, "+OK Logging out.");
+	else
+		client_send_line(client, "+OK Logging out, messages deleted.");
+
+	client_disconnect(client, "Logged out");
+	return 1;
+}
+
+struct fetch_context {
+	struct mail_search_context *search_ctx;
+	struct mail *mail;
+	struct istream *stream;
+	uoff_t body_lines;
+
+	unsigned char last;
+	bool cr_skipped, in_body;
+};
+
+static void fetch_deinit(struct fetch_context *ctx)
+{
+	(void)mailbox_search_deinit(&ctx->search_ctx);
+	mail_free(&ctx->mail);
+	i_free(ctx);
+}
+
+static void fetch_callback(struct client *client)
+{
+	struct fetch_context *ctx = client->cmd_context;
+	const unsigned char *data;
+	unsigned char add;
+	size_t i, size;
+	int ret;
+
+	while ((ctx->body_lines > 0 || !ctx->in_body) &&
+	       i_stream_read_data(ctx->stream, &data, &size, 0) > 0) {
+		if (size > 4096)
+			size = 4096;
+
+		add = '\0';
+		for (i = 0; i < size; i++) {
+			if ((data[i] == '\r' || data[i] == '\n') &&
+			    !ctx->in_body) {
+				if (i == 0 && (ctx->last == '\0' ||
+					       ctx->last == '\n'))
+					ctx->in_body = TRUE;
+				else if (i > 0 && data[i-1] == '\n')
+					ctx->in_body = TRUE;
+			}
+
+			if (data[i] == '\n') {
+				if ((i == 0 && ctx->last != '\r') ||
+				    (i > 0 && data[i-1] != '\r')) {
+					/* missing CR */
+					add = '\r';
+					break;
+				}
+
+				if (ctx->in_body) {
+					if (--ctx->body_lines == 0) {
+						i++;
+						break;
+					}
+				}
+			} else if (data[i] == '.' &&
+				   ((i == 0 && ctx->last == '\n') ||
+				    (i > 0 && data[i-1] == '\n'))) {
+				/* escape the dot */
+				add = '.';
+				break;
+			} else if (data[i] == '\0' &&
+				   (client->workarounds &
+				    WORKAROUND_OUTLOOK_NO_NULS) != 0) {
+				add = 0x80;
+				break;
+			}
+		}
+
+		if (i > 0) {
+			if (o_stream_send(client->output, data, i) < 0)
+				break;
+			ctx->last = data[i-1];
+			i_stream_skip(ctx->stream, i);
+		}
+
+		if (o_stream_get_buffer_used_size(client->output) >= 4096) {
+			if ((ret = o_stream_flush(client->output)) < 0)
+				break;
+			if (ret == 0) {
+				/* continue later */
+				return;
+			}
+		}
+
+		if (add != '\0') {
+			if (o_stream_send(client->output, &add, 1) < 0)
+				break;
+
+			ctx->last = add;
+			if (add == 0x80)
+				i_stream_skip(ctx->stream, 1);
+		}
+	}
+
+	if (ctx->last != '\n') {
+		/* didn't end with CRLF */
+		(void)o_stream_send(client->output, "\r\n", 2);
+	}
+
+	if (!ctx->in_body &&
+	    (client->workarounds & WORKAROUND_OE_NS_EOH) != 0) {
+		/* Add the missing end of headers line. */
+		(void)o_stream_send(client->output, "\r\n", 2);
+	}
+
+	*client->byte_counter +=
+		client->output->offset - client->byte_counter_offset;
+        client->byte_counter = NULL;
+
+	client_send_line(client, ".");
+	fetch_deinit(ctx);
+	client->cmd = NULL;
+}
+
+static int client_reply_msg_expunged(struct client *client, unsigned int msgnum)
+{
+	client_send_line(client, "-ERR Message %u expunged.", msgnum + 1);
+	if (msgnum <= client->highest_expunged_fetch_msgnum) {
+		/* client tried to fetch an expunged message again.
+		   treat this as error so we'll eventually disconnect the
+		   client instead of letting it loop forever. */
+		return -1;
+	}
+	client->highest_expunged_fetch_msgnum = msgnum;
+	return 1;
+}
+
+static int fetch(struct client *client, unsigned int msgnum, uoff_t body_lines)
+{
+        struct fetch_context *ctx;
+	struct mail_search_args *search_args;
+	int ret;
+
+	search_args = pop3_search_build(client, msgnum+1);
+
+	ctx = i_new(struct fetch_context, 1);
+	ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL);
+	mail_search_args_unref(&search_args);
+
+	ctx->mail = mail_alloc(client->trans, MAIL_FETCH_STREAM_HEADER |
+			       MAIL_FETCH_STREAM_BODY, NULL);
+
+	if (mailbox_search_next(ctx->search_ctx, ctx->mail) <= 0 ||
+	    mail_get_stream(ctx->mail, NULL, NULL, &ctx->stream) < 0) {
+		ret = client_reply_msg_expunged(client, msgnum);
+		fetch_deinit(ctx);
+		return ret;
+	}
+
+	if (body_lines == (uoff_t)-1 && client->seen_bitmask != NULL) {
+		if ((mail_get_flags(ctx->mail) & MAIL_SEEN) == 0) {
+			/* mark the message seen with RETR command */
+			client->seen_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT);
+			client->seen_change_count++;
+		}
+	}
+
+	ctx->body_lines = body_lines;
+	if (body_lines == (uoff_t)-1) {
+		client_send_line(client, "+OK %"PRIuUOFF_T" octets",
+				 client->message_sizes[msgnum]);
+	} else {
+		client_send_line(client, "+OK");
+		ctx->body_lines++; /* internally we count the empty line too */
+	}
+
+	client->cmd = fetch_callback;
+	client->cmd_context = ctx;
+	fetch_callback(client);
+	return 1;
+}
+
+static int cmd_retr(struct client *client, const char *args)
+{
+	unsigned int msgnum;
+
+	if (get_msgnum(client, args, &msgnum) == NULL)
+		return -1;
+
+	if (client->last_seen <= msgnum)
+		client->last_seen = msgnum+1;
+
+	client->retr_count++;
+	client->byte_counter = &client->retr_bytes;
+	client->byte_counter_offset = client->output->offset;
+
+	return fetch(client, msgnum, (uoff_t)-1);
+}
+
+static int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
+{
+	struct mail_search_context *search_ctx;
+	struct mail *mail;
+	struct mail_search_args *search_args;
+
+	client->last_seen = 0;
+
+	if (client->deleted) {
+		client->deleted = FALSE;
+		memset(client->deleted_bitmask, 0, MSGS_BITMASK_SIZE(client));
+		client->deleted_count = 0;
+		client->deleted_size = 0;
+	}
+	if (client->seen_change_count > 0) {
+		memset(client->seen_bitmask, 0, MSGS_BITMASK_SIZE(client));
+		client->seen_change_count = 0;
+	}
+
+	if (client->set->pop3_enable_last) {
+		/* remove all \Seen flags (as specified by RFC 1460) */
+		search_args = pop3_search_build(client, 0);
+		search_ctx = mailbox_search_init(client->trans,
+						 search_args, NULL);
+		mail_search_args_unref(&search_args);
+
+		mail = mail_alloc(client->trans, 0, NULL);
+		while (mailbox_search_next(search_ctx, mail) > 0)
+			mail_update_flags(mail, MODIFY_REMOVE, MAIL_SEEN);
+		mail_free(&mail);
+		(void)mailbox_search_deinit(&search_ctx);
+
+		mailbox_transaction_commit(&client->trans);
+		client->trans = mailbox_transaction_begin(client->mailbox, 0);
+	}
+
+	client_send_line(client, "+OK");
+	return 1;
+}
+
+static int cmd_stat(struct client *client, const char *args ATTR_UNUSED)
+{
+	client_send_line(client, "+OK %u %"PRIuUOFF_T, client->
+			 messages_count - client->deleted_count,
+			 client->total_size - client->deleted_size);
+	return 1;
+}
+
+static int cmd_top(struct client *client, const char *args)
+{
+	unsigned int msgnum;
+	uoff_t max_lines;
+
+	args = get_msgnum(client, args, &msgnum);
+	if (args == NULL)
+		return -1;
+	if (get_size(client, args, &max_lines) == NULL)
+		return -1;
+
+	client->top_count++;
+	client->byte_counter = &client->top_bytes;
+	client->byte_counter_offset = client->output->offset;
+
+	return fetch(client, msgnum, max_lines);
+}
+
+struct cmd_uidl_context {
+	struct mail_search_context *search_ctx;
+	struct mail *mail;
+	unsigned int message;
+};
+
+static void pop3_get_uid(struct client *client, struct cmd_uidl_context *ctx,
+			 struct var_expand_table *tab, string_t *str)
+{
+	char uid_str[MAX_INT_STRLEN];
+	const char *uidl;
+
+	if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_BACKEND, &uidl) == 0 &&
+	    *uidl != '\0') {
+		str_append(str, uidl);
+		return;
+	}
+
+	if (client->set->pop3_reuse_xuidl &&
+	    mail_get_first_header(ctx->mail, "X-UIDL", &uidl) > 0) {
+		str_append(str, uidl);
+		return;
+	}
+
+	if ((client->uidl_keymask & UIDL_UID) != 0) {
+		i_snprintf(uid_str, sizeof(uid_str), "%u",
+			   ctx->mail->uid);
+		tab[1].value = uid_str;
+	}
+	if ((client->uidl_keymask & UIDL_MD5) != 0) {
+		if (mail_get_special(ctx->mail, MAIL_FETCH_HEADER_MD5,
+				     &tab[2].value) < 0 ||
+		    *tab[2].value == '\0') {
+			/* broken */
+			i_fatal("UIDL: Header MD5 not found");
+		}
+	}
+	if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) {
+		if (mail_get_special(ctx->mail,
+				     MAIL_FETCH_UIDL_FILE_NAME,
+				     &tab[3].value) < 0 ||
+		    *tab[3].value == '\0') {
+			/* broken */
+			i_fatal("UIDL: File name not found");
+		}
+	}
+	var_expand(str, client->mail_set->pop3_uidl_format, tab);
+}
+
+static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'v', NULL, "uidvalidity" },
+		{ 'u', NULL, "uid" },
+		{ 'm', NULL, "md5" },
+		{ 'f', NULL, "filename" },
+		{ '\0', NULL, NULL }
+	};
+	struct var_expand_table *tab;
+	string_t *str;
+	int ret;
+	bool found = FALSE;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+	tab[0].value = t_strdup_printf("%u", client->uid_validity);
+
+	str = t_str_new(128);
+	while (mailbox_search_next(ctx->search_ctx, ctx->mail) > 0) {
+		if (client->deleted) {
+			uint32_t idx = ctx->mail->seq - 1;
+			if (client->deleted_bitmask[idx / CHAR_BIT] &
+			    (1 << (idx % CHAR_BIT)))
+				continue;
+		}
+		found = TRUE;
+
+		str_truncate(str, 0);
+		str_printfa(str, ctx->message == 0 ? "%u " : "+OK %u ",
+			    ctx->mail->seq);
+		pop3_get_uid(client, ctx, tab, str);
+
+		ret = client_send_line(client, "%s", str_c(str));
+		if (ret < 0)
+			break;
+		if (ret == 0 && ctx->message == 0) {
+			/* output is being buffered, continue when there's
+			   more space */
+			return FALSE;
+		}
+	}
+
+	/* finished */
+	mail_free(&ctx->mail);
+	(void)mailbox_search_deinit(&ctx->search_ctx);
+
+	client->cmd = NULL;
+
+	if (ctx->message == 0)
+		client_send_line(client, ".");
+	i_free(ctx);
+	return found;
+}
+
+static void cmd_uidl_callback(struct client *client)
+{
+	struct cmd_uidl_context *ctx = client->cmd_context;
+
+        (void)list_uids_iter(client, ctx);
+}
+
+static struct cmd_uidl_context *
+cmd_uidl_init(struct client *client, unsigned int message)
+{
+        struct cmd_uidl_context *ctx;
+	struct mail_search_args *search_args;
+	enum mail_fetch_field wanted_fields;
+
+	search_args = pop3_search_build(client, message);
+
+	ctx = i_new(struct cmd_uidl_context, 1);
+	ctx->message = message;
+
+	wanted_fields = 0;
+	if ((client->uidl_keymask & UIDL_MD5) != 0)
+		wanted_fields |= MAIL_FETCH_HEADER_MD5;
+
+	ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL);
+	mail_search_args_unref(&search_args);
+
+	ctx->mail = mail_alloc(client->trans, wanted_fields, NULL);
+	if (message == 0) {
+		client->cmd = cmd_uidl_callback;
+		client->cmd_context = ctx;
+	}
+	return ctx;
+}
+
+static int cmd_uidl(struct client *client, const char *args)
+{
+        struct cmd_uidl_context *ctx;
+
+	if (*args == '\0') {
+		client_send_line(client, "+OK");
+		ctx = cmd_uidl_init(client, 0);
+		(void)list_uids_iter(client, ctx);
+	} else {
+		unsigned int msgnum;
+
+		if (get_msgnum(client, args, &msgnum) == NULL)
+			return -1;
+
+		ctx = cmd_uidl_init(client, msgnum+1);
+		if (!list_uids_iter(client, ctx))
+			return client_reply_msg_expunged(client, msgnum);
+	}
+
+	return 1;
+}
+
+int client_command_execute(struct client *client,
+			   const char *name, const char *args)
+{
+	/* keep the command uppercased */
+	name = t_str_ucase(name);
+
+	while (*args == ' ') args++;
+
+	switch (*name) {
+	case 'C':
+		if (strcmp(name, "CAPA") == 0)
+			return cmd_capa(client, args);
+		break;
+	case 'D':
+		if (strcmp(name, "DELE") == 0)
+			return cmd_dele(client, args);
+		break;
+	case 'L':
+		if (strcmp(name, "LIST") == 0)
+			return cmd_list(client, args);
+		if (strcmp(name, "LAST") == 0 && client->set->pop3_enable_last)
+			return cmd_last(client, args);
+		break;
+	case 'N':
+		if (strcmp(name, "NOOP") == 0)
+			return cmd_noop(client, args);
+		break;
+	case 'Q':
+		if (strcmp(name, "QUIT") == 0)
+			return cmd_quit(client, args);
+		break;
+	case 'R':
+		if (strcmp(name, "RETR") == 0)
+			return cmd_retr(client, args);
+		if (strcmp(name, "RSET") == 0)
+			return cmd_rset(client, args);
+		break;
+	case 'S':
+		if (strcmp(name, "STAT") == 0)
+			return cmd_stat(client, args);
+		break;
+	case 'T':
+		if (strcmp(name, "TOP") == 0)
+			return cmd_top(client, args);
+		break;
+	case 'U':
+		if (strcmp(name, "UIDL") == 0)
+			return cmd_uidl(client, args);
+		break;
+	}
+
+	client_send_line(client, "-ERR Unknown command: %s", name);
+	return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-commands.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,7 @@
+#ifndef POP3_COMMANDS_H
+#define POP3_COMMANDS_H
+
+int client_command_execute(struct client *client,
+			   const char *name, const char *args);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pop3/pop3-common.h	Tue May 05 11:57:04 2009 -0400
@@ -0,0 +1,24 @@
+#ifndef POP3_COMMON_H
+#define POP3_COMMON_H
+
+enum client_workarounds {
+	WORKAROUND_OUTLOOK_NO_NULS		= 0x01,
+	WORKAROUND_OE_NS_EOH			= 0x02
+};
+
+enum uidl_keys {
+	UIDL_UIDVALIDITY	= 0x01,
+	UIDL_UID		= 0x02,
+	UIDL_MD5		= 0x04,
+	UIDL_FILE_NAME		= 0x08
+};
+
+#include "lib.h"
+#include "pop3-client.h"
+#include "pop3-settings.h"
+
+extern struct master_service *service;
+
+extern void (*hook_client_created)(struct client **client);
+
+#endif