#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <dirent.h>

#include "xutil.h"
#include "lutil.h"
#include "ftn.h"
#include "config.h"
#include "nodelist.h"
#include "nlindex.h"

typedef struct
{
	long long int key;
	int pos;
	int next;
} KeyEntryType;

typedef struct
{
	unsigned char num;
	int next0;
	KeyEntryType entry[4];
} KeyIndexType;

#define KeyIndexSize sizeof(KeyIndexType)
typedef struct tagAddr4dType
{
	struct tagAddr4dType *next;
	short  Zone;
	short  Net;
	short  Node;
	short  Point;
} Addr4dType;

#define Addr4dSize sizeof(Addr4dType)
                                        
typedef struct
{
	unsigned char Type;
	Addr4dType ad;
	char *Box;
	char *Pos;
	char *Sysop;
	char *Phone;
	int Speed;
	char *Flags;
} NLentryType;

#define NLentrySize sizeof(NLentryType)

int keyentry(char *, NLentryType *);
int getentry(FILE *, int, NLentryType *);

int Str2Addr(char *s, Addr4dType *a)
{
	char *d1, *d2;
	int madeanything=0;
	a->Zone=a->Net=a->Node=a->Point=0;
	if ((d2=strchr(s,'@'))!=NULL) *d2=0;
	for (d1=s; *d1<'0' || *d1>'9'; d1++);
	if (strchr(d1,':')==NULL)
		a->Zone=0;
	else
	{
		madeanything=1;
		for (a->Zone=0; *d1!=':' && *d1>='0' && *d1<='9'; d1++)
			a->Zone=a->Zone*10+*d1-'0';
		if (*d1!=':')
			return 1;
		d1++;
	}
	if (strchr(d1,'/')==NULL)
		a->Net=0;
	else
	{
		madeanything=1;
		for (a->Net=0; *d1!='/' && *d1>='0' && *d1<='9'; d1++)
			a->Net=a->Net*10+*d1-'0';
		if (*d1!='/')
			return 1;
		d1++;
	}
	for (a->Node=0; *d1!='.' && *d1 && *d1>='0' && *d1<='9'; d1++)
		a->Node=a->Node*10+*d1-'0';
	if (*d1!='.' && *d1!=0)
		return 1;
	else
		madeanything=1;
	a->Point=0;
	if (*d1)
		for (d1++; *d1 && *d1>='0' && *d1<='9'; d1++)
			a->Point=a->Point*10+*d1-'0';
	if (d2) *d2='@';
	if (!madeanything) return 1;
	return 0;
}

node *getnlent(addr)
faddr *addr;
{
	static node nodebuf;
	static char buf[256],*p,*q;
	struct _ixentry xaddr;
	int j;
	int ixflag,stdflag;
	static NLentryType nlent;

	debug(20,"getnlent for %s",ascfnode(addr,0x1f));

	nodebuf.addr.domain=NULL;
	nodebuf.addr.zone=0;
	nodebuf.addr.net=0;
	nodebuf.addr.node=0;
	nodebuf.addr.point=0;
	nodebuf.addr.name=NULL;
	nodebuf.hub=0;
	nodebuf.type=0;
	nodebuf.pflag=0;
	nodebuf.name=NULL;
	nodebuf.location=NULL;
	nodebuf.sysop=NULL;
	nodebuf.phone=NULL;
	nodebuf.speed=0;
	nodebuf.flags=0L;
	nodebuf.uflags[0]=NULL;

	if (addr == NULL) goto retdummy;

	if (addr->zone == 0)
		addr->zone=whoami->addr->zone;
	xaddr.zone=addr->zone;
	nodebuf.addr.zone=addr->zone;
	xaddr.net=addr->net;
	nodebuf.addr.net=addr->net;
	xaddr.node=addr->node;
	nodebuf.addr.node=addr->node;
	xaddr.point=addr->point;
	nodebuf.addr.point=addr->point;

	sprintf(buf,"%u:%u/%u.%u",addr->zone,addr->net,addr->node,addr->point);
	if (keyentry(buf,&nlent))
	{
		debug(20,"found no nlentry");
		logerr("Address %s not found!",ascfnode(addr,0x1f));
		goto retdummy;
	}
	debug(20,"found a nlentry");

	nodebuf.type=pkey[nlent.Type].type;
	nodebuf.pflag=pkey[nlent.Type].pflag;

	nodebuf.name=nlent.Box;
	nodebuf.location=nlent.Pos;
	nodebuf.sysop=nlent.Sysop;
	nodebuf.phone=nlent.Phone;
	nodebuf.speed=nlent.Speed;
	ixflag=0;
	for (p=nlent.Flags;p;p=q)
	{
		if ((q=strchr(p,','))) *q++='\0';
		stdflag=0;
		for (j=0;fkey[j].key;j++)
			if (strcasecmp(p,fkey[j].key) == 0)
			{
				stdflag=1;
				nodebuf.flags|=fkey[j].flag;
			}
		if (!stdflag && (ixflag < MAXUFLAGS))
		{
			nodebuf.uflags[ixflag++]=p;
			if (ixflag < MAXUFLAGS) nodebuf.uflags[ixflag]=NULL;
		}
	}
	nodebuf.addr.name=nodebuf.sysop;
	nodebuf.addr.domain=addr->domain;
	nodebuf.hub=0;
	if (addr->domain == NULL) addr->domain=xstrcpy(nodebuf.addr.domain);

	debug(20,"getnlent: type		%d, pflag=%x",nodebuf.type,nodebuf.pflag);
	debug(20,"getnlent: name		%s",nodebuf.name);
	debug(20,"getnlent: location	%s",nodebuf.location);
	debug(20,"getnlent: sysop	%s",nodebuf.sysop);
	debug(20,"getnlent: phone	%s",nodebuf.phone);
	debug(20,"getnlent: speed	%u",nodebuf.speed);
	debug(20,"getnlent: flags	0x%lx",nodebuf.flags);
	for (j=0;nodebuf.uflags[j];j++)
		debug(20,"getnlent: uflag	%s",nodebuf.uflags[j]);

	return &nodebuf;

retdummy:
	debug(20,"getnlent returns dummy entry");
	nodebuf.type=0;
	nodebuf.pflag=NL_DUMMY;
	nodebuf.name="Unknown";
	nodebuf.location="Nowhere";
	nodebuf.sysop="Sysop";
	nodebuf.phone=NULL;
	nodebuf.speed=2400;
	nodebuf.flags=0L;
	nodebuf.uflags[0]=NULL;
	
	return &nodebuf;
}

