/************************************************************
* MultiUser - MultiUser Task/File Support System				*
* ---------------------------------------------------------	*
* Miscellaneous Routines												*
* ---------------------------------------------------------	*
* © Copyright 1993-1994 Geert Uytterhoeven						*
* All Rights Reserved.													*
************************************************************/


#include <exec/execbase.h>
#include <utility/tagitem.h>

	/*
	 *		Password encryption
	 *
	 *		This function must be defined here because its prototype in
	 *		<clib/alib_protos.h> does not contain the __stdargs keyword
	 */

extern STRPTR __stdargs ACrypt(STRPTR buffer, STRPTR password, STRPTR username);


#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <proto/reqtools.h>
#include <libraries/reqtools.h>
#include <string.h>

#include "Memory.h"
#include "Server.h"
#include "Config.h"
#include "LibHeader.h"
#include "Misc.h"
#include "Task.h"
#include "Segment.h"
#include "Protection.h"
#include "UserInfo.h"
#include "Monitor.h"


	/*
	 *		Library Bases
	 */

struct ExecBase *SysBase;
struct muBase *muBase;
struct DosLibrary *DOSBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *UtilityBase = NULL;
struct ReqToolsBase *ReqToolsBase = NULL;


	/*
	 *		Library Vector Offsets for SetFunction()
	 */

extern __far LVOAddTask;
extern __far LVORemTask;
extern __far LVOLoadSeg;
extern __far LVONewLoadSeg;
extern __far LVOUnLoadSeg;
extern __far LVOInternalLoadSeg;
extern __far LVOInternalUnLoadSeg;
extern __far LVOCreateProc;
extern __far LVOCreateNewProc;
extern __far LVORunCommand;
extern __far LVOSetProtection;


	/*
	 *		Initialisation
	 */

