/*
	Copyright (c) 2010 by Dennis Schridde

	This file is part of dovecot-metadata.

	dovecot-metadata is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	dovecot-metadata is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with dovecot-metadata.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "metadata-entry-private.h"

#include <stdlib.h>
#include <string.h>

#include "mail-user.h"

#include "mailbox-ext.h"

static
const char *
entry_scopes[ENTRY_SCOPE_MAX+1] = {
	"private/", /* ENTRY_SCOPE_PRIVATE */
	"shared/", /* ENTRY_SCOPE_SHARED */
	NULL
};

static
enum metadata_entry_scope
parse_scope(const char *name) {
	if (name == NULL)
		return ENTRY_SCOPE_INVALID;

	/* scope must be empty or begin with '/' */
	if (name[0] != '/') {
		if (name[0] == '\0')
			return ENTRY_SCOPE_NONE;

		return ENTRY_SCOPE_INVALID;
	}

	/* skip '/' */
	name++;

	/* scope is the first component */
	for (int i = 0; i < ENTRY_SCOPE_MAX; i++) {
		if (strncasecmp(entry_scopes[i], name, strlen(entry_scopes[i])) == 0)
			return i;
	}

	return ENTRY_SCOPE_INVALID;
}

static
const char *
entry_types[ENTRY_TYPE_MAX+1] = {
	"vendor/", /* ENTRY_TYPE_VENDOR */
	"", /* ENTRY_TYPE_RFC */
	NULL
};

static
enum metadata_entry_type
parse_type(const char *name) {
	/* lazy evaluation of scope existance */
	if (name == NULL || *name++ != '/')
		return ENTRY_TYPE_INVALID;

	/* type is the second component */
	name = strchr(name, '/');
	if (name++ == NULL)
		return ENTRY_TYPE_NONE;

	for (int i = 0; i < ENTRY_TYPE_MAX; i++) {
		if (strncasecmp(entry_types[i], name, strlen(entry_types[i])) == 0)
			return i;
	}

	return ENTRY_TYPE_INVALID;
}

/* create entry on mailbox with name=value */
struct metadata_entry *
metadata_entry_alloc(struct mailbox *mailbox, const char *name, const char *value) {
	struct metadata_entry *entry = i_new(struct metadata_entry, 1);
	memset(entry, 0, sizeof(*entry));

	if (mailbox != NULL) {
		const char *mailbox_guid = mailbox_get_guid_string(mailbox);
		if (mailbox_guid != NULL)
			entry->mailbox_guid = strdup(mailbox_guid);

		struct mail_storage *storage = mailbox_get_storage(mailbox);
		struct mail_user *user = mail_storage_get_user(storage);
		const char *username = user->username;
		if (username != NULL)
			entry->username = strdup(username);
	}

	entry->scope = parse_scope(name);
	entry->type = parse_type(name);
	if (metadata_entry_is_valid(entry)) {
		if (name != NULL)
			entry->name = strdup(name);
		if (value != NULL)
			entry->value = strdup(value);
	}

	return entry;
}

/* free structures allocated for entry and invalidate it */
void
entry_free(struct metadata_entry *entry) {
	free((char*)entry->value);
	free((char*)entry->name);
	free((char*)entry->mailbox_guid);
	free((char*)entry->username);
	memset(entry, 0, sizeof(*entry));
}

bool
metadata_entry_is_valid(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return false;

	return entry->scope < ENTRY_SCOPE_MAX && entry->type < ENTRY_TYPE_MAX;
}

const char *
metadata_entry_get_name(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return NULL;

	return entry->name;
}

const char *
metadata_entry_get_value(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return NULL;

	return entry->value;
}

enum metadata_entry_subject
metadata_entry_get_subject(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return ENTRY_SUBJECT_INVALID;

	return entry->mailbox_guid ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER;
}

enum metadata_entry_scope
metadata_entry_get_scope(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return ENTRY_SCOPE_INVALID;

	return entry->scope;
}

enum metadata_entry_type
metadata_entry_get_type(struct metadata_entry *entry) {
	i_assert(entry != NULL);
	if (entry == NULL)
		return ENTRY_TYPE_INVALID;

	return entry->type;
}
