/*--------------------------------------------------------------------------*/
/*																			*/
/*																			*/
/*		------------		 Bit-Bucket Software, Co.						*/
/*		\ 10001101 /		 Writers and Distributors of					*/
/*		 \ 011110 / 		 Freely Available<tm> Software. 				*/
/*		  \ 1011 /															*/
/*		   ------															*/
/*																			*/
/*	(C) Copyright 1987-90, Bit Bucket Software Co., a Delaware Corporation. */
/*																			*/
/*																			*/
/*				 This module was written by Vince Perriello 				*/
/*																			*/
/*																			*/
/*				   BinkleyTerm Nodelist processing module					*/
/*																			*/
/*																			*/
/*	  For complete	details  of the licensing restrictions, please refer	*/
/*	  to the License  agreement,  which  is published in its entirety in	*/
/*	  the MAKEFILE and BT.C, and also contained in the file LICENSE.240.	*/
/*																			*/
/*	  USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE	*/
/*	  BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF	*/
/*	  THIS	AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,	OR IF YOU DO	*/
/*	  NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET	*/
/*	  SOFTWARE CO.	AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT	*/
/*	  SHOULD YOU  PROCEED TO USE THIS FILE	WITHOUT HAVING	ACCEPTED THE	*/
/*	  TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER	*/
/*	  AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.		*/
/*																			*/
/*																			*/
/* You can contact Bit Bucket Software Co. at any one of the following		*/
/* addresses:																*/
/*																			*/
/* Bit Bucket Software Co.		  FidoNet  1:104/501, 1:132/491, 1:141/491	*/
/* P.O. Box 460398				  AlterNet 7:491/0							*/
/* Aurora, CO 80046 			  BBS-Net  86:2030/1						*/
/*								  Internet f491.n132.z1.fidonet.org 		*/
/*																			*/
/* Please feel free to contact us at any time to share your comments about	*/
/* our software and/or licensing policies.									*/
/*																			*/
/*--------------------------------------------------------------------------*/

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#ifndef unix
#include <conio.h>
#endif
#include <string.h>

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>

#ifdef unix
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define _fmalloc(n) malloc(n)
#define _ffree(n) free(n)
#else
#ifdef __TOS__
#include <time.h>
#include <ext.h>
#else
#include <sys\types.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <dos.h>
#endif

#ifndef LATTICE
#include <io.h>
#endif

#ifdef OS_2
#define  INCL_DOS
#include <os2.h>
#define _dos_read DosRead
#endif

#if defined(ATARIST)
 #include <stdlib.h>
 #define _fmalloc(n) malloc(n)
 #define _ffree(n) free(n)
#elif defined(__TURBOC__)
 #include <alloc.h>
 #define _fmalloc(n) farmalloc (n)
 #define _ffree(n) farfree (n)
#else
 #include <malloc.h>
#endif
#endif


#include "bink.h"
#include "msgs.h"
#include "com.h"
#include "nodeproc.h"

/*
 * Global variables
 */
 
char *node_prefix = NULL;			/* Dial Prefix for this node */

BOOLEAN newnodelist = TRUE;                             /* if 1, use new nodelist.   */
int autobaud = 0;                                /* Use highest baudrate when
                                                  * calling out */
int found_zone = 0;                              /* What zone found node is in*/
int found_net = 0;                               /* What net found node is in */
struct _newnode newnodedes;                      /* structure in new list     */
char far *node_index = NULL;                     /* pointer to node array     */


/*
 * Local Variables
 */

static char far *get_size (size_t);
static int get_new_info (unsigned);

#ifndef ATARIST
#ifndef unix
static int get_old_info (unsigned);
static int get_TBBS_info (unsigned, ADDR *);
static int Quick_Next_Zone (void);
#endif
#endif

static int opus_next_zone (void);

#if defined(__TURBOC__) /* || defined(LATTICE) */
typedef size_t off_t;
#endif

static off_t  index_filesize = (off_t)	0L;
static time_t index_filetime = (time_t) 0L;
static char   index_filename[80];

static off_t idx_size = 0;				 /* number of entries in it   */
static off_t zone_offset = 0;
static int last_zone = -1;
static size_t extra_bytes = 0;
static char *curr_domain = NULL;
static char *nodelist_name = "NODELIST";
static char *nodelist_base = NULL;

static void get_nodelist_name (ADDR *);

static void get_nodelist_name (opus_addr)
ADDR *opus_addr;
{
   int i;

   curr_domain = opus_addr->Domain;

   idx_size = 0;
   zone_offset = 0;
   last_zone = -1;
   extra_bytes = 0;
   nodelist_base = nodelist_name;
   for (i = 0; domain_name[i] != NULL; i++)
	  {
	  if (domain_name[i] == opus_addr->Domain)
		 {
		 nodelist_base = domain_nodelist[i];
		 if (nodelist_base == NULL)
			nodelist_base = nodelist_name;
		 }
	  }
}

static char far *get_size (size_t n)
{
   /* If we get this far, then we have to use a straight far pointer */
   return (_fmalloc (n));
}

/*
 * Nodelist locking
 *
 * This keeps the nodelist open during nodelist intensive processing
 *
 * It should only be used when accessing a single domain
 *
 * Also it currently only works for opus newnodelist
 *
 * TRUE enables locking, FALSE disables it
 *
 * Calls may be nested, but there must always be an equal number of each.
 */

static FILE *fp_nodelist = NULL;		/* File handle for nodelist when locked */
static int nodelock = 0;

void lock_nodelist(BOOLEAN flag)
{
	if(flag)	/* Lock the nodelist */
		nodelock++;
	else		/* Unlock it */
		if(!--nodelock)
			if(fp_nodelist)
			{
				fclose(fp_nodelist);
				fp_nodelist = NULL;
			}
}



