/*
Copyright 2001-2018 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	

#include "bpqmail.h"

int APIENTRY ChangeSessionIdletime(int Stream, int idletime);
struct MsgInfo * GetMsgFromNumber(int msgno);
BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle);
struct UserInfo * FindBBS(char * Name);
int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user);

static char seps[] = " \t\r";

VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	int AuthInt = 0;
	
	if (!(user->flags & F_SYSOP))
	{
		nodeprintf(conn, "AUTH can only be used by SYSOPs\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1)
		AuthInt = atoi(Arg1);
	else
	{
		nodeprintf(conn, "AUTH Code missing\r");
		SendPrompt(conn, user);
		return;
	}

	if (user->Temp->LastAuthCode == AuthInt)
	{
		nodeprintf(conn, "AUTH Code already used\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1 && CheckOneTimePassword(Arg1, user->pass))
	{
		conn->sysop = TRUE;
		nodeprintf(conn, "Ok\r");
		user->Temp->LastAuthCode = atoi(Arg1);
	}
	else
		nodeprintf(conn, "AUTH Failed\r");

	SendPrompt(conn, user);
	return;
}


VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	char Line[200] = "User Flags:";
	struct UserInfo * EUser = user;

	if (conn->sysop == 0)
	{
		nodeprintf(conn, "Edit User needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0)
	{
		nodeprintf(conn, "EDITUSER CALLSIGN to Display\r");
		nodeprintf(conn, "EDITUSER CALLSIGN FLAG1 FLAG2 ...  to set, -FLAG1 -FLAG2 ...  to clear\r");
		nodeprintf(conn, "EDITUSER: Flags are: EXC(luded) EXP(ert) SYSOP BBS PMS EMAIL HOLD RMS(Express User) APRS(Mail For)\r");

		SendPrompt(conn, user);
		return;
	}

	EUser = LookupCall(Arg1);

	if (EUser == 0)
	{
		nodeprintf(conn, "User %s not found\r", Arg1);
		SendPrompt(conn, user);
		return;
	}

	Arg1 = strtok_s(NULL, seps, &Context);

	if (Arg1 == NULL)
		goto UDisplay;
					
	// A set of flags to change +Flag or -Flag
		
	while(Arg1 && strlen(Arg1) > 2)
	{
		_strupr(Arg1);

		if (strstr(Arg1, "EXC"))
			if (Arg1[0] != '-') EUser->flags |= F_Excluded; else EUser->flags &= ~F_Excluded;
		if (strstr(Arg1, "EXP"))
			if (Arg1[0] != '-') EUser->flags |= F_Expert; else EUser->flags &= ~F_Expert;
		if (strstr(Arg1, "SYS"))
			if (Arg1[0] != '-') EUser->flags |= F_SYSOP; else EUser->flags &= ~F_SYSOP;
		if (strstr(Arg1, "BBS"))
			if (Arg1[0] != '-') EUser->flags |= F_BBS; else EUser->flags &= ~F_BBS;
		if (strstr(Arg1, "PMS"))
			if (Arg1[0] != '-') EUser->flags |= F_PMS; else EUser->flags &= ~F_PMS;
		if (strstr(Arg1, "EMAIL"))
			if (Arg1[0] != '-') EUser->flags |= F_EMAIL; else EUser->flags &= ~F_EMAIL;
		if (strstr(Arg1, "HOLD"))
			if (Arg1[0] != '-') EUser->flags |= F_HOLDMAIL; else EUser->flags &= ~F_HOLDMAIL;
		if (strstr(Arg1, "RMS"))
			if (Arg1[0] != '-') EUser->flags |= F_Temp_B2_BBS; else EUser->flags &= ~F_Temp_B2_BBS;
		if (strstr(Arg1, "APRS"))
			if (Arg1[0] != '-') EUser->flags |= F_APRSMFOR; else EUser->flags &= ~F_APRSMFOR;

		Arg1 = strtok_s(NULL, seps, &Context);
	}

	SaveUserDatabase();

	// Drop through to display
UDisplay:

	if (EUser->flags & F_Excluded)
		strcat(Line, " EXC");

	if (EUser->flags & F_Expert)
		strcat(Line, " EXP");

	if (EUser->flags & F_SYSOP)
		strcat(Line, " SYSOP");

	if (EUser->flags & F_BBS)
		strcat(Line, " BBS");

	if (EUser->flags & F_PMS)
		strcat(Line, " PMS");

	if (EUser->flags & F_EMAIL)
		strcat(Line, " EMAIL");

	if (EUser->flags & F_HOLDMAIL)
		strcat(Line, " HOLD");

	if (EUser->flags & F_Temp_B2_BBS)
		strcat(Line, " RMS");

	if (EUser->flags & F_APRSMFOR)
		strcat(Line, " APRS");
	
	strcat(Line, "\r");	
	nodeprintf(conn, Line);
	
	SendPrompt(conn, user);
	return;
}

VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * inuser, char * Arg1, char * Context)
{
	int i, s;
	char FWLine[10000] = "";
	struct UserInfo * user;
	char RMSCall[20];

	if (conn->sysop == 0)
	{
		nodeprintf(conn, "Command needs SYSOP status\r");
		SendPrompt(conn, inuser);
		return;
	}
			
	for (i = 0; i <= NumberofUsers; i++)
	{
		user = UserRecPtr[i];

		if (user->flags & F_POLLRMS)
		{
			if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1;

			for (s = 0; s < 16; s++)
			{
				if (user->RMSSSIDBits & (1 << s))
				{
					strcat(FWLine, " ");
					if (s)
					{
						sprintf(RMSCall, "%s-%d", user->Call, s);
						strcat(FWLine, RMSCall);
					}
					else
						strcat(FWLine, user->Call);
							
				}
			}
		}
	}
			
	strcat(FWLine, "\r");	

	nodeprintf(conn, FWLine);
}



VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	char RMSLine[200];
	char RMSCall[10];
	struct UserInfo * RMSUser = user;
	int s;
Loop:
	if (Arg1)
	{
		// Update	
		if (_stricmp(Arg1, "Enable") == 0)
		{
			RMSUser->flags |= F_POLLRMS;
			Arg1 = strtok_s(NULL, seps, &Context);
			goto Display;
		}
		else if (_stricmp(Arg1, "Disable") == 0)
		{
			RMSUser->flags &= ~F_POLLRMS;
			Arg1 = strtok_s(NULL, seps, &Context);
			goto Display;
		}
		else if (strlen(Arg1) > 2)
		{
			// Callsign - if SYSOP, following commands apply to selected user

			if (conn->sysop == 0)
			{
				nodeprintf(conn, "Changing RMS Poll params for another user needs SYSOP status\r");
				SendPrompt(conn, user);
				return;
			}
			RMSUser = LookupCall(Arg1);

			if (RMSUser == NULL)
			{
				nodeprintf(conn, "User %s not found\r", Arg1);
				SendPrompt(conn, user);
				return;
			}

			Arg1 = strtok_s(NULL, seps, &Context);

			if (Arg1 == NULL)
				goto Display;
				
			if (_stricmp(Arg1, "Enable") == 0 || _stricmp(Arg1, "Disable") == 0 || (strlen(Arg1) < 3))
				goto Loop;

			goto Display;
		}
	
		// A list of SSID's to poll

		RMSUser->RMSSSIDBits = 0;

		while(Arg1 && strlen(Arg1) < 3)
		{
			s = atoi(Arg1);
			if (s < 16)
				RMSUser->RMSSSIDBits |= (1 << (s));

			Arg1 = strtok_s(NULL, seps, &Context);
		}
	}

	// Drop through to display

Display:
	strcpy(RMSLine, "Polling for calls");

	if (RMSUser->flags & F_POLLRMS)
	{
		if (RMSUser->RMSSSIDBits == 0) RMSUser->RMSSSIDBits = 1;
		{
			for (s = 0; s < 16; s++)
			{
				if (RMSUser->RMSSSIDBits & (1 << s))
				{
					strcat(RMSLine, " ");
					if (s)
					{
						sprintf(RMSCall, "%s-%d", RMSUser->Call, s);
						strcat(RMSLine, RMSCall);
					}
					else
						strcat(RMSLine, RMSUser->Call);
						
				}
			}
		}
		strcat(RMSLine, "\r");	
		nodeprintf(conn, RMSLine);
	}
	else
		nodeprintf(conn, "RMS Polling for %s disabled\r", RMSUser->Call);

	if (Arg1)
		goto Loop;
	
	SaveUserDatabase();
	SendPrompt(conn, user);
}

VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	int IdleTime;

	if (Arg1)
		IdleTime = atoi(Arg1);
	else
	{
		nodeprintf(conn, "Format is IDLETIME nnn\r");
		SendPrompt(conn, user);
		return;
	}

	if (IdleTime < 60 || IdleTime > 900)
	{
		nodeprintf(conn, "Time out of range (60 to 900 seconds)\r");
		SendPrompt(conn, user);
		return;
	}

	if (conn->BPQStream >= 0)
		ChangeSessionIdletime(conn->BPQStream, IdleTime);
	else
	{
		nodeprintf(conn, "Can't set Idle Time on Console)\r");
		SendPrompt(conn, user);
		return;
	}

	nodeprintf(conn, "Idle Tine set to %d\r", IdleTime);
	SendPrompt(conn, user);
	return;
}

VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	int newNumber;

	if (conn->sysop == 0)
	{
		nodeprintf(conn, "Command needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1)
		newNumber = atoi(Arg1);
	else
	{
		nodeprintf(conn, "New Number not specified\r");
		SendPrompt(conn, user);
		return;
	}

	if (newNumber < LatestMsg)
	{
		nodeprintf(conn, "New Number less tham current (%d)\r", LatestMsg);
		SendPrompt(conn, user);
		return;
	}

	if (newNumber > (MaxMsgno - 1000))
	{
		nodeprintf(conn, "New Number too close to Max Message Number\r");
		SendPrompt(conn, user);
		return;
	}

	nodeprintf(conn, "Next message number was %d, now %d\r", LatestMsg, newNumber);
	SendPrompt(conn, user);
	LatestMsg = newNumber;
	SaveMessageDatabase();
}


VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	int msgno;
	char * FN;
	FILE * Handle = NULL;
	struct MsgInfo * Msg;


	if (conn->sysop == 0)
	{
		nodeprintf(conn, "EXPORT command needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0)
	{
		nodeprintf(conn, "EXPORT nnn FILENAME - Export Message nnn to file FILENAME\r");
		SendPrompt(conn, user);
		return;
	}

	msgno = atoi(Arg1);
	
	FN = strtok_s(NULL, " \r", &Context);

	if (FN == NULL)
	{
		nodeprintf(conn, "Missong Filename");
		SendPrompt(conn, user);
		return;
	}


	Msg = GetMsgFromNumber(msgno);

	if (Msg == NULL)
	{
		nodeprintf(conn, "Message %d not found\r", msgno);		
		SendPrompt(conn, user);
		return;
	}

	conn->BBSFlags |= BBS;

	Handle = fopen(FN, "ab");

	if (Handle == NULL)
	{
		nodeprintf(conn, "File %s could not be opened\r", FN);		
		SendPrompt(conn, user);
		return;
	}

//	SetFilePointer(Handle, 0, 0, FILE_END);

	ForwardMessagetoFile(Msg, Handle);

	fclose(Handle);

	nodeprintf(conn, "%Message %d Exported\r", msgno);
	SendPrompt(conn, user);

	return;

}


VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	int count;

	if (conn->sysop == 0)
	{
		nodeprintf(conn, "IMPORT command needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0)
	{
		nodeprintf(conn, "IMPORT FILENAME - Import Messages from file FILENAME\r");
		SendPrompt(conn, user);
		return;
	}

	conn->BBSFlags |= BBS;

	count = ImportMessages(conn, Arg1, TRUE);

	conn->BBSFlags &= ~BBS;
	conn->Flags = 0;

	nodeprintf(conn, "%d Messages Processed\r", count);
	SendPrompt(conn, user);

	return;

}





VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	char Line[200];
	struct UserInfo * FwdBBS;
	struct	BBSForwardingInfo * ForwardingInfo;

	if (conn->sysop == 0)
	{
		nodeprintf(conn, "FWD command needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0)
	{
		nodeprintf(conn, "FWD BBSCALL - Display settings\r");
		nodeprintf(conn, "FWD BBSCALL interval - Set forwarding interval\r");
		nodeprintf(conn, "FWD BBSCALL REV interval - Set reverse forwarding interval\r");
		nodeprintf(conn, "FWD BBSCALL +- Flags (Flags are EN(able) RE(verse Poll) SE(Send Immediately)\r");
		nodeprintf(conn, "FWD BBSCALL NOW - Start a forwarding cycle now\r");
		nodeprintf(conn, "FWD QUEUE - List BBS's with queued messages\r");
		nodeprintf(conn, "FWD NOW can specify a Connect Script to use, overriding the configured script.\r");
		nodeprintf(conn, "Elements are separated by | chars. eg FWD RMS NOW ATT 7|C GM8BPQ-10\r");

		SendPrompt(conn, user);
		return;
	}


	if (_stricmp(Arg1, "QUEUE") == 0)
	{
		struct UserInfo * xuser;
		int Msgs;

		if (Context && Context[0])
		{
			// a bbs name - list all messages queued to it

			strlop(Context, '\r');

			xuser = FindBBS(_strupr(Context));

			if (xuser)
			{
				ListMessagestoForward(conn, xuser);
				SendPrompt(conn, user);
				return;
			}
		}

		for (xuser = BBSChain; xuser; xuser = xuser->BBSNext)
		{
			Msgs = CountMessagestoForward(xuser);

			if (Msgs)
				nodeprintf(conn, "%s %d Msgs\r", xuser->Call, Msgs);

		}
		SendPrompt(conn, user);
		return;
	}

	FwdBBS = LookupCall(Arg1);

	if (FwdBBS == 0 || (FwdBBS->flags & F_BBS) == 0)
	{
		nodeprintf(conn, "BBS %s not found\r", Arg1);
		SendPrompt(conn, user);
		return;
	}

	ForwardingInfo = FwdBBS->ForwardingInfo;

	Arg1 = strtok_s(NULL, seps, &Context);

	if (Arg1 == NULL)
		goto FDisplay;

	if (_stricmp(Arg1, "NOW") == 0)
	{
		char ** Value = NULL;

		if (ForwardingInfo->Forwarding)
		{
			BBSputs(conn, "Already Connected\r");
			SendPrompt(conn, user);
			return;
		}

		while (Context[0] == ' ')
			Context++;

		if (Context)
			strlop(Context, 13);

		if (Context && Context[0])
		{
			// Temp Connect Script to use

			char * ptr1;
			char * MultiString = NULL;
			const char * ptr;
			int Count = 0;

			Value = zalloc(sizeof(void *));				// always NULL entry on end even if no values
			Value[0] = NULL;

			ptr = Context;

			while (ptr && strlen(ptr))
			{
				ptr1 = strchr(ptr, '|');
			
				if (ptr1)
					*(ptr1++) = 0;

				Value = realloc(Value, (Count+2) * sizeof(void *));
			
				Value[Count++] = _strdup(ptr);
			
				ptr = ptr1;
			}
	
			Value[Count] = NULL;
		}

		StartForwarding(FwdBBS->BBSNumber, Value);
		
		if (ForwardingInfo->Forwarding)
			nodeprintf(conn, "Forwarding Started\r");
		else
			nodeprintf(conn, "Start Forwarding failed\r");
		
		SendPrompt(conn, user);
		return;
	}

	if (_stricmp(Arg1, "REV") == 0)
	{
		Arg1 = strtok_s(NULL, seps, &Context);
		ForwardingInfo->RevFwdInterval = atoi(Arg1);
	}
	else
	{
		while(Arg1)
		{
			_strupr(Arg1);

			if (isdigits(Arg1))
				ForwardingInfo->FwdInterval = atoi(Arg1);
			else if (strstr(Arg1, "EN"))
				if (Arg1[0] == '-') ForwardingInfo->Enabled = FALSE; else ForwardingInfo->Enabled = TRUE;
			else if (strstr(Arg1, "RE"))
				if (Arg1[0] == '-') ForwardingInfo->ReverseFlag = FALSE; else ForwardingInfo->ReverseFlag = TRUE;
			else if (strstr(Arg1, "SE"))
				if (Arg1[0] == '-') ForwardingInfo->SendNew = FALSE; else ForwardingInfo->SendNew = TRUE;

			Arg1 = strtok_s(NULL, seps, &Context);
		}
	}

	SaveConfig(ConfigName);
	GetConfig(ConfigName);

FDisplay:

	sprintf(Line, "%s Fwd Interval %d Rev Interval %d Fwd %s, Reverse Poll %s, Send Immediately %s\r",
		FwdBBS->Call, ForwardingInfo->FwdInterval, ForwardingInfo->RevFwdInterval,
		(ForwardingInfo->Enabled)? "Enabled" : "Disabled", (ForwardingInfo->ReverseFlag) ? "Enabled": "Disabled",
		(ForwardingInfo->SendNew) ? "Enabled": "Disabled");

	nodeprintf(conn, Line);
	SendPrompt(conn, user);

	return;
}

void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context)
{
	if (conn->sysop == 0)
	{
		nodeprintf(conn, "DOHOUSEKEEPING command needs SYSOP status\r");
		SendPrompt(conn, user);
		return;
	}

	DoHouseKeeping(FALSE);
			
	nodeprintf(conn, "Ok\r", Arg1);
	SendPrompt(conn, user);
}