;/*
DCC Matrix.C -o Matrix
;Copy Matrix AXsh:bin/matrix
Quit
*/

/*

/***********************************************************************
 *                        MATRIX MENU SYSTEM V1.1                      *
 *                               _                                     *
 *                         by Guru Gnosis Sahib                        *
 ***********************************************************************

v1.0 - 29-Dec-93 - First functional version
v1.1 - 07-Jan-93 - Added carrier-lost logouts; added CTRL-C processing;
				   disabled user from entering MSG_* in menu;
				   changed all gets() to fgets(stdin); fixed bug allowing
				   user to give any menu file as argument; superuser is
				   now {assistant, superuser, wizard, assistant, system}
*/

#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <libraries/dos.h>
#include <dos/dostags.h>
#include <dos/var.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <intuition/intuition.h>

#define VERSION 		"1.1"
/* #define VERSION_2		"Yup" */

#define NORMAL			0
#define SUPERUSER 		1

#define MAX_ITEM 		50			/* Maximum menu items */

struct Item
{
	char Cmd[80];
	char Key[10];
	BOOL CheckPath;
	BOOL RawMode;
	BOOL AskFile;
	char String[80];
	BOOL Pause;
};

struct MatrixMsg
{
	struct Message mm_Msg;
	char mm_Text[80];
};

struct Item il[MAX_ITEM];
struct MsgPort *matrix_mp;
struct MatrixMsg *matrix_msg;

FILE *input, *output, *output2;
int icount;

void breakexit(void);

