// --------------------------------------------------------------------------
// Citadel: NetMail.CPP
//
// Network-mail related code.

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "auxtab.h"
#include "log.h"
#include "net.h"
#include "msg.h"
#include "cfgfiles.h"
#include "blurbs.h"
#include "events.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// asknode()			performs fill in the blanks by reading ROUTE.CIT
// BeenThere()			Checks to see if nodename or alias.locID is in
// buildaddress()		builds ROUTE.CIT #ADDRESS lines from the msg
// isaddress()			returns true if it looks like an address
// makeaddress()		return full address after joining two strings
// FindRoomForNetMail() find the room or net id for mail
// oaddress()			attempts to pull the origin address from a message
// parse_address()		splits a string into alias & locID
// parseNetAddress()	Parse a net address from user input.
// parse_recipient()	break string up into pieces we can use
// save_mail()			save a message bound for another system
// search_route_cit()	returns the route or alias specified

void PrepareMsgScriptInit(Message *Msg);
Bool CheckMsgScript(Message *Msg);
void InitMsgScript(ScriptInfoS *si);

static void parse_recipient(const char *str, char *user, char *node, char *region);
static Bool search_route_cit(char *str, const char *srch);


// --------------------------------------------------------------------------
// asknode(): This routine performs fill in the blanks by reading ROUTE.CIT
//	and asking the user questions.
//
// Return value:
//	TRUE if match found
//	FALSE if no match found