/*---------------------------------------------------------------------------*/
/* CHECKLIST																 */
/* See if nodelist has changed since we first tried to use it and if so,	 */
/* dismiss old copy and get a new one										 */
/*---------------------------------------------------------------------------*/
int checklist ()
{
   struct stat idxstat;
   if (index_filesize == (off_t)0L)
	  return (0);
   stat (index_filename, &idxstat);
   if ((index_filesize == idxstat.st_size) && (index_filetime == idxstat.st_mtime))
	  return (0);
   status_line (msgtxt[M_REFRESH_NODELIST]);
   _ffree ((char far *) node_index);
   node_index = (char far *) NULL;
   index_filesize = (off_t)0L;
   return (1);
}


/*---------------------------------------------------------------------------*/
/* NODEPROC 																 */
/* Find nodelist entry and set baud to nodelist baud for dialing out		 */
/*---------------------------------------------------------------------------*/

int nodeproc (nodeaddr)
char *nodeaddr;
{
   ADDR opus_addr;
   char *c, *skip_blanks ();

   c = skip_blanks (nodeaddr);					 /* get rid of the blanks	  */
   if (!find_address (c, &opus_addr))
	  {
	  return (0);
	  }
   if (!nodefind (&opus_addr, 1))  /* if we can't find the node */
	  return (0);								 /* go away now 			  */
   status_line (msgtxt[M_PROCESSING_NODE], Pretty_Addr_Str (&opus_addr), newnodedes.SystemName);
   if (!CARRIER)								 /* if no carrier yet,		  */
	  {
	  if (autobaud)
		 (void) set_baud (max_baud.rate_value, 1);
												 /* Set to our highest baud rate */
	  else
		 (void) set_baud ((300 * newnodedes.BaudRate), 1);		 /* set baud to nodelist
																 * baud */
	  }
   return (1);									 /* return success to caller  */
}

/*---------------------------------------------------------------------------*/
/* NODEFIND 																 */
/* Find nodelist entry for use by other routines (password, nodeproc)		 */
/* If found, result will be in "newnodedes".								 */
/*                                                                           */
/* If the passed zone is -1 then the next zone is returned                   */
/*                                                                           */
/* This is now passed a 4D address and thus must fiddle about with fakenets  */
/*---------------------------------------------------------------------------*/

