// --------------------------------------------------------------------------
// Citadel: Aplic.CPP
//
// Application code for Citadel

#include "ctdl.h"
#pragma hdrstop

#include "msg.h"
#include "config.h"
#include "room.h"
#include "log.h"
#include "music.h"
#include "group.h"
#include "account.h"
#include "hall.h"
#include "cwindows.h"
#include "filecmd.h"
#include "timer.h"
#include "cfgfiles.h"
#include "events.h"
#include "aplic.h"
#include "mouse.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// ExeAplic 	gets name of aplication and executes
// shellescape	handles the sysop '!' shell command
// wxsnd		do external download
// wxrcv		do external upload
// execDoor 	Execute a door
// apsystem 	turns off interupts and makes a system call

extern	int Zmodem_Send_Batch (const char *filepath, unsigned int baud);
extern	int Zmodem_Receive_Batch (const char *filepath, unsigned int baud);

extern ConsoleLockC ConsoleLock;

int RunApplication(const char *CommandLine, const char *FileName,
		Bool TrapIt, Bool ChangeDir)
	{
	if (TrapIt)
		{
		doEvent(EVT_APPLIC);
		}

	label MData, MDataL, PSpeed, MSpeed, UName;
	sprintf(MData, pctd, cfg.mdata);
	sprintf(PSpeed, pctld, bauds[TI()SerialPort.GetSpeed()]);
	sprintf(MSpeed, pctld, connectbauds[TI()ModemSpeed]);
	strcpy(MDataL, onConsole ? getmsg(376) : MData);
	strcpy(UName, deansi(TI()CurrentUser->GetName()));

	char ExpandedCommandLine[256];
	sformat(ExpandedCommandLine, CommandLine,
			FileName ? getmsg(1480) : getmsg(1481), MData, PSpeed, MSpeed,
			cfg.aplpath, MDataL, UName, FileName);

	if (ChangeDir && !changedir(cfg.aplpath))
		{
		CRmPrintfCR(getmsg(154));
		return (CERROR);
		}
	else
		{
		const int SpawnResult = apsystem(ExpandedCommandLine, TrapIt);

		changedir(cfg.homepath);

		DebugOut(pcts, ExpandedCommandLine);

		return (SpawnResult);
		}
	}


// --------------------------------------------------------------------------
// ExeAplic(): Executes the current room's application.
//
// Return value:
//	negative: Error running application
//	non-negative: Application's result code

int ExeAplic(void)
	{
	SetDoWhat(ENTERAPP);

	if (!IsRoomApplication(TI()thisRoom))
		{
		CRmPrintfCR(getmsg(151), cfg.Uroom_nym);
		changedir(cfg.homepath);
		return (-1);
		}

	if (!changedir(cfg.aplpath))
		{
		CRmPrintfCR(getmsg(154));
		changedir(cfg.homepath);
		return (-1);
		}

	doEvent(EVT_APPLIC);

	char stuff[100];
	char comm[5];

	sprintf(comm, getmsg(1482), cfg.mdata, ns, ns);

	sprintf(stuff, getmsg(1483), GetCurrentRoomApplication(),
			onConsole ? getmsg(376) : comm,
			onConsole ? 2400l : bauds[TI()SerialPort.GetSpeed()],
			TI()CurrentUser->IsSysop(), deansi(TI()CurrentUser->GetName()));

	const int SpawnResult = apsystem(stuff, TRUE);
	changedir(cfg.homepath);

	return (SpawnResult);
	}


// --------------------------------------------------------------------------
// shellescape(): Handles the sysop '!' shell command.
//
// Input:
//	Bool super: TRUE if super-shell, FALSE if not

