/*	$CoreSDI: ia_list.c,v 1.22 2001/08/28 23:06:08 claudio Exp $	*/

/*
 *   Copyright (c) 2000, 2001, Core SDI S.A., Argentina
 *   All rights reserved
 *  
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *   3. Neither name of the Core SDI S.A. nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *  
 *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 *   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Information Accessing support functions 
 * Author: Claudio Castiglia
 */

#ifndef WIN32
#include <sys/param.h>
#endif

#ifdef __linux__
#include <netinet/in.h>		/* in_addr_t */
#endif

#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "sysdep.h"
#include "packet.h"
#include "resource.h"
#include "modtypes.h"
#include "modules.h"
#include "ia_list.h"
#include "ia_perm.h"
#include "iaargs.h"
#include "log.h"

#define WHITE_SPACE	" \f\n\r\t\v"

static ATTR *	_create_attr (ATTR *, ATTRCON *);
static void	_release_attr (ATTR *);
static int	_logset_set_name (LOGSET_INFO *, const char *);
static unsigned int _resperm2uint (const char *, ssize_t);
static void	_logset_set_perms (LOGSET_INFO *, RESLIST *, unsigned int *);
static int	_compare_cmd_line (CMD_LINE *, CMD_LINE *);
static void	_remove_raw_logset (RAW_LOGSET *);


/*
 * _create_attr():
 *	Create ATTR entry and add it into the list.
 *	on success return a pointer to the newly created entry,
 *	on error NULL is returned and errno set.
 */
static ATTR *
_create_attr(ATTR *prev, ATTRCON *context)
{
	ATTR	*attr;

	if (context == NULL)
		return (NULL);
	if ( (attr = (ATTR *) malloc(sizeof(ATTR))) == NULL)
		return (NULL);
	if (prev != NULL)
		prev->next = attr;
	attr->atcon = context;
	attr->next = NULL;
	return (attr);
}


/*
 * _release_attr():
 *	Release ATTR list.
 */
static void
_release_attr(ATTR *list)
{
	ATTR *i;

	if (list != NULL) {
		for (i = list; i != NULL; i = i->next)
			release_module((void *) i->atcon);
		free(list);
	}
}
	

/*
 * _logset_set_name():
 *	Add a name into the logset;
 *	return 0 on success or -1 on error and set errno.
 */
static int
_logset_set_name(LOGSET_INFO *list, const char *name)
{
	const char **tmp;

	if (name != NULL) {
		tmp = (const char **) realloc((void *) list->names_str,
		    sizeof(const char *) * (list->names + 1));
		if (tmp == NULL)
			return (-1);
		list->names_str = tmp;
		list->names_str[list->names++] = name;
	}
 	return (0);
}


/*
 * _resperm2uint():
 *	Convert a resource string indicating logset_info
 *	perms to unsigned int and return it.
 */
static unsigned int
_resperm2uint(const char *string, ssize_t size)
{
	unsigned int		perms, i;
	static struct {
		char		code;
		unsigned int	perm;
	} permcodes[PERM_IA_COMMANDS] = {
	    { 'L', PERM_LIST },
	    { 'I', PERM_INFO },
	    { 'G', PERM_GET },
	    { 'R', PERM_ROTATE },
	    { 'Z', PERM_ZAP },
	    { 'S', PERM_SIGN }
	};

	perms = 0;
	for (size--; size >= 0; size--)
		for (i = 0; i < PERM_IA_COMMANDS; i++)
			if (toupper(string[size]) == permcodes[i].code) {
				perms |= permcodes[i].perm;
				break;
			}
	return (perms);
}


/*
 * _logset_set_perms():
 *	Set logset permissions for current user.
 */
