/*****************************************************************************
 * 
 * $Id$
 * Purpose ...............: Read nodelists information
 *
 *****************************************************************************
 * Copyright (C) 1997-2002
 *   
 * Michiel Broek		FIDO:	2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "libs.h"
#include "memwatch.h"
#include "structs.h"
#include "users.h"
#include "records.h"
#include "clcomm.h"
#include "common.h"


#define NULLDOMAIN "nulldomain"


struct _pkey pkey[] = {
	{(char *)"Down",	NL_NODE,	NL_DOWN},
	{(char *)"Hold",	NL_NODE,	NL_HOLD},
	{(char *)"Region",	NL_REGION,	NL_REGION},
	{(char *)"Host",	NL_HOST,	NL_HOST},
	{(char *)"Hub",		NL_HUB,		NL_HUB},
	{(char *)"Point",	NL_POINT,	NL_POINT},
	{(char *)"Pvt",		NL_NODE,	NL_NODE},
	{NULL,			0,		0}
};



struct _okey okey[] = {
	{(char *)"CM",	OL_CM},
	{(char *)"MO",	OL_MO},
	{(char *)"LO",	OL_LO},
	{(char *)"MN",  OL_MN},
	{NULL, 0}
};

struct _fkey fkey[] = {
	{(char *)"V22",	NL_V22},
	{(char *)"V29",	NL_V29},
	{(char *)"V32",	NL_V32},
	{(char *)"V32B",NL_V32B | NL_V32},
	{(char *)"V34",	NL_V34},
	{(char *)"V42",	NL_V42  | NL_MNP},
	{(char *)"V42B",NL_V42B | NL_V42  | NL_MNP},
	{(char *)"MNP",	NL_MNP},
	{(char *)"H96",	NL_H96},
	{(char *)"HST",	NL_HST  | NL_MNP},
	{(char *)"H14",	NL_H14  | NL_HST  | NL_MNP},
	{(char *)"H16",	NL_H16  | NL_H14  | NL_HST | NL_MNP  | NL_V42 | NL_V42B},
	{(char *)"MAX",	NL_MAX},
	{(char *)"PEP",	NL_PEP},
	{(char *)"CSP",	NL_CSP},
	{(char *)"V32T",NL_V32T | NL_V32B | NL_V32},
	{(char *)"VFC", NL_VFC},
	{(char *)"ZYX",	NL_ZYX  | NL_V32B | NL_V32 | NL_V42B | NL_V42 | NL_MNP},
	{(char *)"X2C", NL_X2C  | NL_X2S  | NL_V34},
	{(char *)"X2S", NL_X2S  | NL_V34},
	{(char *)"V90C",NL_V90C | NL_V90S | NL_V34},
	{(char *)"V90S",NL_V90S | NL_V34},
	{(char *)"Z19", NL_Z19  | NL_V32B | NL_V32 | NL_V42B | NL_V42 | NL_MNP | NL_ZYX},
	{NULL, 0}
};

struct _xkey xkey [] = {
	{(char *)"XA",	RQ_XA},
	{(char *)"XB",	RQ_XB},
	{(char *)"XC",	RQ_XC},
	{(char *)"XP",	RQ_XP},
	{(char *)"XR",	RQ_XR},
	{(char *)"XW",	RQ_XW},
	{(char *)"XX",	RQ_XX},
	{NULL,	0}
};

struct _dkey dkey [] = {
	{(char *)"V110L", ND_V110L},
	{(char *)"V110H", ND_V110H},
	{(char *)"V120L", ND_V120L},
	{(char *)"V120H", ND_V120H},
	{(char *)"X75",   ND_X75},
	{NULL, 0}
};

struct _ikey ikey [] = {
	{(char *)"IBN", IP_IBN},
	{(char *)"IFC", IP_IFC},
	{(char *)"ITN", IP_ITN},
	{(char *)"IVM", IP_IVM},
	{(char *)"IP",  IP_IP},
	{(char *)"IFT", IP_IFT},
	{NULL, 0}
};



int initnl(void)
{
	int		rc = 0;
	FILE		*dbf, *fp;
	char		*filexnm, *path;
	struct _nlfil	fdx;

	filexnm = xstrcpy(CFG.nodelists);
	filexnm	= xstrcat(filexnm,(char *)"/node.files");

	if ((dbf = fopen(filexnm, "r")) == NULL) {
		WriteError("$Can't open %s", filexnm);
		rc = 101;
	} else {
		path = calloc(128, sizeof(char));

		while (fread(&fdx, sizeof(fdx), 1, dbf) == 1) {
			sprintf(path, "%s/%s", CFG.nodelists, fdx.filename);
			if ((fp = fopen(path, "r")) == NULL) {
				WriteError("$Can't open %s", path);
				rc = 101;
			} else {
				fclose(fp);
			}
		}

		fclose(dbf);
		free(path);
	}

	free(filexnm);
	return rc;
}



int comp_node(struct _nlidx, struct _ixentry);
int comp_node(struct _nlidx fap1, struct _ixentry fap2)
{
	if (fap1.zone != fap2.zone)
		return (fap1.zone - fap2.zone);
	else if (fap1.net != fap2.net)
		return (fap1.net - fap2.net);
	else if (fap1.node != fap2.node)
		return (fap1.node - fap2.node);
	else
		return (fap1.point - fap2.point);
}



node *getnlent(faddr *addr)
{
    FILE		    *fp, *np;
    static node		    nodebuf;
    static char		    buf[2048], ebuf[2048], *p, *q;
    struct _ixentry	    xaddr;
    int			    i, j, Found = FALSE, ixflag, stdflag, ndrecord = FALSE;
    char		    *mydomain, *path;
    struct _nlfil	    fdx;
    struct _nlidx	    ndx;
    long		    lowest, highest, current;
    struct _nodeshdr	    ndhdr;
    static struct _nodes    nd;

    Syslog('s', "getnlent: %s", ascfnode(addr,0xff));

    mydomain = xstrcpy(CFG.aka[0].domain);
    if (mydomain == NULL) 
	mydomain = (char *)NULLDOMAIN;

    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.upnet	= 0;
    nodebuf.upnode      = 0;
    nodebuf.region      = 0;
    nodebuf.type        = 0;
    nodebuf.pflag       = 0;
    nodebuf.name        = NULL;
    nodebuf.location    = NULL;
    nodebuf.sysop       = NULL;
    nodebuf.phone       = NULL;
    nodebuf.speed       = 0;
    nodebuf.mflags      = 0L;
    nodebuf.oflags      = 0L;
    nodebuf.xflags      = 0L;
    nodebuf.iflags      = 0L;
    nodebuf.dflags      = 0L;
    nodebuf.uflags[0]   = NULL;
    nodebuf.t1	    = '\0';
    nodebuf.t2	    = '\0';

    if (addr == NULL) 
	goto retdummy;

    if (addr->zone == 0)
	addr->zone = CFG.aka[0].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;

    if (initnl())
	goto retdummy;

    /*
     *  First, lookup node in index. NOTE -- NOT 5D YET
     */
    path = calloc(PATH_MAX, sizeof(char));
    sprintf(path, "%s/%s", CFG.nodelists, "node.index");
    if ((fp = fopen(path, "r")) == NULL) {
	WriteError("$Can't open %s", path);
	free(path);
	goto retdummy;
    }

    fseek(fp, 0, SEEK_END);
    highest = ftell(fp) / sizeof(ndx);
    lowest = 0;

    while (TRUE) {
	current = ((highest - lowest) / 2) + lowest;
	fseek(fp, current * sizeof(ndx), SEEK_SET);
	if (fread(&ndx, sizeof(ndx), 1, fp) != 1)
	    break;

	if (comp_node(ndx, xaddr) == 0) {
	    Found = TRUE;
	    break;
	}
	if (comp_node(ndx, xaddr) < 0)
	    lowest = current;
	else
	    highest = current;
	if ((highest - lowest) <= 1)
	    break;
    }

    fclose(fp);

    if (!Found) {
	free(path);
	goto retdummy;
    }

    sprintf(path, "%s/%s", CFG.nodelists, "node.files");
    if ((fp = fopen(path, "r")) == NULL) {
	WriteError("$Can't open %s", path);
	free(path);
	goto retdummy;
    }

    /*
     *  Get filename from node.files
     */
    for (i = 0; i < (ndx.fileno +1); i++)
	fread(&fdx, sizeof(fdx), 1, fp);
    fclose(fp);

    /* CHECK DOMAIN HERE */

    /*
     *  Open and read in real nodelist
     */
    sprintf(path, "%s/%s", CFG.nodelists, fdx.filename);
    if ((fp = fopen(path, "r")) == NULL) {
	WriteError("$Can't open %s", path);
	free(path);
	goto retdummy;
    }

    if (fseek(fp, ndx.offset, SEEK_SET) != 0) {
	WriteError("$Seek failed for nodelist entry");
	fclose(fp);
	goto retdummy;
    }

    if (fgets(buf, sizeof(buf)-1, fp) == NULL) {
	WriteError("$fgets failed for nodelist entry");
	fclose(fp);
	goto retdummy;
    }

    /*
     * Load noderecord if this node has one, if there is one then
     * nodelist overrides in this record will be used instead of 
     * the nodelist entries.
     */
    sprintf(path, "%s/etc/nodes.data", getenv("MBSE_ROOT"));
    if ((np = fopen(path, "r")) != NULL) {
	fread(&ndhdr, sizeof(nodeshdr), 1, np);

	while (fread(&nd, ndhdr.recsize, 1, np) == 1) {
	    fseek(np, ndhdr.filegrp + ndhdr.mailgrp, SEEK_CUR);
	    for (i = 0; i < 20; i++) {
		if ((addr->zone == nd.Aka[i].zone) && (addr->net == nd.Aka[i].net) &&
		    (addr->node == nd.Aka[i].node) && (addr->point == nd.Aka[i].point)) {
		    ndrecord = TRUE;
		    Syslog('s', "getnlent: node record is present");
		    break;
		}
	    }
	    if (ndrecord)
		break;
	}

	fclose(np);
    }
    free(path);
    
    nodebuf.type = ndx.type;
    nodebuf.pflag = ndx.pflag;

    if (*(p = buf + strlen(buf) -1) == '\n') 
	*p = '\0';
    if (*(p = buf + strlen(buf) -1) == '\r') 
	*p = '\0';
    for (p = buf; *p; p++) 
	if (*p == '_') 
	    *p = ' ';

    p = buf;

    if ((q = strchr(p,','))) 
	*q++ = '\0';

    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    /*
     * Get system name
     */
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    if (ndrecord && strlen(nd.Nl_hostname)) {
	Syslog('s', "getnlent: system name override with %s", nd.Nl_hostname);
	nodebuf.name = nd.Nl_hostname;
    } else
	nodebuf.name = p;
    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    /*
     * Get location
     */
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    nodebuf.location = p;
    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    /*
     * Get sysop name
     */
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    nodebuf.sysop = p;
    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    /*
     * Get phone number
     */
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    if (strcasecmp(p, "-Unpublished-") == 0)
	nodebuf.phone = NULL;
    else
	nodebuf.phone = p;
    p = q;
    if (p == NULL) {
	fclose(fp);
	goto badsyntax;
    }
    
    /*
     * Get modem speed
     */
    if ((q=strchr(p,','))) 
	*q++ = '\0';
    nodebuf.speed = atoi(p);

    /*
     * Process the nodelist flags.
     */
    if (ndrecord && strlen(nd.Nl_flags)) {
	Syslog('s', "getnlent: flags override %s", nd.Nl_flags);
	q = nd.Nl_flags;
    }
    ixflag = 0;
    stdflag = TRUE;
    for (p = q; p; p = q) {
	if ((q = strchr(p, ','))) 
	    *q++ = '\0';
	if ((strncasecmp(p, "U", 1) == 0) && (strlen(p) == 1)) {
	    stdflag = FALSE;
	} else {
	    /*
	     * Process authorized flags and user flags both as authorized.
	     */
	    for (j = 0; fkey[j].key; j++)
		if (strcasecmp(p, fkey[j].key) == 0)
		    nodebuf.mflags |= fkey[j].flag;
	    for (j = 0; okey[j].key; j++)
		if (strcasecmp(p, okey[j].key) == 0) 
		    nodebuf.oflags |= okey[j].flag;
	    for (j = 0; dkey[j].key; j++)
		if (strcasecmp(p, dkey[j].key) == 0)
		    nodebuf.dflags |= dkey[j].flag;
	    for (j = 0; ikey[j].key; j++)
		if (strncasecmp(p, ikey[j].key, strlen(ikey[j].key)) == 0)
		    nodebuf.iflags |= ikey[j].flag;
	    for (j = 0; xkey[j].key; j++)
		if (strcasecmp(p, xkey[j].key) == 0)
		    nodebuf.xflags |= xkey[j].flag;
	    if ((p[0] == 'T') && (strlen(p) == 3)) {
		/*
		 * System open hours flag
		 */
		nodebuf.t1 = p[1];
		nodebuf.t2 = p[2];
	    }
	    if (!stdflag) {
		if (ixflag < MAXUFLAGS) {
		    nodebuf.uflags[ixflag++] = p;
		    if (ixflag < MAXUFLAGS) 
			nodebuf.uflags[ixflag] = NULL;
		}
	    }
	}
    }

    /*
     * Now we read the next line from the nodelist and see if this
     * is a ESLF (Extended St. Louis Format) line. This is for test
     * and nothing is defined yet. For now, debug logging only.
     */
    while (TRUE) {
	if (fgets(ebuf, sizeof(ebuf)-1, fp) == NULL) {
	    WriteError("$fgets failed for nodelist entry");
	    break;
	}
	/*
	 * Linse starting with ;E space are real errors.
	 */
	if (strncmp(ebuf, (char *)";E ", 3) == 0)
	    break;
	if (strncmp(ebuf, (char *)";E", 2))
	    break;
	Syslog('s', "ESLF: \"%s\"", printable(ebuf, 0));
    }
    fclose(fp);

    nodebuf.addr.name = nodebuf.sysop;
    nodebuf.addr.domain = xstrcpy(fdx.domain);
    nodebuf.upnet  = ndx.upnet;
    nodebuf.upnode = ndx.upnode;
    nodebuf.region = ndx.region;
    if (addr->domain == NULL) 
	addr->domain = xstrcpy(nodebuf.addr.domain);

    Syslog('s', "getnlent: system	%s, %s", nodebuf.name, nodebuf.location);
    Syslog('s', "getnlent: sysop	%s, %s", nodebuf.sysop, nodebuf.phone);
    moflags(nodebuf.mflags);
    diflags(nodebuf.dflags);
    ipflags(nodebuf.iflags);
    olflags(nodebuf.oflags);
    rqflags(nodebuf.xflags);
    free(mydomain);

    return &nodebuf;