static Bool asknode(char *rnode, char *rregion, char *raddress)
	{
	FILE *fBuf;
	char line[95];
	char *words[256];
	char path[80];
	label previous_raddress;
	label previous_rnode;
	label previous_rregion;
	int count;
	char raddress_alias[4];
	char raddress_locID[5];
	char routecit_alias[4]; // [xxx].xxxx
	char routecit_locID[5]; // xxx.[xxxx]
	char query[100];

	*raddress_alias = '\0';
	*routecit_alias = '\0';
	*previous_rnode = '\0';
	*raddress_locID = '\0';
	*previous_raddress = '\0';
	*routecit_locID = '\0';
	*previous_rregion = '\0';

	normalizeString(raddress);
	if (*raddress)
		{
		parse_address(raddress, raddress_alias, raddress_locID, TRUE);
		}

	sprintf(path, sbs, cfg.homepath, citfiles[C_ROUTE_CIT]);

	if ((fBuf = fopen(path, FO_R)) == NULL) // ASCII mode
		{
		mPrintfCR(getmsg(15), citfiles[C_ROUTE_CIT]);
		return (FALSE);
		}

	while (fgets(line, 90, fBuf) != NULL)
		{
		if (strnicmp(line, getmsg(897), 8) != SAMESTRING)
			{
			continue;
			}

		count = parse_it(words, line);

		if (count < 4)	// too few parameters
			{
			mPrintfCR(getmsg(516));
			}

		if (!isaddress(words[1])) // naughty alias
			{
			mPrintf(getmsg(515));
			mPrintfCR(getmsg(1489), words[1]);
			}

		// words[1] == xxx.xxxx
		// words[2] == nodename
		// words[3] == region

		// We must match both.

		// This code is less than optimal but it works and I can read it.
		if ( (*rnode && *rregion) || (*raddress_alias && *raddress_locID))
			{
			if (*rnode) // Matching nodes and regions
				{
				if (SameString(rnode, words[2]) &&
						SameString(rregion, words[3]))
					{
					// We found a match. Copy stuff and get out.
					CopyStringToBuffer(rnode, words[2], LABELSIZE);
					CopyStringToBuffer(rregion, words[3], LABELSIZE);
					CopyStringToBuffer(raddress, words[1], LABELSIZE);

					fclose(fBuf);
					return (TRUE);
					}
				}
			else if (*raddress) // Matching full alias
				{
				// if we want to we could do a parse_address
				// on "words[1]" and compare the small strings
				// this would allow us to use addresses such as
				// "tgi" in ROUTE.CIT

				if (SameString(raddress, words[1]))
					{
					// We found a match. Copy stuff and get out.
					CopyStringToBuffer(rnode, words[2], LABELSIZE);
					CopyStringToBuffer(rregion, words[3], LABELSIZE);
					CopyStringToBuffer(raddress, words[1], LABELSIZE);

					fclose(fBuf);

					return (TRUE);
					}
				}
			}
		// We are trying for a partial match.
		else if (*rnode)
			{
			if (SameString(rnode, words[2]))
				{
				if (!*previous_rnode)
					{
					// Fill in temp var and keep Searching
					CopyStringToBuffer(previous_rnode, words[2], LABELSIZE);
					CopyStringToBuffer(previous_rregion, words[3], LABELSIZE);
					CopyStringToBuffer(previous_raddress, words[1], LABELSIZE);
					}
				else
					{
					sprintf(query, getmsg(514), previous_rnode,
							previous_rregion, previous_raddress);

					if (getYesNo(query, 0))
						{
						// Fill in info and get out.
						CopyStringToBuffer(rnode, previous_rnode, LABELSIZE);
						CopyStringToBuffer(rregion, previous_rregion, LABELSIZE);
						CopyStringToBuffer(raddress, previous_raddress, LABELSIZE);

						fclose(fBuf);
						return (TRUE);
						}
					else
						{
						// Fill in temp var and keep Searching
						CopyStringToBuffer(previous_rnode, words[2], LABELSIZE);
						CopyStringToBuffer(previous_rregion, words[3], LABELSIZE);
						CopyStringToBuffer(previous_raddress, words[1], LABELSIZE);
						}
					}
				}
			}
		else if (*raddress_alias)
			{
			// words[1] == xxx.xxxx
			// words[2] == nodename
			// words[3] == region

			parse_address(words[1], routecit_alias, routecit_locID, TRUE);

			if (SameString(raddress_alias, routecit_alias))
				{
				if (!*previous_rnode)
					{
					// Fill in temp var and keep Searching
					CopyStringToBuffer(previous_rnode, words[2], LABELSIZE);
					CopyStringToBuffer(previous_rregion, words[3], LABELSIZE);
					CopyStringToBuffer(previous_raddress, words[1], LABELSIZE);
					}
				else
					{
					sprintf(query, getmsg(514), previous_rnode,
							previous_rregion, previous_raddress);

					if (getYesNo(query, 0))
						{
						// Fill in info and get out.
						CopyStringToBuffer(rnode, previous_rnode, LABELSIZE);
						CopyStringToBuffer(rregion, previous_rregion, LABELSIZE);
						CopyStringToBuffer(raddress, previous_raddress, LABELSIZE);

						fclose(fBuf);
						return (TRUE);
						}
					else
						{
						// Fill in temp var and keep Searching
						CopyStringToBuffer(previous_rnode, words[2], LABELSIZE);
						CopyStringToBuffer(previous_rregion, words[3], LABELSIZE);
						CopyStringToBuffer(previous_raddress, words[1], LABELSIZE);
						}
					}
				}
			}
		}

	if (*previous_rnode)	// Default to previous partial match
		{
		// Fill in info and get out.
		// We could ask them if this is really what they wanted.
		CopyStringToBuffer(rnode, previous_rnode, LABELSIZE);
		CopyStringToBuffer(rregion, previous_rregion, LABELSIZE);
		CopyStringToBuffer(raddress, previous_raddress, LABELSIZE);
		fclose(fBuf);
		return (TRUE);
		}

	fclose(fBuf);
	return (FALSE);
	}


// --------------------------------------------------------------------------
// BeenThere(): Checks to see if nodename or alias.region is in path. Will
//	optionally copy next thing in path to the 'next' variable.

