// --------------------------------------------------------------------------
// Citadel: NetMsgI.CPP
//
// How to read messages in from the network

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "auxtab.h"
#include "msg.h"
#include "log.h"
#include "group.h"
#include "net.h"
#include "maillist.h"
#include "events.h"

// --------------------------------------------------------------------------
// Contents
//
// GetStr() 			gets a null-terminated string from a file
// PutStr() 			puts a null-terminated string to a file
// Cit86_GetMessage()
// netGetMessage()		Gets a message from a file, returns sucess
// PutMessage() 		Puts a message to a file
// NewRoom()			Puts all new messages in a room to a file
// ReadMsgFile()		Reads a message file into thisRoom

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

static Bool BadStr(const char *str)
	{
	for (; *str; str++)
		{
		if (*str > 1 && *str < 32)
			{
			return (TRUE);
			}
		}
	return (FALSE);
	}

extern const char *netcmdTmp;

void Message::FixupNetwork(void)
	{
	if (TI()node)
		{
		if (!*GetLocalID())
			{
			// assign it something...
			SetLocalID(GetCreationTime());

			if (!*GetLocalID())
				{
				// anything at all at this point...
				SetLocalID(getnetmsg(120));
				}
			}

		if (!*GetOriginNodeName())
			{
			SetOriginNodeName(TI()node->GetName());
			}

		if (!*GetOriginRegion())
			{
			SetOriginRegion(TI()node->GetLocID());
			}

		if (!*GetSourceID())
			{
			SetSourceID(GetLocalID());
			}

		if (!isNumeric(GetSourceID()) ||
				BadStr(GetAuthor()) ||
				BadStr(GetSurname()) ||
				BadStr(GetTitle()) ||
				BadStr(GetOriginNodeName()) ||
				BadStr(GetOriginRegion()) ||
				BadStr(GetOriginCountry()) ||
				BadStr(GetTwitRegion()) ||
				BadStr(GetTwitCountry()) ||
				BadStr(GetOriginPhoneNumber()) ||
				BadStr(GetCit86Country()) ||
				BadStr(GetOriginSoftware()))
			{
			// this is a bit of a kludge.
			SetAuthor(ns); // easy way to call it a duplicate
			}

		// If the other node did not set up a from path, do it.
		if (!*GetFromPath())
			{
			if (SameString(GetOriginNodeName(), TI()node->GetName()))
				{
				SetFromPath(GetOriginNodeName());
				}
			else
				{
				// last node did not originate, make due with what we got...
				char Buffer[128];

				sprintf(Buffer, getnetmsg(119), GetOriginNodeName(),
						TI()node->GetName());

				SetFromPath(Buffer);
				}
			}

		if (*GetEZCreationTime())
			{
			label Buffer;
			SetCreationTime(ltoa(net69_time(GetEZCreationTime()), Buffer, 10));
			}

		if (!*GetCreationTime() || !isNumeric(GetCreationTime()))
			{
			// this is a bit of a kludge.
			SetAuthor(ns); // easy way to call it a duplicate
			}
		}
	}


// --------------------------------------------------------------------------
// ReadMsgFl(): Reads a message file into thisRoom.
//
// filename = "name" for old netting or NULL for 6.9
// file = msg.69 for 6.9 or NULL for old netting
// returns # of messages incorporated or CERROR