badsyntax:
    WriteError("nodelist %d offset +%lu: bad syntax in line \"%s\"", ndx.fileno, (unsigned long)ndx.offset, buf);
    /* fallthrough */

retdummy:
    memset(&nodebuf, 0, sizeof(nodebuf));
    nodebuf.pflag = NL_DUMMY;
    nodebuf.name = (char *)"Unknown";
    nodebuf.location = (char *)"Nowhere";
    nodebuf.sysop = (char *)"Sysop";
    nodebuf.phone = NULL;
    nodebuf.speed = 2400;
    free(mydomain);

    return &nodebuf;
}



void olflags(unsigned long flags)
{
    char    *t;

    t = xstrcpy((char *)"Mailer flags :");
    if (flags & OL_CM)
	t = xstrcat(t, (char *)" CM");
    if (flags & OL_MO)
	t = xstrcat(t, (char *)" MO");
    if (flags & OL_LO)
	t = xstrcat(t, (char *)" LO");
    if (flags & OL_MN)
	t = xstrcat(t, (char *)" MN");
    Syslog('s', "%s", t);
    free(t);
}



void rqflags(unsigned long flags)
{
    char    *t;

    t = xstrcpy((char *)"Request flags:");
    if (flags & RQ_RQ_BR)
	t = xstrcat(t, (char *)" RQ_BR");
    if (flags & RQ_RQ_BU)
	t = xstrcat(t, (char *)" RQ_BU");
    if (flags & RQ_RQ_WR)
	t = xstrcat(t, (char *)" RQ_WR");
    if (flags & RQ_RQ_WU)
	t = xstrcat(t, (char *)" RQ_WU");
    Syslog('s', "%s", t);
    free(t);
}



