void auth_request_handler_reply(struct auth_request *request,
				enum auth_client_result result,
				const void *auth_reply, size_t reply_size)
{
        struct auth_request_handler *handler = request->handler;
	string_t *str;
	int ret;

	if (handler->destroyed) {
		/* the client connection was already closed. we can't do
		   anything but abort this request */
		request->internal_failure = TRUE;
		result = AUTH_CLIENT_RESULT_FAILURE;
		/* make sure this request is set to finished state
		   (it's not with result=continue) */
		auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
	}

	switch (result) {
	case AUTH_CLIENT_RESULT_CONTINUE:
		str = t_str_new(16 + MAX_BASE64_ENCODED_SIZE(reply_size));
		str_printfa(str, "CONT\t%u\t", request->id);
		base64_encode(auth_reply, reply_size, str);

		request->accept_cont_input = TRUE;
		handler->callback(str_c(str), handler->context);
		break;
	case AUTH_CLIENT_RESULT_SUCCESS:
		if (reply_size > 0) {
			str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
			base64_encode(auth_reply, reply_size, str);
			auth_fields_add(request->extra_fields, "resp",
					str_c(str), 0);
		}
		ret = auth_request_proxy_finish(request,
				auth_request_handler_proxy_callback);
		if (ret < 0)
			auth_request_handler_reply_failure_finish(request);
		else if (ret > 0)
			auth_request_handler_reply_success_finish(request);
		else
			return;
		break;
	case AUTH_CLIENT_RESULT_FAILURE:
		auth_request_proxy_finish_failure(request);
		auth_request_handler_reply_failure_finish(request);
		break;
	}
	/* NOTE: request may be destroyed now */

        auth_request_handler_unref(&handler);
}
static bool
postfix_input_user(struct auth_postfix_connection *conn, const char *username)
{
	struct auth_request *auth_request;
	const char *error;

	io_remove(&conn->io);
	if (!postfix_input_auth_request(conn, username,
					&auth_request, &error)) {
		auth_request_log_info(auth_request, "postfix", "%s", error);
		user_callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
	} else {
		auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB);
		auth_request_lookup_user(auth_request, user_callback);
	}
	return TRUE;
}
static bool
master_input_user(struct auth_master_connection *conn, const char *args)
{
	struct auth_request *auth_request;
	const char *error;
	int ret;

	ret = master_input_auth_request(conn, args, "USER",
					&auth_request, &error);
	if (ret <= 0) {
		if (ret < 0)
			return FALSE;
		auth_request_log_info(auth_request, "userdb", "%s", error);
		user_callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
	} else {
		auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB);
		auth_request_lookup_user(auth_request, user_callback);
	}
	return TRUE;
}
static void userdb_callback(enum userdb_result result,
			    struct auth_request *request)
{
        struct auth_request_handler *handler = request->handler;
	string_t *str;
	const char *value;

	i_assert(request->state == AUTH_REQUEST_STATE_USERDB);

	auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);

	if (request->userdb_lookup_tempfailed)
		result = USERDB_RESULT_INTERNAL_FAILURE;

	str = t_str_new(128);
	switch (result) {
	case USERDB_RESULT_INTERNAL_FAILURE:
		str_printfa(str, "FAIL\t%u", request->id);
		if (request->userdb_lookup_tempfailed) {
			value = auth_fields_find(request->userdb_reply, "reason");
			if (value != NULL)
				auth_str_add_keyvalue(str, "reason", value);
		}
		break;
	case USERDB_RESULT_USER_UNKNOWN:
		str_printfa(str, "NOTFOUND\t%u", request->id);
		break;
	case USERDB_RESULT_OK:
		str_printfa(str, "USER\t%u\t", request->id);
		str_append_tabescaped(str, request->user);
		auth_str_append_userdb_extra_fields(request, str);
		break;
	}
	handler->master_callback(str_c(str), request->master);

	auth_master_connection_unref(&request->master);
	auth_request_unref(&request);
        auth_request_handler_unref(&handler);
}
bool auth_request_handler_master_request(struct auth_request_handler *handler,
					 struct auth_master_connection *master,
					 unsigned int id, unsigned int client_id,
					 const char *const *params)
{
	struct auth_request *request;
	struct net_unix_cred cred;

	request = hash_table_lookup(handler->requests, POINTER_CAST(client_id));
	if (request == NULL) {
		i_error("Master request %u.%u not found",
			handler->client_pid, client_id);
		return auth_master_request_failed(handler, master, id);
	}

	auth_request_ref(request);
	auth_request_handler_remove(handler, request);

	for (; *params != NULL; params++) {
		const char *name, *param = strchr(*params, '=');

		if (param == NULL) {
			name = *params;
			param = "";
		} else {
			name = t_strdup_until(*params, param);
			param++;
		}

		(void)auth_request_import_master(request, name, param);
	}

	/* verify session pid if specified and possible */
	if (request->session_pid != (pid_t)-1 &&
	    net_getunixcred(master->fd, &cred) == 0 &&
	    cred.pid != (pid_t)-1 && request->session_pid != cred.pid) {
		i_error("Session pid %ld provided by master for request %u.%u "
			"did not match peer credentials (pid=%ld, uid=%ld)",
			(long)request->session_pid,
			handler->client_pid, client_id,
			(long)cred.pid, (long)cred.uid);
		return auth_master_request_failed(handler, master, id);
	}

	if (request->state != AUTH_REQUEST_STATE_FINISHED ||
	    !request->successful) {
		i_error("Master requested unfinished authentication request "
			"%u.%u", handler->client_pid, client_id);
		handler->master_callback(t_strdup_printf("FAIL\t%u", id),
					 master);
		auth_request_unref(&request);
	} else {
		/* the request isn't being referenced anywhere anymore,
		   so we can do a bit of kludging.. replace the request's
		   old client_id with master's id. */
		auth_request_set_state(request, AUTH_REQUEST_STATE_USERDB);
		request->id = id;
		request->master = master;

		/* master and handler are referenced until userdb_callback i
		   s called. */
		auth_master_connection_ref(master);
		handler->refcount++;
		auth_request_lookup_user(request, userdb_callback);
	}
	return TRUE;
}