// --------------------------------------------------------------------------
// Citadel: MailList.CPP
//
// Mailing list stuff, even.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "msg.h"
#include "maillist.h"
#include "net.h"
#include "room.h"
#include "cfgfiles.h"
#include "miscovl.h"


class MailingList
	{
	strList *Members;
	strList *Current;

	Bool MaySubscribe;
	Bool MayTell;
	Bool MayList;
	char InfoLine[80];
	char InfoFile[80];

public:
	MailingList(void)
		{
		assert(this);

		memset(this, 0, sizeof(*this));
		MayTell = TRUE;
		MayList = TRUE;
		}

	~MailingList()
		{
		assert(this);

		disposeLL((void **) &Members);
		}

	const char *GetFirstUser(void)
		{
		assert(this);

		Current = Members;

		if (Current)
			{
			return (Current->string);
			}
		else
			{
			return (ns);
			}
		}

	const char *GetNextUser(void)
		{
		if (Current)
			{
			Current = Current->next;
			}

		if (Current)
			{
			return (Current->string);
			}
		else
			{
			return (ns);
			}
		}

	Bool AddUser(const char *Name);
	Bool RemoveUser(const char *Name);

	Bool Load(const char *ListName);
	Bool Save(const char *ListName);

	Bool IsMayTell(void) const
		{
		assert(this);
		return (MayTell);
		}

	Bool IsMayList(void) const
		{
		assert(this);
		return (MayList);
		}

	Bool IsMaySubscribe(void) const
		{
		assert(this);
		return (MaySubscribe);
		}

	const char *GetInfoLine(void) const
		{
		assert(this);
		return (InfoLine);
		}

	const char *GetInfoFile(void) const
		{
		assert(this);
		return (InfoFile);
		}
	};

Bool MailingList::AddUser(const char *Name)
	{
	assert(this);
	assert(Name);

	if (*Name)
		{
		strList *theStrLL;

		for (theStrLL = Members; theStrLL;
				theStrLL = (strList *) getNextLL(theStrLL))
			{
			if (SameString(theStrLL->string, Name))
				{
				return (FALSE);
				}
			}

		theStrLL = (strList *) addLL((void **) &Members,
				sizeof(*theStrLL) + strlen(Name));

		if (theStrLL)
			{
			strcpy(theStrLL->string, Name);
			return (TRUE);
			}
		}

	return (FALSE);
	}

Bool MailingList::RemoveUser(const char *Name)
	{
	assert(this);
	assert(Name);

	if (*Name)
		{
		int i;
		strList *theStr;

		for (i = 1, theStr = Members; theStr;
				theStr = (strList *) getNextLL(theStr), i++)
			{
			if (SameString(theStr->string, Name))
				{
				deleteLLNode((void **) &Members, i);
				return (TRUE);
				}
			}
		}

	return (FALSE);
	}

Bool MailingList::Load(const char *ListName)
	{
	FILE *fBuf;
	char path[80];

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

	if ((fBuf = fopen(path, FO_R)) != NULL)
		{
		char line[90];

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

			char *words[256];
			int count;
			count = parse_it(words, line);

			if (SameString(words[0], getmsg(1322)))
				{
				if (SameString(ListName, words[1]))
					{
					while (fgets(line, 90, fBuf) != NULL)
						{
						if (line[0] != '#')
							{
							continue;
							}

						parse_it(words, line);

						if (SameString(words[0], getmsg(1323)))
							{
							if (count > 1)
								{
								AddUser(words[1]);
								}
							}
						else if (SameString(words[0], getmsg(1324)))
							{
							MaySubscribe = atoi(words[1]);
							}
						else if (SameString(words[0], getmsg(1325)))
							{
							MayTell = atoi(words[1]);
							}
						else if (SameString(words[0], getmsg(1326)))
							{
							MayList = atoi(words[1]);
							}
						else if (SameString(words[0], getmsg(1327)))
							{
							CopyStringToBuffer(InfoLine, words[1],
									sizeof(InfoLine) - 1);
							}
						else if (SameString(words[0], getmsg(1328)))
							{
							CopyStringToBuffer(InfoFile, words[1],
									sizeof(InfoFile) - 1);
							}
						else if (SameString(words[0], getmsg(1322)))
							{
							break;
							}
						}

					fclose(fBuf);
					return (TRUE);
					}
				}
			}

		fclose(fBuf);
		}

	return (FALSE);
	}