int nodefind (bink_addr, prtflag)
ADDR *bink_addr;								 /* zone, net and node */
int prtflag;
{
   int i, j, k;
   int have_boss_data = 0;
   int need_boss_data = 0;
	ADDRESS *ad;
	ADKEY *key;
	ADDR newad;		/* Address of converting for fakenet */

   (void) checklist ();

	if(bink_addr->Zone == -1)
		return (*nodefunc) (bink_addr, FALSE);

   newnodedes.NetNumber = newnodedes.NodeNumber = found_zone = found_net = 0;

   CurrentOKFile = DEFAULT.rq_OKFile;	 /* Set the default f.req paths */
   CurrentFILES = DEFAULT.rq_FILES;
   CurrentAbout = DEFAULT.rq_About;
   CurrentReqTemplate = DEFAULT.rq_Template;
   CurrentNetFiles = DEFAULT.sc_Inbound;
   CurrentReqLim = DEFAULT.rq_Limit;
   CurrentByteLim = DEFAULT.byte_Limit;
#ifdef NEW
	CurrentTimeLim = DEFAULT.time_Limit;
#endif

	/* Set up the assumed address based on curr_domain/found_zone/found_net */

   	assumed = k = 0; 						  /* Default to zone of first */
	for (j = 0; j < num_addrs; j++)
	{

	/*
	 * If this alias's domain is the curr_domain
	 *    OR
	 * Curr_domain is NULL and this domain is the default
	 *    OR
	 * If this alias doesnt have a domain and curr_domain is default
	 *
	 * This can be simplified by setting all domains to default in initialisation!
	 */
	 
		if((alias[j].ad.Domain == bink_addr->Domain) ||
			  (!bink_addr->Domain && (alias[j].ad.Domain == alias[0].ad.Domain)) ||
			  (!alias[j].ad.Domain && (bink_addr->Domain == alias[0].ad.Domain)))
		{
			if (k == 0)				/* Matched Domain */
			{
				assumed = j;
				++k;
			}

			if(alias[j].ad.Zone == bink_addr->Zone)
			{
				if (k == 1)
				{
				   assumed = j;
				   ++k;
				}

				if(alias[j].ad.Net == bink_addr->Net)
				{
					if(k == 2)
					{
						assumed = j;
						++k;
					}
					if(alias[j].ad.Node == bink_addr->Node)
					{
    				   assumed = j;
    				   break;
    				} /* node */
				} /* net */
			} /* zone */
		} /* domain */
	} /* alias */

	/* Search the adkeys for a password and force assumed node! */
		
	key = adkeys;

	while(key)
	{
		if( (key->wild.net || (key->ad.Net == bink_addr->Net)) &&
			(key->wild.node || (key->ad.Node == bink_addr->Node)) &&
			(key->wild.zone || (key->ad.Zone == bink_addr->Zone)) &&
			(key->wild.domain || !key->ad.Domain || !bink_addr->Domain || (key->ad.Domain == bink_addr->Domain)) &&
			(key->wild.point || (key->ad.Point == bink_addr->Point)) )
		{
			if(key->password)
				++have_boss_data;
			
			if(key->alias)
			{
				j = 0;
				ad = alias;
				while(j < num_addrs)
				{
					if(key->alias == ad)
					{
						assumed = j;
						break;
					}
					j++;
					ad++;
				}
			}
			
			break;
		}
		key = key->next;
	}

	ad = &alias[assumed];

	if(ad->phone || (key && key->phone))
		++have_boss_data;

	/* Are we the boss or are we wanting the boss? and do we have enough info? */

	newad = *bink_addr;			/* Prepare the 3D address */

	/* If we're a point and this is our boss, use the fakenet */

	if(
		(bink_addr->Net == ad->ad.Net) &&
		(bink_addr->Node == ad->ad.Node) &&
		(bink_addr->Zone == ad->ad.Zone) &&
		(bink_addr->Domain == ad->ad.Domain))
	{
		/* If this is our boss */
		
		if(!bink_addr->Point)
			++need_boss_data;
		else
		/* Patch up the address for fakenet if we're accessing a point not our own */
		{
			newad.Net = ad->fakenet;
			newad.Node = bink_addr->Point;
			newad.Point = 0;
		}
	}

	/* Look it up in the nodelist */

	if(!newad.Zone)
		newad.Zone = ad->ad.Zone;
	i = (*nodefunc) (&newad, have_boss_data);

	if(key && key->phone)
	{
		strncpy(newnodedes.PhoneNumber, key->phone, 40);
		newnodedes.PhoneNumber[39] = '\0';
	}
	else if(need_boss_data && ad->phone)	/* Update phone number if set and calling boss */
	{
		strncpy(newnodedes.PhoneNumber, ad->phone, 40);
		newnodedes.PhoneNumber[39] = '\0';
	}

	if(key && key->prefix)
		node_prefix = key->prefix;
	else
		node_prefix = NULL;

	/* Overwrite the password */

	if(key && key->password)
	{
		memset(newnodedes.Password, 0, sizeof(newnodedes.Password));
		strncpy(newnodedes.Password, key->password, sizeof(newnodedes.Password));
	}


	if(!i && !key && (have_boss_data != 2))
	  {
	  if (prtflag)
		 status_line (msgtxt[M_NO_ADDRESS], Pretty_Addr_Str (bink_addr));

	  if (curmudgeon && CARRIER && (bink_addr->Net != -1) && (bink_addr->Node != -1) && (bink_addr->Node != 9999))
		 {
		 status_line (msgtxt[M_NUISANCE_CALLER]);
	      hang_up ();
		 DTR_ON (); 							/* OK, turn modem back on */
		 }

	  }


   /* If we found the entry, then we promote the file request
	* to the "KNOWN" class. If the password field is non-zero,
	* then promote to "PROT". It's OK to do that since the higher
	* level code will hang up before f.req's if the password does
	* not match.
	*
	*/

	if (i || key)
	{
#ifdef DEBUG
		status_line(">nodefind(%s) password='%s'",
			Pretty_Addr_Str(bink_addr), newnodedes.Password);
#endif
		if (newnodedes.Password[0])
		{
			CurrentOKFile = PROT.rq_OKFile;
		 	CurrentFILES = PROT.rq_FILES;
		 	CurrentAbout = PROT.rq_About;
		 	CurrentReqTemplate = PROT.rq_Template;
		 	CurrentNetFiles = PROT.sc_Inbound;
		 	CurrentReqLim = PROT.rq_Limit;
		 	CurrentByteLim = PROT.byte_Limit;
#ifdef NEW
			CurrentTimeLim = PROT.time_Limit;
#endif
		}
	  	else if(i)
		{
		 	CurrentOKFile = KNOWN.rq_OKFile;
		 	CurrentFILES = KNOWN.rq_FILES;
		 	CurrentAbout = KNOWN.rq_About;
		 	CurrentReqTemplate = KNOWN.rq_Template;
		 	CurrentNetFiles = KNOWN.sc_Inbound;
		 	CurrentReqLim = KNOWN.rq_Limit;
		 	CurrentByteLim = KNOWN.byte_Limit;
#ifdef NEW
			CurrentTimeLim = KNOWN.time_Limit;
#endif
		}
	}

	if(!need_boss_data && !key)
	  return (i);

/*
 *	We can get here one of two ways:
 *
 *	1) No nodelist data was found and this is the BOSS.
 *
 *	2) Nodelist lookup occurred, but this is the BOSS.
 *
 *	For case (1), have_boss_data MUST be 2 (meaning we have
 *	both a phone number and a password entry). If that is the
 *	case, fill in newnodedes with mostly zeroes, plugging in
 *	the BOSS net, node, phone number and password.
 *
 *	For case (2), just see if there is any substitution for
 *	BOSSphone and/or BOSSpwd, then exit.
 *
 */

   if (i)
	  return (1);

   /* No BOSS in the nodelist */
	if(!key && (have_boss_data != 2))
	  {
	  status_line (msgtxt[M_NO_BOSS]);
	  return (0);
	  }

   newnodedes.NodeNumber = bink_addr->Node; 	  /* Node Number */
   newnodedes.NetNumber = bink_addr->Net;		  /* Net Number  */
   newnodedes.Cost = newnodedes.RealCost = 0;	 /* Assume boss is free */
   (void) strcpy (newnodedes.SystemName, "Binkley's Boss");    /* System Name defaults */
   (void) strcpy (newnodedes.MiscInfo, "Somewhere out There"); /* As does City */
   newnodedes.HubNode = 0;						 /* Don't know who is HUB */
   newnodedes.BaudRate = (char) (max_baud.rate_value / 300);
												 /* Assume boss speed = ours */
   newnodedes.ModemType = 0;					 /* Or modem type */
   newnodedes.NodeFlags = B_CM; 				 /* Assume boss is CM */
   newnodedes.NodeFiller = 0;					 /* Zero out filler */
   return (1);
}

