changeset 7388:08d31d752893 HEAD

Use auth-stream API to build all TAB-delimited strings to make sure strings are escaped properly where necessary.
author Timo Sirainen <tss@iki.fi>
date Sun, 09 Mar 2008 12:37:26 +0200
parents 2bef36355718
children 1125d2d59e82
files src/auth/auth-client-connection.c src/auth/auth-master-connection.c src/auth/auth-master-connection.h src/auth/auth-request-handler.c src/auth/auth-request-handler.h src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-stream.c src/auth/auth-stream.h src/auth/auth-worker-client.c src/auth/auth-worker-server.c src/auth/auth-worker-server.h src/auth/passdb-blocking.c src/auth/userdb-blocking.c
diffstat 14 files changed, 213 insertions(+), 171 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-client-connection.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-client-connection.c	Sun Mar 09 12:37:26 2008 +0200
@@ -9,6 +9,7 @@
 #include "str.h"
 #include "str-sanitize.h"
 #include "safe-memset.h"
+#include "auth-stream.h"
 #include "auth-request-handler.h"
 #include "auth-client-interface.h"
 #include "auth-client-connection.h"
@@ -63,7 +64,7 @@
 	}
 }
 
-static void auth_callback(const char *reply,
+static void auth_callback(struct auth_stream_reply *reply,
 			  struct auth_client_connection *conn)
 {
 	if (reply == NULL) {
@@ -72,7 +73,7 @@
 		return;
 	}
 
-	auth_client_send(conn, reply);
+	auth_client_send(conn, auth_stream_reply_export(reply));
 }
 
 static bool
--- a/src/auth/auth-master-connection.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-master-connection.c	Sun Mar 09 12:37:26 2008 +0200
@@ -29,16 +29,20 @@
 	struct auth_request *auth_request;
 };
 
-void auth_master_request_callback(const char *reply, void *context)
+void auth_master_request_callback(struct auth_stream_reply *reply,
+				  void *context)
 {
 	struct auth_master_connection *conn = context;
 	struct const_iovec iov[2];
+	const char *reply_str;
+
+	reply_str = auth_stream_reply_export(reply);
 
 	if (conn->listener->auth->verbose_debug)
-		i_info("master out: %s", reply);
+		i_info("master out: %s", reply_str);
 
-	iov[0].iov_base = reply;
-	iov[0].iov_len = strlen(reply);
+	iov[0].iov_base = reply_str;
+	iov[0].iov_len = strlen(reply_str);
 	iov[1].iov_base = "\n";
 	iov[1].iov_len = 1;
 
--- a/src/auth/auth-master-connection.h	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-master-connection.h	Sun Mar 09 12:37:26 2008 +0200
@@ -1,6 +1,8 @@
 #ifndef AUTH_MASTER_CONNECTION_H
 #define AUTH_MASTER_CONNECTION_H
 
+struct auth_stream_reply;
+
 struct auth_master_connection {
 	struct auth_master_listener *listener;
 	int refcount;
@@ -24,6 +26,7 @@
 void auth_master_connection_send_handshake(struct auth_master_connection *conn);
 void auth_master_connections_send_handshake(void);
 
-void auth_master_request_callback(const char *reply, void *context);
+void auth_master_request_callback(struct auth_stream_reply *reply,
+				  void *context);
 
 #endif
--- a/src/auth/auth-request-handler.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-request-handler.c	Sun Mar 09 12:37:26 2008 +0200
@@ -116,53 +116,51 @@
 	hash_iterate_deinit(&iter);
 }
 
-static const char *get_client_extra_fields(struct auth_request *request)
+static void get_client_extra_fields(struct auth_request *request,
+				    struct auth_stream_reply *reply)
 {
-	string_t *str;
 	const char **fields, *extra_fields;
 	unsigned int src, dest;
 	bool seen_pass = FALSE;
 
 	if (auth_stream_is_empty(request->extra_fields))
-		return NULL;
+		return;
 
 	extra_fields = auth_stream_reply_export(request->extra_fields);
 
 	if (!request->proxy) {
 		/* we only wish to remove all fields prefixed with "userdb_" */
-		if (strstr(extra_fields, "userdb_") == NULL)
-			return extra_fields;
+		if (strstr(extra_fields, "userdb_") == NULL) {
+			auth_stream_reply_import(reply, extra_fields);
+			return;
+		}
 	}
 
-	str = t_str_new(128);
 	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)
-				str_append_c(str, '\t');
 			if (!seen_pass && strncmp(fields[src], "pass=", 5) == 0)
 				seen_pass = TRUE;
-			str_append(str, fields[src]);
+			auth_stream_reply_import(reply, fields[src]);
 		}
 	}
 
 	if (request->proxy && !seen_pass && request->mech_password != NULL) {
 		/* we're proxying - send back the password that was
 		   sent by user (not the password in passdb). */
-		str_printfa(str, "\tpass=%s", request->mech_password);
+		auth_stream_reply_add(reply, "pass", request->mech_password);
 	}