static void
_logset_set_perms(LOGSET_INFO *list, RESLIST *reslist,
		  unsigned int *generic_perms)
{
	int		 i;
	unsigned int	 perms;
	char		 resname[MAXPATHLEN];
	RESOURCE	*rperms;

	/* Get generic permissions */
	if (*generic_perms == PERM_INVALID) {
		if ( (rperms = res_find(reslist, IAPERMS_RNAME)) == NULL)
			*generic_perms = 0;
		else
			*generic_perms = _resperm2uint(rperms->data,
			    rperms->dsize);
	}

	/*
	 * Search permissions for all names for each
	 * logset and generate perms based on all of them.
	 */
	perms = PERM_INVALID;
	for (i = 0; i < list->names; i++) {
		snprintf(resname, sizeof(resname), IAPERMS_RNAME "_%s",
		    list->names_str[i]);
		if ( (rperms = res_find(reslist, resname)) != NULL) {
			if (perms == PERM_INVALID)
				perms = 0;
			perms |= _resperm2uint(rperms->data, rperms->dsize);
		}
	}

	/* Set permissions */
	list->perms = (perms == PERM_INVALID) ? *generic_perms : perms;
}


/*
 * _compare_cmd_line():
 *	Compare two cmd_line variables and return 0 if they are equals.
 *	NOTE: If both variables have identical data but differ in order,
 *	      they are equal.
 */
static int
_compare_cmd_line(CMD_LINE *a, CMD_LINE *b)
{
	int collisions, i, j;

	if (a->argc != b->argc)
		return (1);

	collisions = 0;
	for (i = 0; i < a->argc; i++)
		for (j = 0; j < b->argc; j++)
			if (!strcmp(a->argv[i], b->argv[j])) {
				collisions++;
				break;
			}

	return ((collisions == a->argc) ? 0 : 1);
}


/*
 * _remove_raw_logset():
 *	Remove and release a single RAW_LOGSET from the list.
 */
static void
_remove_raw_logset(RAW_LOGSET *rawset)
{
	if (rawset->prev != NULL)
		rawset->prev->next = rawset->next;
	if (rawset->next != NULL)
		rawset->next->prev = rawset->prev;
	rawset->prev = rawset->next = NULL;
	release_raw_logset(rawset);
}


/*
 * create_cmd_line():
 *	Create CMD_LINE entry and add it into the list.
 *	on success return a pointer to the newly created entry,
 *	on error NULL is returned and errno set.
 *	NOTE: double and simple quotes are detected (" and ').
 */
CMD_LINE *
create_cmd_line(CMD_LINE *prev, char *line)
{
	CMD_LINE *cmd;
	char	 *p, *w, **tmp, quote;

	if (line == NULL)
		return (NULL);
	if ( (cmd = (CMD_LINE *) malloc(sizeof(CMD_LINE))) == NULL)
		return (NULL);
	if ( (cmd->data = strdup(line)) == NULL) {
		free(cmd);
		return (NULL);
	}
	cmd->argc = 0;
	cmd->argv = NULL;
	cmd->next = NULL;
	p = cmd->data;
	while (p != NULL) {
		p += strspn(p, WHITE_SPACE);
		if (*p == '\0')
			break;
		w = p;
		if (*p == '\"' || *p == '\'') {
			quote = *p;
			p++;
			w++;
			for (; *p != quote && *p != '\0'; p++);
			if (*p == quote)
				*p++ = '\0';
		}
		tmp = (char **)
		    realloc(cmd->argv, sizeof(char **) * (cmd->argc + 1));
		if (tmp == NULL) {
			release_cmd_line(cmd);
			return (NULL);
		}
		cmd->argv = tmp;
		cmd->argv[cmd->argc++] = w;
		strsep(&p, WHITE_SPACE);
	}
	if (prev != NULL)
		prev->next = cmd;
	return (cmd);
}


/*
 * release_cmd_line():
 *	Release a CMD_LINE list.
 */
void
release_cmd_line(CMD_LINE *list)
{
	CMD_LINE *i;

	if (list != NULL) {
		for (i = list; i != NULL; i = i->next) {
			if (i->argv != NULL)
				free(i->argv);
			if (i->data != NULL)
				free(i->data);
		}
		free(list);
	}
}


/*
 * create_raw_logset():
 *	Create a RAW_LOGSET entry and add it into the list.
 *	on success return a pointer to the new entry,
 *	on error NULL is returned and errno set.
 */
RAW_LOGSET *
create_raw_logset(RAW_LOGSET *prev, CMD_LINE *cmd)
{
	RAW_LOGSET *rawset;

	if (cmd == NULL)
		return (NULL);
	if ( (rawset = (RAW_LOGSET *) calloc(1, sizeof(RAW_LOGSET))) == NULL)
		return (NULL);
	rawset->cmd = cmd;
	rawset->prev = prev;
	if (prev != NULL)
		prev->next = rawset;
	return (rawset);
}