#ifdef ALLNODELISTS

static int Quick_Next_Zone ()
{
   register struct QuickNodeIdxRecord far *nodeidx; 	/* index file		  */
   register off_t i;

   if (no_zones)
	  return (-1);

   nodeidx = (struct QuickNodeIdxRecord far *) (node_index + (zone_offset *
			  sizeof (struct QuickNodeIdxRecord)));
   for (i = zone_offset; i < idx_size; ++nodeidx, i++)
	  {
	  if (nodeidx->QI_Zone != last_zone)
		 {
		 last_zone = nodeidx->QI_Zone;
		 zone_offset = i + 1;
		 return (last_zone);
		 }
	  }

   last_zone = -1;
   zone_offset = 0;
   return (-1);
}


int QuickLookup (Quick_addr, have_boss_data)
ADDR *Quick_addr;								 /* zone, net and node */
int have_boss_data; 							 /* BOSS data flag	   */
{
   register struct QuickNodeIdxRecord far *nodeidx; 	/* index file		  */
   struct QuickNodeListRecord nodedes;			 /* desc. of node	   */

   int foundnet = 0;							 /* 'found the net' flag	  */
   int found = 0;								 /* 'found the node' flag	  */
   int idxrec = 0;								 /* record in QNL_IDX.BBS	  */
   long nodeoff = 0L;							 /* offset into QNL_DAT.BBS   */
   char temp[80];								 /* where we build filenames  */
#if defined(OS_2)
   HFILE stream;
   USHORT i;
   USHORT got;
#else
   int stream;
   off_t i;
#if !defined(LATTICE)
#if !defined(unix)
   int got;
#endif
#endif

#endif
   FILE *stream1;
   struct stat f;

   newnodedes.NetNumber = newnodedes.NodeNumber = 0;

   if (node_index == NULL)
	  {
	  index_filesize = (off_t) 0L;				 /* Cover ourselves for error */
	  index_filename[0] = '\0'; 				 /* "null-terminated string"  */
	  (void) strcpy (index_filename, net_info);  /* take nodelist path		  */
	  (void) strcat (index_filename, "QNL_IDX.BBS"); /* add in the file name  */

	  if ((stream = open(index_filename, O_RDONLY|O_BINARY)) == -1)
		 {
#ifdef MULTIPOINT
		if( (have_boss_data != 2) && (Quick_addr->Zone != -1))
#else
		 if (have_boss_data != 2)
#endif
			status_line (msgtxt[M_UNABLE_TO_OPEN], index_filename);
		 return (0);							 /* no file, no work to do	  */
		 }
#ifdef ATARIST
	  stat(index_filename, &f);
#else
	  (void) fstat (stream, &f);				 /* get file statistics 	  */
#endif	  
	  i = f.st_size;							 /* size of index file, 	  */
	  idx_size = (i / sizeof(struct QuickNodeIdxRecord)); /* number of index entries   */
	  node_index = get_size (i);
	  if (node_index == NULL)
		 {
		 status_line (msgtxt[M_NODELIST_MEM]);
		 (void) close (stream);
		 return (0);
		 }
#ifdef unix
	  if (read (stream, node_index, i) != i)
#else
#ifdef ATARIST
	  if (read (stream, node_index, i) != i)
#else
	  if (_dos_read (stream, node_index, i, &got) != 0)
#endif
#endif
		 {
		 status_line (msgtxt[M_NODELIST_READ_ERR]);
		 (void) close (stream);
		 return (0);
		 }
	  (void) close (stream);


	  index_filesize = f.st_size;				 /* Save parameters for later */
	  index_filetime = f.st_mtime;
	  }

   nodeidx = (struct QuickNodeIdxRecord far *) node_index;


   if (Quick_addr->Zone == (unsigned int) -1)
	  {
	  return (Quick_Next_Zone ());
	  }
   else if (no_zones)
	  Quick_addr->Zone = 0;

   for (i = 1; i <= idx_size; idxrec++, nodeidx++, i++)
	  {
	  if (((Quick_addr->Zone == nodeidx->QI_Zone) || (Quick_addr->Zone == 0))
		  && (Quick_addr->Net == nodeidx->QI_Net))
		 {
		 foundnet = 1;							 /* say we found the net	  */
		 if (((Quick_addr->Node == 0) && (nodeidx->QI_Node <= 0))
			 || (nodeidx->QI_Node == Quick_addr->Node)) /* see if we found the node  */
			{
			found = 1;							 /* say we found it 		  */
			break;								 /* get out 				  */
			}
		 }
	  else if (foundnet)						 /* already past the net?	  */
		 break; 								 /* Yes, we failed...		  */
	  }

   if (!found)
	  {
	  return (0);
	  }


   nodeoff = (long) idxrec *(long) sizeof (nodedes);	/* actual file offset	*/

   (void) strcpy (temp, net_info);						/* take nodelist path		 */
   (void) strcat (temp, "QNL_DAT.BBS"); 				/* add in the file name 	 */
   if ((stream1 = fopen (temp, read_binary)) == NULL)	 /* OK, let's open the file   */
	  {
	  status_line (msgtxt[M_UNABLE_TO_OPEN], temp);
	  return (0);
	  }

   if (fseek (stream1, nodeoff, SEEK_SET))		 /* try to point at record	  */
	  {
	  status_line (msgtxt[M_NODELIST_SEEK_ERR], temp);
	  (void) fclose (stream1);
	  return (0);
	  }

   if (!fread (&nodedes, sizeof (nodedes), 1, stream1))
	  {
	  status_line (msgtxt[M_NODELIST_REC_ERR], temp);
	  (void) fclose (stream1);
	  return (0);
	  }
   (void) fclose (stream1);

   /*
	* Copy data from nodedes into newnodedes.
	*/

   newnodedes.NodeNumber = nodedes.QL_Node; 	 /* Node Number  */
   newnodedes.NetNumber = nodedes.QL_Net;		 /* Net Number	 */
   newnodedes.Cost = nodedes.QL_Cost;			 /* Cost		 */

   i = min (nodedes.QL_Name[0], 19);
   (void) strncpy (&newnodedes.SystemName[0], &nodedes.QL_Name[1], i);
   newnodedes.SystemName[i] = '\0'; 			 /* System Name  */

   i = min (nodedes.QL_Phone[0], 39);
   (void) strncpy (&newnodedes.PhoneNumber[0], &nodedes.QL_Phone[1], i);
   newnodedes.PhoneNumber[i] = '\0';			 /* Phone Number */

   i = min (nodedes.QL_City[0], 29);
   (void) strncpy (&newnodedes.MiscInfo[0], &nodedes.QL_City[1], i);
   newnodedes.MiscInfo[i] = '\0';

   /* This field is not necessarily null terminated */
   i = min (nodedes.QL_Password[0], 8);
   (void) strncpy (&newnodedes.Password[0], &nodedes.QL_Password[1], i);
   if (i < 8)
	  newnodedes.Password[i] = '\0';

   /* Adam Hudson now gives us this, so we might as well use it */
   newnodedes.NodeFlags = nodedes.QL_Flags;

   /* Since we have the stuff we need! */
   newnodelist = 1; 							 /* We have all the
												  * information! */

   newnodedes.RealCost = nodedes.QL_Cost;		 /* Cost					 */
   newnodedes.HubNode = 0;						 /* Don't know who is Hub	 */
   newnodedes.BaudRate = (char) (nodedes.QL_BaudRate / 300);	/* Baud 	 */
   newnodedes.ModemType = 0;					 /* Don't know modem type	 */
   newnodedes.NodeFiller = 0;					 /* Filler should be zero	 */
   found_zone = nodeidx -> QI_Zone; 			 /* Keep track of found zone */
   found_net = nodeidx -> QI_Net;				 /* And of found net		 */
   return (1);
}