void shellescape(Bool super)
	{
	char command[80];

	doEvent(EVT_APPLIC);

	changedir(IsRoomDirectory(TI()thisRoom) ? GetCurrentRoomDirectory() :
			cfg.homepath);

	sprintf(command, getmsg(1484), super ? getmsg(1485) : ns,
			getenv(getmsg(502)));

	apsystem(command, TRUE);

	StatusLine.Update();

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// wxsnd(): Do external download.
//
// Input:
//	const char *path: The path to send files from
//	const char *file: The file or files to send
//	const protocols *theProt: Which protocol to use
//	int type: 1 if this is a queued transfer; 2 if CheckTransfer; 0 if normal

void wxsnd(const char *path, const char *file, const protocols *theProt, int type)
	{
	if (!changedir(path))
		{
		return;
		}

	const char *SendCmd =
			(type == 2) ? theProt->CheckMethod :
			(type == 1) ? theProt->respDown : theProt->snd;

	if (*SendCmd == '[')
		{
		InternalProtocols prot;

		if (SameString(SendCmd, getmsg(531)))
			{
			prot = IP_XMODEM;
			}
		else if (SameString(SendCmd, getmsg(532)))
			{
			prot = IP_CRCXMODEM;
			}
		else if (SameString(SendCmd, getmsg(533)))
			{
			prot = IP_ONEKXMODEM;
			}
		else if (SameString(SendCmd, getmsg(534)))
			{
			prot = IP_ZMODEM;
			}
		else
			{
			CRmPrintf(getmsg(535), SendCmd);
			return;
			}

		if (prot == IP_ZMODEM)
			{
			Zmodem_Send_Batch(file,
					(uint) connectbauds[TI()ModemSpeed]);
			}
		else
			{
			xsend(file, prot);
			}
		}
	else
		{
		RunApplication(SendCmd, file, TRUE, FALSE);
		}

	changedir(path);
	}


// --------------------------------------------------------------------------
// wxrcv(): do external upload.
//
// Input:
//	const char *path: Where to upload to
//	const char *file: File to receive (not used if batch transfer)
//	const protocols *theProt: File transfer protocol to use

void wxrcv(const char *path, const char *file, const protocols *theProt)
	{
	if (!changedir(path))
		{
		return;
		}

	if (theProt->rcv[0] == '[')
		{
		InternalProtocols prot;

		if (SameString(theProt->rcv, getmsg(531)))
			{
			prot = IP_XMODEM;
			}
		else if (SameString(theProt->rcv, getmsg(532)))
			{
			prot = IP_CRCXMODEM;
			}
		else if (SameString(theProt->rcv, getmsg(533)))
			{
			prot = IP_ONEKXMODEM;
			}
		else if (SameString(theProt->rcv, getmsg(534)))
			{
			prot = IP_ZMODEM;
			}
		else
			{
			CRmPrintf(getmsg(535), theProt->rcv);
			return;
			}

		if (prot == IP_ZMODEM)
			{
			Zmodem_Receive_Batch(ns,
					(uint) connectbauds[TI()ModemSpeed]);
			}
		else
			{
			xreceive(file, prot);
			}
		}
	else
		{
		RunApplication(theProt->rcv, file, TRUE, FALSE);
		}

	changedir(path);
	}


// --------------------------------------------------------------------------
// execDoor(): Execute a door
//
// Input:
//	char c: The menu key used
//	int where: Where menu key was pressed:
//		0: .ED command
//		1: x at room prompt
//		2: .x at room prompt
//
// Return value:
//	TRUE: Door found and executed
//	FALSE: Door not found

Bool execDoor(char c, int where)
	{
	doors *theDoor;

	if (c == '?')
		{
		return (FALSE);
		}

	for (theDoor = extDoorList; theDoor; theDoor = (doors *) getNextLL(theDoor))
		{
		if (tolower(c) == tolower(theDoor->name[0]))
			{
			if ((!theDoor->SYSOP	|| TI()CurrentUser->IsSysop()) &&
					(!theDoor->AIDE || TI()CurrentUser->IsAide()) &&
					(!theDoor->CON	|| (onConsole && !ConsoleLock.IsLocked())) &&
					(!theDoor->DIR	|| IsRoomDirectory(TI()thisRoom)) &&
					(!where 		|| (theDoor->WHERE & where)))
				{
				if (theDoor->group[0])
					{
					g_slot GroupSlot = FindGroupByName(theDoor->group);

					if (GroupSlot != CERROR)
						{
						if (TI()CurrentUser->IsInGroup(GroupSlot))
							{
							break;
							}
						}
					}
				else
					{
					break;
					}
				}
			}
		}

	if (theDoor)
		{
		mPrintfCR(pcts, theDoor->name);
		doCR();

		RunApplication(theDoor->cmd, NULL, TRUE, TRUE);

		return (TRUE);
		}
	else
		{
		return (FALSE);
		}
	}


// --------------------------------------------------------------------------
// apsystem(): Actually executes other applications.
//
// Input:
//	const char *stuff: Command line to execute, with flags (! for super-shell,
//		etc.)
//	Bool trapit: TRUE to trap running, FALSE to not.
//
// Return value:
//	negative: Error running application.
//	non-negative: Application's return code

int apsystem(const char *stuff, Bool trapit)
	{
	VerifyHeap();

	Bool clearit = TRUE, superit = FALSE, batch = FALSE, door = TRUE,
			isscr = FALSE;

	char scratch[256];
	char *words[256];
	int count;

#ifndef WINCIT
	uchar c, r;
	int row, col;
#endif

	DebugOut(pcts, stuff);

	while (*stuff == '!' || *stuff == '@' || *stuff == '$' || *stuff == '?' ||
			*stuff == '*')
		{
		if (*stuff == '!')
			{
			superit = TRUE;
			}
		else if (*stuff == '@')
			{
			clearit = FALSE;
			}
		else if (*stuff == '$')
			{
			batch = TRUE;
			}
		else if (*stuff == '?')
			{
			door = FALSE;
			}
		else if (*stuff == '*')
			{
			isscr = TRUE;
			}

		stuff++;
		}

	if (isscr)
		{
		label ScriptName;

		CopyStringToBuffer(ScriptName, stuff, LABELSIZE);

		if (strchr(ScriptName, ' '))
			{
			*strchr(ScriptName, ' ') = 0;
			}

		return (runScript(ScriptName));
		}

	if (door)
		{
		writeAplFile();
		}

	if (!superit)
		{
		char OldPath[128];

		getfpath(OldPath);

		compactMemory(1);

		changedir(OldPath);
		}

#ifndef WINCIT
	if (farcoreleft() < cfg.memfree)
		{
		superit = TRUE;
		}
#endif

	if (TI()DoWhat != NETWORKING && superit && cfg.SwapNote)
		{
		CRmPrintfCR(getmsg(162));
		}

	if (TI()SerialPort.IsDisabled())
		{
		TI()SerialPort.DropDtr();
		}

	TI()SerialPort.Deinit();

	(*deinitkbd)();
	(*close_sound)();
	deinitMouseHandler();
	deinit_internal_sound();

	bufferedClose(msgfl);
	msgfl = NULL;
	citClose(&TI()OC.PrintFile);
	citClose(&roomfl);
	citClose(&journalfl);

#ifndef WINCIT
	if (clearit)
		{
		readpos(&row, &col);
		save_screen();
		outPhys(TRUE);
		cls(SCROLL_NOSAVE);
		outPhys(FALSE);
		}
#endif

	outPhys(TRUE);

	if (TI()loggedIn && clearit)
		{
		label L;
		CopyStringToBuffer(L, cfg.Uuser_nym, sizeof(L) - 1);
		stripansi(L);

		cPrintf(getmsg(163), L, deansi(TI()CurrentUser->GetName()));
		doccr();
		}

	if (SameString(stuff, getenv(getmsg(502))))
		{
		cPrintf(getmsg(164), programName);
		}

	outPhys(FALSE);

	if (batch)
		{
		sprintf(scratch, getmsg(536), getenv(getmsg(502)), stuff);
		}
	else
		{
		CopyStringToBuffer(scratch, stuff, sizeof(scratch) - 1);
		}

	count = parse_it(words, scratch);
	words[count] = NULL;

	curson();

	if (trapit)
		{
		char TrapString[256];

		sprintf(TrapString, getmsg(401), stuff);
		trap(TrapString, T_APPLIC);
		}

	critical(FALSE);
	DeinitializeTimer();

	citClose(&trapfl);

	int SpawnResult;

	if (superit)
		{
#ifndef WINCIT
		SpawnResult = spawnvpeo(cfg.temppath, words[0], (const char **) words,
				(const char **) environ);
#else
		SpawnResult = -1;
#endif
		}
	else
		{
#ifndef WINCIT
		SpawnResult = spawnvp(P_WAIT, words[0], words);

		if (SpawnResult == -1)
			{
			VerifyHeap();

			if (errno == ENOMEM)
				{
				if (TI()DoWhat != NETWORKING)
					{
					CRmPrintfCR(getmsg(18));
					}

				SpawnResult = spawnvpeo(cfg.temppath, words[0],
						(const char **) words, (const char **) environ);
				}
			}
#else
		SpawnResult = -1;
#endif
		}

	citOpen(cfg.trapfile, CO_AP, &trapfl);

	InitializeTimer();
	critical(TRUE);

	if (!ScreenSaver.IsOn())
		{
		ScreenSaver.Update();
		}

	time(&TI()LastActiveTime);

	if (trapit)
		{
		trap(getmsg(402), T_APPLIC);
		}

#ifndef WINCIT
	// restore the screen if applicaton changes modes.
	getScreenSize(&c, &r);
	if (c != conCols || r != conRows)
		{
		setscreen();
		}
#endif

	// back to blank screen saver mode
	if (ScreenSaver.IsOn() || StatusLine.IsFullScreen() || allWindows)
		{
		outPhys(TRUE);
		cls(SCROLL_NOSAVE);
		outPhys(FALSE);

		if (ScreenSaver.IsOn() && !cfg.really_really_fucking_stupid)
			{
			cursoff();
			}

		updateStatus = TRUE;

		if (allWindows)
			{
			RedrawAll();
			}
		}

#ifndef WINCIT
	if (clearit)
		{
		if (!StatusLine.IsFullScreen())
			{
			restore_screen();
			}

		position(row, col);
		}
#endif

	setborder(cfg.battr); // not quite sure about this

	// move things around in memory to maximize free space here.
	compactMemory(1);

	TI()SerialPort.Init();

	(*initkbd)();
	(*init_sound)();
	(*dtrrs)(1);
	initMouseHandler();

	if (!hideCounter)
		{
		showMouse();
		hideCounter = 0;
		}	

	init_internal_sound();

	StatusLine.Update();

	sprintf(scratch, sbs, cfg.msgpath, msgDat);
	msgfl = bufferedOpenExist(scratch);

	sprintf(scratch, sbs, cfg.homepath, roomDat);
	openFile(scratch, &roomfl);

	initMenus();	// may have changed when shelled

	if (TI()SerialPort.IsDisabled())
		{
		TI()SerialPort.DropDtr();
		}

	if (((cfg.offhook == 1) || (cfg.offhook && TI()DoWhat != NETWORKING)) &&
			!SerialPort.HaveCarrier() && TI()loggedIn)
		{
		offhook();
		}

	readAplFile();

	if (TI()OC.Printing)
		{
		if (!citOpen(TI()OC.PrintfileName, CO_A, &TI()OC.PrintFile))
			{
			TI()OC.Printing = FALSE;

			if (!ScreenSaver.IsOn() && cfg.VerboseConsole)
				{
				CitWindowsNote(NULL, getmsg(1674));
				}
			}
		}

	if (!ScreenSaver.IsOn() && onConsole)
		{
		ScreenSaver.Update();
		}

	VerifyHeap();

	return (SpawnResult);
	}