Bool MailingList::Save(const char *ListName)
	{
	Bool Good = TRUE;
	FILE *Original;
	char Opath[80];
	FILE *New;
	char Npath[80];

	sprintf(Opath, sbs, cfg.homepath, citfiles[C_MAILLIST_CIT]);
	strcpy(Npath, Opath);
	strcpy(strchr(Npath, '.'), getmsg(1507));

	if ((New = fopen(Npath, FO_W)) != NULL)
		{
		if ((Original = fopen(Opath, FO_R)) != NULL)
			{
			char line[90];
			char saveline[90];
			Bool Saving = TRUE;

			while (fgets(line, 90, Original) != NULL)
				{
				strcpy(saveline, line);

				if (line[0] == '#')
					{
					char *words[256];

					parse_it(words, line);

					if (SameString(words[0], getmsg(1322)))
						{
						if (SameString(words[1], ListName))
							{
							Saving = FALSE;
							}
						else
							{
							Saving = TRUE;
							}
						}
					}

				if (Saving)
					{
					fwrite(saveline, strlen(saveline), sizeof(char), New);
					}
				}

			fclose(Original);
			}

		fprintf(New, bn);

		char TmpStr[256];
		fprintf(New, getmsg(1329), getmsg(1322), mkDblBck(ListName, TmpStr),
				bn);

		strList *sl;
		for (sl = Members; sl ; sl = (strList *) getNextLL(sl))
			{
			fprintf(New, getmsg(1329), getmsg(1323),
					mkDblBck(sl->string, TmpStr), bn);
			}

		fprintf(New, getmsg(1330), getmsg(1324), MaySubscribe, bn);
		fprintf(New, getmsg(1330), getmsg(1325), MayTell, bn);
		fprintf(New, getmsg(1330), getmsg(1326), MayList, bn);
		fprintf(New, getmsg(1329), getmsg(1327), mkDblBck(InfoLine, TmpStr),
				bn);
		fprintf(New, getmsg(1329), getmsg(1328), mkDblBck(InfoFile, TmpStr),
				bn);

		fclose(New);

		unlink(Opath);
		rename(Npath, Opath);
		}
	else
		{
		mPrintf(getmsg(78), Npath);
		Good = FALSE;
		}

	return (Good);
	}

Bool IsMailingList(const char *Name)
	{
	assert(Name);

	if (*Name)
		{
		FILE *fBuf;
		char path[80];

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

		if ((fBuf = fopen(path, FO_R)) != NULL)
			{
			char line[90];
			int len = strlen(getmsg(1322));

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

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

				char *words[256];
				parse_it(words, line);

				if (SameString(words[0], getmsg(1322)))
					{
					if (SameString(Name, words[1]))
						{
						fclose(fBuf);
						return (TRUE);
						}
					}
				}

			fclose(fBuf);
			}
		}

	return (FALSE);
	}