main(argc,argv)
int argc;
char *argv[];
{
	BPTR fp, lock;
	BOOL exit_chosen = FALSE, timeout = FALSE, skip = FALSE, expert = FALSE,
		cmddone = FALSE, newmenu = FALSE;

	char validdir[10][80];
	char menufile[80], prompt[80], choice[40], execstr[80], user[80],
	 	 logfile[80], temp[80], temp2[80], iobuf[80];
	char c;
	int suspect = 0, access = NORMAL, vdirs = 2, count = 0,
		chars, success, timeleft, i;

/* Set defaults, parse arguments */

	strcpy(menufile, "AXsh:etc/menu/main");
	strcpy(prompt, "Choice (Q to quit): ");
	strcpy(validdir[0], "AXsh:etc/menu");
	strcpy(validdir[1], "Axsh:etc/adm");
	if(argc != 1)
	{
		success = GetVar("accesslevel", temp, 80, GVF_LOCAL_ONLY);
		if(!stricmp(temp, "superuser") || !stricmp(temp, "wizard") ||
          !stricmp(temp, "assistant") || !stricmp(temp, "system"))
          access = SUPERUSER;

		if(!stricmp(argv[1], "page"))
		{
			if(argc < 3) about();
			success = GetVar("user", user, 80, GVF_LOCAL_ONLY);
			strcpy(temp, "");
			for(i = 2; i < argc; i++)
			{
				strcat(temp, argv[i]);
				strcat(temp, " ");
			}
			sprintf(temp2, "Page %s %s", user, temp);
			success = SetVar("MATRIX_MSG", temp2, 80, GVF_GLOBAL_ONLY);
			std_error("Matrix: page sent");
		}
		if(!stricmp(argv[1], "force"))
		{
			if(argc < 3) about();
			if(access != SUPERUSER)
			{
				std_error("Matrix: access denied");
			}
			strcpy(temp, "");
			for(i = 2; i < argc; i++)
			{
				strcat(temp, argv[i]);
				strcat(temp, " ");
			}
			sprintf(temp2, "Frce %s", temp);
			success = SetVar("MATRIX_MSG", temp2, 80, GVF_GLOBAL_ONLY);
			std_error("Matrix: page sent");
		}
		if(!stricmp(argv[1], "chat"))
		{
			if(access != SUPERUSER) std_error("Matrix: access denied");
			success = SetVar("MATRIX_MSG", "Chat", 80, GVF_GLOBAL_ONLY);
			std_error("Matrix: chat request sent");
		}
		if(!stricmp(argv[1], "abort"))
		{
			if(access != SUPERUSER)	std_error("Matrix: access denied");
			success = SetVar("MATRIX_MSG", "Abrt", 80, GVF_GLOBAL_ONLY);
			std_error("Matrix: abort request sent");
		}
		if (argv[1][0] == '?' || argv[1][0] == '-') about();
		strcpy(menufile, argv[1]);
		if (argc > 2) strcpy(prompt, argv[2]);
	}

/* Check menu file's validity */

	if(input = fopen("ENV:MATRIX_DIR", "r"))
	{
		while(!feof(input))
		{
			fscanf(input, "%s", validdir[vdirs]);
			vdirs++;
		}
		fclose(input);
	}
	skip = TRUE;
	for(i = 0; i < vdirs; i++)
	{
		if(!strnicmp(validdir[i], menufile, strlen(validdir[i])))
			skip = FALSE;
	}
	if(skip == TRUE) std_error("Matrix: menu in illegal directory");
		
/* Get time, (re)open log */

	time_t t = time(NULL);			
	struct tm *tp = localtime(&t);
	strftime(logfile, sizeof(logfile) - 1, "AXsh:etc/adm/Log%d%m%y-Matrix", tp);
	if(!(output = fopen(logfile, "a")))
		std_error("Matrix: could not open logfile");
	strftime(temp, sizeof(temp) - 1,
		"Matrix log started on %A %d-%b-%y %H:%M:%S", tp);
	success = GetVar("user", user, 80, GVF_LOCAL_ONLY);
	fprintf(output, "%s by %s\n", temp, user);

/* Check for TimeOut, expert, and autologout */

	strcpy(temp, "Boing");
	success = GetVar("timeout_active", temp, 80, GVF_GLOBAL_ONLY);
	if(strcmp(temp, "Boing"))	/* Timeout exists */
		timeout = TRUE;

	strcpy(temp, "Boing");
	success = GetVar("expert", temp, 80, GVF_LOCAL_ONLY);
	if(!strcmp(temp, "expert"))	/* Expert mode */
		expert = TRUE;

/* Get the menu */

	showmenu(menufile, expert);
	parsemenu(user);
	fclose(input);

#ifdef DEBUG
	for(i = 0; i < icount; i++)
	{
		printf("%d: %c %s %d %d %d\n", i, il[i].Key, il[i].Cmd, il[i].RawMode,
			il[i].CheckPath, il[i].AskFile);
	}
#endif

/* Check & possibly launch the MatrixServer */

#ifdef VERSION_2
	Forbid();
	server_mp = (struct MsgPort *) FindPort("Matrix_Server");
	Permit();
	if(server_mp == 0)
	{
		/* Negotiations with Matrix_Server */
	}
	matrix_msg = (struct MatrixMsg *) AllocMem(sizeof(struct MatrixMsg),
		MEMF_PUBLIC);
	if(matrix_msg == 0)
	{
		freeallmem();
		std_error("Matrix: not enough memory");
	}

	(BPTR) matrix_mp = CreatePort("Matrix_Menu1", 0);
#endif

/* Abort through special routine if CTRL-C'd */

	signal(SIGINT, breakexit);

/* Main loop */

	while(exit_chosen == FALSE)
	{
		strcpy(temp, "Boing");
		success = GetVar("MATRIX_MSG", temp, 80, GVF_GLOBAL_ONLY);
		if(strcmp(temp, "Boing"))	/* Message received */
		{
			DeleteVar("MATRIX_MSG", GVF_GLOBAL_ONLY);
			if(!stricmp(temp, "Abrt"))
			{
				puts("\n*** Superuser forced abort ***\n");
				fprintf(output, "*** Superuser forced abort, exiting ***\n");
				skip = TRUE;
				strcpy(choice, "MSG_ABRT");
				exit_chosen = TRUE;
			}
			if(!stricmp(temp, "Chat"))
			{
				puts("\n*** Entering chat ***\n");
				fprintf(output, "*** Superuser forced chat ***\n");
				skip = TRUE;
				strcpy(choice, "MSG_CHAT");
			}
			if(!strnicmp(temp, "Frce", 4))
			{
				sscanf(temp, "%s %[^\0]", temp2, execstr);
				puts("\n*** Superuser forced command ***\n");
				fprintf(output, "*** Superuser forced command ***\n%s\n", execstr);
				success = System(execstr, TAG_END);
			}
			if(!strnicmp(temp, "Page", 4))
			{
				sscanf(temp, "%s %s %[^\0]", temp2, temp2, execstr);
				printf("\n*** Message from %s ***\n\n", temp2);
				printf("%s\n\n", execstr);
			}
		}	
		if(timeout == TRUE)
		{
			lock = Lock("AXsh:etc/nologin", ACCESS_READ);
			if(lock != 0) 	/* File exists */
			{
				UnLock(lock);
				input = fopen("AXsh:etc/nologin", "r");
				fgets(temp, 80, input);
				while(!feof(input))
				{
					printf("%s\n", temp);
					fgets(temp, 80, input);
				}
				fclose(input);
				fprintf(output, "*** Out of time, forced logout ***\n");
				skip = TRUE;
				exit_chosen = TRUE;
				strcpy(choice, "MSG_ABRT\n");
			}
			else
			{
				success = GetVar("TIMELEFT", temp, 80, GVF_GLOBAL_ONLY);
				timeleft = atoi(temp);
				if(timeleft < 5)
					printf("*** Only %d min left ***\n", timeleft);
				else
					printf("(%d min left)\n", timeleft);
			}
		}

/* Check for strange input */

		if(suspect == 5)
		{
			puts("\nexit");
			fprintf(output, "*** Suspected loss of carrier ***\n");
			skip = TRUE;
			strcpy(choice, "MSG_ABRT");
			exit_chosen = TRUE;
		}

/* Wait for input & messages */

		if(skip == FALSE)
		{
			printf("%s", prompt);
			fflush(stdout);
			fgets(choice, 35, stdin);
			choice[strlen(choice) - 1] = 0;		/* Remove \n */
			if(!strnicmp(choice, "MSG_", 4) && exit_chosen == FALSE)
				strcpy(choice, "?");
			if(!strcmp(choice, "")) strcpy(choice, "?");
		}
		skip = FALSE;
		cmddone = FALSE;
		for(i = 0; i < icount; i++)
		{
			if(!stricmp(il[i].Key, choice))
			{
				strcpy(execstr, il[i].Cmd);
				if(il[i].AskFile == TRUE)
				{
					printf("\n%s", il[i].String);
					fgets(temp, 80, stdin);
					if(il[i].CheckPath == TRUE)
					{
						success = strpbrk(temp, ":/$");
						if(success != 0)
						{
							puts("No paths or variables allowed!");
							skip = TRUE;
						}
					}
					strcat(execstr, " ");
					strcat(execstr, temp);
				}
				if(execstr[0] == '%')
				{
					cmddone = TRUE;
					if(!strnicmp(execstr, "%menu", 5))
					{
						strcpy(menufile, &execstr[6]);
						strcpy(prompt, il[i].String);
						showmenu(menufile, expert);
						parsemenu(user);
						fclose(input);
						cmddone = TRUE;
						newmenu = TRUE;
						fprintf(output, "menu: %s\n", menufile);
						break;
					}
					if(!strnicmp(execstr, "%xprt", 5))
					{
						if(expert == TRUE)
						{
							puts("Verbose mode selected.");
							success = SetVar("expert", "verbose", 80, GVF_LOCAL_ONLY);
							expert = FALSE;
						}
						else
						{
							puts("Expert mode selected.");
							success = SetVar("expert", "expert", 80, GVF_LOCAL_ONLY);
							expert = TRUE;
						}
						puts("To make your selection permanent, add one of these to your .login:\n");
						puts("set expert expert        (for expert mode)");
						puts("set expert verbose       (for verbose mode [default])\n");
						if(expert == FALSE)
						{
							puts("--- Press ENTER ---");
							fgets(temp, 80, stdin);
						}
						cmddone = TRUE;
						skip = TRUE;
					}
					if(!strnicmp(execstr, "%exit", 5) ||
						!strnicmp(execstr, "%quit", 5))
					{
						cmddone = TRUE;
						exit_chosen = TRUE;
						skip = TRUE;
					}
				}
				if(skip == FALSE)
				{
					if(il[i].RawMode == TRUE)
						setvbuf(stdin, NULL, _IONBF, 0);
					cmddone = TRUE;
					fprintf(output, "%s\n", il[i].Cmd);
					success = System(execstr, TAG_END);
					if(il[i].RawMode == TRUE)
						setvbuf(stdin, iobuf, _IOLBF, sizeof(iobuf));
					if(il[i].Pause == TRUE && expert == FALSE)
					{
						puts("--- Press ENTER ---");
						fgets(temp, 80, stdin);
					}
					if(!strnicmp(choice, "MSG_", 4)) break;
				}
				else
					skip = FALSE;
			}
		}
		if(cmddone == FALSE || expert == FALSE)
		{
			if(exit_chosen == FALSE && newmenu == FALSE)
			{
				showmenu(menufile, FALSE);	/* expert mode off */
				fclose(input);
			}
		}
		newmenu = FALSE;
		if (cmddone == FALSE) suspect++;
	}
	
	matrix_msg->mm_Msg.mn_Node.ln_Type = NT_MESSAGE;
	matrix_msg->mm_Msg.mn_Length = sizeof(struct MatrixMsg);
	matrix_msg->mm_Msg.mn_ReplyPort = matrix_mp;
	strcpy(matrix_msg->mm_Text, "Boing");
	
#ifdef VERSION_2
	Forbid();
	server_mp = FindPort("Matrix_Server");
	if(server_mp)
		PutMsg(server_mp, matrix_msg);
	Permit();
#endif

	time_t t = time(NULL);			
	struct tm *tp = localtime(&t);
	strftime(temp, sizeof(temp) - 1,
		"Matrix log closed on %A %d-%b-%y %H:%M:%S", tp);
	fprintf(output, "%s\n\n", temp);
	fclose(output);

#ifdef VERSION_2
	DeletePort((struct MsgPort *) matrix_mp);
	freeallmem();
#endif
	exit(0);
}

