// --------------------------------------------------------------------------
// Citadel: NetNode.CPP
//
// Code for reading NODES.CIT.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "group.h"
#include "net.h"
#include "hall.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// shownode()		show NODES.CIT settings
// readnode()		read NODES.CIT to get the node's info for logbuf
// getnode()		read NODES.CIT to get the node's info


// --------------------------------------------------------------------------
// Static Data

static discardable *nddd;
static int read_node_counter;


static Bool read_node_ddata(void)
	{
	read_node_counter++;

	if (!nddd)
		{
		if ((nddd = readData(10)) == NULL)
			{
			read_node_counter--;
			return (FALSE);
			}
		}

	return (TRUE);
	}

static void dump_node_ddata(void)
	{
	if (!(--read_node_counter))
		{
		discardData(nddd);
		nddd = NULL;
		}

	if (read_node_counter < 0)
		{
		read_node_counter = 0;
		}
	}

#define getnodemsg(x) nodemsgs[x]


// --------------------------------------------------------------------------
// shownode(): Show NODES.CIT settings.
//
// Input:
//	const NodeCitInfo *nd: The info to show.
//	Bool rmprint: TRUE to print to modem; FALSE to print to console only

void shownode(const NodeCitInfo *nd, Bool rmprint)
	{
	void cdecl (*wPrintf)(const char *fmt, ...);
	void (*wDoCR)(void);

	assert (nd);

	if (rmprint)
		{
		wPrintf = rmPrintf;
		wDoCR = doCR;
		}
	else
		{
		wPrintf = cPrintf;
		wDoCR = doccr;
		}

	if (!read_node_ddata())
		{
		wPrintf(getmsg(59));
		return;
		}

	const char **nettypes = (const char **) nddd->next->aux;
	const char **nodemsgs = (const char **) nddd->next->next->aux;

	wPrintf(getnodemsg(14), nd->GetName()); 					wDoCR();
	wPrintf(getnodemsg(15), nd->GetOldRegion());				wDoCR();
	wPrintf(getnodemsg(16), nd->GetAlias(), nd->GetLocID());	wDoCR();
	wPrintf(getnodemsg(17), nd->GetPreDial());					wDoCR();
	wPrintf(getnodemsg(18), nd->GetDialOut());					wDoCR();
	wPrintf(getnodemsg(19), bauds[nd->GetBaud()]);				wDoCR();
	wPrintf(getnodemsg(20), nd->GetLoginMacro());				wDoCR();
	wPrintf(getnodemsg(21), nd->GetDialTimeout());				wDoCR();
	wPrintf(getnodemsg(22), nd->GetProtocol()); 				wDoCR();
	wPrintf(getnodemsg(23), nd->GetWaitTimeout());				wDoCR();
	wPrintf(getnodemsg(24), nd->GetMailFileName()); 			wDoCR();

	wPrintf(getnodemsg(25), nd->GetAutoHall() > 0 ?
			HallData->GetEntry(nd->GetAutoHall())->GetName() :
			getnodemsg(28));
	wDoCR();

	wPrintf(getnodemsg(26), nd->GetAutoGroup() > 0 ?
			GroupData->GetEntry(nd->GetAutoGroup())->GetName() :
			getnodemsg(28));
	wDoCR();

	wPrintf(getnodemsg(27), GroupData->GetEntry(nd->
			GetMapUnknownGroup())->GetName());					wDoCR();

	wPrintf(getnodemsg(29), nettypes[nd->GetNetworkType()]);	wDoCR();

	wPrintf(getnodemsg(30), nd->GetCreatePacket()); 	wDoCR();
	wPrintf(getnodemsg(31), nd->GetExtractPacket());	wDoCR();
	wPrintf(getnodemsg(32), nd->IsAutoRoom());			wDoCR();
	wPrintf(getnodemsg(33), nd->GetRequest());			wDoCR();
	wPrintf(getnodemsg(34), nd->IsGateway());			wDoCR();
	wPrintf(getnodemsg(35), nd->GetFetch());			wDoCR();
	wPrintf(getnodemsg(36), nd->GetNetFail());			wDoCR();
	wPrintf(getnodemsg(37), nd->GetRedial());			wDoCR();
	wPrintf(getnodemsg(38), nd->GetOutputPace());		wDoCR();

	dump_node_ddata();
	}


