Mercurial > dovecot > core-2.2
changeset 9219:97cdfeb57129 HEAD
Renamed headers to prevent collision if they were flattened on an install.
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