Bool BeenThere(const char *path, const char *nodename, const char *alias,
		const char *locID, char *next)
	{
	int i;
	char current_locID[5];
	Bool foundit = FALSE;

	*current_locID = 0;

	if (next)
		{
		*next = 0;
		}

	// Look for the node's name in the path. Note that this will deliver a
	// false positive if the node we are looking for is a substring of a
	// node that is in the path.
	if (*nodename)
		{
		i = substr(path, nodename, FALSE);

		if (i != CERROR)
			{
			foundit = TRUE;
			i += strlen(nodename) + 1;
			}
		}

	if (!foundit)
		{
		// Look for the node's address in the path.

		for (i = 0; path[i] && !foundit;)
			{
			int k;

			for (k = i; path[i] && (path[i] != '!'); i++);

			if (i - k < 9)
				{
				char address[9];

				CopyStringToBuffer(address, path + k, i - k);

				if (isaddress(address))
					{
					char path_alias[4], path_locID[5];
					parse_address(address, path_alias, path_locID, TRUE);

					if (*path_locID)
						{
						CopyStringToBuffer(current_locID, path_locID, 4);
						}

					if (SameString(alias, path_alias) &&
							SameString(locID, current_locID))
						{
						foundit = TRUE;
						}
					}
				}

			if (path[i] == '!')
				{
				i++;
				}
			}
		}

	if (foundit && (next != NULL))
		{
		int j;

		for (j = 0; path[i] && (path[i] != '!') && (j < LABELSIZE); i++, j++)
			{
			next[j] = path[i];
			}

		next[j] = 0;
		}

#ifdef TOGGLE_FOR_VPP

// Note:	This is ONLY a guess as to how it should be done. Fix this before
//			using.

	// this part should be toggleable for people like vpp who want to net
	// anything irregardless of sending things back to their source locID

	// IRREGARDLESS is considered nonstandard because it is
	// ------------ redundant: once the negative idea is
	//				expressed by the -less ending, it is
	//				excessive to add the negative ir-
	//				prefix to express the same.

	if (*locID && SameString(locID, current_locID) && (strlen(locID) < 5))
		{
		sprintf(lookfor, getmsg(1490), locID);

		if (IsSubstr(path, lookfor))
			{
			return (TRUE);
			}
		}
#else
	return (foundit);
#endif
	}


// --------------------------------------------------------------------------
// buildaddress(): builds ROUTE.CIT #ADDRESS lines from the msg base in the
//	format #ADDRESS "xxx.xxxx" "Nodename" "RegionName"

#define UPDATE_TIME 3