-
-	return str_len(str) == 0 ? NULL : str_c(str);
 }
 
 static void
-auth_request_handle_failure(struct auth_request *request, const char *str)
+auth_request_handle_failure(struct auth_request *request,
+			    struct auth_stream_reply *reply)
 {
         struct auth_request_handler *handler = request->context;
 
 	if (request->delayed_failure) {
 		/* we came here from flush_failures() */
-		handler->callback(str, handler->context);
+		handler->callback(reply, handler->context);
 		return;
 	}
 
@@ -173,7 +171,7 @@
 	if (request->no_failure_delay) {
 		/* passdb specifically requested not to delay the
 		   reply. */
-		handler->callback(str, handler->context);
+		handler->callback(reply, handler->context);
 		auth_request_unref(&request);
 		return;
 	}
@@ -194,56 +192,56 @@
 
 static void auth_callback(struct auth_request *request,
 			  enum auth_client_result result,
-			  const void *reply, size_t reply_size)
+			  const void *auth_reply, size_t reply_size)
 {
         struct auth_request_handler *handler = request->context;
+	struct auth_stream_reply *reply;
 	string_t *str;
-	const char *fields;
 
-	str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
+	reply = auth_stream_reply_init(pool_datastack_create());
 	switch (result) {
 	case AUTH_CLIENT_RESULT_CONTINUE:
-		str_printfa(str, "CONT\t%u\t", request->id);
-		base64_encode(reply, reply_size, str);
-                request->accept_input = TRUE;
-		handler->callback(str_c(str), handler->context);
+		auth_stream_reply_add(reply, "CONT", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
+
+		str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
+		base64_encode(auth_reply, reply_size, str);
+		auth_stream_reply_add(reply, NULL, str_c(str));
+
+		request->accept_input = TRUE;
+		handler->callback(reply, handler->context);
 		break;
 	case AUTH_CLIENT_RESULT_SUCCESS:
 		auth_request_proxy_finish(request, TRUE);
 
-		str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
+		auth_stream_reply_add(reply, "OK", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
+		auth_stream_reply_add(reply, "user", request->user);
 		if (reply_size > 0) {
-			str_append(str, "\tresp=");
-			base64_encode(reply, reply_size, str);
+			str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
+			base64_encode(auth_reply, reply_size, str);
+			auth_stream_reply_add(reply, "resp", str_c(str));
 		}
-		fields = get_client_extra_fields(request);
-		if (fields != NULL) {
-			str_append_c(str, '\t');
-			str_append(str, fields);
-		}
-
+		get_client_extra_fields(request, reply);
 		if (request->no_login || handler->master_callback == NULL) {
 			/* this request doesn't have to wait for master
 			   process to pick it up. delete it */
 			auth_request_handler_remove(handler, request);
 		}
-		handler->callback(str_c(str), handler->context);
+		handler->callback(reply, handler->context);
 		break;
 	case AUTH_CLIENT_RESULT_FAILURE:
 		auth_request_proxy_finish(request, FALSE);
 
-		str_printfa(str, "FAIL\t%u", request->id);
+		auth_stream_reply_add(reply, "FAIL", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
 		if (request->user != NULL)
-			str_printfa(str, "\tuser=%s", request->user);
+			auth_stream_reply_add(reply, "user", request->user);
 		if (request->internal_failure)
-			str_append(str, "\ttemp");
-		fields = get_client_extra_fields(request);
-		if (fields != NULL) {
-			str_append_c(str, '\t');
-			str_append(str, fields);
-		}
+			auth_stream_reply_add(reply, "temp", NULL);
+		get_client_extra_fields(request, reply);
 
-		auth_request_handle_failure(request, str_c(str));
+		auth_request_handle_failure(request, reply);
 		break;
 	}
 	/* NOTE: request may be destroyed now */
@@ -255,13 +253,16 @@
 					   struct auth_request *request,
 					   const char *reason)
 {
-	string_t *reply = t_str_new(64);
+	struct auth_stream_reply *reply;
 
 	auth_request_log_info(request, request->mech->mech_name, "%s", reason);
 
-	str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason);
-	handler->callback(str_c(reply), handler->context);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, "FAIL", NULL);
+	auth_stream_reply_add(reply, NULL, dec2str(request->id));
+	auth_stream_reply_add(reply, "reason", reason);
 
+	handler->callback(reply, handler->context);
 	auth_request_handler_remove(handler, request);
 }
 
@@ -391,10 +392,13 @@
 
 	request = hash_lookup(handler->requests, POINTER_CAST(id));
 	if (request == NULL) {
-		string_t *reply = t_str_new(64);
+		struct auth_stream_reply *reply;
 
-		str_printfa(reply, "FAIL\t%u\treason=Timeouted", id);
-		handler->callback(str_c(reply), handler->context);
+		reply = auth_stream_reply_init(pool_datastack_create());
+		auth_stream_reply_add(reply, "FAIL", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(id));
+		auth_stream_reply_add(reply, "reason", "Timeouted");
+		handler->callback(reply, handler->context);
 		return TRUE;
 	}
 
@@ -425,8 +429,7 @@
 			    struct auth_request *request)
 {
         struct auth_request_handler *handler = request->context;
-	struct auth_stream_reply *reply = request->userdb_reply;
-	string_t *str;
+	struct auth_stream_reply *reply;
 
 	i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
 
@@ -435,24 +438,29 @@
 	if (request->userdb_lookup_failed)
 		result = USERDB_RESULT_INTERNAL_FAILURE;
 
-	str = t_str_new(256);
+	reply = auth_stream_reply_init(pool_datastack_create());
 	switch (result) {
 	case USERDB_RESULT_INTERNAL_FAILURE:
-		str_printfa(str, "FAIL\t%u", request->id);
+		auth_stream_reply_add(reply, "FAIL", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
 		break;
 	case USERDB_RESULT_USER_UNKNOWN:
-		str_printfa(str, "NOTFOUND\t%u", request->id);
+		auth_stream_reply_add(reply, "NOTFOUND", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
 		break;
 	case USERDB_RESULT_OK:
+		auth_stream_reply_add(reply, "USER", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(request->id));
 		if (request->master_user != NULL) {
-			auth_stream_reply_add(reply, "master_user",
+			auth_stream_reply_add(request->userdb_reply,
+					      "master_user",
 					      request->master_user);
 		}
-		str_printfa(str, "USER\t%u\t", request->id);
-		str_append(str, auth_stream_reply_export(reply));
+		auth_stream_reply_import(reply,
+			auth_stream_reply_export(request->userdb_reply));
 		break;
 	}
-	handler->master_callback(str_c(str), request->master);
+	handler->master_callback(reply, request->master);
 
 	auth_master_connection_unref(&request->master);
 	auth_request_unref(&request);
@@ -465,16 +473,17 @@
 					 unsigned int client_id)
 {
 	struct auth_request *request;
-	string_t *reply;
+	struct auth_stream_reply *reply;
 
-	reply = t_str_new(64);
+	reply = auth_stream_reply_init(pool_datastack_create());
 
 	request = hash_lookup(handler->requests, POINTER_CAST(client_id));
 	if (request == NULL) {
 		i_error("Master request %u.%u not found",
 			handler->client_pid, client_id);
-		str_printfa(reply, "NOTFOUND\t%u", id);
-		handler->master_callback(str_c(reply), master);
+		auth_stream_reply_add(reply, "NOTFOUND", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(id));
+		handler->master_callback(reply, master);
 		return;
 	}
 
@@ -485,8 +494,9 @@
 	    !request->successful) {
 		i_error("Master requested unfinished authentication request "
 			"%u.%u", handler->client_pid, client_id);
-		str_printfa(reply, "NOTFOUND\t%u", id);
-		handler->master_callback(str_c(reply), master);
+		auth_stream_reply_add(reply, "NOTFOUND", NULL);
+		auth_stream_reply_add(reply, NULL, dec2str(id));
+		handler->master_callback(reply, master);
 		auth_request_unref(&request);
 	} else {
 		/* the request isn't being referenced anywhere anymore,
--- a/src/auth/auth-request-handler.h	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-request-handler.h	Sun Mar 09 12:37:26 2008 +0200
@@ -3,8 +3,10 @@
 
 struct auth_request;
 struct auth_master_connection;
+struct auth_stream_reply;
 
-typedef void auth_request_callback_t(const char *reply, void *context);
+typedef void
+auth_request_callback_t(struct auth_stream_reply *reply, void *context);
 
 struct auth_request_handler *
 auth_request_handler_create(struct auth *auth,
@@ -12,7 +14,7 @@
 			    auth_request_callback_t *master_callback);
 #ifdef CONTEXT_TYPE_SAFETY
 #  define auth_request_handler_create(auth, callback, context, master_callback)\
-	({(void)(1 ? 0 : callback((const char *)NULL, context)); \
+	({(void)(1 ? 0 : callback((struct auth_stream_reply *)NULL, context)); \
 	  auth_request_handler_create(auth, \
 		(auth_request_callback_t *)callback, context, \
 		master_callback); })
--- a/src/auth/auth-request.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-request.c	Sun Mar 09 12:37:26 2008 +0200
@@ -115,38 +115,37 @@
 		pool_unref(&request->pool);
 }
 
-void auth_request_export(struct auth_request *request, string_t *str)
+void auth_request_export(struct auth_request *request,
+			 struct auth_stream_reply *reply)
 {
-	str_append(str, "user=");
-	str_append(str, request->user);
-	str_append(str, "\tservice=");
-	str_append(str, request->service);
+	auth_stream_reply_add(reply, "user", request->user);
+	auth_stream_reply_add(reply, "service", request->service);
 
         if (request->master_user != NULL) {
-                str_append(str, "\tmaster_user=");
-                str_append(str, request->master_user);
+		auth_stream_reply_add(reply, "master_user",
+				      request->master_user);
         }
 
 	if (request->local_ip.family != 0) {
-		str_append(str, "\tlip=");
-		str_append(str, net_ip2addr(&request->local_ip));
+		auth_stream_reply_add(reply, "lip",
+				      net_ip2addr(&request->local_ip));
 	}
 	if (request->remote_ip.family != 0) {
-		str_append(str, "\trip=");
-		str_append(str, net_ip2addr(&request->remote_ip));
+		auth_stream_reply_add(reply, "rip",
+				      net_ip2addr(&request->remote_ip));
 	}
 	if (request->local_port != 0) {
-		str_append(str, "\tlport=");
-		str_printfa(str, "%u", request->local_port);
+		auth_stream_reply_add(reply, "lport",
+				      dec2str(request->local_port));
 	}
 	if (request->remote_port != 0) {
-		str_append(str, "\trport=");
-		str_printfa(str, "%u", request->remote_port);
+		auth_stream_reply_add(reply, "rport",
+				      dec2str(request->remote_port));
 	}
 	if (request->secured)
-		str_append(str, "\tsecured=1");
+		auth_stream_reply_add(reply, "secured", "1");
 	if (request->skip_password_check)
-		str_append(str, "\tskip_password_check=1");
+		auth_stream_reply_add(reply, "skip_password_check", "1");
 }
 
 bool auth_request_import(struct auth_request *request,
@@ -352,8 +351,10 @@
 			}
 		}
 	} else if (*result == PASSDB_RESULT_PASS_EXPIRED) {
-	        if (request->extra_fields == NULL)
-		        request->extra_fields = auth_stream_reply_init(request);
+		if (request->extra_fields == NULL) {
+			request->extra_fields =
+				auth_stream_reply_init(request->pool);
+		}
 	        auth_stream_reply_add(request->extra_fields, "reason",
 				      "Password expired");
 	} else if (request->passdb->next != NULL &&
@@ -632,12 +633,12 @@
 	if (*value == '\0') {
 		/* negative cache entry */
 		*result_r = USERDB_RESULT_USER_UNKNOWN;
-		*reply_r = auth_stream_reply_init(request);
+		*reply_r = auth_stream_reply_init(request->pool);
 		return TRUE;
 	}
 
 	*result_r = USERDB_RESULT_OK;
-	*reply_r = auth_stream_reply_init(request);
+	*reply_r = auth_stream_reply_init(request->pool);
 	auth_stream_reply_import(*reply_r, value);
 	return TRUE;
 }
@@ -994,7 +995,7 @@
 	}
 
 	if (request->extra_fields == NULL)
-		request->extra_fields = auth_stream_reply_init(request);
+		request->extra_fields = auth_stream_reply_init(request->pool);
 	auth_stream_reply_add(request->extra_fields, name, value);
 }
 
