/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: LockDevice-Handler.c
 *	Created ..: Tuesday 26-Jun-90 14:19
 *	Revision .: 1
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	26-Jun-90       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

	/* Arp cli arguments. */

char *CLI_Template	= "Drive,L=Lock/K,K=Key/K,S=Show/S,Info/S,Q=Quit/S";
char *CLI_Help		= "\nUsage: LockDevice <DH0:, DH1:, etc.> [Lock <On|Off>]\n                  [Key <Password>] [Show] [Info] [Quit]\n";

	/* Argument vector offsets. */

#define ARG_DRIVE	1
#define ARG_LOCK	2
#define ARG_KEY		3
#define ARG_SHOW	4
#define ARG_INFO	5
#define ARG_QUIT	6

	/* Global lock segment. */

struct LockSeg		*LSeg;

	/* Prototypes. */

char *			FindDevice(char *DevName);
struct Patch *		FindPatch(char *DeviceName);
VOID			DiskChange(char *DeviceName);
BYTE			DeletePatch(char *DeviceName);
BYTE			DeleteLocks(VOID);
BYTE			DeleteSegment(VOID);
BYTE			CreateSegment(VOID);
VOID			main(int argc,char **argv);

	/* FindDevice(char *DevName):
	 *
	 *	Find the device(s) associated with a device
	 *	driver.
	 */

char *
FindDevice(char *DevName)
{
	static char			 Name[257];
	static struct DeviceNode	*DevInfo = NULL;
	static BYTE			 FirstThrough = TRUE;

	Forbid();

	if(!DevInfo)
	{
		DevInfo = (struct DeviceNode *)BADDR(((struct DosInfo *)BADDR(((struct RootNode *)DOSBase -> dl_Root) -> rn_Info)) -> di_DevInfo);

		if(!FirstThrough)
			return(NULL);
		else
			FirstThrough = FALSE;
	}

	while(DevInfo)
	{
		if(DevInfo -> dn_Type == DLT_DEVICE && DevInfo -> dn_Task && !DevInfo -> dn_Handler)
		{
			struct FileSysStartupMsg *Startup = BADDR(DevInfo -> dn_Startup);

			if(!Strcmp((char *)((ULONG)BADDR(Startup -> fssm_Device) + 1),DevName))
			{
				char	*Pointer;
				SHORT	 i;

				Pointer = (char *)BADDR(DevInfo -> dn_Name);

				for(i = 0 ; i < Pointer[0] ; i++)
					Name[i] = Pointer[i + 1];

				Name[Pointer[0]    ] = ':';
				Name[Pointer[0] + 1] = 0;

				DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);

				Permit();

				return(Name);
			}
		}

		DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);
	}

	Permit();

	return(NULL);
}

	/* FindDriver(char *DevName):
	 *
	 *	Find the device driver associated with a filing device.
	 */

char *
FindDriver(char *DevName)
{
	static char		 Name[257];
	struct DeviceNode	*DevInfo = (struct DeviceNode *)BADDR(((struct DosInfo *)BADDR(((struct RootNode *)DOSBase -> dl_Root) -> rn_Info)) -> di_DevInfo);

	Forbid();

	while(DevInfo)
	{
		char	*Pointer;
		SHORT	 i;

		Pointer = (char *)BADDR(DevInfo -> dn_Name);

		for(i = 0 ; i < Pointer[0] ; i++)
			Name[i] = Pointer[i + 1];

		Name[Pointer[0]    ] = ':';
		Name[Pointer[0] + 1] = 0;

		if(DevInfo -> dn_Type == DLT_DEVICE && DevInfo -> dn_Task && !DevInfo -> dn_Handler)
		{
			if(!Strcmp(Name,DevName))
			{
				struct FileSysStartupMsg *Startup = BADDR(DevInfo -> dn_Startup);

				strcpy(Name,(char *)((ULONG)BADDR(Startup -> fssm_Device) + 1));

				DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);

				Permit();

				return(Name);
			}
		}

		DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);
	}

	Permit();

	return(NULL);
}

	/* FindPatch(char *DeviceName):
	 *
	 *	Find the patch created for a specific filing device.
	 */

struct Patch *
FindPatch(char *DeviceName)
{
	struct Patch *Patch = LSeg -> RootPatch;

	while(Patch)
	{
		if(!Strcmp(Patch -> UnitName,DeviceName))
			return(Patch);

		Patch = Patch -> NextPatch;
	}

	return(NULL);
}

	/* DiskChange(char *DeviceName):
	 *
	 *	Perform a kind of diskchange event for a filing
	 *	device.
	 */