/*
 * release_raw_logset():
 *	Release a RAW_LOGSET list.
 */
void
release_raw_logset(RAW_LOGSET *list)
{
	RAW_LOGSET *i;

	if (list != NULL) {
		for (i = list; i != NULL; i = i->next)
			release_cmd_line(i->cmd);
		free(list);
	}
}


/*
 * remove_raw_logset_dups():
 *	Remove duplicated data in the raw_logset list.
 */
void
remove_raw_logset_dups(RAW_LOGSET *rawlist)
{
	RAW_LOGSET *p, *n;

	if (rawlist != NULL)
		for (p = rawlist; p != NULL; p = p->next)
			for (n = p->next; n != NULL; n = n->next)
				if (!_compare_cmd_line(p->cmd, n->cmd)) {
					_remove_raw_logset(n);
					break;
				}
}


/*
 * create_logset_info():
 *	Create a LOGSET_INFO item and return it into 'ret_logset' pointer;
 *	return 0 on success or -1 on error and set errno.
 *	NOTE: If user has not permissions the item is not created
 *	      (this is not considered an error).
 */
int
create_logset_info(LOGSET_INFO *prev, RAW_LOGSET *rawdata, SESSION *s,
		   unsigned int *generic_perms, LOGSET_INFO **ret_logset)
{
	LOGSET_INFO		*litem;
	CMD_LINE		*cmd;
	ATTR			*attr;
	ATTRCON	    		*atcon;
	struct attrargs_init	 init_args;
	struct attrargs_ret	 proc_args;

	/* Create LOGSET_INFO item */
	if (rawdata == NULL || generic_perms == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if ( (litem = (LOGSET_INFO *) calloc(1, sizeof(LOGSET_INFO))) == NULL)
		return (-1);

	/* Load and initialize all ATTRIBUTE modules */
	attr = NULL;
	for (cmd = rawdata->cmd; cmd != NULL; cmd = cmd->next)
		if (cmd->argc > 0) {
			/* Load and initialize attribute module */
			init_args.argc = cmd->argc;
			init_args.argv = cmd->argv;
			atcon = load_attr(cmd->argv[0], s, &init_args);
			if (atcon == NULL) {
				release_logset_info(litem);
				return (-1);
			}

			/* Add it into the attribute list */
			if ( (attr = _create_attr(attr, atcon)) == NULL) {
				release_module((void *) atcon);
				release_logset_info(litem);
				return (-1);
			}
			if (litem->attr == NULL)
				litem->attr = attr;

			/* Get another name for this logset */
			atcon->mod.proc_entry(ATTR_GET_LOGSET_NAME, atcon,
			    &proc_args);
			if (proc_args.size != 0)
				if (_logset_set_name(litem, proc_args.data)
				    < 0) {
					release_logset_info(litem);
					return (-1);
			}

		}
	_logset_set_perms(litem, s->s_rlist, generic_perms);

	/* If user can't work with this logset, do not add it into the list */
	if (litem->perms == 0)
		release_logset_info(litem);
	else {
		if (prev != NULL)
			prev->next = litem;
		if (ret_logset != NULL)
			*ret_logset = litem;
	}

	return (0);
}


/*
 * release_logset_info():
 *	Release a LOGSET_INFO list.
 */
void
release_logset_info(LOGSET_INFO *list)
{
	LOGSET_INFO *i;

	if (list != NULL) {
		for (i = list; i != NULL; i = i->next) {
			if (i->names_str != NULL)
				free((void *) i->names_str);
			_release_attr(i->attr);
		}
		free(list);
	}
}


/*
 * find_logset_info():
 *	Search for a logset and return it or NULL if not found.
 */
LOGSET_INFO *
find_logset_info(LOGSET_INFO *list, const char *logset_name)
{
	LOGSET_INFO *l;
	int	     i;

	/* Scan all lists */
	for (l = list; l != NULL; l = l->next) {
		for (i = 0; i < l->names; i++)
			if (!strcmp(l->names_str[i], logset_name))
				break;
		if (i < l->names)
			break;
	}
	return (l);
}