@@ -1087,7 +1088,7 @@
 		/* we'll need to get this field stored into cache */
 		if (request->extra_cache_fields == NULL) {
 			request->extra_cache_fields =
-				auth_stream_reply_init(request);
+				auth_stream_reply_init(request->pool);
 		}
 		auth_stream_reply_add(request->extra_cache_fields, name, value);
 	}
@@ -1117,7 +1118,7 @@
 
 void auth_request_init_userdb_reply(struct auth_request *request)
 {
-	request->userdb_reply = auth_stream_reply_init(request);
+	request->userdb_reply = auth_stream_reply_init(request->pool);
 	auth_stream_reply_add(request->userdb_reply, NULL, request->user);
 }
 
--- a/src/auth/auth-request.h	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-request.h	Sun Mar 09 12:37:26 2008 +0200
@@ -111,7 +111,8 @@
 void auth_request_fail(struct auth_request *request);
 void auth_request_internal_failure(struct auth_request *request);
 
-void auth_request_export(struct auth_request *request, string_t *str);
+void auth_request_export(struct auth_request *request,
+			 struct auth_stream_reply *reply);
 bool auth_request_import(struct auth_request *request,
 			 const char *key, const char *value);
 
--- a/src/auth/auth-stream.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-stream.c	Sun Mar 09 12:37:26 2008 +0200
@@ -10,12 +10,12 @@
 	string_t *str;
 };
 