about()
{
	char *version = "\nMatrix for AXsh v"VERSION" by Guru Gnosis Sahib in 1994";

	puts(version);
	puts("Usage: Matrix [menufile] [prompt]");
	puts("       Matrix page [text]");
	puts("       Matrix abort | chat | force <cmd> (superusers only)\n");
	exit(0);
}

showmenu(char menufile[80], BOOL expert)
{
	char temp[80];
	int flag = FALSE;

	if(!(input = fopen(menufile, "r")))	std_error("Matrix: could not open menu file");
	if(expert == FALSE) printf("\f"); else printf("\n");
	fgets(temp, 80, input);
	while(!feof(input) && flag == FALSE)
	{
		if(!strncmp(temp, "#", 1))
		{
			flag = TRUE;
		}
		else
		{
			if(expert == FALSE)
				printf("%s", temp);		/* puts() causes duplicate newlines */
			fgets(temp, 80, input);
		}
	}
	if (flag == FALSE)
	{
		fclose(input);
		std_error("Matrix: not a Topic menu file");
	}
}

parsemenu(char user[80])
{
	char temp[80];
	int offset = 3;

	icount = 1;
	fgets(temp, 80, input);
	while(!feof(input) || strncmp(temp, "#", 1))
	{
		offset = 3;
		icount++;
		strcpy(il[icount].Key, temp);
		il[icount].Key[strlen(il[icount].Key) - 1] = '\0';  /* Remove \n */
		fgets(il[icount].Cmd, 80, input);
		il[icount].Cmd[strlen(il[icount].Cmd) - 1] = '\0';  /* Remove \n */

		fgets(temp, 80, input);
		if(temp[3] == 'x')
			offset = 6;			/* Extended optionset */

/* Standard Topic options */

		if(temp[0] == 'r')
			il[icount].RawMode = TRUE;
		else
			il[icount].RawMode = FALSE;
		if(temp[1] == 'p')
			il[icount].CheckPath = TRUE;
		else
			il[icount].CheckPath = FALSE;
		if(temp[2] == '?')
		{
			il[icount].AskFile = TRUE;
			strcpy(il[icount].String, &temp[offset]);
		}
		else
		{
			il[icount].AskFile = FALSE;
			if(offset == 3 || (temp[5] != 'm' && offset == 6))
			{
				strcat(il[icount].Cmd, " ");
				strcat(il[icount].Cmd, &temp[offset]);
				il[icount].Cmd[strlen(il[icount].Cmd) - 1] = '\0';  /* Remove \n */
				strcpy(il[icount].String, "");
			}
		}

/* Extended Matrix options */

		il[icount].Pause = FALSE;
		if(temp[3] == 'x')
		{
			if(temp[4] == 'z')
			{
				il[icount].Pause = TRUE;
			}
			if(temp[5] == 'm')
			{
				strcpy(il[icount].String, &temp[offset]);
				il[icount].String[strlen(il[icount].String) - 1] = '\0';
			}
		}
		fgets(temp, 80, input);
	}

	icount++;
	strcpy(il[icount].Key, "MSG_CHAT");
	strcpy(il[icount].Cmd, "AXsh:bin/chat");
	il[icount].CheckPath = FALSE;
	il[icount].RawMode = TRUE;
	il[icount].AskFile = FALSE;
	il[icount].Pause = FALSE;

	icount++;
	strcpy(il[icount].Key, "MSG_ABRT");
	strcpy(il[icount].Cmd, "%exit");
	il[icount].CheckPath = FALSE;
	il[icount].RawMode = FALSE;
	il[icount].AskFile = FALSE;
	il[icount].Pause = FALSE;
}

freeallmem()
{
	if(matrix_msg) FreeMem(matrix_msg, sizeof(struct MatrixMsg));
}

std_error(char text[80])
{
	puts(text);
	exit(0);
}

void breakexit()
{
	char temp[80];

	freeallmem();
	puts("\n\n*** Matrix received BREAK, exiting ***\n");
	fprintf(output, "*** Exit by BREAK ***\n");
	time_t t = time(NULL);
	struct tm *tp = localtime(&t);
	strftime(temp, sizeof(temp) - 1,
		"Matrix log closed on %A %d-%b-%y %H:%M:%S", tp);
	fprintf(output, "%s\n\n", temp);
	fclose(output);
	exit(0);
}
