#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
#if HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#if HAVE_WINSOCK_H
# include <winsock.h>
#else
# include <sys/socket.h>
# include <net/if.h>
#endif
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif

#include "asn1.h"
#include "context.h"
#include "system.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "snmp.h"
#include "mib.h"

#define TRUE 1
#define FALSE 0

#define IDENTITY_STATE  1
#define VIEW_STATE	2
#define PROXY_STATE  	3

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

int
read_context_database(char *filename)
{
    FILE *fp;
    char buf[256], buf1[256], buf2[256], buf3[256];
    char *cp;
    int blank;
    int linenumber = 0, chars = 0;
    int state = IDENTITY_STATE;
    oid contextid[MAX_OID_LEN];
    int contextidlen;
    int view = 0, entityLen = 0, icltime = 0;
    u_char entity[64];
    int dstParty, srcParty, proxyIdLen;
    oid proxyId[MAX_OID_LEN];
    char name[64];	/* friendly name */
    struct contextEntry *cxp, *rp;
    u_int myaddr;
    int diff;

    fp = fopen(filename, "r");
    if (fp == NULL)
	return -1;
    while (fgets(buf, sizeof(buf), fp)){
	linenumber++;
	if (strlen(buf) > 250) {
	    error_exit("Line longer than 250 bytes", linenumber, filename);
	    fclose(fp);
	    return -1;
	}
	chars += strlen(buf);
	if (buf[0] == '#')
	    continue;
	blank = TRUE;
	for(cp = buf; *cp; cp++)
	    if (!isspace(*cp)){
		blank = FALSE;
		break;
	    }
	if (blank)
	    continue;
	switch(state){
	  case IDENTITY_STATE:
	    if (sscanf(buf, "%s %s", name, buf1) != 2) {
		error_exit("Bad parse", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    contextidlen = MAX_OID_LEN;
	    if (!read_objid(buf1, contextid, &contextidlen)) {
		error_exit("Bad object identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    state = VIEW_STATE;
	    break;
	  case VIEW_STATE:
	    if (sscanf(buf, "%s %s %s", buf1, entity, buf3) != 3) {
		error_exit("Bad parse", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    for(cp = buf1; *cp; cp++)
		if (!isdigit(*cp)) {
		    error_exit("Not a view index", linenumber, filename);
		    fclose(fp);
		    return -1;
		}
	    view = atoi(buf1);
	    if (!strcasecmp((char *)entity, "Null"))
		entityLen = 0;
	    else
		entityLen = strlen((char *)entity);
	    if (!strcasecmp(buf3, "currentTime"))
		icltime = CURRENTTIME;
	    else if (!strcasecmp(buf3, "restartTime"))
		icltime = RESTARTTIME;
	    else {
		error_exit("Bad local time", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    state = PROXY_STATE;
	    break;
	  case PROXY_STATE:
	    if (sscanf(buf, "%s %s %s", buf1, buf2, buf3) != 3) {
		error_exit("Bad parse", linenumber, filename);
		fclose(fp);
		return -1;
	    }
	    for(cp = buf1; *cp; cp++)
		if (!isdigit(*cp)) {
		    error_exit("Bad destination party index", linenumber,
			       filename);
		    fclose(fp);
		    return -1;
		}
	    dstParty = atoi(buf1);

	    for(cp = buf1; *cp; cp++)
		if (!isdigit(*cp)) {
		    error_exit("Bad source party index", linenumber, filename);
		    fclose(fp);
		    return -1;
		}
	    srcParty = atoi(buf2);

	    proxyIdLen = MAX_OID_LEN;
	    if (!read_objid(buf3, proxyId, &proxyIdLen)) {
		error_exit("Bad object identifier", linenumber, filename);
		fclose(fp);
		return -1;
	    }

	    state = IDENTITY_STATE;

	    cxp = context_getEntry(contextid, contextidlen);
	    if (!cxp)
		cxp = context_createEntry(contextid, contextidlen);
	    rp = cxp->reserved;
	    strcpy(cxp->contextName, name);
	    myaddr = get_myaddr();
	    /* XXX It's bogus to figure out if it is local
	       by testing the ipaddress in the context - fix this XXX */
	    diff  = ((myaddr & 0xFF000000) >> 24) ^ contextid[9];
	    diff |= ((myaddr & 0x00FF0000) >> 16) ^ contextid[10];
	    diff |= ((myaddr & 0x0000FF00) >> 8) ^ contextid[11];
	    diff |= (myaddr & 0x000000FF) ^ contextid[12];
	    if (!diff){
		/* context is local */
		cxp->contextLocal = 1; /* TRUE */
	    } else {
		cxp->contextLocal = 2; /* FALSE */
	    }
	    cxp->contextViewIndex = view;
	    memmove(cxp->contextLocalEntity, entity, entityLen);
	    cxp->contextLocalEntityLen = entityLen;
	    cxp->contextLocalTime = icltime;
	    cxp->contextDstPartyIndex = dstParty;
	    cxp->contextSrcPartyIndex = srcParty;
	    memmove(cxp->contextProxyContext, proxyId, proxyIdLen * sizeof(oid));
	    cxp->contextProxyContextLen = proxyIdLen;
	    cxp->contextStorageType = SNMP_STORAGE_VOLATILE;
	    cxp->contextStatus = rp->contextStatus = SNMP_ROW_ACTIVE;
#define CONTEXTCOMPLETE_MASK              0x03FF
	    /* all collumns - from context_vars.c XXX */
	    cxp->contextBitMask = rp->contextBitMask = CONTEXTCOMPLETE_MASK;
	    break;
	  default:
	    error_exit("unknown state", linenumber, filename);
	    fclose(fp);
	    return -1;
	}
    }
    if (state != IDENTITY_STATE) {
	error_exit("Unfinished entry at EOF", linenumber, filename);
	fclose(fp);
	return -1;
    }
    fclose(fp);
    return 0;
}