VOID
DiskChange(char *DeviceName)
{
	struct MsgPort	*HandlerTask = DeviceProc(DeviceName);
	LONG		 Args[7];

	if(HandlerTask)
	{
		Args[0] = DOSTRUE;

		if(SendPacket(ACTION_INHIBIT,Args,HandlerTask))
		{
			Args[0] = DOSFALSE;

			SendPacket(ACTION_INHIBIT,Args,HandlerTask);
		}
	}
}

	/* DeletePatch(char *DeviceName):
	 *
	 *	Unlink and remove a patch from the linked system
	 *	list.
	 */

BYTE
DeletePatch(char *DeviceName)
{
	if(DeviceName)
	{
		struct Patch *TempPatch = LSeg -> RootPatch,*OldPatch = NULL;

		Disable();

		if(!Strcmp(TempPatch -> UnitName,DeviceName))
		{
			OldPatch = TempPatch;

			LSeg -> RootPatch = TempPatch -> NextPatch;
		}
		else
		{
			while(TempPatch)
			{
				if(!Strcmp(TempPatch -> UnitName,DeviceName))
				{
					OldPatch = TempPatch;
					break;
				}

				TempPatch = TempPatch -> NextPatch;
			}

			if(!OldPatch)
				return(FALSE);
			else
			{
				TempPatch = LSeg -> RootPatch;

				while(TempPatch)
				{
					if(TempPatch -> NextPatch == OldPatch)
					{
						TempPatch -> NextPatch = OldPatch -> NextPatch;
						break;
					}

					TempPatch = TempPatch -> NextPatch;
				}
			}
		}

		SetFunction((struct Library *)OldPatch -> Device,DEV_BEGINIO,OldPatch -> OldBeginIO);

		Enable();

		CloseDevice(OldPatch -> Request);

		FreeMem(OldPatch -> Request,sizeof(struct IOExtTD));
		FreeMem(OldPatch,sizeof(struct Patch));

		DiskChange(DeviceName);

		return(TRUE);
	}

	return(FALSE);
}

	/* DeleteLocks():
	 *
	 *	Delete all patches which are not protected by a
	 *	password.
	 */

BYTE
DeleteLocks()
{
	struct Patch *Patch = LSeg -> RootPatch;

	while(Patch)
	{
		if(!Patch -> PassWord[0])
		{
			DeletePatch(Patch -> UnitName);

			Patch = LSeg -> RootPatch;
			continue;
		}

		Patch = Patch -> NextPatch;
	}

	if(LSeg -> RootPatch)
		return(FALSE);
	else
		return(TRUE);
}

	/* DeleteSegment():
	 *
	 *	Deallocate the lock segment.
	 */

BYTE
DeleteSegment()
{
	if(LSeg)
	{
		if(DeleteLocks())
		{
			LSeg -> HandShake = SIG_SHAKE;

			Signal(LSeg -> Child,SIG_QUIT);

			Wait(SIG_SHAKE);

			if(!LSeg -> Child)
			{
				RemPort(&LSeg -> SignalPort);

				FreeMem(LSeg -> SignalPort . mp_Node . ln_Name,sizeof(PORTNAME));

				UnLoadSeg(LSeg -> HandlerSegment);

				FreeMem(LSeg,sizeof(struct LockSeg));

				return(TRUE);
			}
		}
	}

	return(FALSE);
}

	/* CreateSegment():
	 *
	 *	Create the lock segment.
	 */

