void auth_request_set_field(struct auth_request *request,
			    const char *name, const char *value,
			    const char *default_scheme)
{
	const char *suffix;
	size_t name_len = strlen(name);

	i_assert(*name != '\0');
	i_assert(value != NULL);

	i_assert(request->passdb != NULL);

	if (name_len > 10 && strcmp(name+name_len-10, ":protected") == 0) {
		/* set this field only if it hasn't been set before */
		name = t_strndup(name, name_len-10);
		if (auth_fields_exists(request->fields.extra_fields, name))
			return;
	} else if (name_len > 7 && strcmp(name+name_len-7, ":remove") == 0) {
		/* remove this field entirely */
		name = t_strndup(name, name_len-7);
		auth_fields_remove(request->fields.extra_fields, name);
		return;
	}

	if (strcmp(name, "password") == 0) {
		auth_request_set_password(request, value,
					  default_scheme, FALSE);
		return;
	}
	if (strcmp(name, "password_noscheme") == 0) {
		auth_request_set_password(request, value, default_scheme, TRUE);
		return;
	}

	if (auth_request_try_update_username(request, name, value)) {
		/* don't change the original value so it gets saved correctly
		   to cache. */
	} else if (strcmp(name, "login_user") == 0) {
		auth_request_set_login_username_forced(request, value);
	} else if (strcmp(name, "allow_nets") == 0) {
		auth_request_validate_networks(request, name, value,
					       &request->fields.remote_ip);
	} else if (strcmp(name, "fail") == 0) {
		request->failed = TRUE;
	} else if (strcmp(name, "delay_until") == 0) {
		time_t timestamp;
		unsigned int extra_secs = 0;
		const char *p;

		p = strchr(value, '+');
		if (p != NULL) {
			value = t_strdup_until(value, p++);
			if (str_to_uint(p, &extra_secs) < 0) {
				e_error(authdb_event(request),
					"Invalid delay_until randomness number '%s'", p);
				request->failed = TRUE;
			} else {
				extra_secs = i_rand_limit(extra_secs);
			}
		}
		if (str_to_time(value, &timestamp) < 0) {
			e_error(authdb_event(request),
				"Invalid delay_until timestamp '%s'", value);
			request->failed = TRUE;
		} else if (timestamp <= ioloop_time) {
			/* no more delays */
		} else if (timestamp - ioloop_time > AUTH_REQUEST_MAX_DELAY_SECS) {
			e_error(authdb_event(request),
				"delay_until timestamp %s is too much in the future, failing", value);
			request->failed = TRUE;
		} else {
			/* add randomness, but not too much of it */
			timestamp += extra_secs;
			if (timestamp - ioloop_time > AUTH_REQUEST_MAX_DELAY_SECS)
				timestamp = ioloop_time + AUTH_REQUEST_MAX_DELAY_SECS;
			request->delay_until = timestamp;
		}
	} else if (strcmp(name, "allow_real_nets") == 0) {
		auth_request_validate_networks(request, name, value,
					       &request->fields.real_remote_ip);
	} else if (str_begins(name, "userdb_", &suffix)) {
		/* for prefetch userdb */
		request->userdb_prefetch_set = TRUE;
		if (request->fields.userdb_reply == NULL)
			auth_request_init_userdb_reply(request, TRUE);
		if (strcmp(name, "userdb_userdb_import") == 0) {
			/* we can't put the whole userdb_userdb_import
			   value to extra_cache_fields or it doesn't work
			   properly. so handle this explicitly. */
			auth_request_passdb_import(request, value,
						   "userdb_", default_scheme);
			return;
		}
		auth_request_set_userdb_field(request, suffix, value);
	} else if (strcmp(name, "noauthenticate") == 0) {
		/* add "nopassword" also so that passdbs won't try to verify
		   the password. */
		auth_fields_add(request->fields.extra_fields, name, value, 0);
		auth_fields_add(request->fields.extra_fields, "nopassword", NULL, 0);
	} else if (strcmp(name, "nopassword") == 0) {
		/* NULL password - anything goes */
		const char *password = request->passdb_password;

		if (password != NULL &&
		    !auth_fields_exists(request->fields.extra_fields, "noauthenticate")) {
			(void)password_get_scheme(&password);
			if (*password != '\0') {
				e_error(authdb_event(request),
					"nopassword set but password is "
					"non-empty");
				return;
			}
		}
		request->passdb_password = NULL;
		auth_fields_add(request->fields.extra_fields, name, value, 0);
		return;
	} else if (strcmp(name, "passdb_import") == 0) {
		auth_request_passdb_import(request, value, "", default_scheme);
		return;
	} else {
		/* these fields are returned to client */
		auth_fields_add(request->fields.extra_fields, name, value, 0);
		return;
	}

	/* add the field unconditionally to extra_fields. this is required if
	   a) auth cache is used, b) if we're a worker and we'll need to send
	   this to the main auth process that can store it in the cache,
	   c) for easily checking :protected fields' existence. */
	auth_fields_add(request->fields.extra_fields, name, value,
			AUTH_FIELD_FLAG_HIDDEN);
}