#endif	/* ALLNODELISTS */

static int opus_next_zone ()
{
   register struct _ndi far *nodeidx;			 /* index file				  */
   register off_t i;

   if (no_zones)
	  return (-1);

   if (zone_offset == 0)
	  zone_offset = 1;

   nodeidx = (struct _ndi far *) (node_index + (zone_offset *
			 sizeof (struct _ndi)));

   for (i = zone_offset; i < idx_size; ++nodeidx, i++)
	  {
	  if (nodeidx->node == -2)
		 {
		 last_zone = nodeidx->net;
		 zone_offset = i + 1;
		 return (last_zone);
		 }
	  }

   last_zone = -1;
   zone_offset = 0;
   return (-1);
}

int opusfind (opus_addr, have_boss_data)
ADDR *opus_addr;								 /* zone, net and node */
int have_boss_data; 							 /* BOSS data flag	   */
{
   register struct _ndi far *nodeidx;			 /* index file				  */
/* int foundnet = 0;   */						 /* 'found the net' flag	  */
   int found = 0;								 /* 'found the node' flag	  */
   int nodeoff = 0; 							 /* offset into nodelist.sys  */
   char temp[80];								 /* where we build filenames  */
#ifndef OS_2
   int stream;
   off_t i;
#ifndef ATARIST
   int got;
#endif	 
#else
   HFILE stream;
   USHORT i;
   USHORT got;
#endif
   struct stat f;
   int current_zone = 0;

   newnodedes.NetNumber = newnodedes.NodeNumber = 0;

#ifdef MULTIPOINT
	/* Don't even TRY looking for points! */

	if(opus_addr->Point && (opus_addr->Zone != -1))
		return 0;
#endif

   if (nodelist_base == NULL)
	  nodelist_base = nodelist_name;


	/*
	 * If we need a new index because either:
	 *   We havn't had one yet
	 *   we are looking at a different domain
	 */


#ifdef MULTIPOINT
   if (!node_index ||
   	   ((curr_domain != opus_addr->Domain) &&
        (opus_addr->Domain || (curr_domain != alias[0].ad.Domain))))
#else
   if ((node_index == NULL) || ((curr_domain != opus_addr->Domain) && ((opus_addr->Domain != NULL) || (curr_domain != my_addr.ad.Domain))))
#endif
	  {
	  if (node_index != NULL)				/* Free the old index */
		 {
		 _ffree (node_index);
		 }

	  get_nodelist_name (opus_addr);
	  index_filesize = (off_t) 0L;			 	/* Cover ourselves for error */
	  index_filename[0] = '\0'; 				 /* "null-terminated string"  */
	  (void) strcpy (index_filename, net_info);  /* take nodelist path		  */
	  (void) strcat (index_filename, nodelist_base); /* add in the file name  */
	  (void) strcat (index_filename, ".IDX");	 /* add in the file ext 	  */
	  if ((stream = open(index_filename, O_RDONLY|O_BINARY)) == -1)
		 {
		 i = 0; 								 /* Need this later 		  */
#ifdef MULTIPOINT
		if((have_boss_data != 2) && (((int)opus_addr->Zone) != -1))
#else
		 if (have_boss_data != 2)
#endif
			status_line (msgtxt[M_UNABLE_TO_OPEN], index_filename);
		 return (0);							 /* no file, no work to do	  */
		 }
#ifdef ATARIST
	  stat(index_filename, &f);
#else
	  (void) fstat (stream, &f);						/* get file statistics		 */
#endif
	  i = f.st_size; 			 /* size of index file, 	  */
	  idx_size = (i / sizeof (struct _ndi));		 /* number of index entries   */

	  node_index = get_size (i);
	  if (node_index == NULL)
		 {
		 status_line (msgtxt[M_NODELIST_MEM]);
		 (void) close (stream);
		 return (0);
		 }
#ifdef ATARIST
	  if (read (stream, node_index, i) != i)
#else
	  if (read (stream, node_index, i) != i)
#ifdef unix
#else
	  if (_dos_read (stream, node_index, i, &got) != 0)
#endif
#endif
		 {
		 status_line (msgtxt[M_NODELIST_READ_ERR]);
		 (void) close (stream);
		 return (0);
		 }
	  (void) close (stream);

	  index_filesize = f.st_size;				 /* Save parameters for later */
	  index_filetime = f.st_mtime;

	  /*
	   * Now take into account that the .DAT file can be bigger than we
	   * really expect it to be.  Just take the number of records, and
	   * divide into the size of the .DAT file to find the true record size
	   */
	  if (newnodelist)
		 {
		 temp[0] = '\0';						 /* "null-terminated string"  */
		 (void) strcpy (temp, net_info);		 /* take nodelist path		  */
		 (void) strcat (temp, nodelist_base);	 /* add in the file name	  */
		 (void) strcat (temp, ".DAT");			 /* add in the file name	  */
		 if (!stat (temp, &f))
			{
			extra_bytes = f.st_size / idx_size - sizeof (newnodedes);
			}
		 }


	  }

	/* we have now loaded in the index */
	
   nodeidx = (struct _ndi far *) node_index;

   /* If using the old nodelist, bypass zone stuff - it isn't there */

   if (opus_addr->Zone == (unsigned int) -1)
	  {
	  return (opus_next_zone ());
	  }
   else if ((!newnodelist) || (alias[0].ad.Zone == 0) || (no_zones))
	  {
	  opus_addr->Zone = 0;
	  }

	/* Scan through the index looking for the current zone:net/node address */

   for (i = 1; i <= idx_size; nodeoff++, nodeidx++, i++)
	  {
	  if (nodeidx->node == -2)
		 current_zone = nodeidx->net;

	  if (opus_addr->Zone > 0 && current_zone != opus_addr->Zone)
		 continue;

	  if (nodeidx->net == opus_addr->Net)				/* if a match on net,		 */
		 {
/*		 foundnet = 1;	 */ 					 /* say we found the net	  */
		 if (((opus_addr->Node == 0) && (nodeidx->node <= 0))
			 || (nodeidx->node == opus_addr->Node)) 	/* see if we found the node  */
			{
			found = 1;							 /* say we found it 		  */
			break;								 /* get out 				  */
			}
		 }

	  /*
	   * This code is good, but many people complained about not being able
	   * to add nodes to nets by adding them at the end of the nodelist.  So,
	   * I commented this stuff out for now. - Bob
	   */
/*		else*/
	  /*	  if (foundnet)*//* already past the net?	  */
	  /*		 break;*//* Yes, we failed...		  */
	  }

   if (!found)
	  return (0);

   found_zone = current_zone;					 /* Keep track of found zone */
   found_net  = nodeidx->net;					 /* Keep track of found net  */

#if defined(ATARIST) || defined(unix)
	return get_new_info(nodeoff);
#else
   if (newnodelist)
	  return get_new_info (nodeoff);
   else
	  return get_old_info (nodeoff);
#endif
}

