/*****************************************************************************
 * 
 * $Id$
 * Purpose ...............: Read nodelists information
 *
 *****************************************************************************
 * Copyright (C) 1997-2006
 *   
 * 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, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "../config.h"
#include "mbselib.h"
#include "nodelist.h"


#define NULLDOMAIN "nulldomain"


static char		*k, *v;
static char		*nlpath = NULL;
static int		nlinitdone = FALSE;
static int		linecnt = 0;
static unsigned int	mypots = 0, myisdn = 0, mytcpip =0;


static int		getkwd(char**);
static int		getmdm(char**);
static int		getarr(char**);
static int		getdom(char**);
static int		getsrv(char**);



/*
 * Table to parse the ~/etc/nodelist.conf file
 */
static struct _keytab {
    char    *key;
    int	    (*prc)(char **);
    char**  dest;
} keytab[] = {
    {(char *)"online",	    getkwd,	    (char **)&nl_online},
    {(char *)"request",	    getkwd,	    (char **)&nl_request},
    {(char *)"reqbits",	    getkwd,	    (char **)&nl_reqbits},
    {(char *)"pots",	    getmdm,	    (char **)&nl_pots},
    {(char *)"isdn",	    getmdm,	    (char **)&nl_isdn},
    {(char *)"tcpip",	    getmdm,	    (char **)&nl_tcpip},
    {(char *)"search",	    getarr,	    (char **)&nl_search},
    {(char *)"dialer",	    getarr,	    (char **)&nl_dialer},
    {(char *)"ipprefix",    getarr,	    (char **)&nl_ipprefix},
    {(char *)"domsuffix",   getdom,	    (char **)&nl_domsuffix},
    {(char *)"service",	    getsrv,	    (char **)&nl_service},
    {NULL,		    NULL,	    NULL}
};



/*
 * Get a keyword, string, unsigned int
 */