-struct auth_stream_reply *auth_stream_reply_init(struct auth_request *request)
+struct auth_stream_reply *auth_stream_reply_init(pool_t pool)
 {
 	struct auth_stream_reply *reply;
 
-	reply = p_new(request->pool, struct auth_stream_reply, 1);
-	reply->str = str_new(request->pool, 256);
+	reply = p_new(pool, struct auth_stream_reply, 1);
+	reply->str = str_new(pool, 256);
 	return reply;
 }
 
@@ -110,3 +110,8 @@
 {
 	return t_strsplit(str_c(reply->str), "\t");
 }
+
+string_t *auth_stream_reply_get_str(struct auth_stream_reply *reply)
+{
+	return reply->str;
+}
--- a/src/auth/auth-stream.h	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-stream.h	Sun Mar 09 12:37:26 2008 +0200
@@ -3,7 +3,7 @@
 
 struct auth_request;
 
-struct auth_stream_reply *auth_stream_reply_init(struct auth_request *request);
+struct auth_stream_reply *auth_stream_reply_init(pool_t pool);
 void auth_stream_reply_add(struct auth_stream_reply *reply,
 			   const char *key, const char *value);
 void auth_stream_reply_reset(struct auth_stream_reply *reply);
@@ -14,5 +14,6 @@
 bool auth_stream_is_empty(struct auth_stream_reply *reply);
 
 const char *const *auth_stream_split(struct auth_stream_reply *reply);