#ifdef ALLNODELIST

static int get_old_info (recno)
unsigned recno;
{
   struct _node nodedes;						 /* desc. of node			  */
   long nodeoff;								 /* Offset into NODELIST.SYS  */
   char temp[80];								 /* where we build filenames  */
   char *c, *ch;
   int i;
   FILE *stream;

   nodeoff = (long) recno *(long) sizeof (nodedes); 	/* actual file offset		 */

   (void) strcpy (temp, net_info);						/* take nodelist path		 */
   (void) strcat (temp, nodelist_base); 			   /* add in the file name		*/
   (void) strcat (temp, ".SYS");				/* add in the file name 	 */
   if ((stream = fopen (temp, read_binary)) == NULL)	 /* OK, let's open the file   */
	  {
	  status_line (msgtxt[M_UNABLE_TO_OPEN], temp);
	  return (0);
	  }

   if (fseek (stream, nodeoff, SEEK_SET))		 /* try to point at record	  */
	  {
	  status_line (msgtxt[M_NODELIST_SEEK_ERR], temp);
	  (void) fclose (stream);
	  return (0);
	  }

   if (!fread (&nodedes, sizeof (nodedes), 1, stream))
	  {
	  status_line (msgtxt[M_NODELIST_REC_ERR], temp);
	  (void) fclose (stream);
	  return (0);
	  }
   (void) fclose (stream);

   /*
	* Copy data from nodedes into newnodedes.
	*/

   newnodedes.NodeNumber = nodedes.number;		 /* Node Number  */
   newnodedes.NetNumber = nodedes.net;			 /* Net Number	 */
   newnodedes.Cost = nodedes.cost;				 /* Cost		 */

   (void) strncpy (&newnodedes.SystemName[0], &nodedes.name[0], 20);
   newnodedes.SystemName[19] = '\0';			 /* System Name  */

   (void) strncpy (&newnodedes.PhoneNumber[0], &nodedes.phone[0], 40);
   newnodedes.PhoneNumber[39] = '\0';			 /* Phone Number */

   (void) strncpy (&newnodedes.MiscInfo[0], &nodedes.city[0], 30);
   newnodedes.MiscInfo[29] = '\0';

   ch = NULL;									 /* Pointer to password 	 */
   c = nodedes.city;							 /* Point to start of city	 */
   i = 37;										 /* No password if this = 0  */
   while (i--)									 /* Enforce that limit		 */
	  {
	  if (*c++ != '\0') 						 /* If not at end of city,	 */
		 continue;								 /* go on to next character  */
	  if (*c++ != '!')							 /* End of city, got '!' ??  */
		 break; 								 /* No, treat like a failure */
	  ch = c;									 /* Got it, point to it 	 */
	  break;									 /* Exit with success code	 */
	  }
   if (ch != NULL)
	  {
	  (void) strncpy (&newnodedes.Password[0], ch, 8);	/* Copy the password		*/
	  }
   else newnodedes.Password[0] = '\0';			 /* Else set zero length	 */

   newnodedes.RealCost = nodedes.cost;			 /* Cost					 */
   newnodedes.HubNode = 0;						 /* Don't know who is Hub	 */
   newnodedes.BaudRate = (char) (nodedes.rate / 300);	/* Baud Rate				*/
   newnodedes.ModemType = 0;					 /* Don't know modem type	 */
   newnodedes.NodeFlags = 0;					 /* Don't know any flags	 */
   newnodedes.NodeFiller = 0;					 /* Filler should be zero	 */
   return (1);
}