static int getkwd(char **dest)
{
    char	    *p;
    unsigned int    tmp;
    nodelist_flag   **tmpm;
    
    for (p = v; *p && !isspace(*p); p++);
    if (*p)
	*p++ = '\0';
    while (*p && isspace(*p))
	p++;
    if (*p == '\0') {
	WriteError("%s(%s): less then two tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (tmpm = (nodelist_flag**)dest; *tmpm; tmpm=&((*tmpm)->next));
    (*tmpm) = (nodelist_flag *) xmalloc(sizeof(nodelist_flag));
    (*tmpm)->next = NULL;
    (*tmpm)->name = xstrcpy(v);
    tmp = strtoul(p, NULL, 0);
    (*tmpm)->value = tmp;
    
    return 0;
}



/*
 * Get a keyword, string, unsigned int, unsigned int
 */
static int getmdm(char **dest)
{
    char            *p, *q;
    unsigned int    tmp1, tmp2;
    nodelist_modem  **tmpm;

    for (p = v; *p && !isspace(*p); p++);
    if (*p)
	*p++ = '\0';
    while (*p && isspace(*p))
	p++;
    if (*p == '\0') {
	WriteError("%s(%s): less then two tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (q = p; *q && !isspace(*q); q++);
    if (*q)
	*q++ = '\0';
    while (*q && isspace(*q))
	q++;
    if (*q == '\0') {
	WriteError("%s(%s): less then three tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (tmpm = (nodelist_modem**)dest; *tmpm; tmpm=&((*tmpm)->next));
    (*tmpm) = (nodelist_modem *) xmalloc(sizeof(nodelist_modem));
    (*tmpm)->next = NULL;
    (*tmpm)->name = xstrcpy(v);
    tmp1 = strtoul(p, NULL, 0);
    tmp2 = strtoul(q, NULL, 0);
    (*tmpm)->mask = tmp1;
    (*tmpm)->value = tmp2;

    return 0;
}



/*
 * Get a keyword, string array
 */
static int getarr(char **dest)
{
    char            *p;
    nodelist_array  **tmpm;

    for (p = v; *p && !isspace(*p); p++);
    if (*p)
	*p++ = '\0';

    for (tmpm = (nodelist_array**)dest; *tmpm; tmpm=&((*tmpm)->next));
    (*tmpm) = (nodelist_array *) xmalloc(sizeof(nodelist_array));
    (*tmpm)->next = NULL;
    (*tmpm)->name = xstrcpy(v);
    return 0;
}



/*
 * Get a keyword, unsigned short, string
 */
static int getdom(char **dest)
{
    char            *p;
    unsigned short  tmp;
    nodelist_domsuf **tmpm;
		    
    for (p = v; *p && !isspace(*p); p++);
    if (*p)
	*p++ = '\0';
    while (*p && isspace(*p))
	p++;
    if (*p == '\0') {
	WriteError("%s(%s): less then two tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (tmpm = (nodelist_domsuf**)dest; *tmpm; tmpm=&((*tmpm)->next));
    (*tmpm) = (nodelist_domsuf *) xmalloc(sizeof(nodelist_domsuf));
    (*tmpm)->next = NULL;
    tmp = strtod(v, NULL);
    (*tmpm)->zone = tmp;
    (*tmpm)->name = xstrcpy(p);

    return 0;
}



/*
 * Get a keyword, string, string, unsigned int
 */
static int getsrv(char **dest)
{
    char		*p, *q;
    unsigned int	tmp;
    nodelist_service	**tmpm;

    for (p = v; *p && !isspace(*p); p++);
    if (*p)
	*p++ = '\0';
    while (*p && isspace(*p))
	p++;
    if (*p == '\0') {
	WriteError("%s(%s): less then two tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (q = p; *q && !isspace(*q); q++);
    if (*q)
	*q++ = '\0';
    while (*q && isspace(*q))
	q++;
    if (*q == '\0') {
	WriteError("%s(%s): less then three tokens", nlpath, linecnt);
	return MBERR_INIT_ERROR;
    }

    for (tmpm = (nodelist_service**)dest; *tmpm; tmpm=&((*tmpm)->next));
    (*tmpm) = (nodelist_service *) xmalloc(sizeof(nodelist_service));
    (*tmpm)->next = NULL;
    (*tmpm)->flag = xstrcpy(v);
    (*tmpm)->service = xstrcpy(p);
    tmp = strtoul(q, NULL, 0);
    (*tmpm)->defport = (*tmpm)->tmpport = tmp;
    return 0;
}



void tidy_nl_flag(nodelist_flag **);
void tidy_nl_flag(nodelist_flag **fap)
{
    nodelist_flag   *tmp, *old;

    for (tmp = *fap; tmp; tmp = old) {
	old = tmp->next;
	if (tmp->name)
	    free(tmp->name);
	free(tmp);
    }
    *fap = NULL;
}



void tidy_nl_modem(nodelist_modem **);
void tidy_nl_modem(nodelist_modem **fap)
{
    nodelist_modem  *tmp, *old;

    for (tmp = *fap; tmp; tmp = old) {
	old = tmp->next;
	if (tmp->name)
	    free(tmp->name);
	free(tmp);
    }
    *fap = NULL;
}



void tidy_nl_array(nodelist_array **);
void tidy_nl_array(nodelist_array **fap)
{
    nodelist_array	*tmp, *old;

    for (tmp = *fap; tmp; tmp = old) {
	old = tmp->next;
	if (tmp->name)
	    free(tmp->name);
	free(tmp);
    }
    *fap = NULL;
}



void tidy_nl_domsuf(nodelist_domsuf **);
void tidy_nl_domsuf(nodelist_domsuf **fap)
{
    nodelist_domsuf *tmp, *old;

    for (tmp = *fap; tmp; tmp = old) {
	old = tmp->next;
	if (tmp->name)
	    free(tmp->name);
	free(tmp);
    }
    *fap = NULL;
}



void tidy_nl_service(nodelist_service **);
void tidy_nl_service(nodelist_service **fap)
{
    nodelist_service	*tmp, *old;

    for (tmp = *fap; tmp; tmp = old) {
	old = tmp->next;
	if (tmp->flag)
	    free(tmp->flag);
	if (tmp->service)
	    free(tmp->service);
	free(tmp);
    }
    *fap = NULL;
}



/*
 * De-init nodelists, free all allocated memory
 */
void deinitnl(void)
{
    if (!nlinitdone)
	return;

    tidy_nl_flag(&nl_online);
    tidy_nl_flag(&nl_request);
    tidy_nl_flag(&nl_reqbits);
    tidy_nl_modem(&nl_pots);
    tidy_nl_modem(&nl_isdn);
    tidy_nl_modem(&nl_tcpip);
    tidy_nl_array(&nl_search);
    tidy_nl_array(&nl_dialer);
    tidy_nl_array(&nl_ipprefix);
    tidy_nl_domsuf(&nl_domsuffix);
    tidy_nl_service(&nl_service);

    Syslog('n', "De-init nodelists done");
    nlinitdone = FALSE;
}



/*
 *  Init nodelists.
 */
int initnl(void)
{
    int		    i, rc = 0, Found;
    FILE	    *dbf, *fp;
    char	    *filexnm, buf[256], *p, *q;
    struct _nlfil   fdx;
    struct taskrec  TCFG;
    nodelist_modem  **tmpm;

    if (nlinitdone == TRUE)
	return 0;

    nl_online = NULL;
    nl_pots = NULL;
    nl_request = NULL;
    nl_reqbits = NULL;
    nl_isdn = NULL;
    nl_tcpip = NULL;
    nl_search = NULL;
    nl_domsuffix = NULL;
    nl_ipprefix = NULL;
    nl_dialer = NULL;
    nl_service = NULL;

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

    /*
     * Check if all installed nodelists are present.
     */
    if ((dbf = fopen(filexnm, "r")) == NULL) {
	WriteError("$Can't open %s", filexnm);
	rc = MBERR_INIT_ERROR;
    } else {
	while (fread(&fdx, sizeof(fdx), 1, dbf) == 1) {
	    snprintf(nlpath, PATH_MAX -1, "%s/%s", CFG.nodelists, fdx.filename);
	    if ((fp = fopen(nlpath, "r")) == NULL) {
		WriteError("$Can't open %s", nlpath);
		rc = MBERR_INIT_ERROR;
	    } else {
		fclose(fp);
	    }
	}

	fclose(dbf);
    }
    free(filexnm);

    /*
     * Read and parse ~/etc/nodelist.conf
     */
    snprintf(nlpath, PATH_MAX -1, "%s/etc/nodelist.conf", getenv("MBSE_ROOT"));
    if ((dbf = fopen(nlpath, "r")) == NULL) {
	WriteError("$Can't open %s", nlpath);
	rc = MBERR_INIT_ERROR;
    } else {
	while (fgets(buf, sizeof(buf) -1, dbf)) {
	    linecnt++;
	    if (*(p = buf + strlen(buf) -1) != '\n') {
		WriteError("%s(%d): \"%s\" - line too long", nlpath, linecnt, buf);
		rc = MBERR_INIT_ERROR;
		break;
	    }
	    *p-- = '\0';
	    while ((p >= buf) && isspace(*p))
		*p-- = '\0';
	    k = buf;
	    while (*k && isspace(*k))
		k++;
	    p = k;
	    while (*p && !isspace(*p))
		p++;
	    *p++='\0';
	    v = p;
	    while (*v && isspace(*v)) 
		v++;

	    if ((*k == '\0') || (*k == '#')) {
		continue;
	    }
	    
	    for (i = 0; keytab[i].key; i++)
		if (strcasecmp(k,keytab[i].key) == 0)
		    break;

	    if (keytab[i].key == NULL) {
		WriteError("%s(%d): %s %s - unknown keyword", nlpath, linecnt, MBSE_SS(k), MBSE_SS(v));
		rc = MBERR_INIT_ERROR;
		break;
	    } else if ((keytab[i].prc(keytab[i].dest))) {
		rc = MBERR_INIT_ERROR;
		break;
	    }
	}
	fclose(dbf);
    }

    Found = FALSE;

    /*
     * Howmany TCP sessions are allowd
     */
    snprintf(nlpath, PATH_MAX -1, "%s/etc/task.data", getenv("MBSE_ROOT"));
    if ((fp = fopen(nlpath, "r"))) {
	fread(&TCFG, sizeof(TCFG), 1, fp);
	fclose(fp);
    } else {
	TCFG.max_tcp = 0;
    }

    /*
     *  Read all our TCP/IP capabilities and set the global flag.
     */
    if (TCFG.max_tcp) {
	snprintf(buf, 256, "%s", CFG.IP_Flags);
	q = buf;
	for (p = q; p; p = q) {
	    if ((q = strchr(p, ',')))
		*q++ = '\0';
	    for (tmpm = &nl_tcpip; *tmpm; tmpm=&((*tmpm)->next))
		if (strncasecmp((*tmpm)->name, p, strlen((*tmpm)->name)) == 0)
		    mytcpip |= (*tmpm)->value;
	}
    }
 
    /*
     * Read the ports configuration for all pots and isdn lines.
     * All lines are ORed so we have a global and total lines
     * capability.
     */
    snprintf(nlpath, PATH_MAX -1, "%s/etc/ttyinfo.data", getenv("MBSE_ROOT"));
    if ((fp = fopen(nlpath, "r"))) {
	fread(&ttyinfohdr, sizeof(ttyinfohdr), 1, fp);

	while (fread(&ttyinfo, ttyinfohdr.recsize, 1, fp) == 1) {
	    if (((ttyinfo.type == POTS) || (ttyinfo.type == ISDN)) && (ttyinfo.available) && (ttyinfo.callout)) {

		snprintf(buf, 256, "%s", ttyinfo.flags);
		q = buf;
		for (p = q; p; p = q) {
		    if ((q = strchr(p, ',')))
			*q++ = '\0';
		    for (tmpm = &nl_pots; *tmpm; tmpm=&((*tmpm)->next))
			if (strcasecmp((*tmpm)->name, p) == 0)
			    mypots |= (*tmpm)->value;
		    for (tmpm = &nl_isdn; *tmpm; tmpm=&((*tmpm)->next))
			if (strcasecmp((*tmpm)->name, p) == 0)
			    myisdn |= (*tmpm)->value;
		}
	    }
	}
	fclose(fp);
    }

    free(nlpath);
    nlinitdone = TRUE;
    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[MAXNLLINELEN], ebuf[MAXNLLINELEN], *p, *q, tbuf[256];
    struct _ixentry	    xaddr;
    int			    i, Found = FALSE, ixflag, stdflag, ndrecord = FALSE;
    char		    *mydomain, *path, *r;
    struct _nlfil	    fdx;
    struct _nlidx	    ndx;
    int			    lowest, highest, current;
    struct _nodeshdr	    ndhdr;
    static struct _nodes    nd;
    nodelist_modem	    **tmpm;
    nodelist_flag	    **tmpf;
    nodelist_service	    **tmps;
    nodelist_array	    **tmpa, **tmpaa;
    nodelist_domsuf	    **tmpd;
    unsigned int	    tport = 0;
    
    Syslog('n', "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';
    nodebuf.url		= NULL;
    
    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;

    /*
     * Search domainname for the requested aka, should not fail.
     */
    path = calloc(PATH_MAX, sizeof(char));
    snprintf(path, PATH_MAX -1, "%s/etc/fidonet.data", getenv("MBSE_ROOT"));
    if ((fp = fopen(path, "r"))) {
	fread(&fidonethdr, sizeof(fidonethdr), 1, fp);
	while (fread(&fidonet, fidonethdr.recsize, 1, fp) == 1) {
	    for (i = 0; i < 6; i++) {
		if (addr->zone == fidonet.zone[i]) {
		    nodebuf.addr.domain = xstrcpy(fidonet.domain);
		    Found = TRUE;
		    break;
		}
	    }
	    if (Found)
		break;
	}
	fclose(fp);
    }
    Found = FALSE;

    /*
     *  First, lookup node in index. NOTE -- NOT 5D YET
     */
    snprintf(path, PATH_MAX -1, "%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;
    }

    snprintf(path, PATH_MAX -1, "%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
     */
    snprintf(path, PATH_MAX -1, "%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;
    }
    Syslog('n', "getnlent: %s", buf);

    /*
     * 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.
     */
    snprintf(path, PATH_MAX -1, "%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;
		    break;
		}
	    }
	    if (ndrecord)
		break;
	}

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

    /*
     * If an override exists on Hold and Down status,
     * clear these bits.
     */
    if (ndrecord && nd.IgnHold && (nodebuf.pflag & (NL_HOLD + NL_DOWN))) {
	nodebuf.pflag &= ~NL_DOWN;
	nodebuf.pflag &= ~NL_HOLD;
	Syslog('+', "getnlent: %s override node Down/Hold status", ascfnode(addr,0xff));
    }

    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('+', "getnlent: %s system name override with %s", ascfnode(addr,0xff), 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);

    /*
     * Reset all possible overridden portnumbers to the default from nodelist.conf
     */
    for (tmps = &nl_service; *tmps; tmps=&((*tmps)->next))
	(*tmps)->tmpport = (*tmps)->defport;
    
    /*
     * Process the nodelist flags.
     */
    if (ndrecord && strlen(nd.Nl_flags)) {
	Syslog('+', "getnlent: %s flags override %s", ascfnode(addr,0xff), 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 (tmpf = &nl_online; *tmpf; tmpf=&((*tmpf)->next))
		if (strcasecmp(p, (*tmpf)->name) == 0)
		    nodebuf.oflags |= (*tmpf)->value;
	    for (tmpm = &nl_pots; *tmpm; tmpm=&((*tmpm)->next))
		if (strcasecmp(p, (*tmpm)->name) == 0)
		    nodebuf.mflags |= (*tmpm)->value;
	    for (tmpm = &nl_isdn; *tmpm; tmpm=&((*tmpm)->next))
		if (strcasecmp(p, (*tmpm)->name) == 0)
		    nodebuf.dflags |= (*tmpm)->value;
	    for (tmpm = &nl_tcpip; *tmpm; tmpm=&((*tmpm)->next))
		if (strncasecmp(p, (*tmpm)->name, strlen((*tmpm)->name)) == 0) {
		    nodebuf.iflags |= (*tmpm)->value;
		    /*
		     * Parse the IP flag for a optional port number.
		     */
		    if ((r = strrchr(p, ':'))) {
			*r++;
			for (tmps = &nl_service; *tmps; tmps=&((*tmps)->next)) {
			    if (strncmp(p, (*tmps)->flag, 3) == 0) {
				/*
				 * Check for format IBN:70.66.52.252 because this is not
				 * a port number but a dotted IP quad with default port number.
				 */
				if ((strchr(r, '.') == NULL) && atoi(r)) {
				    (*tmps)->tmpport = atoi(r);
				    Syslog('n', "getnlent: port override %s %d to %d", 
					    (*tmpm)->name, (*tmps)->defport, (*tmps)->tmpport);
				}
			    }
			}
		    }
		}
	    for (tmpf = &nl_request; *tmpf; tmpf=&((*tmpf)->next))
		if (strcasecmp(p, (*tmpf)->name) == 0)
		    nodebuf.xflags = (*tmpf)->value;
	    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;
	}
	/*
	 * Lines starting with ;E space are real errors.
	 */
	if (strncmp(ebuf, (char *)";E ", 3) == 0)
	    break;
	if (strncmp(ebuf, (char *)";E", 2))
	    break;
	Syslog('n', "ESLF: \"%s\"", printable(ebuf, 0));
    }

    /*
     * Build the connection URL
     *
     * If the node has some IP flags and we allow TCP, then search the best protocol.
     */
    if (nodebuf.iflags & mytcpip) {
	memset(&tbuf, 0, sizeof(tbuf));
	for (tmpm = &nl_tcpip; *tmpm; tmpm=&((*tmpm)->next)) {
	    if ((*tmpm)->mask & nodebuf.iflags) {
		for (tmps = &nl_service; *tmps; tmps=&((*tmps)->next)) {
		    if (strcmp((*tmps)->flag, (*tmpm)->name) == 0) {
			snprintf(tbuf, 256, "%s", (*tmps)->service);
			tport = (*tmps)->tmpport;
		    }
		}
	    }
	}

	/*
	 * The last setting is the best
	 */
	nodebuf.url = xstrcpy(tbuf);
	nodebuf.url = xstrcat(nodebuf.url, (char *)"://");

	/*
	 * Next, try to find out the FQDN for this node, we have a search
	 * preference list in the nodelist.conf file.
	 */
	memset(&tbuf, 0, sizeof(tbuf));
	if (ndrecord && strlen(nd.Nl_hostname)) {
	    Syslog('n', "getnlent: using override %s for FQDN", nd.Nl_hostname);
	    snprintf(tbuf, 256, nodebuf.name);
	    nodebuf.url = xstrcat(nodebuf.url, tbuf);
	} else {
	    for (tmpa = &nl_search; *tmpa; tmpa=&((*tmpa)->next)) {
		Syslog('n', "getnlent: search FQDN method %s", (*tmpa)->name);
		if (strcasecmp((*tmpa)->name, "field3") == 0) {
		    snprintf(tbuf, 256, nodebuf.name);
		    if (strchr(tbuf, '.')) {
			/*
			 * Okay, there are dots, this can be a FQDN or IP address.
			 */
			Syslog('n', "getnlent: using field3 \"%s\"", tbuf);
			nodebuf.url = xstrcat(nodebuf.url, tbuf);
			break;
		    } else {
			memset(&tbuf, 0, sizeof(tbuf));
		    }
		} else if (strcasecmp((*tmpa)->name, "field6") == 0) {
		    memset(&tbuf, 0, sizeof(tbuf));
		    for (tmpaa = &nl_ipprefix; *tmpaa; tmpaa=&((*tmpaa)->next)) {
			if (nodebuf.phone && strncmp(nodebuf.phone, (*tmpaa)->name, strlen((*tmpaa)->name)) == 0) {
			    Syslog('n', "getnlent: found %s prefix", (*tmpaa)->name);
			    snprintf(tbuf, 256, "%s", nodebuf.phone+strlen((*tmpaa)->name));
			    for (i = 0; i < strlen(tbuf); i++)
				if (tbuf[i] == '-')
				    tbuf[i] = '.';
			    Syslog('n', "getnlent: using field6 \"%s\"", tbuf);
			    nodebuf.url = xstrcat(nodebuf.url, tbuf);
			    break;
			}
		    }
		    if (strlen(tbuf))
			break;
		} else if (strcasecmp((*tmpa)->name, "field8") == 0) {
		    /*
		     * Read nodelist line again in another buffer, the original
		     * buffer is divided into pieces by all previous actions.
		     */
		    memset(&tbuf, 0, sizeof(tbuf));
		    if (fseek(fp, ndx.offset, SEEK_SET) != 0) {
			WriteError("$Seek failed for nodelist entry");
			fclose(fp);
			goto retdummy;
		    }
		    if (fgets(ebuf, sizeof(ebuf)-1, fp) == NULL) {
			WriteError("$fgets failed for nodelist entry");
			fclose(fp);
			goto retdummy;
		    }
		    if (*(p = ebuf + strlen(ebuf) -1) == '\n')
			*p = '\0';
		    if (*(p = ebuf + strlen(ebuf) -1) == '\r')
			*p = '\0';
		    p = ebuf;
		    /*
		     * Shift to field 8
		     */
		    for (i = 0; i < 7; i++) {
			if ((q = strchr(p,',')))
			    *q++ = '\0';
			p = q;
			if (p == NULL) {
			    fclose(fp);
			    goto badsyntax;
			}
		    }
		    for (p = q; p; p = q) {
			if ((q = strchr(p, ',')))
		            *q++ = '\0';
			if ((r = strchr(p, ':'))) {
			    r++;
			    /*
			     * If there is a user@domain then strip the userpart.
			     */
			    if (strchr(r, '@')) {
				r = strchr(r, '@');
				r++;
			    }
			    if (*r == '*') {
				Syslog('n', "getnlent: possible default domain marking \"%s\"", MBSE_SS(r));
				for (tmpd = &nl_domsuffix; *tmpd; tmpd=&((*tmpd)->next)) {
				    if ((*tmpd)->zone == nodebuf.addr.zone) {
					if (*r++ == '\0')
					    snprintf(tbuf, 256, "f%d.n%d.z%d.%s.%s", nodebuf.addr.node, nodebuf.addr.net,
						    nodebuf.addr.zone, nodebuf.addr.domain, (*tmpd)->name);
					else
					    snprintf(tbuf, 256, "f%d.n%d.z%d.%s.%s%s", nodebuf.addr.node, nodebuf.addr.net,
						    nodebuf.addr.zone, nodebuf.addr.domain, (*tmpd)->name, r);
					Syslog('n', "getnlent: will try default domain \"%s\"", tbuf);
					nodebuf.url = xstrcat(nodebuf.url, tbuf);
					break;
				    }
				}
				if (strlen(tbuf))
				    break;
				Syslog('n', "getnlent: no matching default domain found for zone %d", nodebuf.addr.zone);
			    }
			    if (strchr(r, '.')) {
				Syslog('n', "getnlent: found a FQDN \"%s\"", MBSE_SS(r));
				snprintf(tbuf, 256, "%s", r);
				nodebuf.url = xstrcat(nodebuf.url, tbuf);
				break;
			    }
			}
		    }
		    if (strlen(tbuf))
			break;
		    memset(&tbuf, 0, sizeof(tbuf));
		} else if (strcasecmp((*tmpa)->name, "defdomain") == 0) {
		    Syslog('n', "getnlent: trying default domain");
		    if (nodebuf.addr.domain) {
			for (tmpd = &nl_domsuffix; *tmpd; tmpd=&((*tmpd)->next)) {
			    if ((*tmpd)->zone == nodebuf.addr.zone) {
				snprintf(tbuf, 256, "f%d.n%d.z%d.%s.%s", nodebuf.addr.node, nodebuf.addr.net,
					nodebuf.addr.zone, nodebuf.addr.domain, (*tmpd)->name);
				Syslog('n', "getnlent: will try default domain \"%s\"", tbuf);
				nodebuf.url = xstrcat(nodebuf.url, tbuf);
				break;
			    }
			}
		    } else {
			Syslog('+', "getnlent: no domain name for zone %d, check setup", nodebuf.addr.zone);
		    }
		    if (strlen(tbuf))
			break;
		    Syslog('n', "getnlent: no matching default domain found for zone %d", nodebuf.addr.zone);
		    memset(&tbuf, 0, sizeof(tbuf));
		}
	    }
	}

	if (strlen(tbuf) == 0) {
	    Syslog('+', "getnlent: no FQDN found, cannot call");
	    if (nodebuf.url)
		free(nodebuf.url);
	    nodebuf.url = NULL;
	} else if (strchr(tbuf, ':') == NULL) {
	    /*
	     * No optional port number, add one from the default
	     * for this protocol.
	     */
	    snprintf(tbuf, 256, ":%u", tport);
	    nodebuf.url = xstrcat(nodebuf.url, tbuf);
	}

    } else if (nodebuf.dflags & myisdn) {
	nodebuf.url = xstrcpy((char *)"isdn://");
	nodebuf.url = xstrcat(nodebuf.url, nodebuf.phone);
    } else if (nodebuf.mflags & mypots) {
	nodebuf.url = xstrcpy((char *)"pots://");
	nodebuf.url = xstrcat(nodebuf.url, nodebuf.phone);
    }
    fclose(fp);

    nodebuf.addr.name = nodebuf.sysop;
    if (nodebuf.addr.domain)
	free(nodebuf.addr.domain);
    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);

    /*
     * FSP-1033, the ICM (IP Continuous Mail) flag.
     */
    nodebuf.can_pots = (nodebuf.mflags || nodebuf.dflags)   ? TRUE : FALSE;
    nodebuf.can_ip   = (nodebuf.iflags)			    ? TRUE : FALSE;
    nodebuf.is_cm    = (nodebuf.oflags & 0x00000001)	    ? TRUE : FALSE;
    nodebuf.is_icm   = (nodebuf.oflags & 0x00000002)	    ? TRUE : FALSE; 
    /*
     * Now correct if node is ION with a CM flag instead of ICM flag.
     * This is the case the first months after approval of the ICM flag
     * and with nodes who won't change flags.
     */
    if (!nodebuf.can_pots && nodebuf.can_ip && nodebuf.is_cm && !nodebuf.is_icm) {
	Syslog('n', "getnlent: correct CM into ICM flag");
	nodebuf.is_cm  = FALSE;
	nodebuf.is_icm = TRUE;
    }

    Syslog('n', "getnlent: system  %s, %s", nodebuf.name, nodebuf.location);
    Syslog('n', "getnlent: sysop   %s, %s", nodebuf.sysop, nodebuf.phone);
    Syslog('n', "getnlent: URL     %s", printable(nodebuf.url, 0));
    Syslog('n', "getnlent: online  POTS %s CM %s, IP %s ICM %s", nodebuf.can_pots ?"Yes":"No", 
				nodebuf.is_cm ?"Yes":"No", nodebuf.can_ip ?"Yes":"No", nodebuf.is_icm ?"Yes":"No");
//    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 int)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;
    nodebuf.url = NULL;
    free(mydomain);

    return &nodebuf;
}



void olflags(unsigned int flags)
{
    char	    *t;
    nodelist_flag   **tmpm;

    t = xstrcpy((char *)"Mailer flags :");
    for (tmpm = &nl_online; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->value & flags) {
	    t = xstrcat(t, (char *)" ");
	    t = xstrcat(t, (*tmpm)->name);
	}
    }
    Syslog('n', "%s", t);
    free(t);
}



void rqflags(unsigned int flags)
{
    char	    *t;
    nodelist_flag   **tmpm;
    
    t = xstrcpy((char *)"Request flags:");
    for (tmpm = &nl_reqbits; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->value & flags) {
	    t = xstrcat(t, (char *)" ");
	    t = xstrcat(t, (*tmpm)->name);
	}
    }
    for (tmpm = &nl_request; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->value == flags) {
	    t = xstrcat(t, (char *)" (");
	    t = xstrcat(t, (*tmpm)->name);
	    t = xstrcat(t, (char *)")");
	}
    }
    Syslog('n', "%s", t);
    free(t);
}



void moflags(unsigned int flags)
{
    char	    *t;
    nodelist_modem  **tmpm;

    if (!flags)
	return;
    t = xstrcpy((char *)"Modem flags  :");
    for (tmpm = &nl_pots; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->mask & flags) {
	    t = xstrcat(t, (char *)" ");
	    t = xstrcat(t, (*tmpm)->name);
	}
    }
    Syslog('n', "%s", t);
    free(t);
}



void diflags(unsigned int flags)
{
    char	    *t;
    nodelist_modem  **tmpm;
    
    if (!flags)
	return;
    t = xstrcpy((char *)"ISDN flags   :");
    for (tmpm = &nl_isdn; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->mask & flags) {
	    t = xstrcat(t, (char *)" ");
	    t = xstrcat(t, (*tmpm)->name);
	}
    }
    Syslog('n', "%s", t);
    free(t);
}



void ipflags(unsigned int flags)
{
    char	    *t;
    nodelist_modem  **tmpm;
    
    if (!flags)
	return;
    t = xstrcpy((char *)"TCP/IP flags :");
    for (tmpm = &nl_tcpip; *tmpm; tmpm=&((*tmpm)->next)) {
	if ((*tmpm)->mask & flags) {
	    t = xstrcat(t, (char *)" ");
	    t = xstrcat(t, (*tmpm)->name);
	}
    }
    Syslog('n', "%s", t);
    free(t);
}