+string_t *auth_stream_reply_get_str(struct auth_stream_reply *reply);
 
 #endif
--- a/src/auth/auth-worker-client.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-worker-client.c	Sun Mar 09 12:37:26 2008 +0200
@@ -71,56 +71,58 @@
 	return auth_request;
 }
 
-static void add_userdb_replies(string_t *str, const char *data)
+static void
+add_userdb_replies(struct auth_stream_reply *reply,
+		   struct auth_stream_reply *userdb_reply)
 {
 	const char *const *tmp;
 
-	tmp = t_strsplit(data, "\t");
+	tmp = auth_stream_split(userdb_reply);
 	i_assert(*tmp != NULL);
 	/* first field is the user name */
 	tmp++;
-	for (; *tmp != NULL; tmp++)
-		str_printfa(str, "\tuserdb_%s", *tmp);
+	for (; *tmp != NULL; tmp++) {
+		auth_stream_reply_import(reply,
+					 t_strconcat("userdb_", *tmp, NULL));
+	}
 }
 
 static void verify_plain_callback(enum passdb_result result,
 				  struct auth_request *request)
 {
 	struct auth_worker_client *client = request->context;
+	struct auth_stream_reply *reply;
 	string_t *str;
 
 	if (request->passdb_failure && result == PASSDB_RESULT_OK)
 		result = PASSDB_RESULT_PASSWORD_MISMATCH;
 
-	str = t_str_new(64);
-	str_printfa(str, "%u\t", request->id);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, NULL, dec2str(request->id));
 