BOOL Init(void)
{
	struct Task *task;

		/*
		 *		Initialise Memory Management
		 */

	if (!InitMemory())
		return(FALSE);

		/*
		 *		Initialise the Semaphores
		 */

	InitSemaphore(&muBase->TaskOwnerSem);
	InitSemaphore(&muBase->SegOwnerSem);
	InitSemaphore(&muBase->VolumesSem);
	InitSemaphore(&muBase->MonitorSem);

		/*
		 *		Open dos.library, intuition.library and utility.library V37+,
		 *		reqtools.library V38+ and create the Server
		 */

	if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)) ||
		 !(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)) ||
		 !(UtilityBase = OpenLibrary("utility.library", 37)) ||
		 !(ReqToolsBase = (struct ReqToolsBase *)OpenLibrary("reqtools.library", 38)) ||
		 !CreateServer())
		return(FALSE);

		/*
		 *		Initialise Task Owner, Segment Owner and Monitor Lists
		 */

	InitTaskList();
	InitSegList();
	InitMonList();

		/*
		 *		Initialise Task Control
		 */

	NewList((struct List *)&muBase->Frozen);

		/*
		 *		Create Task/User list
		 *
		 *		All tasks have no owner.
		 */

	Forbid();
	PushTask(SysBase->ThisTask, NULL);
	for (task = (struct Task *)SysBase->TaskReady.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  CreateOrphanTask(task, DEFPROTECTION);
	for (task = (struct Task *)SysBase->TaskWait.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  CreateOrphanTask(task, DEFPROTECTION);

		/*
		 *		Make sure the Server is owned by the system
		 */

	PushTask((struct Task *)muBase->Server, &RootExtOwner);

		/*
		 *		Patch exec.library functions:
		 *
		 *			AddTask()
		 *			RemTask()
		 *
		 *		Patch dos.library functions:
		 *
		 *			LoadSeg()
		 *			NewLoadSeg()
		 *			UnLoadSeg()
		 *			InternalLoadSeg()
		 *			InternalUnLoadSeg()
		 *			CreateProc()
		 *			CreateNewProc()
		 *			RunCommand()
		 *			SetProtection()
		 *
		 *		This must be done inside this Forbid()/Permit() pair
		 *		to prevent the birth of orphan-tasks
		 */

	muBase->OLDAddTask = SetFunction((struct Library *)SysBase,
												(LONG)&LVOAddTask,
												(ULONG (*)())NEWAddTask);
	muBase->OLDRemTask = SetFunction((struct Library *)SysBase,
												(LONG)&LVORemTask,
												(ULONG (*)())NEWRemTask);
	muBase->OLDLoadSeg = SetFunction((struct Library *)DOSBase,
														  (LONG)&LVOLoadSeg,
														  (ULONG (*)())NEWLoadSeg);
	muBase->OLDNewLoadSeg = SetFunction((struct Library *)DOSBase,
														  (LONG)&LVONewLoadSeg,
														  (ULONG (*)())NEWNewLoadSeg);
	muBase->OLDUnLoadSeg = SetFunction((struct Library *)DOSBase,
														  (LONG)&LVOUnLoadSeg,
														  (ULONG (*)())NEWUnLoadSeg);
	muBase->OLDInternalLoadSeg = SetFunction((struct Library *)DOSBase,
														  (LONG)&LVOInternalLoadSeg,
														  (ULONG (*)())NEWInternalLoadSeg);
	muBase->OLDInternalUnLoadSeg = SetFunction((struct Library *)DOSBase,
															 (LONG)&LVOInternalUnLoadSeg,
															 (ULONG (*)())NEWInternalUnLoadSeg);
	muBase->OLDCreateProc = SetFunction((struct Library *)DOSBase,
													(LONG)&LVOCreateProc,
													(ULONG (*)())NEWCreateProc);
	muBase->OLDCreateNewProc = SetFunction((struct Library *)DOSBase,
														(LONG)&LVOCreateNewProc,
														(ULONG (*)())NEWCreateNewProc);
	muBase->OLDRunCommand = SetFunction((struct Library *)DOSBase,
													(LONG)&LVORunCommand,
													(ULONG (*)())NEWRunCommand);
	muBase->OLDSetProtection = SetFunction((struct Library *)DOSBase,
														(LONG)&LVOSetProtection,
														(ULONG (*)())NEWSetProtection);

	Permit();

		/*
		 *		Activate the Server
		 */

	if (!StartServer())
		return(FALSE);

	return(TRUE);
}


	/*
	 *		Clean Up
	 */

BOOL CleanUp(void)
{
		/*
		 *		Kill the Server
		 */

	if (!KillServer())
		return(FALSE);

		/*
		 *		Restore exec.library functions:
		 *
		 *			AddTask()
		 *			RemTask()
		 *
		 *		Restore dos.library functions:
		 *
		 *			LoadSeg()
		 *			NewLoadSeg()
		 *			UnLoadSeg()
		 *			InternalLoadSeg()
		 *			InternalUnLoadSeg()
		 *			CreateProc()
		 *			CreateNewProc()
		 *			RunCommand()
		 *			SetProtection()
		 *
		 *		WARNING:	This routine does NOT check for a pending patch
		 *					by someone else!!
		 */

	if (muBase->OLDAddTask) {
		SetFunction((struct Library *)SysBase, (LONG)&LVOAddTask,
						(ULONG (*)())muBase->OLDAddTask);
		muBase->OLDAddTask = NULL;
	}
	if (muBase->OLDRemTask) {
		SetFunction((struct Library *)SysBase, (LONG)&LVORemTask,
						(ULONG (*)())muBase->OLDRemTask);
		muBase->OLDRemTask = NULL;
	}
	if (muBase->OLDLoadSeg) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOLoadSeg,
						(ULONG (*)())muBase->OLDLoadSeg);
		muBase->OLDLoadSeg = NULL;
	}
	if (muBase->OLDNewLoadSeg) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVONewLoadSeg,
						(ULONG (*)())muBase->OLDNewLoadSeg);
		muBase->OLDNewLoadSeg = NULL;
	}
	if (muBase->OLDUnLoadSeg) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOUnLoadSeg,
						(ULONG (*)())muBase->OLDUnLoadSeg);
		muBase->OLDUnLoadSeg = NULL;
	}
	if (muBase->OLDInternalLoadSeg) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOInternalLoadSeg,
						(ULONG (*)())muBase->OLDInternalLoadSeg);
		muBase->OLDInternalLoadSeg = NULL;
	}
	if (muBase->OLDInternalUnLoadSeg) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOInternalUnLoadSeg,
						(ULONG (*)())muBase->OLDInternalUnLoadSeg);
		muBase->OLDInternalUnLoadSeg = NULL;
	}
	if (muBase->OLDCreateProc) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOCreateProc,
						(ULONG (*)())muBase->OLDCreateProc);
		muBase->OLDCreateProc = NULL;
	}
	if (muBase->OLDCreateNewProc) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOCreateNewProc,
						(ULONG (*)())muBase->OLDCreateNewProc);
		muBase->OLDCreateNewProc = NULL;
	}
	if (muBase->OLDRunCommand) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVORunCommand,
						(ULONG (*)())muBase->OLDRunCommand);
		muBase->OLDRunCommand = NULL;
	}
	if (muBase->OLDSetProtection) {
		SetFunction((struct Library *)DOSBase, (LONG)&LVOSetProtection,
						(ULONG (*)())muBase->OLDSetProtection);
		muBase->OLDSetProtection = NULL;
	}

	RemAllTaskNodes();

	CleanUpMemory();

	if (ReqToolsBase)
		CloseLibrary((struct Library *)ReqToolsBase);
	if (UtilityBase)
		CloseLibrary(UtilityBase);
	if (IntuitionBase)
		CloseLibrary((struct Library *)IntuitionBase);
	if (DOSBase)
		CloseLibrary((struct Library *)DOSBase);

	return(TRUE);
}


	/*
	 *		Entry for Obsolete Library Functions
	 */