int getentry(FILE *fl, int pos, NLentryType *e)
{
	char line[256], *d1, *d2;
	
	fseek(fl,pos,SEEK_SET);
	fgets(line,255,fl);
	d1=strchr(line,'\n');
	if (d1) *d1=0;
	switch (line[0])
	{
		case 'Z': e->Type=7; break;
		case 'R': e->Type=6; break;
		case 'H': e->Type=2; break;
		case 'U': e->Type=3; break;
		case 'V': e->Type=5; break;
		case 'O': e->Type=1; break;
		case 'D': e->Type=0; break;
		case 'N': e->Type=3; break; 		/*??*/
		case 'P': e->Type=4; break;
		default : e->Type=255; break;
	}
	d1=strchr(line,',');
	*d1++=0;
	Str2Addr(&line[1],&(e->ad));
	d2=strchr(d1,',');
	*d2++=0;
	e->Box=(char *)malloc(strlen(d1)+1);
	strcpy(e->Box,d1);
	d1=strchr(d2,',');
	*d1++=0;
	e->Pos=(char *)malloc(strlen(d2)+1);
	strcpy(e->Pos,d2);
	d2=strchr(d1,',');
	*d2++=0;
	e->Sysop=(char *)malloc(strlen(d1)+1);
	strcpy(e->Sysop,d1);
	d1=strchr(d2,',');
	*d1++=0;
	e->Phone=(char *)malloc(strlen(d2)+1);
	strcpy(e->Phone,d2);
	d2=strchr(d1,',');
	if (d2)
	{
		*d2++=0;
		e->Flags=(char *)malloc(strlen(d2)+1);
		strcpy(e->Flags,d2);
	} else e->Flags=NULL;
	e->Speed=atoi(d1);
	return 0;
}

long long int getkey(char *address)
{
	char *end;
	long long int i=0;
	int z;
	end=address;
	do
	{
		z = strtol(end, &end, 10);
		i = (i<<16)|z;
	} while (*end++);
	if (strchr(address, '.')==NULL) i<<=16;
	return i;
}

int keyentry(char *addr, NLentryType *e)
{
	FILE *fl, *fi;
	char file[PATH_MAX], *d1;
	int pos, prim, ok=0, j, q;
	long long int key;
	struct stat st;
	
	sprintf(file,"%sfnlc.Nodeindex",nlbase);
	stat(file,&st);
	prim=st.st_size/sizeof(int);
	if ((fi=fopen(file,"rb"))!=NULL)
	{
		sprintf(file,"%sfnlc.Userlist",nlbase);
		if ((fl=fopen(file,"rt"))!=NULL)
		{
			key=getkey(addr);
			j=(key%prim)+1;
			q=(key%79)+1;
			do
			{
				fseek(fi,(j-1)*sizeof(int),SEEK_SET);
				fread(&pos,sizeof(int),1,fi);
				if (pos==-1) break;
				fseek(fl,pos,SEEK_SET);
				fgets(file,255,fl);
				d1=strchr(file,',');
				*d1=0;
				j=((j+q)%prim)+1;
			} while (key!=getkey(&file[1]));
			if (pos!=-1)
				getentry(fl,pos,e);
			else
				ok=1;
			fclose(fl);
		} else
		{
			logerr("Can't open %sfnlc.Userlist",nlbase);
			ok=1;
		}
		fclose(fi);
	} else
	{
		logerr("Can't open %sfnlc.Nodelist",nlbase);
		ok=1;
	}
	return ok;
}