-	if (result == PASSDB_RESULT_INTERNAL_FAILURE)
-		str_printfa(str, "FAIL\t%d", result);
+	if (result == PASSDB_RESULT_OK)
+		auth_stream_reply_add(reply, "OK", NULL);
 	else {
-		if (result != PASSDB_RESULT_OK)
-			str_printfa(str, "FAIL\t%d\t", result);
-		else
-			str_append(str, "OK\t");
-		str_append(str, request->user);
-		str_append_c(str, '\t');
-		if (request->passdb_password != NULL)
-			str_append(str, request->passdb_password);
+		auth_stream_reply_add(reply, "FAIL", NULL);
+		auth_stream_reply_add(reply, NULL,
+				      t_strdup_printf("%d", result));
+	}
+	if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
+		auth_stream_reply_add(reply, NULL, request->user);
+		auth_stream_reply_add(reply, NULL,
+				      request->passdb_password == NULL ? "" :
+				      request->passdb_password);
 		if (request->no_password)
-			str_append(str, "\tnopassword");
-		if (request->userdb_reply != NULL) {
-			const char *data =
-				auth_stream_reply_export(request->userdb_reply);
-			add_userdb_replies(str, data);
-		}
+			auth_stream_reply_add(reply, "nopassword", NULL);
+		if (request->userdb_reply != NULL)
+			add_userdb_replies(reply, request->userdb_reply);
 		if (request->extra_fields != NULL) {
-			const char *field =
+			const char *fields =
 				auth_stream_reply_export(request->extra_fields);
-
-			str_append_c(str, '\t');
-			str_append(str, field);
+			auth_stream_reply_import(reply, fields);
 		}
 	}