BYTE
CreateSegment()
{
	if(LSeg = (struct LockSeg *)AllocMem(sizeof(struct LockSeg),MEMF_PUBLIC | MEMF_CLEAR))
	{
		LSeg -> SegSize	= sizeof(struct LockSeg);
		LSeg -> Father	= SysBase -> ThisTask;

		LSeg -> SignalPort . mp_Node . ln_Type	= NT_MSGPORT;
		LSeg -> SignalPort . mp_Node . ln_Pri	= 1;
		LSeg -> SignalPort . mp_Node . ln_Name	= (char *)AllocMem(sizeof(PORTNAME),MEMF_PUBLIC);
		LSeg -> SignalPort . mp_Flags		= PA_IGNORE;

		if(LSeg -> SignalPort . mp_Node . ln_Name)
		{
			strcpy(LSeg -> SignalPort . mp_Node . ln_Name,PORTNAME);

			if(!(LSeg -> HandlerSegment = LoadSeg("LockDevice-Handler")))
				LSeg -> HandlerSegment = LoadSeg("L:LockDevice-Handler");

			if(LSeg -> HandlerSegment)
			{
				LSeg -> HandShake = SIG_SHAKE;

				AddPort(&LSeg -> SignalPort);

				if(CreateProc(PORTNAME,5,LSeg -> HandlerSegment,4000))
				{
					Wait(SIG_SHAKE);

					if(LSeg -> Child)
						return(TRUE);
				}

				RemPort(&LSeg -> SignalPort);

				UnLoadSeg(LSeg -> HandlerSegment);
			}

			FreeMem(LSeg -> SignalPort . mp_Node . ln_Name,sizeof(PORTNAME));
		}

		FreeMem(LSeg,sizeof(struct LockSeg));
	}

	return(FALSE);
}

	/* main(int argc,char **argv):
	 *
	 *	The main program.
	 */