#endif

static int get_new_info (recno)
unsigned recno;
{
	long nodeoff;								 /* Offset into NODELIST.DAT  */
   	char temp[80];								 /* where we build filenames  */
   	FILE *stream;

   	/* actual file offset */
   	nodeoff = (long) recno * ((long) (sizeof (newnodedes) + extra_bytes));

#ifdef NEW	/* Nodelock */
	if(!nodelock || (fp_nodelist == NULL))
	{
#endif
	   	strcpy (temp, net_info);						/* take nodelist path		 */
	   	strcat (temp, nodelist_base); 			   /* add in the file name		*/
	   	strcat (temp, ".DAT");				/* add in the file name 	 */
	   	if ((stream = fopen (temp, read_binary)) == NULL)	 /* OK, let's open the file   */
		{
			status_line (msgtxt[M_UNABLE_TO_OPEN], temp);
		  	return (0);
	  	}
#ifdef NEW
		if(nodelock)
			fp_nodelist = stream;
	}
	else
		stream = fp_nodelist;
#endif

   	if (fseek (stream, nodeoff, SEEK_SET))		 /* try to point at record	  */
	{
		status_line (msgtxt[M_NODELIST_SEEK_ERR], temp);
#ifdef NEW
	  	if(!nodelock)
#endif
	  		fclose (stream);
	  	return (0);
	}

   	if (!fread (&newnodedes, sizeof (newnodedes), 1, stream))
	{
	  	status_line (msgtxt[M_NODELIST_REC_ERR], temp);
#ifdef NEW
	  	if(!nodelock)
#endif
	  		fclose (stream);
	  	return (0);
	}
#ifdef NEW
	if(!nodelock)
#endif
   		fclose (stream);
   	return (1);
}

#ifdef ALLNODELIST

static int get_TBBS_info (recno, TBBS_addr)
unsigned recno;
ADDR *TBBS_addr;
{
   struct nodels nodedes;						 /* desc. of node			  */
   struct extrastuff ext;						 /* Extra stuff for Binkley   */
   long nodeoff;								 /* Offset into NODELIST.DOG  */
   char temp[80];								 /* where we build filenames  */
   FILE *stream;

   --recno; 									 /* TBBS list starts at first
												  * record - no filler */

   nodeoff = (long) recno *(long) sizeof (nodedes); 	/* actual file offset		 */

   (void) strcpy (temp, net_info);						/* take nodelist path		 */
   (void) strcat (temp, "NODELIST.DOG");				/* add in the file name 	 */
   if ((stream = fopen (temp, read_binary)) == NULL)	 /* OK, let's open the file   */
	  {
	  status_line (msgtxt[M_UNABLE_TO_OPEN], temp);
	  return (0);
	  }

   if (fseek (stream, nodeoff, SEEK_SET))		 /* try to point at record	  */
	  {
	  status_line (msgtxt[M_NODELIST_SEEK_ERR], temp);
	  (void) fclose (stream);
	  return (0);
	  }

   if (!fread (&nodedes, sizeof (nodedes), 1, stream))
	  {
	  status_line (msgtxt[M_NODELIST_REC_ERR], temp);
	  (void) fclose (stream);
	  return (0);
	  }
   (void) fclose (stream);

   /*
	* Copy data from nodedes into newnodedes.
	*/

   newnodedes.NodeNumber = TBBS_addr->Node; 			/* Node Number	*/
   newnodedes.NetNumber = TBBS_addr->Net;				/* Net Number	*/
   newnodedes.Cost = nodedes.nodecost;			 /* Cost		 */

   (void) strncpy (&newnodedes.SystemName[0], &nodedes.nodename[0], 14);
   newnodedes.SystemName[19] = '\0';			 /* System Name  */

   (void) strncpy (&newnodedes.PhoneNumber[0], &nodedes.nodephone[0], 40);
   newnodedes.PhoneNumber[39] = '\0';			 /* Phone Number */

   (void) strncpy (&newnodedes.MiscInfo[0], &nodedes.nodecity[0], 30);
   newnodedes.MiscInfo[29] = '\0';

   newnodedes.Password[0] = '\0';				 /* Else set zero length	 */

   newnodedes.RealCost = nodedes.nodecost;		 /* Cost					 */
   newnodedes.HubNode = nodedes.nodehub;		 /* Hub */
   newnodedes.BaudRate = (char) (nodedes.nodebaud / 300);		/* Baud Rate				*/
   newnodedes.ModemType = 0;					 /* Don't know modem type	 */
   newnodedes.NodeFlags = 0;					 /* Don't know any flags	 */
   newnodedes.NodeFiller = 0;					 /* Filler should be zero	 */

   /* Now get information from secondary file */
   ++recno;
   nodeoff = (long) recno *(long) sizeof (ext);  /* actual file offset		  */

   (void) strcpy (temp, net_info);						/* take nodelist path		 */
   (void) strcat (temp, "NODELIST.EXT");				/* add in the file name 	 */
   newnodelist = 0;
   if ((stream = fopen (temp, read_binary)) == NULL)	 /* OK, let's open the file   */
	  {
	  return (1);
	  }

   if (fseek (stream, nodeoff, SEEK_SET))		 /* try to point at record	  */
	  {
	  (void) fclose (stream);
	  return (1);
	  }

   if (!fread (&ext, sizeof (ext), 1, stream))
	  {
	  (void) fclose (stream);
	  return (1);
	  }
   (void) fclose (stream);

   /*
	* Copy data from ext into newnodedes.
	*/

   (void) strncpy (newnodedes.Password, ext.password, 8);	   /* Password */

   newnodedes.NodeFlags = ext.flags1;			 /* Nodelist flags */
   newnodedes.ModemType = ext.modem;			 /* Modem type */
   newnodelist = 1; 							 /* We have all the
												  * information! */
   return (1);
}

