changeset 3520:e2fe8222449d HEAD

s/occured/occurred/
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Aug 2005 14:41:19 +0300
parents 7addee16b026
children 3ccf9db0d117
files doc/auth-protocol.txt doc/index.txt src/auth/Makefile.am src/auth/auth-master-connection.c src/auth/auth-request-handler.c src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-worker-client.c src/auth/passdb-blocking.c src/auth/passdb-checkpassword.c src/auth/passdb.h src/auth/userdb-blocking.c src/auth/userdb-ldap.c src/auth/userdb-passdb.c src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-sql.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.c src/auth/userdb.h src/imap/client.h src/imap/cmd-idle.c src/lib-imap/imap-parser.h src/lib-mail/message-parser.h src/lib-sql/sql-api.h src/lib-storage/index/maildir/maildir-sync.c src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/subscription-file/subscription-file.h src/lib/file-cache.h src/lib/istream.h src/lib/read-full.h src/lib/strfuncs.c src/lib/write-full.h src/login-common/ssl-proxy-gnutls.c src/login-common/ssl-proxy.h
diffstat 37 files changed, 236 insertions(+), 187 deletions(-) [+]
line wrap: on
line diff
--- a/doc/auth-protocol.txt	Sun Aug 07 14:28:13 2005 +0300
+++ b/doc/auth-protocol.txt	Sun Aug 07 14:41:19 2005 +0300
@@ -169,7 +169,7 @@
 it happens it means either a timeout caused by very high load, or client
 lying to master about the request.
 
-FAIL reply means an internal error occured. Usually either a configuration
+FAIL reply means an internal error occurred. Usually either a configuration
 mistake or temporary error caused by lost resource (eg. database down).
 
 USER reply is sent if request succeeded. It can return parameters:
--- a/doc/index.txt	Sun Aug 07 14:28:13 2005 +0300
+++ b/doc/index.txt	Sun Aug 07 14:41:19 2005 +0300
@@ -87,7 +87,7 @@
 Dovecot uses modify log file to log changes made to index files; currently
 message flag changes and expunges. This way only one of the Dovecot
 processes has to scan the mailbox, other processes simply check from the
-log file what changes occured.
+log file what changes occurred.
 
 All external changes that Dovecot notices (eg. another MUA expunging mails)
 are also saved into log file. They can quickly be found from there when
--- a/src/auth/Makefile.am	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/Makefile.am	Sun Aug 07 14:41:19 2005 +0300
@@ -40,6 +40,7 @@
 	auth-module.c \
 	auth-request.c \
 	auth-request-handler.c \
+	auth-stream.c \
 	auth-worker-client.c \
 	auth-worker-server.c \
 	db-ldap.c \
@@ -88,6 +89,7 @@
 	auth-module.h \
 	auth-request.h \
 	auth-request-handler.h \
+	auth-stream.h \
 	auth-worker-client.h \
 	auth-worker-server.h \
 	db-ldap.h \
--- a/src/auth/auth-master-connection.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/auth-master-connection.c	Sun Aug 07 14:41:19 2005 +0300
@@ -78,17 +78,18 @@
 }
 
 static void