void buildaddress(void)
	{
	Message *Msg = new Message;

	if (Msg)
		{
		// Used to keep track of addresses that have been found so far.
		struct addr
			{
			struct addr *next;
			char address[9];
			};

		FILE *fBuf;
		char line[95];
		char *words[256];
		char path[80];
		int count;
		m_slot i, size_table;
		struct addr *addressList = NULL;
		struct addr *thisAddress = NULL;
		char found;
		ulong loc;
		char raddress[9];
		m_slot progress;
		int percent, nowpercent;
		long tm;

		compactMemory(1);

		sprintf(path, sbs, cfg.homepath, citfiles[C_ROUTE_CIT]);

		if ((fBuf = fopen(path, FO_AP)) == NULL)	// ASCII mode
			{
			cPrintf(getmsg(15), citfiles[C_ROUTE_CIT]);
			doccr();
			delete Msg;
			return;
			}

		fseek(fBuf, 0l, SEEK_SET);

		while (fgets(line, 90, fBuf) != NULL)
			{
			if (strnicmp(line, getmsg(897), 8) != SAMESTRING)
				{
				continue;
				}

			count = parse_it(words, line);

			if (count < 4)	// too few parameters
				{
				cPrintf(getmsg(516));
				doccr();
				}

			if (!isaddress(words[1])) // naughty alias
				{
				cPrintf(getmsg(515));
				cPrintf(getmsg(1489), words[1]);
				doccr();
				}

			// Copy address info into memory
			thisAddress = (addr *) addLL((void **) &addressList,
					sizeof(*addressList));

			if (thisAddress == NULL)
				{
				cPrintf(getmsg(188), getmsg(1313));
				doccr();
				}
			else
				{
				// words[1] == xxx.xxxx
				// words[2] == nodename
				// words[3] == region
				CopyStringToBuffer(thisAddress->address, words[1], 8);
				}
			}

		size_table = sizetable();
		i = (m_slot) indexslot(cfg.newestbuildaddress);

		if (i == M_SLOT_ERROR)
			{
			i = 0;
			}

		doccr();
		cPrintf(getmsg(1016));
		progress = (size_table - i);	// # of new messsages
		percent = 0;

		if (!progress)
			{
			progress = 1;
			}

		time(&tm);
		nowpercent = percent;
		int pctLength = 2;

		for (; i < size_table; i++)
			{
			if (time(NULL) - tm >= UPDATE_TIME)
				{
				time(&tm);

				nowpercent = 99 - (int)(((m_slot) 99 * (size_table - i)) / progress);

				if (nowpercent != percent)
					{
					percent = nowpercent;

					while (pctLength--)
						{
						outConRawBs();
						TI()SerialPort.Output('\b');
						}

					label pctStr;
					sprintf(pctStr, getmsg(47), percent);
					pctLength = strlen(pctStr);

					cPrintf(pctStr);
					}
				}

			if (getFlags(i)->IsNetworked())
				{
				loc = getLocation(i);

				bufferedSeek(msgfl, loc);

				Msg->ReadHeader(RMC_NORMAL, NULL);

				CopyStringToBuffer(raddress, oaddress(Msg), 8);

				if (!isaddress(raddress))
					{
					continue;
					}

				found = FALSE;
				for (thisAddress = addressList;
						thisAddress && !found;
						thisAddress = (addr *) getNextLL(thisAddress))
					{
					if (SameString(thisAddress->address, raddress))
						{
						found = TRUE;
						}
					}

				if (!found)
					{
					// Copy address info into memory
					thisAddress = (struct addr *) addLL((void **) &addressList,
							sizeof(*addressList));

					if (thisAddress == NULL)
						{
						cPrintf(getmsg(188), getmsg(1313));
						doccr();
						}
					else
						{
						CopyStringToBuffer(thisAddress->address, raddress, 8);
						}

					char yuk[128], yuk2[128], yuk3[128];

					mkDblBck(raddress, line);
					sprintf(yuk, getmsg(1491), line);

					mkDblBck(Msg->GetOriginNodeName(), line);
					sprintf(yuk2, getmsg(1491), line);

					mkDblBck(Msg->GetOriginRegion(), line);
					sprintf(yuk3, getmsg(1491), line);

					sprintf(line, getmsg(1492), bn, getmsg(897), yuk, yuk2,
							yuk3);
					fwrite(line, sizeof(char), strlen(line), fBuf);

					if (cfg.NewNodeAlert)
						{
						label Name, Region, Phone, MsgNo;

						CopyStringToBuffer(Name, Msg->GetOriginNodeName(),
								LABELSIZE);
						CopyStringToBuffer(Region, Msg->GetOriginRegion(),
								LABELSIZE);
						CopyStringToBuffer(Phone, Msg->GetOriginPhoneNumber(),
								LABELSIZE);
						CopyStringToBuffer(MsgNo, Msg->GetLocalID(),
								LABELSIZE);

						Msg->ClearAll();
						Msg->SetRoomNumber(AIDEROOM);

						Msg->SetTextWithFormat(getmsg(1391), MsgNo, Name,
								Region, raddress, Phone);

						systemMessage(Msg);
						}
					}
				}
			}

		while (pctLength--)
			{
			outConRawBs();
			TI()SerialPort.Output('\b');
			}

		cPrintf(getmsg(47), 100);
		cPrintf(getmsg(1013));
		doccr();

		cfg.newestbuildaddress = cfg.newest;

		disposeLL((void **) &addressList);
		fclose(fBuf);

		delete Msg;
		}
	else
		{
		cPrintf(getmsg(188), getmsg(1313));
		}
	}


// --------------------------------------------------------------------------
// isaddress(): Examines string and returns TRUE if it looks like an address.

Bool isaddress(const char *str)
	{
	int len;
	int dotpos;
	Bool FoundDot = FALSE;
	int i;

	len = strlen(str);

	// Don't be fooled by "St. Dism"
	if (strchr(str, ' ') != NULL)
		{
		return (FALSE);
		}

	for (i = 0; i < len; i++)
		{
		if (str[i] == '.')
			{
			// we can only have one dot.
			if (FoundDot)
				{
				return (FALSE);
				}

			dotpos = i + 1;
			FoundDot = TRUE;
			}
		}

	if	(	// 1, 2, or 3 chars, no dots
			((len > 0) && (len < 4) && !FoundDot)

			||

			// 5, 6, 7, or 8 chars, there is a dot, and it is at char
			// pos 2, 3, or 4, and not too far from the end.
			(	(len > 4) && (len < 9) && FoundDot && (dotpos > 1) &&
				(dotpos < 5) && ((len - dotpos) < 5)
			)
		)
		{
		return (TRUE);
		}

	return (FALSE);
	}