ULONG __asm muOBSOLETE(void)
{
	return(NULL);
}


	/*
	 *		Make a Rendez-Vous with the MultiUserFileSystem
	 *
	 *		Private Library Function
	 */

BOOL __asm __saveds muFSRendezVous(register __a6 struct muBase *muBase)
{
	BOOL res = FALSE;

	Forbid();
	if (muBase->Server && muBase->ConsistencySig) {
		Signal(muBase->Server, 1<<muBase->ConsistencySig);
		res = TRUE;
	}
	Permit();
	return(res);
}


	/*
	 *		Get a Shared Lock on the Directory of the Password File
	 *
	 *		Public Library Function
	 */

BPTR __asm __saveds muGetPasswdDirLock(void)
{
	return((BPTR)SendServerPacket(muSAction_PasswdDirLock, NULL, NULL, NULL,
											NULL));
}


	/*
	 *		Get a Shared Lock on the Directory of the Configuration File
	 *
	 *		Public Library Function
	 */

BPTR __asm __saveds muGetConfigDirLock(void)
{
	return((BPTR)SendServerPacket(muSAction_ConfigDirLock, NULL, NULL, NULL,
											NULL));
}


	/*
	 *		Interprete a Tag List
	 */

BOOL InterpreteTagList(struct TagItem *taglist, struct muTags *tags)
{
	struct TagItem *tstate = taglist;
	struct TagItem *tag;
	struct muExtOwner *user1, *user2;
	BOOL res = TRUE;

		/*
		 *		Fill in the default values
		 */

	tags->Input = Input();
	tags->Output = Output();
	tags->PubScrName = NULL;
	tags->Task = NULL;
	tags->UserID = NULL;
	tags->Password = NULL;
	tags->DefProtection = DEFPROTECTION;
	tags->Graphical = FALSE;
	tags->Own = FALSE;
	tags->Global = FALSE;
	tags->Quiet = FALSE;
	tags->All = FALSE;
	tags->NoLog = FALSE;

		/*
		 *		Change them according to the tags
		 */

	if (taglist)
		while (tag = NextTagItem(&tstate))
			switch (tag->ti_Tag) {
				case muT_Input:
					tags->Input = (BPTR)tag->ti_Data;
					break;

				case muT_Output:
					tags->Output = (BPTR)tag->ti_Data;
					break;

				case muT_PubScrName:
					tags->PubScrName = (STRPTR)tag->ti_Data;
					break;

				case muT_Task:
					tags->Task = (struct Task *)tag->ti_Data;
					break;

				case muT_UserID:
					tags->UserID = (STRPTR)tag->ti_Data;
					break;

				case muT_Password:
					tags->Password = (STRPTR)tag->ti_Data;
					break;

				case muT_DefProtection:
					tags->DefProtection = (ULONG)tag->ti_Data;
					break;

				case muT_Graphical:
					tags->Graphical = (BOOL)tag->ti_Data;
					break;

				case muT_Own:
					tags->Own = (BOOL)tag->ti_Data;
					break;

				case muT_Global:
					tags->Global = (BOOL)tag->ti_Data;
					break;

				case muT_Quiet:
					tags->Quiet = (BOOL)tag->ti_Data;
					break;

				case muT_All:
					tags->All = (BOOL)tag->ti_Data;
					break;

				case muT_NoLog:
					tags->NoLog = (BOOL)tag->ti_Data;
					break;
			}

	if (!tags->Task)
		tags->Task = SysBase->ThisTask;
	else if (tags->Task != SysBase->ThisTask) {
		user1 = GetTaskOwner(SysBase->ThisTask);
		user2 = GetTaskOwner(tags->Task);
		if (!(muGetRelationshipA(user1, muExtOwner2ULONG(user2), NULL) &
				muRelF_UID_MATCH))
			res = FALSE;
	}
	if (tags->NoLog && !muGetRelationshipA(GetTaskOwner(SysBase->ThisTask),
														NULL, NULL) & muRelF_ROOT_UID)
		tags->NoLog = FALSE;
	if (tags->Graphical &&
		 (((struct Process *)SysBase->ThisTask)->pr_WindowPtr == (APTR)-1))
		tags->Graphical = FALSE;
	return(res);
}


	/*
	 *		Post a warning
	 */


