#include <config.h>

#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#if TIME_WITH_SYS_TIME
# ifdef WIN32
#  include <sys/timeb.h>
# else
#  include <sys/time.h>
# endif
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_WINSOCK_H
#include <winsock.h>
#endif

#include "asn1.h"
#include "acl.h"
#include "party.h"
#include "context.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "snmp.h"
#include "mib.h"

#define TRUE 1
#define FALSE 0

static void error_exit __P((char *, int, char *));

static void error_exit(str, linenumber, filename)
    char *str;
    int linenumber;
    char *filename;
{
  char tmpbuf[1024];
  snmp_errno = SNMPERR_BAD_ACL;
  sprintf(tmpbuf, "%s on line %d of %s", str, linenumber, filename);
  snmp_set_detail(tmpbuf);
}

int
read_acl_database(filename)
    char *filename;
{
    FILE *fp;
    char buf[256], buf1[256], buf2[256], buf3[256], buf4[256];
    char *cp;
    int blank;
    int linenumber = 0;
    oid targetParty[64], subjectParty[64], resources[64];
    int targetPartyLen, subjectPartyLen, resourcesLen;
    int priveleges;
    struct aclEntry *ap;
    struct partyEntry *pp;
    struct contextEntry *cxp;
    int subject, target, res;

    fp = fopen(filename, "r");
    if (fp == NULL)
	return -1;
    while (fgets(buf, 256, fp)){
	linenumber++;
	if (strlen(buf) > 250) {
	    error_exit("Line longer than 250 bytes", linenumber, filename);
	    fclose(fp);
	    return -1;
	}
	if (buf[0] == '#')
	    continue;
	blank = TRUE;
	for(cp = buf; *cp; cp++)
	    if (!isspace(*cp)){
		blank = FALSE;
		break;
	    }
	if (blank)
	    continue;

	if (sscanf(buf, "%s %s %s %s", buf1, buf2, buf3, buf4) != 4) {
	    error_exit("Bad parse", linenumber, filename);
	    fclose(fp);
	    return -1;
	}
	party_scanInit();
	for(pp = party_scanNext(); pp; pp = party_scanNext()){
	    if (!strcasecmp(pp->partyName, buf1)){
		break;
	    }
	}
	if (!pp){
	    targetPartyLen = 64;
	    if (!read_objid(buf1, targetParty, &targetPartyLen)) {
		error_exit("Bad target object identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    
	    pp = party_getEntry(targetParty, targetPartyLen);
	    if (!pp) {
		error_exit("Unknown target party identifier",
			   linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    /* why do I have subject and target mixed up here? */
	}
	subject = pp->partyIndex;

	party_scanInit();
	for(pp = party_scanNext(); pp; pp = party_scanNext()){
	    if (!strcasecmp(pp->partyName, buf2)){
		break;
	    }
	}
	if (!pp){
	    subjectPartyLen = 64;
	    if (!read_objid(buf2, subjectParty, &subjectPartyLen)) {
		error_exit("Bad subject object identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    
	    
	    pp = party_getEntry(subjectParty, subjectPartyLen);
	    if (!pp) {
		error_exit("Unknown subject party identifier",
			   linenumber, filename);
		fclose(fp);
		return -1;
	    }
	}
	target = pp->partyIndex;
	
	context_scanInit();
	for(cxp = context_scanNext(); cxp; cxp = context_scanNext()){
	    if (!strcasecmp(cxp->contextName, buf3)){
		break;
	    }
	}
	if (!cxp){
	    resourcesLen = 64;
	    if (!read_objid(buf3, resources, &resourcesLen)) {
		error_exit("Bad context object identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    
	    cxp = context_getEntry(resources, resourcesLen);
	    if (!cxp) {
		error_exit("Unknown context identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	}
	res = cxp->contextIndex;

	priveleges = 0;
	for(cp = buf4; *cp; cp++){
	    switch(*cp){
	      case 'g':
	      case 'G':
		priveleges |= ACLPRIVELEGESGET;
		break;
	      case 'n':
	      case 'N':
		priveleges |= ACLPRIVELEGESGETNEXT;
		break;
	      case 'r':
	      case 'R':
		priveleges |= ACLPRIVELEGESGETRESPONSE;
		break;
	      case 's':
	      case 'S':
		priveleges |= ACLPRIVELEGESSET;
		break;
#if 0
	      case 't':
	      case 'T':
		priveleges |= ACLPRIVELEGESTRAP;
		break;
#endif
	      case 'b':
	      case 'B':
		priveleges |= ACLPRIVELEGESBULK;
		break;
	      case 'i':
	      case 'I':
		priveleges |= ACLPRIVELEGESINFORM;
		break;
	      case 'u':	/* find a better letter XXXXXXXXX */
	      case 'U':
		priveleges |= ACLPRIVELEGESTRAP2;
		break;
	      default:
		error_exit("Bad priveleges code", linenumber, filename);
		fclose(fp);
		return -1;
		break;
	    }
	}

	ap = acl_getEntry(target, subject, res);
	if (!ap)
	    ap = acl_createEntry(target, subject, res);
	ap->aclPriveleges = priveleges;
	ap->aclStorageType = SNMP_STORAGE_VOLATILE;
	ap->aclStatus = SNMP_ROW_ACTIVE;
#define ACLCOMPLETE_MASK              0x3F
	/* all collumns - from acl_vars.c XXX */
	ap->aclBitMask = ACLCOMPLETE_MASK;
	ap->reserved->aclBitMask = ap->aclBitMask;
    }
    fclose(fp);
    return 0;
}