// --------------------------------------------------------------------------
// makeaddress(): Return full address after joining two strings

const char *makeaddress(const char *alias, const char *locID)
	{
	TI()netMailAddress[0] = '\0';

	if (*alias || *locID)
		{
		CopyStringToBuffer(TI()netMailAddress, alias, 3);

		strcat(TI()netMailAddress, getmsg(1433));

		char temp[5];
		CopyStringToBuffer(temp, locID, 4);
		strcat(TI()netMailAddress, temp);

		strlwr(TI()netMailAddress);
		}

	return (TI()netMailAddress);
	}


// --------------------------------------------------------------------------
// FindRoomForNetMail(): Find the room or net id for mail.

r_slot FindRoomForNetMail(const char *str)
	{
	int i;

	if (cfg.netmail)
		{
		i = RoomExists(str);

		if (i == CERROR)
			{
			i = IdExists(str, 0);
			}

		if (i == CERROR)
			{
			i = MAILROOM;
			}
		}
	else
		{
		i = MAILROOM;
		}

	return (i);
	}


// --------------------------------------------------------------------------
// oaddress(): Attempts to pull the origin address from msgbuf.

const char *oaddress(const Message *mb)
	{
	assert(mb);

	const char *FPath = mb->GetFromPath();

	int i;

	for (i = 0; FPath[i] && (FPath[i] != '!') && (i < 8); i++)
		{
		TI()netMailAddress[i] = FPath[i];
		}

	TI()netMailAddress[i] = 0;

	// If we filled up the address, make sure there is not more. If there is
	// more, then the address is not an address.
	if (i == 9 && !(FPath[i] == '!' || FPath[i] == 0))
		{
		TI()netMailAddress[0] = 0;
		}

	if (!isaddress(TI()netMailAddress))
		{
		*TI()netMailAddress = 0;
		}

	return (TI()netMailAddress);
	}


// --------------------------------------------------------------------------
// parse_address(): Splits a string into alias & locID.

void parse_address(const char *str, char *alias, char *locID, Bool option)
	{
	char periods[2];
	int i;
	int j;
	int len;
	int numperiods = 0;

	alias[0] = 0;
	locID[0] = 0;

	len = strlen(str);

	for (i = len - 1; i >= 0 && (numperiods < 2) ; i--)
		{
		if (str[i] == '.')
			{
			periods[numperiods] = i;
			numperiods++;
			}
		}

	// No periods, Copy alias from beginning, default to SEA or cfg.locID
	if (!numperiods)
		{
		for (i = 0; i < 3 && str[i] && !ispunct(str[i]) && !isspace(str[i]); i++)
			{
			alias[i] = str[i];
			}
		alias[i] = '\0';

		if (!option && alias[0])
			{
			strcpy(locID, cfg.locID[0] ? cfg.locID : getmsg(1493));
			}
		}
	else
		{
		// Copy locID from first dot from the end
		for (i = periods[0]+1, j = 0; ((i - periods[0]) < 5) && str[i] && !ispunct(str[i]) && !isspace(str[i]); i++, j++)
			{
			locID[j] = str[i];
			}
		locID[j] = '\0';


		if (numperiods > 1)
			{
			// Copy alias from second dot from the end
			for (i = periods[1]+1, j = 0; ((i - periods[1]) < 4) && str[i] && !ispunct(str[i]) && !isspace(str[i]); i++, j++)
				{
				alias[j] = str[i];
				}
			alias[j] = '\0';
			}
		else
			{
			// Copy alias from beginning of string
			for (i = 0; i < 3 && str[i] && !ispunct(str[i]) && !isspace(str[i]); i++)
				{
				alias[i] = str[i];
				}
			alias[i] = '\0';
			}
		}

	strlwr(alias);
	strlwr(locID);
	}


// --------------------------------------------------------------------------
// parseNetAddress(): Parse a net address from user input.