void Warn(STRPTR msg, APTR argarray)
{
	struct rtHandlerInfo *hinfo;
	ULONG sigs, timersig, ret;
	struct MsgPort *timerport;
	struct timerequest *timerequest;

	if (timerport = CreateMsgPort()) {
		if (timerequest = CreateIORequest(timerport,
													 sizeof(struct timerequest))) {
			if (!OpenDevice(TIMERNAME, UNIT_VBLANK,
								 (struct IORequest *)timerequest, 0)) {
				if (rtEZRequestTags(msg, "Resume", NULL, argarray,
										  RTEZ_ReqTitle, "MultiUser Warning",
										  RTEZ_Flags, EZREQF_CENTERTEXT,
										  RT_ReqPos, REQPOS_CENTERSCR,
										  RT_ReqHandler, &hinfo,
										  TAG_DONE) == CALL_HANDLER) {
					timerequest->tr_node.io_Command = TR_ADDREQUEST;
					timerequest->tr_time.tv_secs = 10;
					timerequest->tr_time.tv_micro = 0;
					SendIO((struct IORequest *)timerequest);
					timersig = 1<<timerport->mp_SigBit;
					do {
						if (!hinfo->DoNotWait)
							sigs = Wait(hinfo->WaitMask | timersig);
						if (sigs & timersig)
							ret = rtReqHandler(hinfo, sigs,
													 RTRH_EndRequest, TRUE, TAG_END);
						else
							ret = rtReqHandler(hinfo, sigs, TAG_END);
					} while (ret == CALL_HANDLER);
					AbortIO((struct IORequest *)timerequest);
					WaitIO((struct IORequest *)timerequest);
				}
				CloseDevice((struct IORequest*)timerequest);
			}
			DeleteIORequest(timerequest);
		}
		DeleteMsgPort(timerport);
	}
}


	/*
	 *		Painless dead :-)
	 */

void Die(STRPTR msg, ULONG code)
{
	if (code)
		Alert(code);
	else {
		static UBYTE string[] = {
			0, 236, 19, 'M', 'u', 'l', 't', 'i', 'U', 's', 'e', 'r', ' ',
							'F', 'a', 't', 'a', 'l', ' ',
							'E', 'r', 'r', 'o', 'r', 0, 1,
			0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
						 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
						 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
						 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
		};
		int xpos;

		xpos = 320-4*strlen(msg);
		string[26] = xpos>>8;
		string[27] = xpos & 0xff;
		strncpy(&string[29], msg, 78);
		DisplayBeep(NULL);
		Delay(20);
		DisplayAlert(RECOVERY_ALERT, string, 51);
	}

	for (;;)
		Wait(NULL);
}


	/*
	 *		Encrypt a Password
	 */

STRPTR Encrypt(STRPTR buffer, STRPTR pwd, STRPTR userid)
{
	if (strlen(pwd))
		return(ACrypt(buffer, pwd, userid));
	else
		return(memset(buffer, '\0', 12));
}
