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

#include "common.h"
#include "str.h"
#include "imap-quote.h"
#include "imap-resp-code.h"
#include "commands.h"
#include "mail-storage.h"
#include "mail-namespace.h"
#include "acl-api.h"
#include "acl-storage.h"
#include "imap-acl-plugin.h"

#include <stdlib.h>

#define ERROR_NOT_ADMIN "["IMAP_RESP_CODE_NOPERM"] " \
	"You lack administrator privileges on this mailbox."

#define ACL_MAILBOX_OPEN_FLAGS \
	(MAILBOX_OPEN_READONLY | MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT)

#define IMAP_ACL_ANYONE "anyone"
#define IMAP_ACL_AUTHENTICATED "authenticated"
#define IMAP_ACL_OWNER "owner"
#define IMAP_ACL_GROUP_PREFIX "$"
#define IMAP_ACL_GROUP_OVERRIDE_PREFIX "!$"
#define IMAP_ACL_GLOBAL_PREFIX "#"

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

static const struct imap_acl_letter_map imap_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 }
};

const char *imap_acl_plugin_version = PACKAGE_VERSION;

static bool acl_anyone_allow = FALSE;

static struct mailbox *
acl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name)
{
	struct mail_storage *storage;
	struct mailbox *box;
	int ret;

	storage = client_find_storage(cmd, &name);
	if (storage == NULL)
		return NULL;

	/* Force opening the mailbox so that we can give a nicer error message
	   if mailbox isn't selectable but is listable. */
	box = mailbox_open(&storage, name, NULL, ACL_MAILBOX_OPEN_FLAGS |
			   MAILBOX_OPEN_IGNORE_ACLS);
	if (box == NULL) {
		client_send_storage_error(cmd, storage);
		return NULL;
	}

	ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN);
	if (ret > 0)
		return box;

	/* not an administrator. */
	if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP) <= 0) {
		client_send_tagline(cmd, t_strdup_printf(
			"NO ["IMAP_RESP_CODE_NONEXISTENT"] "
			MAIL_ERRSTR_MAILBOX_NOT_FOUND, name));
	} else {
		client_send_tagline(cmd, "NO "ERROR_NOT_ADMIN);
	}
	mailbox_close(&box);
	return NULL;
}

static const struct imap_acl_letter_map *
imap_acl_letter_map_find(const char *name)
{
	unsigned int i;

	for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
		if (strcmp(imap_acl_letter_map[i].name, name) == 0)
			return &imap_acl_letter_map[i];
	}
	return NULL;
}

static void
imap_acl_write_rights_list(string_t *dest, const char *const *rights)
{
	const struct imap_acl_letter_map *map;
	unsigned int i;
	bool append_c = FALSE, append_d = FALSE;

	for (i = 0; rights[i] != NULL; i++) {
		/* write only letters */
		map = imap_acl_letter_map_find(rights[i]);
		if (map != NULL) {
			str_append_c(dest, map->letter);
			if (map->letter == 'k' || map->letter == 'x')
				append_c = TRUE;
			if (map->letter == 't' || map->letter == 'e')
				append_d = TRUE;
		}
	}
	if (append_c)
		str_append_c(dest, 'c');
	if (append_d)
		str_append_c(dest, 'd');
}

static void
imap_acl_write_right(string_t *dest, string_t *tmp,
		     const struct acl_rights *right, bool neg)
{
	const char *const *rights = neg ? right->neg_rights : right->rights;

	if (neg) str_append_c(dest,'-');

	str_truncate(tmp, 0);
	if (right->global)
		str_append(tmp, IMAP_ACL_GLOBAL_PREFIX);
	switch (right->id_type) {
	case ACL_ID_ANYONE:
		str_append(tmp, IMAP_ACL_ANYONE);
		break;
	case ACL_ID_AUTHENTICATED:
		str_append(tmp, IMAP_ACL_AUTHENTICATED);
		break;
	case ACL_ID_OWNER:
		str_append(tmp, IMAP_ACL_OWNER);
		break;
	case ACL_ID_USER:
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_GROUP:
		str_append(tmp, IMAP_ACL_GROUP_PREFIX);
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_GROUP_OVERRIDE:
		str_append(tmp, IMAP_ACL_GROUP_OVERRIDE_PREFIX);
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_TYPE_COUNT:
		i_unreached();
	}

	imap_quote_append(dest, str_data(tmp), str_len(tmp), FALSE);
	str_append_c(dest, ' ');
	imap_acl_write_rights_list(dest, rights);
}

static int imap_acl_write_aclobj(string_t *dest, struct acl_object *aclobj)
{
	struct acl_object_list_iter *iter;
	struct acl_rights rights;
	string_t *tmp;
	int ret;

	tmp = t_str_new(128);
	iter = acl_object_list_init(aclobj);
	while ((ret = acl_object_list_next(iter, &rights)) > 0) {
		str_append_c(dest, ' ');
		if (rights.rights != NULL)
			imap_acl_write_right(dest, tmp, &rights, FALSE);
		if (rights.neg_rights != NULL)
			imap_acl_write_right(dest, tmp, &rights, TRUE);
	}
	acl_object_list_deinit(&iter);<commit_msg>imap-acl: Fixed GETACL when listing both negative and postitive rights.<commit_after>/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */

#include "common.h"
#include "str.h"
#include "imap-quote.h"
#include "imap-resp-code.h"
#include "commands.h"
#include "mail-storage.h"
#include "mail-namespace.h"
#include "acl-api.h"
#include "acl-storage.h"
#include "imap-acl-plugin.h"

#include <stdlib.h>

#define ERROR_NOT_ADMIN "["IMAP_RESP_CODE_NOPERM"] " \
	"You lack administrator privileges on this mailbox."

#define ACL_MAILBOX_OPEN_FLAGS \
	(MAILBOX_OPEN_READONLY | MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT)

#define IMAP_ACL_ANYONE "anyone"
#define IMAP_ACL_AUTHENTICATED "authenticated"
#define IMAP_ACL_OWNER "owner"
#define IMAP_ACL_GROUP_PREFIX "$"
#define IMAP_ACL_GROUP_OVERRIDE_PREFIX "!$"
#define IMAP_ACL_GLOBAL_PREFIX "#"

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

static const struct imap_acl_letter_map imap_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 }
};