int TBBSLookup (TBBS_addr, have_boss_data)
ADDR *TBBS_addr;								 /* zone, net and node */
int have_boss_data; 							 /* BOSS data flag	   */
{
   register struct _ndi far *nodeidx;			 /* index file				  */
/* int foundnet = 0; */ 						 /* 'found the net' flag	  */
   int found = 0;								 /* 'found the node' flag	  */
   int nodeoff = 0; 							 /* offset into nodelist.sys  */
   char temp[80];								 /* where we build filenames  */
#ifndef OS_2
   int stream;
   off_t i;
#ifndef ATARIST
   int got;
#endif	 
#else
   HFILE stream;
   USHORT i;
   USHORT got;
#endif
   struct stat f;
   int current_zone = 0;

   newnodedes.NetNumber = newnodedes.NodeNumber = 0;

   if (node_index == NULL)
	  {
	  temp[0] = '\0';							 /* "null-terminated string"  */
	  (void) strcpy (temp, net_info);			 /* take nodelist path		  */
	  (void) strcat (temp, "NODELIST.IDX"); 	 /* add in the file name 	  */
	  if ((stream = open(temp, O_RDONLY|O_BINARY)) == -1)
		 {
		 i = 0; 								 /* Need this later 		  */
#ifdef MULTIPOINT
		if( (have_boss_data != 2) && (TBBS_addr->Zone != -1))
#else
		 if (have_boss_data != 2)
#endif
			status_line (msgtxt[M_UNABLE_TO_OPEN], temp);
		 return (0);							 /* no file, no work to do	  */
		 }
#ifdef ATARIST
	  stat(temp, &f);
#else	  
	  (void) fstat (stream, &f);				 /* get file statistics		  */
#endif
	  i = f.st_size;							 /* size of index file, 	  */
	  idx_size = (i / sizeof (struct _ndi));		 /* number of index entries   */
	  node_index = get_size (i);
	  if (node_index == NULL)
		 {
		 status_line (msgtxt[M_NODELIST_MEM]);
		 (void) close (stream);
		 return (0);
		 }
#ifdef ATARIST
	  if (read (stream, node_index, i) != i)
#else
#ifdef unix
	  if (read (stream, node_index, i) != i)
#else
	  if (_dos_read (stream, node_index, i, &got) != 0)
#endif
#endif
		 {
		 status_line (msgtxt[M_NODELIST_READ_ERR]);
		 (void) close (stream);
		 return (0);
		 }
	  (void) close (stream);
	  }
   nodeidx = (struct _ndi far *) node_index;

   if (TBBS_addr->Zone == (unsigned int) -1)
	  {
	  return (opus_next_zone ());
	  }

   for (i = 1; i <= idx_size; nodeoff++, nodeidx++, i++)
	  {
	  if (nodeidx->node == -2)
	 current_zone = nodeidx->net;

	  if (TBBS_addr->Zone > 0 && current_zone != TBBS_addr->Zone)
			continue;

	  if (nodeidx->net == TBBS_addr->Net)				/* if a match on net,		 */
		 {
/*		 foundnet = 1;	*/						 /* say we found the net	  */
		 if (((TBBS_addr->Node == 0) && (nodeidx->node <= 0))
			 || (nodeidx->node == TBBS_addr->Node)) 	/* see if we found the node  */
			{
			found = 1;							 /* say we found it 		  */
			break;								 /* get out 				  */
			}
		 }

	  /*
	   * This code is good, but many people complained about not being able
	   * to add nodes to nets by adding them at the end of the nodelist.  So,
	   * I commented this stuff out for now. - Bob
	   */
/*		else*/
	  /*	  if (foundnet)*//* already past the net?	  */
	  /*		 break;*//* Yes, we failed...		  */
	  }

   if (!found)
	  return (0);

   found_zone = current_zone;					 /* Keep track of found zone */
   found_net  = nodeidx->net;					 /* Keep track of found net  */

   return get_TBBS_info (nodeoff, TBBS_addr);
}

#endif	/* ALLNODELIST */

/* 
 * I moved this here from misc.c
 */

long cost_of_call (s, e)
long s, e;
{
   long a;

   a = e - s;
   a = (a + 59) / 60;
   return (a * newnodedes.RealCost);
}