void moflags(unsigned long flags)
{
    char    *t;

    if (!flags)
	return;
    t = xstrcpy((char *)"Modem flags  :");
    if (flags & NL_V22)
	t = xstrcat(t, (char *)" V22");
    if (flags & NL_V29)
	t = xstrcat(t, (char *)" V29");
    if (flags & NL_V32)
	t = xstrcat(t, (char *)" V32");
    if (flags & NL_V32B)
	t = xstrcat(t, (char *)" V32B");
    if (flags & NL_V34)
	t = xstrcat(t, (char *)" V34");
    if (flags & NL_V42)
	t = xstrcat(t, (char *)" V42");
    if (flags & NL_V42B)
	t = xstrcat(t, (char *)" V42B");
    if (flags & NL_MNP)
	t = xstrcat(t, (char *)" MNP");
    if (flags & NL_H96)
	t = xstrcat(t, (char *)" H96");
    if (flags & NL_HST)
	t = xstrcat(t, (char *)" HST");
    if (flags & NL_H14)
	t = xstrcat(t, (char *)" H14");
    if (flags & NL_H16)
	t = xstrcat(t, (char *)" H16");
    if (flags & NL_MAX)
	t = xstrcat(t, (char *)" MAX");
    if (flags & NL_PEP)
	t = xstrcat(t, (char *)" PEP");
    if (flags & NL_CSP)
	t = xstrcat(t, (char *)" CSP");
    if (flags & NL_V32T)
	t = xstrcat(t, (char *)" V32T");
    if (flags & NL_VFC)
	t = xstrcat(t, (char *)" VFC");
    if (flags & NL_ZYX)
	t = xstrcat(t, (char *)" ZYX");
    if (flags & NL_X2C)
	t = xstrcat(t, (char *)" X2C");
    if (flags & NL_X2S)
	t = xstrcat(t, (char *)" X2S");
    if (flags & NL_V90C)
	t = xstrcat(t, (char *)" V90C");
    if (flags & NL_V90S)
	t = xstrcat(t, (char *)" V90S");
    Syslog('s', "%s", t);
    free(t);
}