const char *imap_acl_plugin_version = PACKAGE_VERSION;

static bool acl_anyone_allow = FALSE;

static struct mailbox *
acl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name)
{
	struct mail_storage *storage;
	struct mailbox *box;
	int ret;

	storage = client_find_storage(cmd, &name);
	if (storage == NULL)
		return NULL;

	/* Force opening the mailbox so that we can give a nicer error message
	   if mailbox isn't selectable but is listable. */
	box = mailbox_open(&storage, name, NULL, ACL_MAILBOX_OPEN_FLAGS |
			   MAILBOX_OPEN_IGNORE_ACLS);
	if (box == NULL) {
		client_send_storage_error(cmd, storage);
		return NULL;
	}

	ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN);
	if (ret > 0)
		return box;

	/* not an administrator. */
	if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP) <= 0) {
		client_send_tagline(cmd, t_strdup_printf(
			"NO ["IMAP_RESP_CODE_NONEXISTENT"] "
			MAIL_ERRSTR_MAILBOX_NOT_FOUND, name));
	} else {
		client_send_tagline(cmd, "NO "ERROR_NOT_ADMIN);
	}
	mailbox_close(&box);
	return NULL;
}

static const struct imap_acl_letter_map *
imap_acl_letter_map_find(const char *name)
{
	unsigned int i;

	for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
		if (strcmp(imap_acl_letter_map[i].name, name) == 0)
			return &imap_acl_letter_map[i];
	}
	return NULL;
}

static void
imap_acl_write_rights_list(string_t *dest, const char *const *rights)
{
	const struct imap_acl_letter_map *map;
	unsigned int i;
	bool append_c = FALSE, append_d = FALSE;

	for (i = 0; rights[i] != NULL; i++) {
		/* write only letters */
		map = imap_acl_letter_map_find(rights[i]);
		if (map != NULL) {
			str_append_c(dest, map->letter);
			if (map->letter == 'k' || map->letter == 'x')
				append_c = TRUE;
			if (map->letter == 't' || map->letter == 'e')
				append_d = TRUE;
		}
	}
	if (append_c)
		str_append_c(dest, 'c');
	if (append_d)
		str_append_c(dest, 'd');
}

static void
imap_acl_write_right(string_t *dest, string_t *tmp,
		     const struct acl_rights *right, bool neg)
{
	const char *const *rights = neg ? right->neg_rights : right->rights;

	if (neg) str_append_c(dest,'-');

	str_truncate(tmp, 0);
	if (right->global)
		str_append(tmp, IMAP_ACL_GLOBAL_PREFIX);
	switch (right->id_type) {
	case ACL_ID_ANYONE:
		str_append(tmp, IMAP_ACL_ANYONE);
		break;
	case ACL_ID_AUTHENTICATED:
		str_append(tmp, IMAP_ACL_AUTHENTICATED);
		break;
	case ACL_ID_OWNER:
		str_append(tmp, IMAP_ACL_OWNER);
		break;
	case ACL_ID_USER:
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_GROUP:
		str_append(tmp, IMAP_ACL_GROUP_PREFIX);
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_GROUP_OVERRIDE:
		str_append(tmp, IMAP_ACL_GROUP_OVERRIDE_PREFIX);
		str_append(tmp, right->identifier);
		break;
	case ACL_ID_TYPE_COUNT:
		i_unreached();
	}

	imap_quote_append(dest, str_data(tmp), str_len(tmp), FALSE);
	str_append_c(dest, ' ');
	imap_acl_write_rights_list(dest, rights);
}

static int imap_acl_write_aclobj(string_t *dest, struct acl_object *aclobj)
{
	struct acl_object_list_iter *iter;
	struct acl_rights rights;
	string_t *tmp;
	int ret;

	tmp = t_str_new(128);
	iter = acl_object_list_init(aclobj);
	while ((ret = acl_object_list_next(iter, &rights)) > 0) {
		if (rights.rights != NULL) {
			str_append_c(dest, ' ');
			imap_acl_write_right(dest, tmp, &rights, FALSE);
		}
		if (rights.neg_rights != NULL) {
			str_append_c(dest, ' ');
			imap_acl_write_right(dest, tmp, &rights, TRUE);
		}
	}
	acl_object_list_deinit(&iter);
{'fraction_non_alphanumeric': 0.1302165045497333, 'fraction_numerical': 0.003974479656939651, 'mean_word_length': 5.180995475113122, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 33, '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': '960893', 'n_tokens_mistral': 4152, 'n_tokens_neox': 3903, 'n_words': 791}