static struct acl_object *
acl_backend_vfile_object_init(struct acl_backend *_backend,
			      const char *name)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;
	struct acl_object_vfile *aclobj;
	const char *dir, *vname, *error;

	aclobj = i_new(struct acl_object_vfile, 1);
	aclobj->aclobj.backend = _backend;
	aclobj->aclobj.name = i_strdup(name);

	T_BEGIN {
		if (*name == '\0' ||
		    mailbox_list_is_valid_name(_backend->list, name, &error)) {
			vname = *name == '\0' ? "" :
				mailbox_list_get_vname(_backend->list, name);

			dir = acl_backend_vfile_get_local_dir(_backend, name, vname);
			aclobj->local_path = dir == NULL ? NULL :
				i_strconcat(dir, "/"ACL_FILENAME, NULL);
			if (backend->global_path != NULL &&
			    _backend->global_file == NULL) {
				aclobj->global_path =
					i_strconcat(backend->global_path, "/", vname, NULL);
			}
		} else {
			/* Invalid mailbox name, just use the default
			   global ACL files */
		}
	} T_END;
	return &aclobj->aclobj;
}
static bool
acl_backend_vfile_has_acl(struct acl_backend *_backend, const char *name)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;
	struct acl_backend_vfile_validity *old_validity, new_validity;
	const char *path, *local_path, *global_path, *dir, *vname = "";
	const char *error;
	int ret;

	old_validity = acl_cache_get_validity(_backend->cache, name);
	if (old_validity != NULL)
		new_validity = *old_validity;
	else
		memset(&new_validity, 0, sizeof(new_validity));

	/* See if the mailbox exists. If we wanted recursive lookups we could
	   skip this, but at least for now we assume that if an existing
	   mailbox has no ACL it's equivalent to default ACLs. */
	if (mailbox_list_get_path(_backend->list, name,
				  MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0)
		ret = -1;
	else {
		ret = acl_backend_vfile_exists(backend, path,
					       &new_validity.mailbox_validity);
	}

	if (ret == 0 &&
	    (*name == '\0' ||
	     mailbox_list_is_valid_name(_backend->list, name, &error))) {
		vname = *name == '\0' ? "" :
			mailbox_list_get_vname(_backend->list, name);
		dir = acl_backend_vfile_get_local_dir(_backend, name, vname);
		if (dir != NULL) {
			local_path = t_strconcat(dir, "/", name, NULL);
			ret = acl_backend_vfile_exists(backend, local_path,
						       &new_validity.local_validity);
		}
	}

	if (ret == 0 && backend->global_path != NULL) {
		if (_backend->global_file != NULL) {
			ret = acl_global_file_refresh(_backend->global_file);
			if (ret == 0 && acl_global_file_have_any(_backend->global_file, vname))
				ret = 1;
		} else {
			global_path = t_strconcat(backend->global_path, "/", name, NULL);
			ret = acl_backend_vfile_exists(backend, global_path,
						       &new_validity.global_validity);
		}
	}
	acl_cache_set_validity(_backend->cache, name, &new_validity);
	return ret > 0;
}
const struct mailbox_info *
mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *_ctx)
{
	struct subscriptions_mailbox_list_iterate_context *ctx =
		(struct subscriptions_mailbox_list_iterate_context *)_ctx;
	struct mailbox_list *list = _ctx->list;
	struct mailbox_node *node;
	enum mailbox_info_flags subs_flags;
	const char *vname, *storage_name, *error;
	int ret;

	node = mailbox_tree_iterate_next(ctx->iter, &vname);
	if (node == NULL)
		return NULL;

	ctx->info.vname = vname;
	subs_flags = node->flags & (MAILBOX_SUBSCRIBED |
				    MAILBOX_CHILD_SUBSCRIBED);

	if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 &&
	    (_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) {
		/* don't care about flags, just return it */
		ctx->info.flags = subs_flags;
		return &ctx->info;
	}

	storage_name = mailbox_list_get_storage_name(list, vname);
	if (!mailbox_list_is_valid_name(list, storage_name, &error)) {
		/* broken entry in subscriptions file */
		ctx->info.flags = MAILBOX_NONEXISTENT;
	} else if (mailbox_list_mailbox(list, storage_name,
					&ctx->info.flags) < 0) {
		ctx->info.flags = 0;
		_ctx->failed = TRUE;
	} else if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 &&
		   (ctx->info.flags & (MAILBOX_CHILDREN |
				       MAILBOX_NOCHILDREN)) == 0) {
		ret = mailbox_has_children(list, storage_name);
		if (ret < 0)
			_ctx->failed = TRUE;
		else if (ret == 0)
			ctx->info.flags |= MAILBOX_NOCHILDREN;
		else
			ctx->info.flags |= MAILBOX_CHILDREN;

	}

	ctx->info.flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED);
	ctx->info.flags |=
		node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED);
	return &ctx->info;
}
static int
mailbox_list_subscription_fill_one(struct mailbox_list *list,
				   struct mailbox_list *src_list,
				   const char *name)
{
	struct mail_namespace *ns, *default_ns = list->ns;
	struct mail_namespace *namespaces = default_ns->user->namespaces;
	struct mailbox_node *node;
	const char *vname, *ns_name, *error;
	unsigned int len;
	bool created;

	/* default_ns is whatever namespace we're currently listing.
	   if we have e.g. prefix="" and prefix=pub/ namespaces with
	   pub/ namespace having subscriptions=no, we want to:

	   1) when listing "" namespace we want to skip over any names
	   that begin with pub/. */
	if (src_list->ns->prefix_len == 0)
		ns_name = name;
	else {
		/* we could have two-level namespace: ns/ns2/ */
		ns_name = t_strconcat(src_list->ns->prefix, name, NULL);
	}
	ns = mail_namespace_find_unsubscribable(namespaces, ns_name);
	if (ns != NULL && ns != default_ns) {
		if (ns->prefix_len > 0)
			return 0;
		/* prefix="" namespace=no : catching this is basically the
		   same as not finding any namespace. */
		ns = NULL;
	}

	/* 2) when listing pub/ namespace, skip over entries that don't
	   begin with pub/. */
	if (ns == NULL &&
	    (default_ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0)
		return 0;

	/* When listing shared namespace's subscriptions, we need to
	   autocreate all the visible child namespaces. their subscriptions
	   are listed later. */
	if (ns != NULL && mail_namespace_is_shared_user_root(ns)) {
		/* we'll need to get the namespace autocreated.
		   one easy way is to just ask to join a reference and
		   pattern */
		(void)mailbox_list_join_refpattern(ns->list, ns_name, "");
	}

	/* When listing pub/ namespace, skip over the namespace
	   prefix in the name. the rest of the name is storage_name. */
	if (ns == NULL)
		ns = default_ns;
	else if (strncmp(ns_name, ns->prefix, ns->prefix_len) == 0) {
		ns_name += ns->prefix_len;
		name = ns_name;
	} else {
		/* "pub" entry - this shouldn't be possible normally, because
		   it should be saved as "pub/", but handle it anyway */
		i_assert(strncmp(ns_name, ns->prefix, ns->prefix_len-1) == 0 &&
			 ns_name[ns->prefix_len-1] == '\0');
		name = ns_name = "";
	}

	len = strlen(name);
	if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) {
		/* entry ends with hierarchy separator, remove it.
		   this exists mainly for backwards compatibility with old
		   Dovecot versions and non-Dovecot software that added them */
		name = t_strndup(name, len-1);
	}

	if (!mailbox_list_is_valid_name(list, name, &error)) {
		/* we'll only get into trouble if we show this */
		return -1;
	} else {
		vname = mailbox_list_get_vname(list, name);
		if (!uni_utf8_str_is_valid(vname))
			return -1;
		node = mailbox_tree_get(list->subscriptions, vname, &created);
		node->flags = MAILBOX_SUBSCRIBED;
	}
	return 0;
}