<commit_before>/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "str.h"
#include "strescape.h"
#include "istream.h"
#include "ostream.h"
#include "file-dotlock.h"
#include "nfs-workarounds.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "acl-cache.h"
#include "acl-backend-vfile.h"

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

#define ACL_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
#define ACL_VFILE_DEFAULT_CACHE_SECS 30

#define VALIDITY_MTIME_NOTFOUND 0
#define VALIDITY_MTIME_NOACCESS -1

struct acl_vfile_validity {
	time_t last_check;

	time_t last_read_time;
	time_t last_mtime;
	off_t last_size;
};

struct acl_backend_vfile_validity {
	struct acl_vfile_validity global_validity, local_validity;
	struct acl_vfile_validity mailbox_validity;
};

struct acl_letter_map {
	char letter;
	const char *name;
};

static const struct acl_letter_map acl_letter_map[] = {
	{ 'l', MAIL_ACL_LOOKUP },
	{ 'r', MAIL_ACL_READ },
	{ 'w', MAIL_ACL_WRITE },
	{ 's', MAIL_ACL_WRITE_SEEN },
	{ 't', MAIL_ACL_WRITE_DELETED },
	{ 'i', MAIL_ACL_INSERT },
	{ 'p', MAIL_ACL_POST },
	{ 'e', MAIL_ACL_EXPUNGE },
	{ 'k', MAIL_ACL_CREATE },
	{ 'x', MAIL_ACL_DELETE },
	{ 'a', MAIL_ACL_ADMIN },
	{ '\0', NULL }
};

static struct dotlock_settings dotlock_set = {
	.timeout = 30,
	.stale_timeout = 120
};

static struct acl_backend *acl_backend_vfile_alloc(void)
{
	struct acl_backend_vfile *backend;
	pool_t pool;

	pool = pool_alloconly_create("ACL backend", 512);
	backend = p_new(pool, struct acl_backend_vfile, 1);
	backend->backend.pool = pool;
	return &backend->backend;
}

static int
acl_backend_vfile_init(struct acl_backend *_backend, const char *data)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;
	const char *const *tmp;

	tmp = t_strsplit(data, ":");
	backend->global_dir = p_strdup_empty(_backend->pool, *tmp);
	backend->cache_secs = ACL_VFILE_DEFAULT_CACHE_SECS;

	if (*tmp != NULL)
		tmp++;
	for (; *tmp != NULL; tmp++) {
		if (strncmp(*tmp, "cache_secs=", 11) == 0) {
			if (str_to_uint(*tmp + 11, &backend->cache_secs) < 0) {
				i_error("acl vfile: Invalid cache_secs value: %s",
					*tmp + 11);
				return -1;
			}
		} else {
			i_error("acl vfile: Unknown parameter: %s", *tmp);
			return -1;
		}
	}
	if (_backend->debug) {
		i_debug("acl vfile: Global ACL directory: %s",
			backend->global_dir);
	}

	_backend->cache =
		acl_cache_init(_backend,
			       sizeof(struct acl_backend_vfile_validity));
	return 0;
}

static void acl_backend_vfile_deinit(struct acl_backend *_backend)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;

	if (backend->acllist_pool != NULL) {
		array_free(&backend->acllist);
		pool_unref(&backend->acllist_pool);
	}
	pool_unref(&backend->backend.pool);
}