+	str = auth_stream_reply_get_str(reply);
 	str_append_c(str, '\n');
 	o_stream_send(client->output, str_data(str), str_len(str));
 
@@ -189,32 +191,37 @@
 			    struct auth_request *request)
 {
 	struct auth_worker_client *client = request->context;
+	struct auth_stream_reply *reply;
 	string_t *str;
 
 	if (request->passdb_failure && result == PASSDB_RESULT_OK)
 		result = PASSDB_RESULT_PASSWORD_MISMATCH;
 
-	str = t_str_new(64);
-	str_printfa(str, "%u\t", request->id);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, NULL, dec2str(request->id));
 
-	if (result != PASSDB_RESULT_OK)
-		str_printfa(str, "FAIL\t%d", result);
-	else {
-		str_printfa(str, "OK\t%s\t{%s.b64}", request->user,
-			    request->credentials_scheme);
+	if (result != PASSDB_RESULT_OK) {
+		auth_stream_reply_add(reply, "FAIL", NULL);
+		auth_stream_reply_add(reply, NULL,
+				      t_strdup_printf("%d", result));
+	} else {
+		auth_stream_reply_add(reply, "OK", NULL);
+		auth_stream_reply_add(reply, NULL, request->user);
+
+		str = t_str_new(64);
+		str_printfa(str, "{%s.b64}", request->credentials_scheme);
 		base64_encode(credentials, size, str);
-		str_append_c(str, '\t');
+		auth_stream_reply_add(reply, NULL, str_c(str));
+
 		if (request->extra_fields != NULL) {
-			const char *field =
+			const char *fields =
 				auth_stream_reply_export(request->extra_fields);
-			str_append(str, field);
+			auth_stream_reply_import(reply, fields);
 		}
-		if (request->userdb_reply != NULL) {
-			const char *data =
-				auth_stream_reply_export(request->userdb_reply);
-			add_userdb_replies(str, data);
-		}
+		if (request->userdb_reply != NULL)
+			add_userdb_replies(reply, request->userdb_reply);
 	}
+	str = auth_stream_reply_get_str(reply);
 	str_append_c(str, '\n');
 	o_stream_send(client->output, str_data(str), str_len(str));
 
--- a/src/auth/auth-worker-server.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-worker-server.c	Sun Mar 09 12:37:26 2008 +0200
@@ -251,12 +251,13 @@
 	return request != NULL ? request : array_append_space(&conn->requests);
 }
 
