/*
 * Copyright (C) 2002 Roaring Penguin Software Inc.
 *
 * Copyright (C) 1995,1996,1997 Lars Fenneberg
 *
 * Copyright 1992 Livingston Enterprises, Inc.
 *
 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
 * and Merit Network, Inc. All Rights Reserved
 *
 * See the file COPYRIGHT for the respective terms and conditions.
 * If the file is missing contact me at lf@elemental.net
 * and I'll send you a copy.
 *
 */

#include <includes.h>
#include <radiusclient.h>

static DICT_ATTR *dictionary_attributes = NULL;
static DICT_VALUE *dictionary_values = NULL;
static VENDOR_DICT *vendor_dictionaries = NULL;

/*
 * Function: rc_read_dictionary
 *
 * Purpose: Initialize the dictionary.  Read all ATTRIBUTES into
 *	    the dictionary_attributes list.  Read all VALUES into
 *	    the dictionary_values list.  Construct VENDOR dictionaries
 *          as required.
 *
 */

int rc_read_dictionary (char *filename)
{
	FILE           *dictfd;
	char            dummystr[AUTH_ID_LEN];
	char            namestr[AUTH_ID_LEN];
	char            valstr[AUTH_ID_LEN];
	char            attrstr[AUTH_ID_LEN];
	char            typestr[AUTH_ID_LEN];
	char            vendorstr[AUTH_ID_LEN];
	int             line_no;
	DICT_ATTR      *attr;
	DICT_VALUE     *dval;
	VENDOR_DICT    *vdict;
	char            buffer[256];
	int             value;
	int             type;
	int             n;
	int             retcode;
	if ((dictfd = fopen (filename, "r")) == (FILE *) NULL)
	{
		error( "rc_read_dictionary: couldn't open dictionary %s: %s",
				filename, strerror(errno));
		return (-1);
	}

	line_no = 0;
	retcode = 0;
	while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL)
	{
		line_no++;

		/* Skip empty space */
		if (*buffer == '#' || *buffer == '\0' || *buffer == '\n')
		{
			continue;
		}

		if (strncmp (buffer, "VENDOR", 6) == 0) {
		    /* Read the VENDOR line */
		    if (sscanf(buffer, "%s%s%d", dummystr, namestr, &value) != 3) {
			error("rc_read_dictionary: invalid vendor on line %d of dictionary %s",
			      line_no, filename);
			retcode = -1;
			break;
		    }
		    /* Validate entry */
		    if (strlen (namestr) > NAME_LENGTH) {
			error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
			      line_no, filename);
			retcode = -1;
			break;
		    }
		    /* Create new vendor entry */
		    vdict = (VENDOR_DICT *) malloc (sizeof (VENDOR_DICT));
		    if (!vdict) {
			novm("rc_read_dictionary");
			retcode = -1;
			break;
		    }
		    strcpy(vdict->vendorname, namestr);
		    vdict->vendorcode = value;
		    vdict->attributes = NULL;
		    vdict->next = vendor_dictionaries;
		    vendor_dictionaries = vdict;
		}
		else if (strncmp (buffer, "ATTRIBUTE", 9) == 0)
		{

			/* Read the ATTRIBUTE line.  It is one of:
			 * ATTRIBUTE attr_name attr_val type         OR
			 * ATTRIBUTE attr_name attr_val type vendor  */
			vendorstr[0] = 0;
			n = sscanf(buffer, "%s%s%s%s%s", dummystr, namestr, valstr, typestr, vendorstr);
			if (n != 4 && n != 5)
			{
				error("rc_read_dictionary: invalid attribute on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			/*
			 * Validate all entries
			 */
			if (strlen (namestr) > NAME_LENGTH)
			{
				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			if (strlen (vendorstr) > NAME_LENGTH)
			{
				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			if (!isdigit (*valstr))
			{
				error("rc_read_dictionary: invalid value on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}
			value = atoi (valstr);

			if (strcmp (typestr, "string") == 0)
			{
				type = PW_TYPE_STRING;
			}
			else if (strcmp (typestr, "integer") == 0)
			{
				type = PW_TYPE_INTEGER;
			}
			else if (strcmp (typestr, "ipaddr") == 0 || strcmp (typestr, "ipv4addr") == 0)
			{
				type = PW_TYPE_IPADDR;
			}
			else if (strcmp (typestr, "date") == 0)
			{
				type = PW_TYPE_DATE;
			}
			else if (strcmp (typestr, "ifid") == 0)
			{
				type = PW_TYPE_IFID;
			}
			else if (strcmp (typestr, "ipv6addr") == 0)
			{
				type = PW_TYPE_IPV6ADDR;
			}
			else if (strcmp (typestr, "ipv6prefix") == 0)
			{
				type = PW_TYPE_IPV6PREFIX;
			}
			else
			{
				error("rc_read_dictionary: invalid type on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			/* Search for vendor if supplied */
			if (*vendorstr) {
			    vdict = rc_dict_findvendor(vendorstr);
			    if (!vdict) {
				    error("rc_read_dictionary: unknown vendor on line %d of dictionary %s",
					  line_no, filename);
				    retcode = -1;
				    break;
			    }
			} else {
			    vdict = NULL;
			}
			/* Create a new attribute for the list */
			if ((attr =
				(DICT_ATTR *) malloc (sizeof (DICT_ATTR)))
							== (DICT_ATTR *) NULL)
			{
				novm("rc_read_dictionary");
				retcode = -1;
				break;
			}
			strcpy (attr->name, namestr);
			if (vdict) {
			    attr->vendorcode = vdict->vendorcode;
			} else {
			    attr->vendorcode = VENDOR_NONE;
			}
			attr->value = value;
			attr->type = type;

			/* Insert it into the list */
			if (vdict) {
			    attr->next = vdict->attributes;
			    vdict->attributes = attr;
			} else {
			    attr->next = dictionary_attributes;
			    dictionary_attributes = attr;
			}
		}
		else if (strncmp (buffer, "VALUE", 5) == 0)
		{
			/* Read the VALUE line */
			if (sscanf (buffer, "%s%s%s%s", dummystr, attrstr,
				    namestr, valstr) != 4)
			{
				error("rc_read_dictionary: invalid value entry on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			/*
			 * Validate all entries
			 */
			if (strlen (attrstr) > NAME_LENGTH)
			{
				error("rc_read_dictionary: invalid attribute length on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			if (strlen (namestr) > NAME_LENGTH)
			{
				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}

			if (!isdigit (*valstr))
			{
				error("rc_read_dictionary: invalid value on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}
			value = atoi (valstr);

			/* Create a new VALUE entry for the list */
			if ((dval =
				(DICT_VALUE *) malloc (sizeof (DICT_VALUE)))
							== (DICT_VALUE *) NULL)
			{
				novm("rc_read_dictionary");
				retcode = -1;
				break;
			}
			strcpy (dval->attrname, attrstr);
			strcpy (dval->name, namestr);
			dval->value = value;

			/* Insert it into the list */
			dval->next = dictionary_values;
			dictionary_values = dval;
		}
		else if (strncmp (buffer, "INCLUDE", 7) == 0)
		{
			/* Read the INCLUDE line */
			if (sscanf (buffer, "%s%s", dummystr, namestr) != 2)
			{
				error("rc_read_dictionary: invalid include entry on line %d of dictionary %s",
				      line_no, filename);
				retcode = -1;
				break;
			}
			if (rc_read_dictionary(namestr) == -1)
			{
				retcode = -1;
				break;
			}
		}
	}
	fclose (dictfd);
	return retcode;
}

/*
 * Function: rc_dict_getattr
 *
 * Purpose: Return the full attribute structure based on the
 *	    attribute id number and vendor code.  If vendor code is VENDOR_NONE,
 *          non-vendor-specific attributes are used
 *
 */

DICT_ATTR *rc_dict_getattr (int attribute, int vendor)
{
	DICT_ATTR      *attr;
	VENDOR_DICT    *dict;

	if (vendor == VENDOR_NONE) {
	    attr = dictionary_attributes;
	    while (attr != (DICT_ATTR *) NULL) {
		if (attr->value == attribute) {
		    return (attr);
		}
		attr = attr->next;
	    }
	} else {
	    dict = rc_dict_getvendor(vendor);
	    if (!dict) {
		return NULL;
	    }
	    attr = dict->attributes;
	    while (attr) {
		if (attr->value == attribute) {
		    return attr;
		}
		attr = attr->next;
	    }
	}
	return NULL;
}

/*
 * Function: rc_dict_findattr
 *
 * Purpose: Return the full attribute structure based on the
 *	    attribute name.
 *
 */

DICT_ATTR *rc_dict_findattr (char *attrname)
{
	DICT_ATTR      *attr;
	VENDOR_DICT    *dict;

	attr = dictionary_attributes;
	while (attr != (DICT_ATTR *) NULL)
	{
		if (strcasecmp (attr->name, attrname) == 0)
		{
			return (attr);
		}
		attr = attr->next;
	}

	/* Search vendor-specific dictionaries */
	dict = vendor_dictionaries;
	while (dict) {
	    attr = dict->attributes;
	    while (attr) {
		if (strcasecmp (attr->name, attrname) == 0) {
		    return (attr);
		}
		attr = attr->next;
	    }
	    dict = dict->next;
	}
	return ((DICT_ATTR *) NULL);
}


/*
 * Function: rc_dict_findval
 *
 * Purpose: Return the full value structure based on the
 *         value name.
 *
 */

DICT_VALUE *rc_dict_findval (char *valname)
{
	DICT_VALUE     *val;

	val = dictionary_values;
	while (val != (DICT_VALUE *) NULL)
	{
		if (strcasecmp (val->name, valname) == 0)
		{
			return (val);
		}
		val = val->next;
	}
	return ((DICT_VALUE *) NULL);
}

/*
 * Function: dict_getval
 *
 * Purpose: Return the full value structure based on the
 *          actual value and the associated attribute name.
 *
 */

DICT_VALUE * rc_dict_getval (UINT4 value, char *attrname)
{
	DICT_VALUE     *val;

	val = dictionary_values;
	while (val != (DICT_VALUE *) NULL)
	{
		if (strcmp (val->attrname, attrname) == 0 &&
				val->value == value)
		{
			return (val);
		}
		val = val->next;
	}
	return ((DICT_VALUE *) NULL);
}

/*
 * Function: rc_dict_findvendor
 *
 * Purpose: Return the vendor's dictionary given the vendor name.
 *
 */
VENDOR_DICT * rc_dict_findvendor (char *vendorname)
{
    VENDOR_DICT *dict;

    dict = vendor_dictionaries;
    while (dict) {
	if (!strcmp(vendorname, dict->vendorname)) {
	    return dict;
	}
	dict = dict->next;
    }
    return NULL;
}

/*
 * Function: rc_dict_getvendor
 *
 * Purpose: Return the vendor's dictionary given the vendor ID
 *
 */
VENDOR_DICT * rc_dict_getvendor (int id)
{
    VENDOR_DICT *dict;

    dict = vendor_dictionaries;
    while (dict) {
	if (id == dict->vendorcode) {
	    return dict;
	}
	dict = dict->next;
    }
    return NULL;
}