void parseNetAddress(const char *str, char *ruser, char *rnode,
		char *rregion, char *raddress)
	{
	char path[80];

	// Blank 'em out just in case
	*rnode = 0;
	*ruser = 0;
	*raddress = 0;
	*rregion = 0;

	parse_recipient(str, ruser, rnode, rregion);

	if (!*ruser)
		{
		CopyStringToBuffer(ruser, getmsg(1020), LABELSIZE);
		}

	if (!*rnode)
		{
		if (*raddress)
			{
			mPrintfCR(getmsg(724), raddress);
			}

		return;
		}

	if (isaddress(rnode))
		{
		strcpy(raddress, rnode);
		*rnode = '\0';
		}

	sprintf(path, sbs, cfg.homepath, citfiles[C_ROUTE_CIT]);
	if (!filexists(path)) // If there is no ROUTE.CIT
		{
		dispBlb(B_NOROUTE);
		return;
		}

	if (!asknode(rnode, rregion, raddress))
		{
		if (*rnode && nodexists(rnode) == CERROR)
			{
			mPrintf(getmsg(894));

			if (*raddress)
				{
				mPrintf(pcts, raddress);
				}
			else if (*rnode && *rregion)
				{
				mPrintf(getmsg(1494), rnode, rregion);
				}
			else
				{
				mPrintf(pcts, rnode);
				}

			mPrintfCR(getmsg(1495));
			}
		}
	}


// --------------------------------------------------------------------------
// parse_recipient(): Break string up into pieces we can use.

static void parse_recipient(const char *str, char *user, char *node, char *region)
	{
	int posAt; 

	*user = '\0';
	*node = '\0';
	*region = '\0';

	posAt = strpos('@', str);

	if (posAt)
		{
		posAt--;

		strncpy(user, str, min(posAt, LABELSIZE));
		user[min(posAt, LABELSIZE)] = '\0';

		posAt++;

		str += posAt;

		if (str)
			{
			int posComma;

			posComma = strpos(',', str);

			if (posComma)
				{
				posComma--;

				strncpy(node, str, min(posComma, LABELSIZE));
				node[min(posComma, LABELSIZE)] = '\0';

				posComma++;

				str += posComma;

				strcpy(region, str);
				}
			else
				{
				CopyStringToBuffer(node, str, LABELSIZE);
				}
			}
		}
	else
		{
		CopyStringToBuffer(user, str, LABELSIZE);
		}

	normalizeString(user);
	normalizeString(node);
	normalizeString(region);
	}

static Bool IsSameLocID(const char *Addr1, const char *Addr2)
	{
	const char *DP1 = strchr(Addr1, '.');
	const char *DP2 = strchr(Addr2, '.');

	if (DP1 && DP2)
		{
		return (SameString(DP1, DP2));
		}
	else
		{
		return (FALSE);
		}
	}


// --------------------------------------------------------------------------
// save_mail(): Save a message bound for another system.
//
// Return value:
//	TRUE if was able to route somewhere
//	FASLE if was not able to route anywhere