void diflags(unsigned long flags)
{
    char    *t;

    if (!flags)
	return;

    t = xstrcpy((char *)"ISDN flags   :");
    if (flags & ND_V110L)
	t = xstrcat(t, (char *)" V110L");
    if (flags & ND_V110H)
	t = xstrcat(t, (char *)" V110H");
    if (flags & ND_V120L)
	t = xstrcat(t, (char *)" V120L");
    if (flags & ND_V120H)
	t = xstrcat(t, (char *)" V120H");
    if (flags & ND_X75)
	t = xstrcat(t, (char *)" X75");
    Syslog('s', "%s", t);
    free(t);
}



void ipflags(unsigned long flags)
{
    char    *t;

    if (!flags)
	return;

    t = xstrcpy((char *)"TCP/IP flags :");
    if (flags & IP_IBN)
	t = xstrcat(t, (char *)" IBN");
    if (flags & IP_IFC)
	t = xstrcat(t, (char *)" IFC");
    if (flags & IP_ITN)
	t = xstrcat(t, (char *)" ITN");
    if (flags & IP_IVM)
	t = xstrcat(t, (char *)" IVM");
    if (flags & IP_IP)
	t = xstrcat(t, (char *)" IP");
    if (flags & IP_IFT)
	t = xstrcat(t, (char *)" IFT");
    Syslog('s', "%s", t);
    free(t);
}