VOID
main(int argc,char **argv)
{
	LSeg = (struct LockSeg *)FindPort(PORTNAME);

	if(argc == 1)
	{
		Puts(CLI_Help);
		exit(RETURN_WARN);
	}

		/* Remove LockDevice? */

	if(argv[ARG_QUIT])
	{
		Printf("\nRemoving \33[33mLockDevice\33[31m, ");

		if(!DeleteSegment())
		{
			Puts("FAILED!\a\n");
			exit(RETURN_FAIL);
		}
		else
		{
			Puts("\33[3mOkay.\33[0m\n");
			exit(RETURN_OK);
		}
	}

		/* Show program info? */

	if(argv[ARG_INFO])
	{
		Puts("This  program  installs  patches  to prevent write access to");
		Puts("data  media.   Unlike  the write protection simulated by the");
		Puts("FastFileSystem,  'LockDevice' goes directly  to  the  device");
		Puts("driver  itself  which  enables  it  to  reject  write/format");
		Puts("requests  even  in  situations in which FastFileSystem fails");
		Puts("and the disk drive is still formatted.");
		Puts("   Furthermore,  'LockDevice' will work on  any  device  and");
		Puts("with any filing system.\n");

		Puts("If  you  like  this  program  and  use it frequently, send a");
		Puts("Share-Ware fee of at least 15 US$ or DM 20,- to:\n");

		Puts("                     Olaf Barthel, MXM");
		Puts("                     Brabeckstrasse 35");
		Puts("                     D-3000 Hannover 71\n");

		Puts("                Federal Republic of Germany\n");

		Puts("If you fail to pay this fee, then your conscience will haunt");
		Puts("you for the rest of your life!\n");

		exit(RETURN_OK);
	}

		/* Install the lock segment. */

	if(!LSeg)
	{
		Printf("\nInstalling \33[33m\33[1mLockDevice\33[31m\33[0m, ");

		if(!CreateSegment())
		{
			Puts("FAILED!\n\a");
			exit(RETURN_FAIL);
		}
		else
			Printf("\33[3mOkay.\33[0m v1.%ld © Copyright 1990 by \33[4mMXM\33[0m, \33[1mSHARE-WARE\33[0m.\n\n",REVISION);
	}

		/* Install/remove a patch. */

	if(argv[ARG_DRIVE])
	{
			/* Remove a patch. */

		if(!Strcmp(argv[ARG_LOCK],"OFF"))
		{
			struct Patch *Patch;

			if(!(Patch = FindPatch(argv[ARG_DRIVE])))
			{
				Printf("\33[1mLockDevice:\33[0m Unable to find locked device '%s'.\a\n",argv[ARG_DRIVE]);
				exit(RETURN_FAIL);
			}

				/* Protected by a password? */

			if(Patch -> PassWord[0])
			{
					/* Password given? */

				if(argv[ARG_KEY])
				{
						/* Does it match? */

					if(Strcmp(argv[ARG_KEY],(char *)Patch -> PassWord))
					{
						Printf("\33[1mLockDevice:\33[0m Invalid access key for locked device '%s'.\a\n",argv[ARG_DRIVE]);
						exit(RETURN_FAIL);
					}
					else
					{
						DeletePatch(argv[ARG_DRIVE]);
						Printf("\33[1mLockDevice:\33[0m Device '%s' unlocked.\n",argv[ARG_DRIVE]);
					}
				}
				else
				{
					Printf("\33[1mLockDevice:\33[0m Device '%s' needs a key to be unlocked.\a\n",argv[ARG_DRIVE]);
					exit(RETURN_FAIL);
				}
			}
			else
			{
				DeletePatch(argv[ARG_DRIVE]);
				Printf("\33[1mLockDevice:\33[0m Device '%s' unlocked.\n",argv[ARG_DRIVE]);
			}
		}
		else
		{
				/* Lock a device. */

			if(!Strcmp(argv[ARG_LOCK],"ON"))
			{
				struct LockMsg	 Message;
				char		*LockOut[40],*Pointer,*DevName;
				SHORT		 NumLocked = 0;

				if(!(DevName = FindDriver(argv[ARG_DRIVE])))
				{
					Printf("\33[1mLockDevice:\33[0m Unable to find driver for device '%s'!\a\n",argv[ARG_DRIVE]);
					exit(RETURN_FAIL);
				}

					/* Look how many filing devices this lock will affect. */

				while(Pointer = FindDevice(DevName))
				{
					if(!(LockOut[NumLocked] = ArpAlloc(strlen(Pointer) + 1)))
					{
						Printf("\33[1mLockDevice:\33[0m Out of memory!\a\n");
						exit(RETURN_FAIL);
					}

					strcpy(LockOut[NumLocked++],Pointer);

					if(NumLocked == 40)
						break;
				}

					/* More than one device locked out? */

				if(NumLocked > 1)
				{
					SHORT	i;
					char 	Line[MaxInputBuf];

					Printf("\33[33mWarning:\33[31m Locking '%s' will also lock the following device(s):\n\n\t\33[1m",argv[ARG_DRIVE]);

					for(i = 0 ; i < NumLocked ; i++)
						if(Strcmp(LockOut[i],argv[ARG_DRIVE]))
							Printf("%s ",LockOut[i]);

					Printf("\n\n\33[0mDo you wish to continue (\33[33my\33[31m/\33[33mN\33[31m) ? ");

					ReadLine(Line);

					if(Line[0] != 'y' && Line[0] != 'Y')
						exit(RETURN_WARN);
				}

				Message . ExecMessage . mn_Node . ln_Type	= NT_MESSAGE;
				Message . ExecMessage . mn_Length		= sizeof(struct LockMsg);

				Message . Success = FALSE;

					/* Ask the handler to lock the device(s). */

				if(Message . ExecMessage . mn_ReplyPort = (struct MsgPort *)CreatePort(NULL,0))
				{
					Message . DeviceName	= argv[ARG_DRIVE];
					Message . PassWord	= argv[ARG_KEY];

					PutMsg(&LSeg -> SignalPort,&Message);

					WaitPort(Message . ExecMessage . mn_ReplyPort);
					GetMsg(Message . ExecMessage . mn_ReplyPort);

					DeletePort(Message . ExecMessage . mn_ReplyPort);
				}

				if(!Message . Success)
				{
					Printf("\33[1mLockDevice:\33[0m Could not create lock for device '%s'.\a\n",argv[ARG_DRIVE]);
					exit(RETURN_FAIL);
				}
				else
				{
					Printf("\33[1mLockDevice:\33[0m Device '%s' locked.\n",argv[ARG_DRIVE]);
					DiskChange(argv[ARG_DRIVE]);
				}
			}
			else
			{
				Puts(CLI_Help);
				exit(RETURN_FAIL);
			}
		}
	}

		/* Show all locked devices. */

	if(argv[ARG_SHOW])
	{
		struct Patch *Patch = LSeg -> RootPatch;

		if(!Patch)
		{
			Puts("\33[1mLockDevice:\33[0m There are no locked devices.");
			exit(RETURN_WARN);
		}
		else
		{
			char *TempName;

			Puts("Name     Driver              \n-------- --------------------");

			while(Patch)
			{
				while(TempName = FindDevice(Patch -> DriverName))
				{
					if(!Strcmp(Patch -> UnitName,TempName))
						Printf("\33[33m%-8.8s\33[31m %-20.20s\n",TempName,Patch -> DriverName);
					else
						Printf("%-8.8s %-20.20s\n",TempName,Patch -> DriverName);
				}

				Patch = Patch -> NextPatch;
			}
		}
	}

	exit(RETURN_OK);
}