static const char *
acl_backend_vfile_get_local_dir(struct acl_backend *backend, const char *name)
{
	struct mail_namespace *ns;
	const char *dir, *inbox;

	if (*name == '\0')
		name = NULL;

	ns = mailbox_list_get_namespace(backend->list);
	if (mail_storage_is_mailbox_file(ns->storage)) {
		dir = mailbox_list_get_path(ns->list, name,
					    MAILBOX_LIST_PATH_TYPE_CONTROL);
	} else {
		dir = mailbox_list_get_path(ns->list, name,
					    MAILBOX_LIST_PATH_TYPE_MAILBOX);
	}
	if (name == NULL && dir != NULL) {
		/* verify that the directory isn't same as INBOX's directory.
		   this is mainly for Maildir. */
		inbox = mailbox_list_get_path(ns->list, "INBOX",
					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
		if (strcmp(inbox, dir) == 0) {
			/* can't have default ACLs with this setup */
			return NULL;
		}
	}
	return dir;
}

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;

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

	if (backend->global_dir != NULL) T_BEGIN {
		struct mail_namespace *ns =
			mailbox_list_get_namespace(_backend->list);
		string_t *vname;

		vname = t_str_new(128);
		mail_namespace_get_vname(ns, vname, name);
		aclobj->global_path = i_strconcat(backend->global_dir, "/",
						  str_c(vname), NULL);
	} T_END;

	dir = acl_backend_vfile_get_local_dir(_backend, name);
	aclobj->local_path = dir == NULL ? NULL :<commit_msg>acl: Avoid extra open() on a directory for default acl.<commit_after>/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "str.h"
#include "strescape.h"
#include "istream.h"
#include "ostream.h"
#include "file-dotlock.h"
#include "nfs-workarounds.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "acl-cache.h"
#include "acl-backend-vfile.h"

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

#define ACL_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
#define ACL_VFILE_DEFAULT_CACHE_SECS 30

#define VALIDITY_MTIME_NOTFOUND 0
#define VALIDITY_MTIME_NOACCESS -1

struct acl_vfile_validity {
	time_t last_check;

	time_t last_read_time;
	time_t last_mtime;
	off_t last_size;
};

struct acl_backend_vfile_validity {
	struct acl_vfile_validity global_validity, local_validity;
	struct acl_vfile_validity mailbox_validity;
};

struct acl_letter_map {
	char letter;
	const char *name;
};

static const struct acl_letter_map acl_letter_map[] = {
	{ 'l', MAIL_ACL_LOOKUP },
	{ 'r', MAIL_ACL_READ },
	{ 'w', MAIL_ACL_WRITE },
	{ 's', MAIL_ACL_WRITE_SEEN },
	{ 't', MAIL_ACL_WRITE_DELETED },
	{ 'i', MAIL_ACL_INSERT },
	{ 'p', MAIL_ACL_POST },
	{ 'e', MAIL_ACL_EXPUNGE },
	{ 'k', MAIL_ACL_CREATE },
	{ 'x', MAIL_ACL_DELETE },
	{ 'a', MAIL_ACL_ADMIN },
	{ '\0', NULL }
};

static struct dotlock_settings dotlock_set = {
	.timeout = 30,
	.stale_timeout = 120
};

static struct acl_backend *acl_backend_vfile_alloc(void)
{
	struct acl_backend_vfile *backend;
	pool_t pool;

	pool = pool_alloconly_create("ACL backend", 512);
	backend = p_new(pool, struct acl_backend_vfile, 1);
	backend->backend.pool = pool;
	return &backend->backend;
}

static int
acl_backend_vfile_init(struct acl_backend *_backend, const char *data)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;
	const char *const *tmp;

	tmp = t_strsplit(data, ":");
	backend->global_dir = p_strdup_empty(_backend->pool, *tmp);
	backend->cache_secs = ACL_VFILE_DEFAULT_CACHE_SECS;

	if (*tmp != NULL)
		tmp++;
	for (; *tmp != NULL; tmp++) {
		if (strncmp(*tmp, "cache_secs=", 11) == 0) {
			if (str_to_uint(*tmp + 11, &backend->cache_secs) < 0) {
				i_error("acl vfile: Invalid cache_secs value: %s",
					*tmp + 11);
				return -1;
			}
		} else {
			i_error("acl vfile: Unknown parameter: %s", *tmp);
			return -1;
		}
	}
	if (_backend->debug) {
		i_debug("acl vfile: Global ACL directory: %s",
			backend->global_dir);
	}

	_backend->cache =
		acl_cache_init(_backend,
			       sizeof(struct acl_backend_vfile_validity));
	return 0;
}

static void acl_backend_vfile_deinit(struct acl_backend *_backend)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;

	if (backend->acllist_pool != NULL) {
		array_free(&backend->acllist);
		pool_unref(&backend->acllist_pool);
	}
	pool_unref(&backend->backend.pool);
}

static const char *
acl_backend_vfile_get_local_dir(struct acl_backend *backend, const char *name)
{
	struct mail_namespace *ns;
	const char *dir, *inbox;

	if (*name == '\0')
		name = NULL;

	ns = mailbox_list_get_namespace(backend->list);
	if (mail_storage_is_mailbox_file(ns->storage)) {
		dir = mailbox_list_get_path(ns->list, name,
					    MAILBOX_LIST_PATH_TYPE_CONTROL);
	} else {
		dir = mailbox_list_get_path(ns->list, name,
					    MAILBOX_LIST_PATH_TYPE_MAILBOX);
	}
	if (name == NULL && dir != NULL) {
		/* verify that the directory isn't same as INBOX's directory.
		   this is mainly for Maildir. */
		inbox = mailbox_list_get_path(ns->list, "INBOX",
					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
		if (strcmp(inbox, dir) == 0) {
			/* can't have default ACLs with this setup */
			return NULL;
		}
	}
	return dir;
}

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;

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

	if (backend->global_dir != NULL) T_BEGIN {
		struct mail_namespace *ns =
			mailbox_list_get_namespace(_backend->list);
		string_t *vname;

		vname = t_str_new(128);
		mail_namespace_get_vname(ns, vname, name);
		if (str_len(vname) > 0) {
			aclobj->global_path =
				i_strconcat(backend->global_dir, "/",
					    str_c(vname), NULL);
		}
	} T_END;

	dir = acl_backend_vfile_get_local_dir(_backend, name);
	aclobj->local_path = dir == NULL ? NULL :
{'fraction_non_alphanumeric': 0.13165905631659056, 'fraction_numerical': 0.008588823657316808, 'mean_word_length': 4.710117939168218, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1348327', 'n_tokens_mistral': 3899, 'n_tokens_neox': 3622, 'n_words': 744}