Bool save_mail(Message *Msg, Bool savestub)
	{
	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(15),
				*Msg->GetForward() ? Msg->GetForward() : Msg->GetToUser(),
				Msg->GetToNodeName());

		if (*Msg->GetToPath())
			{
			doccr();
			cPrintf(getdbmsg(16), Msg->GetToPath());
			}

		if (*Msg->GetFromPath())
			{
			doccr();
			cPrintf(getmsg(17), Msg->GetFromPath());
			}
		}

	Message *SaveMessage = new Message;

	if (SaveMessage)
		{
		label tosystem, oldtosystem;
		if (savestub)
			{
			SaveStub(Msg, getmsg(1012), cfg.poopdebug);
			}

		// ------------------------- Determine route ------------------------

		// Use Forced Path
		if (*Msg->GetToPath())
			{
			if (debug)
				{
				doccr();
				cPrintf(getdbmsg(18));
				}

			Bool its_in_there;

			// Find next node in path
			its_in_there = BeenThere(Msg->GetToPath(), cfg.nodeTitle,
					cfg.alias, cfg.locID, tosystem);

			if (!(its_in_there && *tosystem))
				{
				if (debug)
					{
					cPrintf(getdbmsg(19));
					}

				CopyStringToBuffer(tosystem, Msg->GetToNodeName(), LABELSIZE);
				}
			}
		else
			{
			// to where are we sending it
			CopyStringToBuffer(tosystem, Msg->GetToNodeName(), LABELSIZE);
			}

		CopyStringToBuffer(oldtosystem, tosystem, LABELSIZE);

		Bool FoundRoute = search_route_cit(tosystem, getmsg(898));

		if (debug)
			{
			doccr();
			cPrintf(getdbmsg(20), tosystem);
			}

		const int nodenum = nodexists(tosystem);
		if (nodenum != CERROR)
			{
			FILE *fl;
			char filename[100];

			sprintf(filename, getmsg(1387), cfg.transpath,
					LogTab->GetEntry(nodenum)->GetAlias());

			if ((fl = fopen(filename, FO_AB)) == NULL)
				{
				if (debug)
					{
					doccr();
					cPrintf(getdbmsg(21), filename);
					}

				delete SaveMessage;
				return (FALSE);
				}

			*SaveMessage = *Msg;

			PrepareMsgScriptInit(Msg);
			strcpy(TI()ContextSensitiveScriptText,
					LogTab->GetEntry(nodenum)->GetName());
			doEvent(EVT_WRITENETMAIL, InitMsgScript);

			if (CheckMsgScript(Msg))
				{
				Msg->Store(SMC_NETWORK, fl);
				}

			*Msg = *SaveMessage;

			fclose(fl);
			}
		else
			{
			if (debug)
				{
				doccr();
				cPrintf(getdbmsg(22));
				}

			if (FoundRoute)
				{
				if (debug)
					{
					cPrintf(getdbmsg(23));
					}

				// We have a #ROUTE to a place we don't net with.
				SaveMessage->SetTextWithFormat(getmsg(899), oldtosystem,
						tosystem);

				SaveMessage->SetRoomNumber(AIDEROOM);

				systemMessage(SaveMessage);

				SaveMessage->ClearAll();
				}

			//	spasm!!!! goes through userlog starting with most recent call
			//	until it reaches a call where the call time and current time
			//	are greater than expire days apart or the end of the userlog
			//	is found.  send it to each node found, skipping any that are
			//	already in the path.


			// make sure all outgoing mail has the same time... needed for
			// duplicate checking.
			if (!*Msg->GetCreationTime())
				{
				label Buffer;
				Msg->SetCreationTime(ltoa(time(NULL), Buffer, 10));
				}

			l_slot LogSlot;
			Bool done;

			// New development (94Apr18) - look at #ADDRESSes and locIDs
			char ToLocID[5];
			*ToLocID = '\0';

			if (*Msg->GetDestinationAddress() ||
					isaddress(Msg->GetToNodeName()))
				{
				char ToAddress[9];

				if (*Msg->GetDestinationAddress())
					{
					CopyStringToBuffer(ToAddress,
							Msg->GetDestinationAddress(), 8);
					}
				else
					{
					CopyStringToBuffer(ToAddress,
							Msg->GetToNodeName(), 8);
					}

				if (IsSameLocID(cfg.Address, ToAddress))
					{
					strcpy(ToLocID, cfg.locID);
					if (debug)
						{
						doccr();
						cPrintf(getdbmsg(24));
						}
					}
				else
					{
					if (debug)
						{
						doccr();
						cPrintf(getdbmsg(25));
						}

					for (LogSlot = TI()loggedIn ? 1 : 0, done = FALSE;
							LogSlot < cfg.MAXLOGTAB && !done; LogSlot++)
						{
						LogEntry1 Log1;

						if (LogTab->GetEntry(LogSlot)->IsNode() &&
								Log1.Load(LogTab->GetEntry(LogSlot)->GetLogIndex()))
							{
							if (cfg.expire && Log1.GetCallTime() <
									(time(NULL) - ((long) cfg.expire * SECSINDAY)))
								{
								done = TRUE;
								}
							else
								{
								char NodesAddress[9];
								sprintf(NodesAddress, getmsg(1443),
										Log1.GetAlias(), Log1.GetLocID());

								if (IsSameLocID(NodesAddress, ToAddress))
									{
									strcpy(ToLocID, Log1.GetLocID());
									done = TRUE;
									if (debug)
										{
										doccr();
										cPrintf(getdbmsg(26), ToLocID);
										}
									}
								}
							}
						}
					}
				}

			*SaveMessage = *Msg;

			// if we are logged in, don't start with us: we are not going
			// to send mail to us.	we are always at position 0 if logged in.
			// we do this because our last login time is the time of the
			// last login, which could mess up our expire testing.
			for (LogSlot = TI()loggedIn ? 1 : 0, done = FALSE;
					LogSlot < cfg.MAXLOGTAB && !done; LogSlot++)
				{
				if (LogTab->GetEntry(LogSlot)->IsInuse() &&
						LogTab->GetEntry(LogSlot)->IsNode())
					{
					LogEntry1 Log1;

					if (Log1.Load(LogTab->GetEntry(LogSlot)->GetLogIndex()))
						{
						if (debug)
							{
							doccr();
							cPrintf(getdbmsg(27), Log1.GetName());
							}

						// Expired
						if (cfg.expire && Log1.GetCallTime() <
								(time(NULL) - ((long) cfg.expire * SECSINDAY)))
							{
							if (debug)
								{
								doccr();
								cPrintf(getdbmsg(28));
								}

							done = TRUE;
							}
						else
							{
							if (*ToLocID && !SameString(ToLocID,
									Log1.GetLocID()))
								{
								if (debug)
									{
									doccr();
									cPrintf(getdbmsg(29));
									}

								continue;
								}

							label Poop;
							CopyStringToBuffer(Poop, Log1.GetName(), LABELSIZE);

							if (
									!BeenThere(Msg->GetFromPath(),
										Log1.GetName(), Log1.GetAlias(),
										Log1.GetLocID(), NULL) &&

									!search_route_cit(Poop, getmsg(9))
								)
								{
								FILE *fl;
								char filename[100];

								PrepareMsgScriptInit(Msg);
								strcpy(TI()ContextSensitiveScriptText,
										Log1.GetName());
								doEvent(EVT_WRITENETMAIL, InitMsgScript);

								if (CheckMsgScript(Msg))
									{
									sprintf(filename, getmsg(1387),
											cfg.transpath, Log1.GetAlias());

									if ((fl = fopen(filename, FO_AB)) != NULL)
										{
										if (debug)
											{
											doccr();
											cPrintf(getdbmsg(30), filename);
											}
										Msg->Store(SMC_NETWORK, fl);

										fclose(fl);
										}
									else if (debug)
										{
										doccr();
										cPrintf(getdbmsg(21), filename);
										}
									}

								*Msg = *SaveMessage;
								}
							else if (debug)
								{
								doccr();

								if (BeenThere(Msg->GetFromPath(),
										Log1.GetName(), Log1.GetAlias(),
										Log1.GetLocID(), NULL))
									{
									cPrintf(getdbmsg(31));
									}
								else
									{
									cPrintf(getdbmsg(95));
									}
								}
							}
						}
					}
				}
			}

		delete SaveMessage;
		if (debug)
			{
			doccr();
			}
		return (TRUE);
		}
	else
		{
		mPrintf(getmsg(188), getmsg(1191));
		return (FALSE);
		}
	}


// --------------------------------------------------------------------------
// search_route_cit(): Returns the route or alias specified.

static Bool search_route_cit(char *str, const char *srch)
	{
	FILE *fBuf;
	char line[95];
	char *words[256];
	char path[80];

	sprintf(path, sbs, cfg.homepath, citfiles[C_ROUTE_CIT]);

	if ((fBuf = fopen(path, FO_R)) == NULL) // ASCII mode
		{
		mPrintfCR(getmsg(15), citfiles[C_ROUTE_CIT]);
		return (FALSE);
		}

	const int len = strlen(srch);

	while (fgets(line, 90, fBuf) != NULL)
		{
		if (line[0] != '#')
			{
			continue;
			}

		if (strnicmp(line, srch, len) != SAMESTRING)
			{
			continue;
			}

		const int count = parse_it(words, line);

		if (count > 1 && SameString(srch, words[0]))
			{
			if (SameString(str, words[1]))
				{
				fclose(fBuf);

				if (count > 2)
					{
					CopyStringToBuffer(str, words[2], LABELSIZE);
					}

				return (TRUE);
				}
			}
		}

	fclose(fBuf);
	return (FALSE);
	}