-user_callback(const char *result, struct auth_request *auth_request)
+user_callback(struct auth_stream_reply *reply,
+	      struct auth_request *auth_request)
 {
 	struct auth_master_connection *conn = auth_request->context;
 	string_t *str;
 
 	str = t_str_new(128);
-	if (result == NULL)
+	if (reply == NULL)
 		str_printfa(str, "NOTFOUND\t%u\n", auth_request->id);
 	else {
 		str_printfa(str, "USER\t%u\t", auth_request->id);
-		str_append(str, result);
+		str_append(str, auth_stream_reply_export(reply));
 		str_append_c(str, '\n');
 	}
 	(void)o_stream_send(conn->output, str_data(str), str_len(str));
--- a/src/auth/auth-request-handler.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/auth-request-handler.c	Sun Aug 07 14:41:19 2005 +0300
@@ -104,16 +104,19 @@
 static const char *get_client_extra_fields(struct auth_request *request)
 {
 	string_t *str;
-	const char **fields;
+	const char **fields, *extra_fields;
 	unsigned int src, dest;
 
+	extra_fields = request->extra_fields == NULL ? NULL :
+		auth_stream_reply_export(request->extra_fields);
+
 	if (!request->proxy) {
-		if (request->extra_fields == NULL)
+		if (extra_fields == NULL)
 			return NULL;
 
 		/* we only wish to remove all fields prefixed with "userdb_" */
-		if (strstr(str_c(request->extra_fields), "userdb_") == NULL)
-			return str_c(request->extra_fields);
+		if (strstr(extra_fields, "userdb_") == NULL)
+			return extra_fields;
 	}
 
 	str = t_str_new(128);
@@ -123,7 +126,7 @@
 		str_printfa(str, "pass=%s", request->mech_password);
 	}
 
-	fields = t_strsplit(str_c(request->extra_fields), "\t");
+	fields = t_strsplit(extra_fields, "\t");
 	for (src = dest = 0; fields[src] != NULL; src++) {
 		if (strncmp(fields[src], "userdb_", 7) != 0) {
 			if (str_len(str) > 0)
@@ -374,23 +377,24 @@
 	return TRUE;
 }
 
-static void userdb_callback(const char *result, struct auth_request *request)
+static void userdb_callback(struct auth_stream_reply *reply,
+			    struct auth_request *request)
 {
         struct auth_request_handler *handler = request->context;
-	string_t *reply;
+	string_t *str;
 
 	i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
 
 	request->state = AUTH_REQUEST_STATE_FINISHED;
 
-	reply = t_str_new(256);
-	if (result == NULL)
-		str_printfa(reply, "NOTFOUND\t%u", request->id);
+	str = t_str_new(256);
+	if (reply == NULL)
+		str_printfa(str, "NOTFOUND\t%u", request->id);
 	else {
-		str_printfa(reply, "USER\t%u\t", request->id);
-		str_append(reply, result);
+		str_printfa(str, "USER\t%u\t", request->id);
+		str_append(str, auth_stream_reply_export(reply));
 	}
-	handler->master_callback(str_c(reply), request->master);
+	handler->master_callback(str_c(str), request->master);
 
 	auth_request_unref(request);
         auth_request_handler_unref(handler);
--- a/src/auth/auth-request.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/auth-request.c	Sun Aug 07 14:41:19 2005 +0300
@@ -153,11 +153,14 @@
 				    enum passdb_result result)
 {
 	struct passdb_module *passdb = request->passdb->passdb;
+	const char *extra_fields;
 	string_t *str;
 
-	i_assert(request->extra_fields == NULL ||
-		 (strstr(str_c(request->extra_fields), "\tpass=") == NULL &&
-		  strncmp(str_c(request->extra_fields), "pass=", 5) != 0));
+	extra_fields = request->extra_fields == NULL ? NULL :
+		auth_stream_reply_export(request->extra_fields);
+	i_assert(extra_fields == NULL ||
+		 (strstr(extra_fields, "\tpass=") == NULL &&
+		  strncmp(extra_fields, "pass=", 5) != 0));
 
 	if (passdb_cache == NULL)
 		return;
@@ -175,8 +178,7 @@
 	}
 
 	/* save all except the currently given password in cache */
-	str = t_str_new(32 + (request->extra_fields != NULL ? 
-			      str_len(request->extra_fields) : 0));
+	str = t_str_new(256);
 	if (request->passdb_password != NULL) {
 		if (*request->passdb_password != '{') {
 			/* cached passwords must have a known scheme */
@@ -184,12 +186,16 @@
 			str_append(str, passdb->default_pass_scheme);
 			str_append_c(str, '}');
 		}
+		if (strchr(request->passdb_password, '\t') != NULL)
+			i_panic("%s: Password contains TAB", request->user);
+		if (strchr(request->passdb_password, '\n') != NULL)
+			i_panic("%s: Password contains LF", request->user);
 		str_append(str, request->passdb_password);
 	}
 
-	if (request->extra_fields != NULL) {
+	if (extra_fields != NULL) {
 		str_append_c(str, '\t');
-		str_append_str(str, request->extra_fields);
+		str_append(str, extra_fields);
 	}
 	if (request->no_failure_delay) {
 		str_append_c(str, '\t');
@@ -235,7 +241,7 @@
 	    request->passdb->next != NULL) {
 		/* try next passdb. */
 		if (request->extra_fields != NULL)
-			str_truncate(request->extra_fields, 0);
+			auth_stream_reply_reset(request->extra_fields);
 
                 request->state = AUTH_REQUEST_STATE_MECH_CONTINUE;
 		request->passdb = request->passdb->next;
@@ -355,13 +361,13 @@
 	}
 }
 
-void auth_request_userdb_callback(const char *result,
+void auth_request_userdb_callback(struct auth_stream_reply *reply,
 				  struct auth_request *request)
 {
-	if (result == NULL && request->userdb->next != NULL) {
+	if (reply == NULL && request->userdb->next != NULL) {
 		/* try next userdb. */
 		if (request->extra_fields != NULL)
-			str_truncate(request->extra_fields, 0);
+			auth_stream_reply_reset(request->extra_fields);
 
 		request->userdb = request->userdb->next;
 		auth_request_lookup_user(request,
@@ -369,13 +375,13 @@
 		return;
 	}
 
-	if (result == NULL && request->client_pid != 0) {
+	if (reply == NULL && request->client_pid != 0) {
 		/* this was actual login attempt */
 		auth_request_log_error(request, "userdb",
 				       "user not found from userdb");
 	}
 
-        request->private_callback.userdb(result, request);
+        request->private_callback.userdb(reply, request);
 }
 
 void auth_request_lookup_user(struct auth_request *request,
@@ -426,8 +432,6 @@
 			    const char *name, const char *value,
 			    const char *default_scheme)
 {
-	string_t *str;
-
 	i_assert(value != NULL);
 
 	if (strcmp(name, "password") == 0) {
@@ -466,29 +470,19 @@
 		return;
 	}
 
-	str = request->extra_fields;
-	if (str == NULL)
-		request->extra_fields = str = str_new(request->pool, 64);
-
 	if (strcmp(name, "nologin") == 0) {
 		/* user can't actually login - don't keep this
 		   reply for master */
 		request->no_login = TRUE;
-		if (str_len(str) > 0)
-			str_append_c(str, '\t');
-		str_append(str, name);
 	} else if (strcmp(name, "proxy") == 0) {
 		/* we're proxying authentication for this user. send
 		   password back if using plaintext authentication. */
 		request->proxy = TRUE;
-		if (str_len(str) > 0)
-			str_append_c(str, '\t');
-		str_append(str, name);
-	} else {
-		if (str_len(str) > 0)
-			str_append_c(str, '\t');
-		str_printfa(str, "%s=%s", name, value);
 	}
+
+	if (request->extra_fields == NULL)
+		request->extra_fields = auth_stream_reply_init(request);
+	auth_stream_reply_add(request->extra_fields, name, value);
 }
 
 static const char *escape_none(const char *str)
--- a/src/auth/auth-request.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/auth-request.h	Sun Aug 07 14:41:19 2005 +0300
@@ -24,7 +24,7 @@
 	char *user;
 	char *mech_password; /* set if verify_plain() is called */
 	char *passdb_password; /* set after password lookup if successful */
-	string_t *extra_fields;
+        struct auth_stream_reply *extra_fields;
 
 	struct mech_module *mech;
 	struct auth *auth;
@@ -116,7 +116,7 @@
 void auth_request_lookup_credentials_callback(enum passdb_result result,
 					      const char *credentials,
 					      struct auth_request *request);
-void auth_request_userdb_callback(const char *result,
+void auth_request_userdb_callback(struct auth_stream_reply *reply,
 				  struct auth_request *request);
 
 #endif
--- a/src/auth/auth-worker-client.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/auth-worker-client.c	Sun Aug 07 14:41:19 2005 +0300
@@ -90,8 +90,11 @@
 		if (request->passdb_password != NULL)
 			str_append(str, request->passdb_password);
 		str_append_c(str, '\t');
-		if (request->extra_fields != NULL)
-			str_append_str(str, request->extra_fields);
+		if (request->extra_fields != NULL) {
+			const char *field =
+				auth_stream_reply_export(request->extra_fields);
+			str_append(str, field);
+		}
 	}
 	str_append_c(str, '\n');
 	o_stream_send(client->output, str_data(str), str_len(str));
@@ -161,8 +164,11 @@
 		str_printfa(str, "OK\t%s\t{%s}%s\t", request->user,
 			    passdb_credentials_to_str(request->credentials),
 			    credentials);
-		if (request->extra_fields != NULL)
-			str_append_str(str, request->extra_fields);
+		if (request->extra_fields != NULL) {
+			const char *field =
+				auth_stream_reply_export(request->extra_fields);
+			str_append(str, field);
+		}
 	}
 	str_append_c(str, '\n');
 	o_stream_send(client->output, str_data(str), str_len(str));
@@ -220,15 +226,16 @@
 }
 
 static void
-lookup_user_callback(const char *result, struct auth_request *auth_request)
+lookup_user_callback(struct auth_stream_reply *reply,
+		     struct auth_request *auth_request)
 {
 	struct auth_worker_client *client = auth_request->context;
 	string_t *str;
 
 	str = t_str_new(64);
 	str_printfa(str, "%u\t", auth_request->id);
-	if (result != NULL)
-		str_append(str, result);
+	if (reply != NULL)
+		str_append(str, auth_stream_reply_export(reply));
 	str_append_c(str, '\n');
 
 	o_stream_send(client->output, str_data(str), str_len(str));
--- a/src/auth/passdb-blocking.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/passdb-blocking.c	Sun Aug 07 14:41:19 2005 +0300
@@ -87,8 +87,8 @@
 		if (p != NULL && (p[6] == '\0' || p[6] == '\t'))
 			request->proxy = TRUE;
 
-		request->extra_fields = str_new(request->pool, 128);
-		str_append(request->extra_fields, reply);
+		request->extra_fields = auth_stream_reply_init(request);
+		auth_stream_reply_import(request->extra_fields, reply);
 	}
 	return 0;
 }
--- a/src/auth/passdb-checkpassword.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/passdb-checkpassword.c	Sun Aug 07 14:41:19 2005 +0300
@@ -62,10 +62,9 @@
 
 	if (result == PASSDB_RESULT_OK) {
 		request->request->extra_fields =
-			str_new(request->request->pool,
-				str_len(request->input_buf));
-		str_append_str(request->request->extra_fields,
-			       request->input_buf);
+			auth_stream_reply_init(request->request);
+		auth_stream_reply_import(request->request->extra_fields,
+					 str_c(request->input_buf));
 	}
 
 	if (auth_request_unref(request->request)) {
--- a/src/auth/passdb.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/passdb.h	Sun Aug 07 14:41:19 2005 +0300
@@ -67,7 +67,7 @@
 			       lookup_credentials_callback_t *callback,
                                struct auth_request *auth_request);
 
-const char * passdb_credentials_to_str(enum passdb_credentials credentials);
+const char *passdb_credentials_to_str(enum passdb_credentials credentials);
 
 void passdb_preinit(struct auth *auth, const char *driver, const char *args);
 void passdb_init(struct auth_passdb *passdb);
--- a/src/auth/userdb-blocking.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-blocking.c	Sun Aug 07 14:41:19 2005 +0300
@@ -10,10 +10,15 @@
 
 static void user_callback(struct auth_request *request, const char *reply)
 {
+	struct auth_stream_reply *stream_reply;
+
 	if (*reply == '\0')
 		reply = NULL;
 
-        request->private_callback.userdb(reply, request);
+	stream_reply = auth_stream_reply_init(request);
+	auth_stream_reply_import(stream_reply, reply);
+
+        request->private_callback.userdb(stream_reply, request);
 }
 
 void userdb_blocking_lookup(struct auth_request *request)
--- a/src/auth/userdb-ldap.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-ldap.c	Sun Aug 07 14:41:19 2005 +0300
@@ -25,57 +25,53 @@
 
 static struct ldap_connection *userdb_ldap_conn;
 
-static int append_uid_list(struct auth_request *auth_request, string_t *str,
+static int append_uid_list(struct auth_request *auth_request,
+                           struct auth_stream_reply *reply,
 			   const char *name, char **vals)
 {
 	uid_t uid;
 
 	for (; *vals != NULL; vals++) {
-		str_append_c(str, '\t');
-		str_append(str, name);
-		str_append_c(str, '=');
-
 		uid = userdb_parse_uid(auth_request, *vals);
 		if (uid == (uid_t)-1)
 			return FALSE;
-		str_append(str, dec2str(uid));
+
+		auth_stream_reply_add(reply, name, dec2str(uid));
 	}
 
 	return TRUE;
 }
 
-static int append_gid_list(struct auth_request *auth_request, string_t *str,
+static int append_gid_list(struct auth_request *auth_request,
+                           struct auth_stream_reply *reply,
 			   const char *name, char **vals)
 {
 	gid_t gid;
 
 	for (; *vals != NULL; vals++) {
-		str_append_c(str, '\t');
-		str_append(str, name);
-		str_append_c(str, '=');
-
 		gid = userdb_parse_gid(auth_request, *vals);
 		if (gid == (gid_t)-1)
 			return FALSE;
-		str_append(str, dec2str(gid));
+
+		auth_stream_reply_add(reply, name, dec2str(gid));
 	}
 
 	return TRUE;
 }
 
-static const char *
+static struct auth_stream_reply *
 ldap_query_get_result(struct ldap_connection *conn, LDAPMessage *entry,
 		      struct auth_request *auth_request)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 	BerElement *ber;
 	const char *name;
 	char *attr, **vals;
 	unsigned int i;
 	int seen_uid = FALSE, seen_gid = FALSE;
 
-	str = t_str_new(256);
-	str_append(str, auth_request->user);
+	reply = auth_stream_reply_init(auth_request);
+	auth_stream_reply_add(reply, NULL, auth_request->user);
 
 	attr = ldap_first_attribute(conn->ld, entry, &ber);
 	while (attr != NULL) {
@@ -84,21 +80,19 @@
 
 		if (name != NULL && vals != NULL && vals[0] != NULL) {
 			if (strcmp(name, "uid") == 0) {
-				if (!append_uid_list(auth_request, str,
+				if (!append_uid_list(auth_request, reply,
 						     name, vals))
 					return NULL;
 				seen_uid = TRUE;
 			} else if (strcmp(name, "gid") == 0) {
-				if (!append_gid_list(auth_request, str,
-						     name, vals))
+				if (!append_gid_list(auth_request, reply,
+						     name, vals)) 
 					return NULL;
 				seen_gid = TRUE;
 			} else {
 				for (i = 0; vals[i] != NULL; i++) {
-					str_append_c(str, '\t');
-					str_append(str, name);
-					str_append_c(str, '=');
-					str_append(str, vals[i]);
+					auth_stream_reply_add(reply, name,
+							      vals[i]);
 				}
 			}
 		}
@@ -109,30 +103,27 @@
 	}
 
 	if (!seen_uid) {
-	}
-
-	if (!seen_uid) {
 		if (conn->set.uid == (uid_t)-1) {
 			auth_request_log_error(auth_request, "ldap",
 				"uid not in user_attrs and no default given in "
 				"user_global_uid");
+			return NULL;
 		}
 
-		str_append(str, "\tuid=");
-		str_append(str, dec2str(conn->set.uid));
+		auth_stream_reply_add(reply, "uid", dec2str(conn->set.uid));
 	}
 	if (!seen_gid) {
 		if (conn->set.gid == (gid_t)-1) {
 			auth_request_log_error(auth_request, "ldap",
 				"gid not in user_attrs and no default given in "
 				"user_global_gid");
+			return NULL;
 		}
 
-		str_append(str, "\tgid=");
-		str_append(str, dec2str(conn->set.gid));
+		auth_stream_reply_add(reply, "gid", dec2str(conn->set.gid));
 	}
 
-	return str_c(str);
+	return reply;
 }
 
 static void handle_request(struct ldap_connection *conn,
@@ -142,7 +133,7 @@
 		(struct userdb_ldap_request *) request;
 	struct auth_request *auth_request = urequest->auth_request;
 	LDAPMessage *entry;
-	const char *result;
+	struct auth_stream_reply *reply = NULL;
 	int ret;
 
 	ret = ldap_result2error(conn->ld, res, 0);
@@ -159,17 +150,16 @@
 			auth_request_log_error(auth_request, "ldap",
 					       "Authenticated user not found");
 		}
-		result = NULL;
 	} else {
-		result = ldap_query_get_result(conn, entry, auth_request);
+		reply = ldap_query_get_result(conn, entry, auth_request);
 		if (ldap_next_entry(conn->ld, entry) != NULL) {
 			auth_request_log_error(auth_request, "ldap",
 				"Multiple replies found for user");
-			result = NULL;
+			reply = NULL;
 		}
 	}
 
-	urequest->userdb_callback(result, auth_request);
+	urequest->userdb_callback(reply, auth_request);
 }
 
 static void userdb_ldap_lookup(struct auth_request *auth_request,
--- a/src/auth/userdb-passdb.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-passdb.c	Sun Aug 07 14:41:19 2005 +0300
@@ -2,7 +2,8 @@
 
 #include "common.h"
 
-#ifdef USERDB_PASSDB
+//#ifdef USERDB_PASSDB
+#if 1
 
 #include "str.h"
 #include "var-expand.h"
@@ -34,7 +35,10 @@
 	str = t_str_new(256);
 	str_append(str, auth_request->user);
 
-	args = t_strsplit(str_c(auth_request->extra_fields), "\t");
+	/* export the request. keep all keys starting with userdb_ but strip
+	   the userdb_ away. */
+	args = t_strsplit(auth_stream_reply_export(auth_request->extra_fields),
+		"\t");
 	for (; *args != NULL; args++) {
 		const char *arg = *args;
 
@@ -75,8 +79,15 @@
 
 	if (uid == (uid_t)-1 || gid == (gid_t)-1)
 		callback(NULL, auth_request);
-	else
-		callback(str_c(str), auth_request);
+	else {
+		struct auth_stream_reply *reply;
+
+		/* import the string into request. since the values were
+		   exported they are already in escaped form in the string. */
+		reply = auth_stream_reply_init(auth_request);
+		auth_stream_reply_import(reply, str_c(str));
+		callback(reply, auth_request);
+	}
 	t_pop();
 }
 
--- a/src/auth/userdb-passwd-file.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-passwd-file.c	Sun Aug 07 14:41:19 2005 +0300
@@ -13,8 +13,8 @@
 static void passwd_file_lookup(struct auth_request *auth_request,
 			       userdb_callback_t *callback)
 {
+	struct auth_stream_reply *reply;
 	struct passwd_user *pu;
-	string_t *str;
 
 	pu = db_passwd_file_lookup(userdb_pwf, auth_request);
 	if (pu == NULL) {
@@ -22,16 +22,17 @@
 		return;
 	}
 
-	str = t_str_new(128);
-	str_printfa(str, "%s\tuid=%s\tgid=%s",
-		    auth_request->user, dec2str(pu->uid), dec2str(pu->gid));
+	reply = auth_stream_reply_init(auth_request);
+	auth_stream_reply_add(reply, NULL, auth_request->user);
+	auth_stream_reply_add(reply, "uid", dec2str(pu->uid));
+	auth_stream_reply_add(reply, "gid", dec2str(pu->gid));
 
 	if (pu->home != NULL)
-		str_printfa(str, "\thome=%s", pu->home);
+		auth_stream_reply_add(reply, "home", pu->home);
 	if (pu->mail != NULL)
-		str_printfa(str, "\tmail=%s", pu->mail);
+		auth_stream_reply_add(reply, "mail", pu->mail);
 
-	callback(str_c(str), auth_request);
+	callback(reply, auth_request);
 }
 
 static void passwd_file_init(const char *args)
--- a/src/auth/userdb-passwd.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-passwd.c	Sun Aug 07 14:41:19 2005 +0300
@@ -12,7 +12,7 @@
 			  userdb_callback_t *callback)
 {
 	struct passwd *pw;
-	const char *result;
+	struct auth_stream_reply *reply;
 
 	pw = getpwnam(auth_request->user);
 	if (pw == NULL) {
@@ -29,11 +29,14 @@
 			pw->pw_name, auth_request->user);
 	}
 
-	result = t_strdup_printf("%s\tsystem_user=%s\tuid=%s\tgid=%s\t"
-				 "home=%s", pw->pw_name, pw->pw_name,
-				 dec2str(pw->pw_uid), dec2str(pw->pw_gid),
-				 pw->pw_dir);
-	callback(result, auth_request);
+	reply = auth_stream_reply_init(auth_request);
+	auth_stream_reply_add(reply, NULL, pw->pw_name);
+	auth_stream_reply_add(reply, "system_user", pw->pw_name);
+	auth_stream_reply_add(reply, "uid", dec2str(pw->pw_uid));
+	auth_stream_reply_add(reply, "gid", dec2str(pw->pw_gid));
+	auth_stream_reply_add(reply, "home", pw->pw_dir);
+
+	callback(reply, auth_request);
 }
 
 struct userdb_module userdb_passwd = {
--- a/src/auth/userdb-sql.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-sql.c	Sun Aug 07 14:41:19 2005 +0300
@@ -22,10 +22,11 @@
 
 static struct sql_connection *userdb_sql_conn;
 
-static const char *sql_query_get_result(struct sql_result *result,
-					struct auth_request *auth_request)
+static struct auth_stream_reply *
+sql_query_get_result(struct sql_result *result,
+		     struct auth_request *auth_request)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 	uid_t uid, gid;
 	const char *name, *value;
 	unsigned int i, fields_count;
@@ -33,8 +34,8 @@
 	uid = (uid_t)-1;
 	gid = (gid_t)-1;
 
-	str = t_str_new(256);
-	str_append(str, auth_request->user);
+	reply = auth_stream_reply_init(auth_request);
+	auth_stream_reply_add(reply, NULL, auth_request->user);
 
 	fields_count = sql_result_get_fields_count(result);
 	for (i = 0; i < fields_count; i++) {
@@ -44,10 +45,6 @@
 		if (value == NULL)
 			continue;
 
-		str_append_c(str, '\t');
-		str_append(str, name);
-		str_append_c(str, '=');
-
 		/* some special handling for UID and GID. */
 		if (strcmp(name, "uid") == 0) {
 			uid = userdb_parse_uid(auth_request, value);
@@ -61,26 +58,28 @@
 			value = dec2str(gid);
 		}
 
-		str_append(str, value);
+		auth_stream_reply_add(reply, name, value);
 	}
 
 	if (uid == (uid_t)-1) {
 		auth_request_log_error(auth_request, "sql",
 			"Password query didn't return uid, or it was NULL");
+		return NULL;
 	}
 	if (gid == (gid_t)-1) {
 		auth_request_log_error(auth_request, "sql",
 			"Password query didn't return gid, or it was NULL");
+		return NULL;
 	}
 
-	return str_c(str);
+	return reply;
 }
 
 static void sql_query_callback(struct sql_result *result, void *context)
 {
 	struct userdb_sql_request *sql_request = context;
 	struct auth_request *auth_request = sql_request->auth_request;
-	const char *user_result = NULL;
+	struct auth_stream_reply *reply = NULL;
 	int ret;
 
 	ret = sql_result_next_row(result);
@@ -90,10 +89,10 @@
 	} else if (ret == 0) {
 		auth_request_log_info(auth_request, "sql", "User not found");
 	} else {
-                user_result = sql_query_get_result(result, auth_request);
+                reply = sql_query_get_result(result, auth_request);
 	}
 
-	sql_request->callback(user_result, auth_request);
+	sql_request->callback(reply, auth_request);
 	i_free(sql_request);
 }
 
--- a/src/auth/userdb-static.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-static.c	Sun Aug 07 14:41:19 2005 +0300
@@ -4,74 +4,97 @@
 
 #ifdef USERDB_STATIC
 
+#include "array.h"
 #include "str.h"
 #include "var-expand.h"
 #include "userdb.h"
 
 #include <stdlib.h>
 
-static char *static_template;
+static pool_t static_pool;
+static array_t ARRAY_DEFINE(static_template, const char *);
 
 static void static_lookup(struct auth_request *auth_request,
 			  userdb_callback_t *callback)
 {
+        const struct var_expand_table *table;
+	struct auth_stream_reply *reply;
 	string_t *str;
+	const char *const *args;
+	unsigned int i, count;
+
+	t_push();
+	str = t_str_new(256);
+	table = auth_request_get_var_expand_table(auth_request, NULL);
 
-	str = t_str_new(128);
-	str_append(str, auth_request->user);
-	var_expand(str, static_template,
-		   auth_request_get_var_expand_table(auth_request, NULL));
-	callback(str_c(str), auth_request);
+	reply = auth_stream_reply_init(auth_request);
+	auth_stream_reply_add(reply, NULL, auth_request->user);
+
+	args = array_get(&static_template, &count);
+	i_assert((count % 2) == 0);
+	for (i = 0; i < count; i += 2) {
+		str_truncate(str, 0);
+		var_expand(str, args[i+1], table);
+		auth_stream_reply_add(reply, args[i], str_c(str));
+	}
+
+	callback(reply, auth_request);
 }
 
 static void static_init(const char *args)
 {
-	const char *const *tmp;
+	const char *const *tmp, *key, *value;
 	uid_t uid;
 	gid_t gid;
-	string_t *str;
 
+	static_pool = pool_alloconly_create("static userdb", 256);
 	uid = (uid_t)-1;
 	gid = (gid_t)-1;
 
+	ARRAY_CREATE(&static_template, static_pool, const char *, 16);
+
 	t_push();
-	str = t_str_new(128);
-
 	for (tmp = t_strsplit_spaces(args, " "); *tmp != NULL; tmp++) {
-		str_append_c(str, '\t');
-		if (strncasecmp(*tmp, "uid=", 4) == 0) {
-			uid = userdb_parse_uid(NULL, *tmp + 4);
+		value = strchr(*tmp, '=');
+		if (value == NULL)
+			key = *tmp;
+		else {
+			key = t_strdup_until(*tmp, value);
+			value++;
+		}
+
+		if (strcasecmp(key, "uid") == 0) {
+			uid = userdb_parse_uid(NULL, value);
 			if (uid == (uid_t)-1) {
 				i_fatal("static userdb: Invalid uid: %s",
-					*tmp + 4);
+					value);
 			}
-			str_append(str, "uid=");
-			str_append(str, dec2str(uid));
-		} else if (strncasecmp(*tmp, "gid=", 4) == 0) {
-			gid = userdb_parse_gid(NULL, *tmp + 4);
+			value = dec2str(uid);
+		} else if (strcasecmp(key, "gid") == 0) {
+			gid = userdb_parse_gid(NULL, value);
 			if (gid == (gid_t)-1) {
 				i_fatal("static userdb: Invalid gid: %s",
-					*tmp + 4);
+					value);
 			}
-			str_append(str, "gid=");
-			str_append(str, dec2str(gid));
-		} else {
-			str_append(str, *tmp);
+			value = dec2str(gid);
 		}
+		key = p_strdup(static_pool, key);
+		value = p_strdup(static_pool, value);
+
+		array_append(&static_template, &key, 1);
+		array_append(&static_template, &value, 1);
 	}
+	t_pop();
 
 	if (uid == (uid_t)-1)
 		i_fatal("static userdb: uid missing");
 	if (gid == (gid_t)-1)
 		i_fatal("static userdb: gid missing");
-
-	static_template = i_strdup(str_c(str));
-	t_pop();
 }
 
 static void static_deinit(void)
 {
-	i_free(static_template);
+	pool_unref(static_pool);
 }
 
 struct userdb_module userdb_static = {
--- a/src/auth/userdb-vpopmail.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb-vpopmail.c	Sun Aug 07 14:41:19 2005 +0300
@@ -44,7 +44,7 @@
 {
 	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
 	struct vqpasswd *vpw;
-	const char *result;
+	struct auth_stream_reply *reply;
 	uid_t uid;
 	gid_t gid;
 
@@ -84,11 +84,13 @@
 		}
 	}
 
-	result = t_strdup_printf("%s\tuid=%s\tgid=%s\thome=%s",
-				 vpw->pw_name, dec2str(uid), dec2str(gid),
-				 vpw->pw_dir);
+	reply = auth_stream_reply_init();
+	auth_stream_reply_add(reply, NULL, vpw->pw_name);
+	auth_stream_reply_add(reply, "uid", dec2str(uid));
+	auth_stream_reply_add(reply, "gid", dec2str(gid));
+	auth_stream_reply_add(reply, "home", vpw->pw_dir);
 
-	callback(result, auth_request);
+	callback(reply, auth_request);
 }
 
 struct userdb_module userdb_vpopmail = {
--- a/src/auth/userdb.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb.c	Sun Aug 07 14:41:19 2005 +0300
@@ -45,6 +45,9 @@
 {
 	struct passwd *pw;
 
+	if (str == NULL)
+		return (uid_t)-1;
+
 	if (*str >= '0' && *str <= '9')
 		return (uid_t)strtoul(str, NULL, 10);
 
@@ -63,6 +66,9 @@
 {
 	struct group *gr;
 
+	if (str == NULL)
+		return (uid_t)-1;
+
 	if (*str >= '0' && *str <= '9')
 		return (gid_t)strtoul(str, NULL, 10);
 
--- a/src/auth/userdb.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/auth/userdb.h	Sun Aug 07 14:41:19 2005 +0300
@@ -1,9 +1,11 @@
 #ifndef __USERDB_H
 #define __USERDB_H
 
+#include "auth-stream.h"
+
 struct auth_request;
 
-typedef void userdb_callback_t(const char *result,
+typedef void userdb_callback_t(struct auth_stream_reply *reply,
 			       struct auth_request *request);
 
 struct userdb_module {
--- a/src/imap/client.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/imap/client.h	Sun Aug 07 14:41:19 2005 +0300
@@ -74,7 +74,7 @@
 			       const char *msg);
 
 /* Read a number of arguments. Returns TRUE if everything was read or
-   FALSE if either needs more data or error occured. */
+   FALSE if either needs more data or error occurred. */
 int client_read_args(struct client_command_context *cmd, unsigned int count,
 		     unsigned int flags, struct imap_arg **args);
 /* Reads a number of string arguments. ... is a list of pointers where to
--- a/src/imap/cmd-idle.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/imap/cmd-idle.c	Sun Aug 07 14:41:19 2005 +0300
@@ -159,7 +159,7 @@
 		/* outlook workaround */
 		idle_send_expunge(ctx);
 	} else if (ctx->sync_pending) {
-		/* more changes occured while we were sending changes to
+		/* more changes occurred while we were sending changes to
 		   client */
                 idle_callback(client->mailbox, client);
 	}
--- a/src/lib-imap/imap-parser.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-imap/imap-parser.h	Sun Aug 07 14:41:19 2005 +0300
@@ -93,7 +93,7 @@
 
 /* Read a number of arguments. This function doesn't call i_stream_read(), you
    need to do that. Returns number of arguments read (may be less than count
-   in case of EOL), -2 if more data is needed or -1 if error occured.
+   in case of EOL), -2 if more data is needed or -1 if error occurred.
 
    count-sized array of arguments are stored into args when return value is
    0 or larger. If all arguments weren't read, they're set to NIL. count
--- a/src/lib-mail/message-parser.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-mail/message-parser.h	Sun Aug 07 14:41:19 2005 +0300
@@ -110,7 +110,7 @@
 
 /* Read and return next header line. Returns 1 if header is returned, 0 if
    input stream is non-blocking and more data needs to be read, -1 when all is
-   done or error occured (see stream's error status). */
+   done or error occurred (see stream's error status). */
 int message_parse_header_next(struct message_header_parser_ctx *ctx,
 			      struct message_header_line **hdr_r);
 
--- a/src/lib-sql/sql-api.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-sql/sql-api.h	Sun Aug 07 14:41:19 2005 +0300
@@ -34,7 +34,7 @@
 	       sql_query_callback_t *callback, void *context);
 
 /* Go to next row, returns 1 if ok, 0 if this was the last row or -1 if error
-   occured. This needs to be the first call for result. */
+   occurred. This needs to be the first call for result. */
 int sql_result_next_row(struct sql_result *result);
 
 /* Return number of fields in result. */
--- a/src/lib-storage/index/maildir/maildir-sync.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-storage/index/maildir/maildir-sync.c	Sun Aug 07 14:41:19 2005 +0300
@@ -5,14 +5,14 @@
    it's problems:
 
    We want to be as efficient as we can. The most efficient way to
-   check if changes have occured is to stat() the new/ and cur/
+   check if changes have occurred is to stat() the new/ and cur/
    directories and uidlist file - if their mtimes haven't changed,
    there's no changes and we don't need to do anything.
 
    Problem 1: Multiple changes can happen within a single second -
    nothing guarantees that once we synced it, someone else didn't just
    then make a modification. Such modifications wouldn't get noticed
-   until a new modification occured later.
+   until a new modification occurred later.
 
    Problem 2: Syncing cur/ directory is much more costly than syncing
    new/. Moving mails from new/ to cur/ will always change mtime of
--- a/src/lib-storage/mail-storage.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-storage/mail-storage.c	Sun Aug 07 14:41:19 2005 +0300
@@ -11,7 +11,7 @@
 
 /* Message to show to users when critical error occurs */
 #define CRITICAL_MSG \
-	"Internal error occured. Refer to server log for more information."
+	"Internal error occurred. Refer to server log for more information."
 #define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
 
 unsigned int mail_storage_module_id = 0;
--- a/src/lib-storage/mail-storage.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-storage/mail-storage.h	Sun Aug 07 14:41:19 2005 +0300
@@ -268,7 +268,7 @@
 struct mailbox_list *
 mail_storage_mailbox_list_next(struct mailbox_list_context *ctx);
 /* Deinitialize mailbox list request. Returns FALSE if some error
-   occured while listing. */
+   occurred while listing. */
 int mail_storage_mailbox_list_deinit(struct mailbox_list_context *ctx);
 
 /* Subscribe/unsubscribe mailbox. There should be no error when
@@ -282,7 +282,7 @@
 					 const char *name,
 					 enum mailbox_name_status *status);
 
-/* Returns the error message of last occured error. */
+/* Returns the error message of last occurred error. */
 const char *mail_storage_get_last_error(struct mail_storage *storage,
 					int *syntax_error_r,
 					int *temporary_error_r);
@@ -296,7 +296,7 @@
 struct mailbox *mailbox_open(struct mail_storage *storage, const char *name,
 			     struct istream *input,
 			     enum mailbox_open_flags flags);
-/* Close the box. Returns FALSE if some cleanup errors occured, but
+/* Close the box. Returns FALSE if some cleanup errors occurred, but
    the mailbox was closed anyway. */
 int mailbox_close(struct mailbox *box);
 
@@ -416,18 +416,18 @@
 int mail_set_seq(struct mail *mail, uint32_t seq);
 
 /* Get the time message was received (IMAP INTERNALDATE).
-   Returns (time_t)-1 if error occured. */
+   Returns (time_t)-1 if error occurred. */
 time_t mail_get_received_date(struct mail *mail);
 /* Get the Date-header in mail. Timezone is in minutes.
-   Returns (time_t)-1 if error occured, 0 if field wasn't found or
+   Returns (time_t)-1 if error occurred, 0 if field wasn't found or
    couldn't be parsed. */
 time_t mail_get_date(struct mail *mail, int *timezone);
 
 /* Get the full virtual size of mail (IMAP RFC822.SIZE).
-   Returns (uoff_t)-1 if error occured */
+   Returns (uoff_t)-1 if error occurred */
 uoff_t mail_get_virtual_size(struct mail *mail);
 /* Get the full physical size of mail.
-   Returns (uoff_t)-1 if error occured */
+   Returns (uoff_t)-1 if error occurred */
 uoff_t mail_get_physical_size(struct mail *mail);
 
 /* Get value for single header field */
--- a/src/lib-storage/subscription-file/subscription-file.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib-storage/subscription-file/subscription-file.h	Sun Aug 07 14:41:19 2005 +0300
@@ -7,7 +7,7 @@
 struct subsfile_list_context *
 subsfile_list_init(struct mail_storage *storage, const char *path);
 
-/* Deinitialize subscription file listing. Returns FALSE if some error occured
+/* Deinitialize subscription file listing. Returns FALSE if some error occurred
    while listing. */
 int subsfile_list_deinit(struct subsfile_list_context *ctx);
 /* Returns the next subscribed mailbox, or NULL. */
--- a/src/lib/file-cache.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib/file-cache.h	Sun Aug 07 14:41:19 2005 +0300
@@ -10,7 +10,7 @@
 void file_cache_set_fd(struct file_cache *cache, int fd);
 
 /* Read data from file, returns how many bytes was actually read or -1 if
-   error occured. */
+   error occurred. */
 ssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size);
 
 /* Returns pointer to beginning of cached file. Only parts of the returned
--- a/src/lib/istream.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib/istream.h	Sun Aug 07 14:41:19 2005 +0300
@@ -63,7 +63,7 @@
    needed to make a full line. */
 char *i_stream_next_line(struct istream *stream);
 /* Like i_stream_next_line(), but reads for more data if needed. Returns NULL
-   if more data is needed or error occured. */
+   if more data is needed or error occurred. */
 char *i_stream_read_next_line(struct istream *stream);
 
 /* Returns pointer to beginning of read data, or NULL if there's no data
--- a/src/lib/read-full.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib/read-full.h	Sun Aug 07 14:41:19 2005 +0300
@@ -1,7 +1,7 @@
 #ifndef __READ_FULL_H
 #define __READ_FULL_H
 
-/* Read data from file. Returns -1 if error occured, or 0 if EOF came before
+/* Read data from file. Returns -1 if error occurred, or 0 if EOF came before
    everything was read, or 1 if all was ok. */
 int read_full(int fd, void *data, size_t size);
 int pread_full(int fd, void *data, size_t size, off_t offset);
--- a/src/lib/strfuncs.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib/strfuncs.c	Sun Aug 07 14:41:19 2005 +0300
@@ -98,7 +98,7 @@
 	va_end(args);
 
 	if (len < 0) {
-		/* some error occured */
+		/* some error occurred */
 		len = 0;
 		ret = -1;
 	} else if ((size_t)len >= max_chars) {
--- a/src/lib/write-full.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/lib/write-full.h	Sun Aug 07 14:41:19 2005 +0300
@@ -1,7 +1,7 @@
 #ifndef __WRITE_FULL_H
 #define __WRITE_FULL_H
 
-/* Write data into file. Returns -1 if error occured, or 0 if all was ok.
+/* Write data into file. Returns -1 if error occurred, or 0 if all was ok.
    If there's not enough space in device, -1 with ENOSPC is returned, and
    it's unspecified how much data was actually written. */
 int write_full(int fd, const void *data, size_t size);
--- a/src/login-common/ssl-proxy-gnutls.c	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/login-common/ssl-proxy-gnutls.c	Sun Aug 07 14:41:19 2005 +0300
@@ -80,7 +80,7 @@
 	}
 
 	if (verbose_ssl) {
-		/* fatal error occured */
+		/* fatal error occurred */
 		if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
 			i_warning("Received SSL fatal alert: %s [%s]",
 				  get_alert_text(proxy),
--- a/src/login-common/ssl-proxy.h	Sun Aug 07 14:28:13 2005 +0300
+++ b/src/login-common/ssl-proxy.h	Sun Aug 07 14:41:19 2005 +0300
@@ -7,7 +7,7 @@
 extern int ssl_initialized;
 
 /* establish SSL connection with the given fd, returns a new fd which you
-   must use from now on, or -1 if error occured. Unless -1 is returned,
+   must use from now on, or -1 if error occurred. Unless -1 is returned,
    the given fd must be simply forgotten. */
 int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r);
 int ssl_proxy_has_valid_client_cert(struct ssl_proxy *proxy);