-void auth_worker_call(struct auth_request *auth_request, const char *data,
+void auth_worker_call(struct auth_request *auth_request,
+		      struct auth_stream_reply *data,
 		      auth_worker_callback_t *callback)
 {
 	struct auth_worker_connection *conn;
 	struct auth_worker_request *request;
-	const char *reply;
+	const char *reply, *data_str;
 	struct const_iovec iov[3];
 
 	conn = auth_worker_find_free();
@@ -276,10 +277,11 @@
 
 	i_assert(conn->requests_left > 0);
 
+	data_str = auth_stream_reply_export(data);
 	iov[0].iov_base = t_strdup_printf("%d\t", ++conn->id_counter);
 	iov[0].iov_len = strlen(iov[0].iov_base);
-	iov[1].iov_base = data;
-	iov[1].iov_len = strlen(data);
+	iov[1].iov_base = data_str;
+	iov[1].iov_len = strlen(data_str);
 	iov[2].iov_base = "\n";
 	iov[2].iov_len = 1;
 
--- a/src/auth/auth-worker-server.h	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/auth-worker-server.h	Sun Mar 09 12:37:26 2008 +0200
@@ -2,11 +2,13 @@
 #define AUTH_WORKER_SERVER_H
 
 struct auth_request;
+struct auth_stream_reply;
 
 typedef void auth_worker_callback_t(struct auth_request *request,
 				    const char *reply);
 
-void auth_worker_call(struct auth_request *auth_request, const char *data,
+void auth_worker_call(struct auth_request *auth_request,
+		      struct auth_stream_reply *data,
 		      auth_worker_callback_t *callback);
 
 void auth_worker_server_init(void);
--- a/src/auth/passdb-blocking.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/passdb-blocking.c	Sun Mar 09 12:37:26 2008 +0200
@@ -70,18 +70,18 @@
 
 void passdb_blocking_verify_plain(struct auth_request *request)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 
 	i_assert(auth_stream_is_empty(request->extra_fields) ||
 		 request->master_user != NULL);
 
-	str = t_str_new(64);
-	str_printfa(str, "PASSV\t%u\t", request->passdb->id);
-	str_append(str, request->mech_password);
-	str_append_c(str, '\t');
-	auth_request_export(request, str);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, "PASSV", NULL);
+	auth_stream_reply_add(reply, NULL, dec2str(request->passdb->id));
+	auth_stream_reply_add(reply, NULL, request->mech_password);
+	auth_request_export(request, reply);
 
-	auth_worker_call(request, str_c(str), verify_plain_callback);
+	auth_worker_call(request, reply, verify_plain_callback);
 }
 
 static void
@@ -109,17 +109,18 @@
 
 void passdb_blocking_lookup_credentials(struct auth_request *request)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 
 	i_assert(auth_stream_is_empty(request->extra_fields) ||
 		 request->master_user != NULL);
 
-	str = t_str_new(64);
-	str_printfa(str, "PASSL\t%u\t%s\t",
-		    request->passdb->id, request->credentials_scheme);
-	auth_request_export(request, str);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, "PASSL", NULL);
+	auth_stream_reply_add(reply, NULL, dec2str(request->passdb->id));
+	auth_stream_reply_add(reply, NULL, request->credentials_scheme);
+	auth_request_export(request, reply);
 
-	auth_worker_call(request, str_c(str), lookup_credentials_callback);
+	auth_worker_call(request, reply, lookup_credentials_callback);
 }
 
 static void
@@ -134,12 +135,13 @@
 void passdb_blocking_set_credentials(struct auth_request *request,
 				     const char *new_credentials)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 
-	str = t_str_new(64);
-	str_printfa(str, "SETCRED\t%u\t%s\t",
-		    request->passdb->id, new_credentials);
-	auth_request_export(request, str);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, "SETCRED", NULL);
+	auth_stream_reply_add(reply, NULL, dec2str(request->passdb->id));
+	auth_stream_reply_add(reply, NULL, new_credentials);
+	auth_request_export(request, reply);
 
-	auth_worker_call(request, str_c(str), set_credentials_callback);
+	auth_worker_call(request, reply, set_credentials_callback);
 }
--- a/src/auth/userdb-blocking.c	Sun Mar 09 10:32:59 2008 +0200
+++ b/src/auth/userdb-blocking.c	Sun Mar 09 12:37:26 2008 +0200
@@ -18,7 +18,7 @@
 		result = USERDB_RESULT_USER_UNKNOWN;
 	else if (strncmp(reply, "OK\t", 3) == 0) {
 		result = USERDB_RESULT_OK;
-		request->userdb_reply = auth_stream_reply_init(request);
+		request->userdb_reply = auth_stream_reply_init(request->pool);
 		auth_stream_reply_import(request->userdb_reply, reply + 3);
 	} else {
 		result = USERDB_RESULT_INTERNAL_FAILURE;
@@ -30,11 +30,12 @@
 
 void userdb_blocking_lookup(struct auth_request *request)
 {
-	string_t *str;
+	struct auth_stream_reply *reply;
 
-	str = t_str_new(64);
-	str_printfa(str, "USER\t%u\t", request->userdb->num);
-	auth_request_export(request, str);
+	reply = auth_stream_reply_init(pool_datastack_create());
+	auth_stream_reply_add(reply, "USER", NULL);
+	auth_stream_reply_add(reply, NULL, dec2str(request->userdb->num));
+	auth_request_export(request, reply);
 
-	auth_worker_call(request, str_c(str), user_callback);
+	auth_worker_call(request, reply, user_callback);
 }