int ReadMsgFl(r_slot room, const char *filename, FILE *file, const char *here,
		int *newMsgs, int *expired, int *duplicate, const LogEntry *FromNode)
	{
	Message *NewMsg = new Message;
	Message *DupeTest = new Message;

	if (NewMsg && DupeTest)
		{
		FILE *fl;

		*newMsgs = 0;
		*expired = 0;
		*duplicate = 0;

		KBReady();

		if (file == NULL)
			{
			char FileName[80];

			sprintf(FileName, sbs, cfg.temppath, filename);

			if ((file = fopen(FileName, FO_RB)) == NULL)
				{
				delete NewMsg;
				delete DupeTest;
				return (CERROR);
				}
			}

		while (NewMsg->ReadAll(RMC_NETWORK, file) == RMS_OK)
			{
			NewMsg->SetRoomNumber(room);

			Bool bad = FALSE;

			if (SameString(cfg.nodeTitle, NewMsg->GetOriginNodeName()))
				{
				if (SameString(cfg.nodeRegion, NewMsg->GetOriginRegion()))
					{
					bad = TRUE;
					(*duplicate)++;
					}
				else
					{
					bad = TRUE;
					(*duplicate)++;
					}
				}

			// Throw away authorless messages
			if (!bad && !NewMsg->GetAuthor()[0])
				{
				bad = TRUE;
				(*duplicate)++;
				}

			// Expired?
			if (
					!bad &&

					cfg.expire &&


					(
						labs(atol(NewMsg->GetCreationTime()) - time(NULL)) >

						(cfg.expire * SECSINDAY)
					)
				)
				{
				bad = TRUE;
				(*expired)++;
				}

			// Don't accept messages into a room a node doesn't have access to
			if (!bad && FromNode && !FromNode->CanAccessRoom(room))
				{
				bad = TRUE;
				(*duplicate)++;
				}

			if (!bad)
				{
				m_slot size_table = sizetable();
				ulong oid = atol(NewMsg->GetSourceID());

				for (	m_slot i = size_table - 1;

						#if defined(AUXMEM) || defined(WINCIT)
							i < (m_slot) size_table && !bad; i--
						#else
							i != -1 && !bad; i--
						#endif
						)
					{
					if ((uint) oid == getOriginID(i))
						{
						ulong loc = getLocation(i);

						bufferedSeek(msgfl, loc);
						DupeTest->ReadHeader(RMC_DUPLICATECHECK, NULL);

						if (
								// author or origin node the same
								(
								SameString(NewMsg->GetAuthor(),
								DupeTest->GetAuthor())

								||

								SameString(NewMsg->GetOriginNodeName(),
								DupeTest->GetOriginNodeName())
								)

								&&

								// and same time
								SameString(NewMsg->GetCreationTime(),
								DupeTest->GetCreationTime())

								&&

								// and same source id.
								SameString(NewMsg->GetSourceID(),
								DupeTest->GetSourceID())
							)
							{
							bad = TRUE;
							(*duplicate)++;
							}
						}
					}

				if (!bad)
					{
					PrepareMsgScriptInit(NewMsg);
					doEvent(EVT_LOADNETMESSAGE, InitMsgScript);

					if (!CheckMsgScript(NewMsg))
						{
						bad = TRUE;
						}
					}

				if (!bad && NewMsg->IsNetworkedMail())
					{
					// is mail
					if (!NewMsg->IsNetworkedMailForHere())
						{
						if (!save_mail(NewMsg, TRUE))
							{
							(*duplicate)++;
							}
						else
							{
							(*expired)++;
							}

						bad = TRUE;
						}
					else
						{
						PrepareMsgScriptInit(NewMsg);
						doEvent(EVT_READNETMAIL, InitMsgScript);

						if (CheckMsgScript(NewMsg))
							{
							// for this system?
							if (	SameString(NewMsg->GetToUser(),
									cfg.nodeTitle) ||

									SameString(NewMsg->GetToUser(), cfg.Address)
									)
								{
								char FileName[80];

								// Net command
								sprintf(FileName, sbs, cfg.temppath, netcmdTmp);

								if ((fl = fopen(FileName, FO_AB)) != NULL)
									{
									NewMsg->Store(SMC_NETWORK, fl);
									fclose(fl);
									}

								SaveStub(NewMsg, getnetmsg(185), cfg.poopdebug);

								bad = TRUE;
								(*expired)++;
								}
							else
								{
								// handled forwarded E-mail. Check existence of
								// forwardee if forwarded
								if (IsMailingList(NewMsg->GetToUser()))
									{
									SendMailToList(NewMsg);
									(*newMsgs)++;
									bad = TRUE;
									}
								else if (*NewMsg->GetToUser() &&

										FindPersonByName(*NewMsg->GetForward() ?
										NewMsg->GetForward() :
										NewMsg->GetToUser()) == CERROR

										&&

										!SameString(*NewMsg->GetForward() ?
										NewMsg->GetForward() :
										NewMsg->GetToUser(), getmsg(1020))

										&&

										!SameString(*NewMsg->GetForward() ?
										NewMsg->GetForward() :
										NewMsg->GetToUser(), getmsg(1019)))
									{
									// Generate a "User not found" error

									Message *ErrorMsg = new Message;

									if (ErrorMsg)
										{
										ErrorMsg->SetAuthor(programName);
										ErrorMsg->SetToUser(NewMsg->GetAuthor());
										ErrorMsg->SetToNodeName(NewMsg->GetOriginNodeName());

										ErrorMsg->SetToRegion(NewMsg->GetOriginRegion());

										// this next one is so silly that i just have
										// to leave it in.
										ErrorMsg->SetRoomNumber(NewMsg->GetRoomNumber());
										ErrorMsg->SetDestinationAddress(oaddress(NewMsg));

										ErrorMsg->SetTextWithFormat(
												getnetmsg(121), bn,
												NewMsg->GetToUser(),
												cfg.nodeTitle);

										save_mail(ErrorMsg, FALSE);

										// and unsubscribe from mailing list?

										long Idx, NumCmt = NewMsg->GetCommentCount();
										const char *Cmt;

										for (Idx = 1; Idx <= NumCmt; Idx++)
											{
											Cmt = NewMsg->GetComment(Idx);

											if (strncmpi(Cmt, getmsg(1350),
												strlen(getmsg(1350))) == SAMESTRING)
												{
												break;
												}
											}

										if (Idx <= NumCmt)
											{
											char listname[LABELSIZE+LABELSIZE];
											strcpy(listname, Cmt + strlen(getmsg(1350)));
											char *ptr = strrchr(listname, '@');

											if (ptr)
												{
												ptr[-1] = 0;	// strip " @ nodename"
												}

											ErrorMsg->SetAuthor(NewMsg->GetAuthor());	//forged
											ErrorMsg->SetToUser(listname);
											ErrorMsg->SetSubject(getmsg(1344));
											// Leave error message
											save_mail(ErrorMsg, FALSE);
											}

										delete ErrorMsg;
										}
									else
										{
										cPrintf(getmsg(188), getnetmsg(122));
										}

									bad = TRUE;
									(*duplicate)++;
									}
								}
							}
						else
							{
							// We really need more flags here. Silly Peter.
							bad = TRUE;
							(*expired)++;
							}
						}
					}

				// fix group only messages
				if (!bad && *NewMsg->GetGroup())
					{
					bad = TRUE; 	// be pessimistic

					const nodeGroupList *theGroup;

					for (theGroup = TI()node->GetMappedGroups(); theGroup && bad;
							theGroup = (nodeGroupList *) getNextLL(theGroup))
						{
						if (SameString(theGroup->there, NewMsg->GetGroup()))
							{
							NewMsg->SetGroup(theGroup->here);
							bad = FALSE;
							}
						}

					// put it in #MAPUNKGROUP
					if (bad)
						{
						char ReportString[128];

						bad = FALSE;
						sprintf(ReportString, getnetmsg(171), cfg.nodeTitle,
								NewMsg->GetGroup());
						NewMsg->AddComment(ReportString);
						NewMsg->SetGroup(GroupData->GetEntry
								(TI()node->GetMapUnknownGroup())->GetName());
						}
					}
				}

			if (!bad)
				{							
				// it's good; save it

				// put roomname here
				// new development: always put roomname. We decided
				// not to worry about preserving the room names.
				// too ugly and too much of a hassle

				if (!filename)
					{
					NewMsg->SetCreationRoom(GetRoomName(room));
					}
				else
					{
					NewMsg->SetCreationRoom(here);
					}

				r_slot temproom = room;
				if (*NewMsg->GetToUser())
					{
					temproom = FindRoomForNetMail(NewMsg->GetCreationRoom());
					}

				NewMsg->SetRoomNumber(temproom);

				// this is some shit added 7-9-92 to forward messages
				// to 'sysop' to #sysop if #forward

				if (SameString(NewMsg->GetToUser(), getmsg(1020)) &&
						cfg.forward &&
						(FindPersonByName(cfg.sysop) != CERROR))
					{
					NewMsg->SetToUser(cfg.sysop);
					}

				putAndNoteMessage(NewMsg, TRUE);

				// I am not sure about this
				// Set newpointers here because if I don't then the new messages
				// Will be sent back next time there's a network
				// logBuf.newpointer[TI()mb.mbroomno] = cfg.newest;

				if (filename)
					{
					TI()CurrentUser->SetRoomNewPointer(NewMsg->GetRoomNumber(),
							cfg.newest);
					}
				(*newMsgs)++;

				if (*NewMsg->GetToUser())
					{
					// okay. This is about the most silly thing that I think
					// I have ever seen. How can I ever remove this??
					r_slot lp = TI()thisRoom;
					TI()thisRoom = temproom;
					TI()thisRoom = lp;
					}
				}

			if (!filename)
				{
				long position = ftell(file);

				if (fgetc(file) == 0)
					{
					break;
					}
				else
					{
					fseek(file, position, SEEK_SET);
					}
				}
			}

		if (filename)
			{
			fclose(file);
			}

		delete NewMsg;
		delete DupeTest;

		return (*newMsgs);
		}
	else
		{
		cPrintf(getmsg(188), getnetmsg(122));
		delete NewMsg;
		delete DupeTest;

		return (CERROR);
		}
	}