// --------------------------------------------------------------------------
// readnode(): Read NODES.CIT to get the node's info
//
// Return value:
//	TRUE: Read
//	FALSE: Not read

Bool readnode(Bool option)
	{
	Bool toreturn = getnode(&TI()node, TI()CurrentUser->GetName(), CONSOLE,
			option);

	if (debug && toreturn)
		{
		doccr();
		shownode(TI()node, FALSE);
		doccr();
		}

	return (toreturn);
	}

static Bool clearNode(NodeCitInfo **node)
	{
	if (!*node)
		{
		// allocate space
		*node = new NodeCitInfo;

		if (!*node)
			{
			return (FALSE);
			}
		}

	(*node)->Clear();
	return (TRUE);
	}

void freeNode(NodeCitInfo **node)
	{
	if (*node)
		{
		delete *node;
		*node = NULL;
		}
	}


// --------------------------------------------------------------------------
// getnode(): Read NODES.CIT to get the node's info
//
// Input:
//	const char *NameOrAddrToFind: The node to load
//	ModemConsoleE Warn: MODEM to display to modem; CONSOLE to display to
//		console only; NOMODCON to display nowhere
//	Bool Option: TRUE if entry is optional; FALSE if not
//
// Output:
//	NodeCitInfo **newNode: Filled with node info. Allocated if NULL on entry

