void auth_request_userdb_callback(enum userdb_result result,
				  struct auth_request *request)
{
	struct auth_userdb *userdb = request->userdb;
	struct auth_userdb *next_userdb;
	enum auth_db_rule result_rule;
	const char *error;
	bool userdb_continue = FALSE;

	switch (result) {
	case USERDB_RESULT_OK:
		result_rule = userdb->result_success;
		break;
	case USERDB_RESULT_INTERNAL_FAILURE:
		result_rule = userdb->result_internalfail;
		break;
	case USERDB_RESULT_USER_UNKNOWN:
	default:
		result_rule = userdb->result_failure;
		break;
	}

	switch (result_rule) {
	case AUTH_DB_RULE_RETURN:
		break;
	case AUTH_DB_RULE_RETURN_OK:
		request->userdb_success = TRUE;
		break;
	case AUTH_DB_RULE_RETURN_FAIL:
		request->userdb_success = FALSE;
		break;
	case AUTH_DB_RULE_CONTINUE:
		userdb_continue = TRUE;
		break;
	case AUTH_DB_RULE_CONTINUE_OK:
		userdb_continue = TRUE;
		request->userdb_success = TRUE;
		break;
	case AUTH_DB_RULE_CONTINUE_FAIL:
		userdb_continue = TRUE;
		request->userdb_success = FALSE;
		break;
	}

	auth_request_userdb_lookup_end(request, result);

	next_userdb = userdb->next;
	while (next_userdb != NULL &&
		auth_request_want_skip_userdb(request, next_userdb))
		next_userdb = next_userdb->next;

	if (userdb_continue && next_userdb != NULL) {
		/* try next userdb. */
		if (result == USERDB_RESULT_INTERNAL_FAILURE)
			request->userdbs_seen_internal_failure = TRUE;

		if (result == USERDB_RESULT_OK) {
			/* this userdb lookup succeeded, preserve its extra
			   fields */
			if (userdb_template_export(userdb->override_fields_tmpl,
						   request, &error) < 0) {
				e_error(request->event,
					"%sFailed to expand override_fields: %s",
					auth_request_get_log_prefix_db(request), error);
				request->private_callback.userdb(
					USERDB_RESULT_INTERNAL_FAILURE, request);
				return;
			}
			auth_fields_snapshot(request->fields.userdb_reply);
		} else {
			/* this userdb lookup failed, remove any extra fields
			   it set */
			auth_fields_rollback(request->fields.userdb_reply);
		}
		request->user_changed_by_lookup = FALSE;

		request->userdb = next_userdb;
		auth_request_lookup_user(request,
					 request->private_callback.userdb);
		return;
	}

	if (request->userdb_success) {
		if (userdb_template_export(userdb->override_fields_tmpl,
					   request, &error) < 0) {
			e_error(request->event,
				"%sFailed to expand override_fields: %s",
				auth_request_get_log_prefix_db(request), error);
			result = USERDB_RESULT_INTERNAL_FAILURE;
		} else {
			result = USERDB_RESULT_OK;
		}
	} else if (request->userdbs_seen_internal_failure ||
		   result == USERDB_RESULT_INTERNAL_FAILURE) {
		/* one of the userdb lookups failed. the user might have been
		   in there, so this is an internal failure */
		result = USERDB_RESULT_INTERNAL_FAILURE;
	} else if (request->client_pid != 0) {
		/* this was an actual login attempt, the user should
		   have been found. */
		if (auth_request_get_auth(request)->userdbs->next == NULL) {
			e_error(request->event,
				"%suser not found from userdb",
				auth_request_get_log_prefix_db(request));
		} else {
			e_error(request->mech_event,
				"user not found from any userdbs");
		}
		result = USERDB_RESULT_USER_UNKNOWN;
	} else {
		result = USERDB_RESULT_USER_UNKNOWN;
	}

	if (request->userdb_lookup_tempfailed) {
		/* no caching */
	} else if (result != USERDB_RESULT_INTERNAL_FAILURE) {
		if (request->userdb_cache_result != AUTH_REQUEST_CACHE_HIT)
			auth_request_userdb_save_cache(request, result);
	} else if (passdb_cache != NULL && userdb->cache_key != NULL) {
		/* lookup failed. if we're looking here only because the
		   request was expired in cache, fallback to using cached
		   expired record. */
		const char *cache_key = userdb->cache_key;

		if (auth_request_lookup_user_cache(request, cache_key,
						   &result, TRUE)) {
			e_info(request->event,
			       "%sFalling back to expired data from cache",
				auth_request_get_log_prefix_db(request));
		}
	}

	 request->private_callback.userdb(result, request);
}