void SendMailToList(Message *Msg)
	{
	MailingList List;

	if (!List.Load(Msg->GetToUser()))
		{
		return;
		}

	if (SameString(Msg->GetSubject(), getmsg(1331)))
		{
		doccr();
		cPrintf(getmsg(1332), getmsg(1331));

		if (*Msg->GetOriginNodeName())
			{
			SaveStub(Msg, getmsg(1333), cfg.poopdebug);
			}

		Message *NewMsg = new Message;

		if (NewMsg)
			{
			NewMsg->SetCreationRoom(Msg->GetCreationRoom());

			NewMsg->SetRoomNumber(Msg->GetRoomNumber());

			NewMsg->SetToUser(Msg->GetAuthor());
			NewMsg->SetAuthor(Msg->GetToUser());

			NewMsg->SetReplyToMessage(*Msg->GetSourceID() ?
					Msg->GetSourceID() : Msg->GetLocalID());

			if (*Msg->GetSourceID())
				{
				NewMsg->SetToNodeName(Msg->GetOriginNodeName());
				NewMsg->SetToRegion(Msg->GetOriginRegion());
				NewMsg->SetToCountry(Msg->GetOriginCountry());
				NewMsg->SetDestinationAddress(oaddress(Msg));
				}

			label Subject;
			sprintf(Subject, getmsg(1341), getmsg(1331));
			NewMsg->SetSubject(Subject);

			if (List.IsMayTell())
				{
				NewMsg->SetText(ns);

				if (*List.GetInfoLine())
					{
					NewMsg->AppendText(List.GetInfoLine());
					NewMsg->AppendText(bn);
					}

				if (*List.GetInfoFile())
					{
					char *TextFromFile = new char[MAXTEXT];

					if (TextFromFile)
						{
						FILE *fd;

						if ((fd = fopen(List.GetInfoFile(), FO_RB)) != NULL)
							{
							GetFileMessage(fd, TextFromFile, MAXTEXT - 1);
							fclose(fd);

							NewMsg->AppendText(TextFromFile);
							NewMsg->AppendText(bn);
							}
						else
							{
							cPrintf(getmsg(78), List.GetInfoFile());
							doccr();
							}

						delete [] TextFromFile;
						}
					else
						{
						CRmPrintfCR(getmsg(188), getmsg(1334));
						}
					}

				if (List.IsMayList())
					{
					if (*List.GetFirstUser())
						{
						NewMsg->AppendText(getmsg(1335));
						NewMsg->AppendText(bn);

						char destination[4 * LABELSIZE + 7];

						for (	CopyStringToBuffer(destination,
								List.GetFirstUser(), sizeof(destination) - 1);

								*destination;

								CopyStringToBuffer(destination,
								List.GetNextUser(), sizeof(destination) - 1))
							{
							NewMsg->AppendText(spc);
							NewMsg->AppendText(destination);
							NewMsg->AppendText(bn);
							}
						}
					else
						{
						NewMsg->AppendText(getmsg(1336));
						NewMsg->AppendText(bn);
						}
					}
				else
					{
					NewMsg->AppendText(getmsg(1337));
					NewMsg->AppendText(bn);
					}
				}
			else
				{
				NewMsg->SetText(getmsg(1338));
				}

			label Buffer;
			NewMsg->SetLocalID(ltoa(cfg.newest + 1, Buffer, 10));

			if (*NewMsg->GetToNodeName())
				{
				if (save_mail(NewMsg, FALSE))
					{
					SaveStub(NewMsg, getmsg(1333), cfg.poopdebug);
					}
				else
					{
					cPrintf(getmsg(1339));
					}
				}
			else
				{
				putAndNoteMessage(NewMsg, TRUE);
				}

			delete NewMsg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(1334));
			}

		doccr();
		}
	else if (SameString(Msg->GetSubject(), getmsg(1340)))
		{
		doccr();
		cPrintf(getmsg(1332), getmsg(1340));

		if (*Msg->GetOriginNodeName())
			{
			SaveStub(Msg, getmsg(1333), cfg.poopdebug);
			}

		Message *NewMsg = new Message;

		if (NewMsg)
			{
			NewMsg->SetCreationRoom(Msg->GetCreationRoom());

			NewMsg->SetRoomNumber(Msg->GetRoomNumber());

			NewMsg->SetToUser(Msg->GetAuthor());
			NewMsg->SetAuthor(Msg->GetToUser());

			NewMsg->SetReplyToMessage(*Msg->GetSourceID() ?
					Msg->GetSourceID() : Msg->GetLocalID());

			if (*Msg->GetSourceID())
				{
				NewMsg->SetToNodeName(Msg->GetOriginNodeName());
				NewMsg->SetToRegion(Msg->GetOriginRegion());
				NewMsg->SetToCountry(Msg->GetOriginCountry());
				NewMsg->SetDestinationAddress(oaddress(Msg));
				}

			label Subject;
			sprintf(Subject, getmsg(1341), getmsg(1340));
			NewMsg->SetSubject(Subject);

			if (List.IsMaySubscribe())
				{
				NewMsg->SetTextWithFormat(getmsg(1342), Msg->GetToUser(),
						cfg.nodeTitle);

				if (*Msg->GetOriginNodeName())
					{
					char string[256];
					sprintf(string, getmsg(101), Msg->GetAuthor(),
							Msg->GetOriginNodeName());

					List.AddUser(string);
					}
				else
					{
					List.AddUser(Msg->GetAuthor());
					}

				List.Save(Msg->GetToUser());
				}
			else
				{
				NewMsg->SetTextWithFormat(getmsg(1343), Msg->GetToUser(),
						cfg.nodeTitle);
				}

			label Buffer;
			NewMsg->SetLocalID(ltoa(cfg.newest + 1, Buffer, 10));

			if (*NewMsg->GetToNodeName())
				{
				if (save_mail(NewMsg, FALSE))
					{
					SaveStub(NewMsg, getmsg(1333), cfg.poopdebug);
					}
				else
					{
					cPrintf(getmsg(1339));
					}
				}
			else
				{
				putAndNoteMessage(NewMsg, TRUE);
				}

			delete NewMsg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(1334));
			}

		doccr();
		}
	else if (SameString(Msg->GetSubject(), getmsg(1344)))
		{
		doccr();
		cPrintf(getmsg(1332), getmsg(1344));

		if (*Msg->GetOriginNodeName())
			{
			SaveStub(Msg, getmsg(1333), cfg.poopdebug);
			}

		Message *NewMsg = new Message;

		if (NewMsg)
			{
			NewMsg->SetCreationRoom(Msg->GetCreationRoom());

			NewMsg->SetRoomNumber(Msg->GetRoomNumber());

			NewMsg->SetToUser(Msg->GetAuthor());
			NewMsg->SetAuthor(Msg->GetToUser());

			NewMsg->SetReplyToMessage(*Msg->GetSourceID() ?
					Msg->GetSourceID() : Msg->GetLocalID());

			if (*Msg->GetSourceID())
				{
				NewMsg->SetToNodeName(Msg->GetOriginNodeName());
				NewMsg->SetToRegion(Msg->GetOriginRegion());
				NewMsg->SetToCountry(Msg->GetOriginCountry());
				NewMsg->SetDestinationAddress(oaddress(Msg));
				}

			label Subject;
			sprintf(Subject, getmsg(1341), getmsg(1344));
			NewMsg->SetSubject(Subject);

			char string[256];

			if (*Msg->GetOriginNodeName())
				{
				sprintf(string, getmsg(101), Msg->GetAuthor(),
						Msg->GetOriginNodeName());
				}
			else
				{
				strcpy(string, Msg->GetAuthor());
				}

			if (List.RemoveUser(string))
				{
				NewMsg->SetTextWithFormat(getmsg(1345), Msg->GetToUser(),
						cfg.nodeTitle);

				List.Save(Msg->GetToUser());
				}
			else
				{
				NewMsg->SetTextWithFormat(getmsg(1346), Msg->GetToUser(),
						cfg.nodeTitle);
				}

			label Buffer;
			NewMsg->SetLocalID(ltoa(cfg.newest + 1, Buffer, 10));

			if (*NewMsg->GetToNodeName())
				{
				if (save_mail(NewMsg, FALSE))
					{
					SaveStub(NewMsg, getmsg(1333), cfg.poopdebug);
					}
				else
					{
					cPrintf(getmsg(1339));
					}
				}
			else
				{
				putAndNoteMessage(NewMsg, TRUE);
				}

			delete NewMsg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(1334));
			}

		doccr();
		}
	else if (Msg->GetSubject()[0] == '#')
		{
		doccr();
		cPrintf(getmsg(1347), Msg->GetSubject());

		if (*Msg->GetOriginNodeName())
			{
			SaveStub(Msg, getmsg(1333), cfg.poopdebug);
			}

		Message *NewMsg = new Message;

		if (NewMsg)
			{
			NewMsg->SetCreationRoom(Msg->GetCreationRoom());

			NewMsg->SetRoomNumber(Msg->GetRoomNumber());

			NewMsg->SetToUser(Msg->GetAuthor());
			NewMsg->SetAuthor(Msg->GetToUser());

			NewMsg->SetReplyToMessage(*Msg->GetSourceID() ?
					Msg->GetSourceID() : Msg->GetLocalID());

			if (*Msg->GetSourceID())
				{
				NewMsg->SetToNodeName(Msg->GetOriginNodeName());
				NewMsg->SetToRegion(Msg->GetOriginRegion());
				NewMsg->SetToCountry(Msg->GetOriginCountry());
				NewMsg->SetDestinationAddress(oaddress(Msg));
				}

			NewMsg->SetSubject(getmsg(1348));

			NewMsg->SetTextWithFormat(getmsg(1349), Msg->GetSubject());

			label Buffer;
			NewMsg->SetLocalID(ltoa(cfg.newest + 1, Buffer, 10));

			if (*NewMsg->GetToNodeName())
				{
				if (save_mail(NewMsg, FALSE))
					{
					SaveStub(NewMsg, getmsg(1333), cfg.poopdebug);
					}
				else
					{
					cPrintf(getmsg(1339));
					}
				}
			else
				{
				putAndNoteMessage(NewMsg, TRUE);
				}

			delete NewMsg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(1334));
			}

		doccr();
		}
	else
		{
		char String[128];

		int Idx, NumComments = (int) Msg->GetCommentCount();

		for (Idx = 1; Idx <= NumComments; Idx++)
			{
			const char *Cmt = Msg->GetComment(Idx);

			if (strncmpi(Cmt, getmsg(1350), strlen(getmsg(1350))) ==
					SAMESTRING)
				{
				label ToUser, ToNode, ToRegion, ToAddress;

				parseNetAddress(strchr(Cmt, ':') + 2, ToUser, ToNode,
						ToRegion, ToAddress);

				if (SameString(ToUser, Msg->GetToUser()) &&
						SameString(ToNode, cfg.nodeTitle))
					{
					return;
					}
				}
			}

		sprintf(String, getmsg(1351), Msg->GetToUser(), cfg.nodeTitle);

		Msg->AddComment(String);

		if (*List.GetFirstUser())
			{
			char destination[LABELSIZE + LABELSIZE + LABELSIZE + LABELSIZE + 7];
			Bool FirstSaved = FALSE;

			strList *removeList = NULL;
			label listName;
			strcpy(listName, Msg->GetToUser()); // For unsubscribing

			for (	CopyStringToBuffer(destination, List.GetFirstUser(),
					sizeof(destination) - 1);

					*destination;

					CopyStringToBuffer(destination, List.GetNextUser(),
					sizeof(destination) - 1))
				{
				Bool Saveit = TRUE;
				label ToUser, ToNode, ToRegion, ToAddress;

				parseNetAddress(destination, ToUser, ToNode, ToRegion, ToAddress);

				if (!*ToUser)
					{
					strcpy(ToUser, getmsg(1020));
					}

				if (*ToNode)
					{
					cPrintf(getmsg(1352), ToUser, ToNode);
					}
				else
					{
					cPrintf(getmsg(1353), ToUser);
					}
				doccr();

				if (SameString(ToUser, Msg->GetAuthor()) &&
						SameString(ToNode, Msg->GetOriginNodeName()))
					{
					cPrintf(getmsg(1354));
					doccr();
					}
				else
					{
					Msg->SetToUser(ToUser);
					Msg->SetToNodeName(ToNode);
					Msg->SetToRegion(ToRegion);
					Msg->SetDestinationAddress(ToAddress);

					Saveit = ResolveMessageAddressingIssues(Msg, CONSOLE);

					if (!Saveit)
						{
						strList *theStr = (strList *) addLL((void **) &removeList,
								sizeof(*theStr) + strlen(Msg->GetToUser()));

						if (theStr)
							{
							strcpy(theStr->string, Msg->GetToUser());
							}
						}

					label Buffer;
					Msg->SetLocalID(ltoa(cfg.newest + 1, Buffer, 10));

					if (Saveit)
						{
						// If this came in over the network, we have to give
						// each copy some sort of unique Source ID. Here we
						// go...
						if (*Msg->GetSourceID())
							{
							Msg->SetSourceID(ltoa(time(NULL) + cfg.newest, Buffer, 10));
							}

						if (*Msg->GetToNodeName())	// save it for netting
							{
							// room names get included in networked mail
							Msg->SetCreationRoom(GetRoomName(Msg->GetRoomNumber()));

							if (save_mail(Msg, FALSE))
								{
								SaveStub(Msg, getmsg(1333), FirstSaved ?
										cfg.poopdebug : FALSE);

								FirstSaved = FALSE;
								}
							else
								{
								cPrintf(getmsg(1339));
								}
							}
						else
							{
							putAndNoteMessage(Msg, TRUE);
							}

						if (!*Msg->GetToNodeName() &&
								IsMailingList(Msg->GetToUser()))
							{
							// send a copy because of the Comments stuff.
							Message *Msg2 = new Message;

							if (Msg2)
								{
								*Msg2 = *Msg;
								SendMailToList(Msg2);
								delete Msg2;
								}
							else
								{
								mPrintf(getmsg(188), getmsg(1334));
								}
							}
						}
					}
				}

			if (removeList)
				{
				for (const strList *theStr = removeList; theStr;
						theStr = (const strList *) getNextLL(theStr))
					{
					List.RemoveUser(theStr->string);
					cPrintf(getmsg(1384), theStr->string, listName);
					doCR();
					}

				disposeLL((void **) &removeList);
				List.Save(listName);
				}
			}
		}
	}