Bool getnode(NodeCitInfo **newNode, const char *NameOrAddrToFind, ModemConsoleE Warn, Bool option)
	{
	FILE *fBuf;
	char *words[256];
	int i;
	char path[80];
	int count;
	uint lineno = 0;
	int logslot;
	label NameToFind;

	if (!read_node_ddata())
		{
		cPrintf(getmsg(59));
		return (FALSE);
		}

	const char **nodekeywords = (const char **) nddd->aux;
	const char **nettypes = (const char **) nddd->next->aux;
	const char **nodemsgs = (const char **) nddd->next->next->aux;

	void cdecl (*wPrintf)(const char *fmt, ...);
	void (*wDoCR)(void);

	switch (Warn)
		{
		case MODEM:
			{
			wPrintf = mPrintf;
			wDoCR = doCR;
			break;
			}

		case CONSOLE:
			{
			wPrintf = cPrintf;
			wDoCR = doccr;
			break;
			}

		default:
			{
			wPrintf = noPrintf;
			wDoCR = noDoCR;
			break;
			}
		}

	CopyStringToBuffer(NameToFind, NameOrAddrToFind, LABELSIZE);

	if (SameString(NameOrAddrToFind, getnodemsg(2)))
		{
		if (!clearNode(newNode)) 	// if default, start fresh
			{
			dump_node_ddata();
			return (FALSE);
			}
		}
	else
		{
		// else first read in default
		getnode(newNode, getnodemsg(2), Warn, FALSE);

		if (*newNode)
			{
			logslot = nodexists(NameOrAddrToFind);

			if (logslot == CERROR)
				{
				wDoCR();
				wPrintf(getnodemsg(3), NameOrAddrToFind);

				if (strlen(NameOrAddrToFind) < 4)
					{
					char temp[4];

					strcpy(temp, NameOrAddrToFind);
					strlwr(temp);
					wPrintf(getnodemsg(4), temp, cfg.locID);
					}

				dump_node_ddata();
				return (FALSE);
				}

			if (!*LogTab->GetEntry(logslot)->GetAlias() ||
					!*LogTab->GetEntry(logslot)->GetLocID())
				{
				wDoCR();
				wPrintf(getnodemsg(5), NameOrAddrToFind);

				dump_node_ddata();
				return (FALSE);
				}

			(*newNode)->SetAlias(LogTab->GetEntry(logslot)->GetAlias());
			(*newNode)->SetLocID(LogTab->GetEntry(logslot)->GetLocID());

			label FileName;
			sprintf(FileName, getnodemsg(6), (*newNode)->GetAlias());
			(*newNode)->SetMailFileName(FileName);

			// Make sure NameToFind is a name, not address.
			CopyStringToBuffer(NameToFind,
					LogTab->GetEntry(logslot)->GetName(), LABELSIZE);

			// Support for optional nodes.cit entries.
			(*newNode)->SetName(NameToFind);
			}
		}

	if (!*newNode)
		{
		doccr();
		cPrintf(getmsg(188), getnodemsg(7));
		doccr();
		dump_node_ddata();
		return (FALSE);
		}

	compactMemory();

	sprintf(path, sbs, cfg.homepath, getnodemsg(8));

	if ((fBuf = fopen(path, FO_R)) == NULL) // ASCII mode
		{
		doccr();
		cPrintf(getmsg(15), getnodemsg(8));
		doccr();

		dump_node_ddata();
		return (option);
		}

	char line[256];
	Bool FoundOurEntry = FALSE;
	long pos = ftell(fBuf);
	while (fgets(line, 254, fBuf) != NULL)
		{
		lineno++;

		if (line[0] != '#')
			{
			pos = ftell(fBuf);
			continue;
			}

		// Don't read anything until we found our entry, except possible
		// beginnings of our entry. (#NODE...)
		if (!FoundOurEntry && strnicmp(line + 1, nodekeywords[NOK_NODE],
				strlen(nodekeywords[NOK_NODE])) != SAMESTRING)
			{
			pos = ftell(fBuf);
			continue;
			}

		// Save a copy of the line in case this is the #LOGIN macro: parse_it
		// changes what is passed to it.
		char ltmp[256];
		CopyStringToBuffer(ltmp, line, sizeof(ltmp) - 1);

		count = parse_it(words, line);

		// Look up our first word in table of keywords.
		for (i = 0; i < NOK_NUM; i++)
			{
			if (SameString(words[0] + 1, nodekeywords[i]))
				{					//^ add one for '#'
				break;
				}
			}

		if (i < NOK_NUM && !words[1][0])	// valid keywords need a param
			{
			cPrintf(getnodemsg(9), getnodemsg(8), ltoac(lineno),
					nodekeywords[i], ns);
			doccr();
			continue;
			}

		if (i == NOK_NODE)
			{
			// This is a #NODE line... if we have alread found our entry,
			// then this is the start of the next one, and we are done.
			if (FoundOurEntry)
				{
				fclose(fBuf);
				dump_node_ddata();
				return (TRUE);
				}

			// This is a #NODE line... if it is for us, then we have now
			// found our entry. If not, then we continue looking.
			if (SameString(NameToFind, words[1]))
				{
				FoundOurEntry = TRUE;
				}
			else
				{
				pos = ftell(fBuf);
				continue;
				}
			}

		switch (i)
			{
			case NOK_BAUD:
				{
				PortSpeedE PS = digitbaud(atol(words[1]));

				if (PS == PS_ERROR)
					{
					cPrintf(getnodemsg(9), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], words[1]);
					doccr();
					}
				else
					{
					(*newNode)->SetBaud(PS);
					}

				break;
				}

			case NOK_DIALOUT:
			case NOK_PHONE:
				{
				(*newNode)->SetDialOut(words[1]);

				if (strlen(words[1]) > 49)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], 50);
					doccr();
					}

				break;
				}

			case NOK_PREDIAL:
				{
				(*newNode)->SetPreDial(words[1]);

				if (strlen(words[1]) > 63)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], 64);
					doccr();
					}

				break;
				}

			case NOK_FETCH_TIMEOUT:
				{
				(*newNode)->SetFetchTimeout(atoi(words[1]));
				break;
				}

			case NOK_ZIP:
				{
				if (!words[2][0])	// need second param
					{
					cPrintf(getnodemsg(9), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], ns);
					doccr();
					continue;
					}
				else
					{
					(*newNode)->SetCreatePacket(words[1]);

					if (strlen(words[1]) > 39)
						{
						cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
								nodekeywords[i], 40);
						doccr();
						}

					(*newNode)->SetExtractPacket(words[2]);

					if (strlen(words[2]) > 39)
						{
						cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
								nodekeywords[i], 40);
						doccr();
						}
					}

				break;
				}

			case NOK_NETWORK:
				{
				for (NETTYPES j = (NETTYPES) 0; j < NET_NUM;
						j = (NETTYPES) (j + 1))
					{
					if (SameString(words[1], nettypes[j]))
						{
						break;
						}
					}

				if (j == NET_DCIT11)
					{
					j = NET_DCIT10;
					}

				if (j == NET_6_9a)
					{
					(*newNode)->SetNetworkRevisionNumber(1);
					j = NET_6_9;
					}

				if (j == NET_NUM)
					{
					cPrintf(getnodemsg(9), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], words[1]);
					doccr();
					}
				else
					{
					(*newNode)->SetNetworkType(j);
					}

				break;
				}

			case NOK_PROTOCOL:
				{
				(*newNode)->SetProtocol(words[1][0]);
				break;
				}

			case NOK_AUTOHALL:
				{
				(*newNode)->SetAutoHall(hallexists(words[1]));

				if (strlen(words[1]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				break;
				}

			case NOK_AUTOGROUP:
				{
				(*newNode)->SetAutoGroup(FindGroupByName(words[1]));

				if (strlen(words[1]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				break;
				}

			case NOK_MAPUNKGROUP:
				{
				g_slot GroupSlot = FindGroupByName(words[1]);

				if (GroupSlot == CERROR)
					{
					GroupSlot = 1;		// Reserved_2
					}

				(*newNode)->SetMapUnknownGroup(GroupSlot);

				if (strlen(words[1]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				break;
				}

			case NOK_LOGIN:
				{
				(*newNode)->SetLoginMacro(ltmp +
						strlen(nodekeywords[NOK_LOGIN]) + 1);
				break;
				}

			case NOK_NODE:
				{
				// A bit silly.
				(*newNode)->SetName(words[1]);

				if (strlen(words[1]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				if (count > 2)
					{
					(*newNode)->SetOldRegion(words[2]);

					if (strlen(words[2]) > LABELSIZE)
						{
						cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
								nodekeywords[i], LABELSIZE + 1);
						doccr();
						}
					}
				else
					{
					(*newNode)->SetOldRegion(ns);
					}

				(*newNode)->FreeMappedGroups();
				(*newNode)->SetRoomOffset(0);

				break;
				}

			case NOK_DIAL_TIMEOUT:
				{
				(*newNode)->SetDialTimeout(atoi(words[1]));
				break;
				}

			case NOK_REQUEST:
				{
				(*newNode)->SetRequest(atoi(words[1]));
				break;
				}

			case NOK_WAIT_TIMEOUT:
				{
				(*newNode)->SetWaitTimeout(atoi(words[1]));
				break;
				}

			case NOK_AUTOROOM:
				{
				(*newNode)->SetAutoRoom(atoi(words[1]));
				break;
				}

			case NOK_VERBOSE:
				{
				(*newNode)->SetDefaultVerbose();

				const char **verbosekeywords =
						(const char **) nddd->next->next->next->aux;

				for (int j = 1; j < count; j++)
					{
					if (SameString(words[j], getnodemsg(39)))
						{
						(*newNode)->SetDefaultVerbose();
						}
					else if (SameString(words[j], getnodemsg(40)))
						{
						(*newNode)->SetDefaultVerbose();
						(*newNode)->SetVerbose(NCV_FILE69INFULL, TRUE);
						(*newNode)->SetVerbose(NCV_NOACCESS, TRUE);
						}
					else if (SameString(words[j], getnodemsg(41)))
						{
						(*newNode)->SetDefaultVerbose();
						(*newNode)->SetVerbose(NCV_FILE69INFULL, TRUE);
						(*newNode)->SetVerbose(NCV_NOACCESS, TRUE);
						(*newNode)->SetVerbose(NCV_ROOMCREATED, TRUE);
						(*newNode)->SetVerbose(NCV_ROOMNOTCREATED, TRUE);
						(*newNode)->SetVerbose(NCV_NETIDNOTFOUND, TRUE);
						(*newNode)->SetVerbose(NCV_NONETIDONSYSTEM, TRUE);
						}
					else
						{
						const char *Keyword;
						Bool NewSetting;

						if (words[j][0] == '!')
							{
							Keyword = words[j] + 1;
							NewSetting = FALSE;
							}
						else
							{
							Keyword = words[j];
							NewSetting = TRUE;
							}

						NCV_Type TestType;

						for (TestType = (NCV_Type) 0; TestType < NCV_MAX;
								TestType = (NCV_Type) (TestType + 1))
							{
							if (SameString(Keyword, verbosekeywords[TestType]))
								{
								(*newNode)->SetVerbose(TestType, NewSetting);
								break;
								}
							}

						if (TestType == NCV_MAX)
							{
							if (SameString(Keyword, getnodemsg(13)))
								{
								// ALL
								for (TestType = (NCV_Type) 0;
										TestType < NCV_MAX;
										TestType = (NCV_Type) (TestType + 1))
									{
									(*newNode)->SetVerbose(TestType,
											NewSetting);
									}
								}
							else
								{
								cPrintf(getnodemsg(12), getnodemsg(8),
										ltoac(lineno), words[0], Keyword);
								doccr();
								}
							}
						}
					}

				break;
				}

			case NOK_REDIAL:
				{
				(*newNode)->SetRedial(atoi(words[1]));
				break;
				}

			case NOK_ROOM:
				{
				(*newNode)->SetRoomOffset(pos);
				fclose(fBuf);
				dump_node_ddata();
				return (TRUE);
				}

			case NOK_GROUP:
				{
				if (!(*newNode)->AddMappedGroups(words[1], words[2]))
					{
					cPrintf(getnodemsg(10), words[1]);
					doccr();
					}

				if (strlen(words[1]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				if (strlen(words[2]) > LABELSIZE)
					{
					cPrintf(getnodemsg(1), getnodemsg(8), ltoac(lineno),
							nodekeywords[i], LABELSIZE + 1);
					doccr();
					}

				break;
				}

			case NOK_GATEWAY:
				{
				(*newNode)->SetGateway(atoi(words[1]));
				break;
				}

			case NOK_FETCH:
				{
				(*newNode)->SetFetch(atoi(words[1]));
				break;
				}

			case NOK_NETFAIL:
				{
				(*newNode)->SetNetFail(atoi(words[1]));
				break;
				}

			case NOK_OUTPUTPACE:
				{
				(*newNode)->SetOutputPace(atoi(words[1]));
				break;
				}

			default:
				{
				cPrintf(getnodemsg(11), getnodemsg(8), ltoac(lineno),
						words[0]);
				doccr();

				break;
				}
			}

		pos = ftell(fBuf);
		}

	fclose(fBuf);

	if (!FoundOurEntry && !option && debug)
		{
		wDoCR();
		wPrintf(getmsg(206), NameOrAddrToFind);
		wDoCR();
		}

	dump_node_ddata();

	if (option)
		{
		return (TRUE);
		}
	else
		{
		return (FoundOurEntry);
		}
	}
