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


#include <exec/alerts.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/filehandler.h>
#include <libraries/reqtools.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/reqtools.h>
#include <string.h>

#include "Memory.h"
#include "Misc.h"
#include "Config.h"
#include "Locale.h"
#include "LibHeader.h"


	/*
	 *		Static Routines
	 */

static void ClearBuffer(void);
static void InitDefs(void);
static struct muUserDef *ParseUserLine(STRPTR line, ULONG linenum);
static struct muGroupDef *ParseGroupLine(STRPTR line, ULONG linenum);
static void ParseRelationLine(STRPTR line, ULONG linenum);
static BOOL ReadKeyFiles(void);
static BOOL ReadKeyFile(struct MsgPort *fs);
static BOOL ParseDirLockLine(struct MsgPort *fs, BPTR file, BPTR *dir);
static void RemTerminatingLF(char *buffer);
static void LoadConfig(void);
static STRPTR SafeFGets(BPTR fh, STRPTR buf, ULONG len);


	/*
	 *		Configuration Stuff
	 */

char KeyFileName[] = muKey_FileName;
char PasswdFileName[] = muPasswd_FileName;
char ConfigFileName[] = muConfig_FileName;
char GroupFileName[] = muGroup_FileName;
char LogFileName[] = muLog_FileName;
BPTR PasswdDirLock = NULL;
BPTR ConfigDirLock = NULL;
struct muUserDef *UserDef = NULL;
struct muGroupDef *GroupDef = NULL;
char Key[12];
BOOL FirstStartup = TRUE;


	/*
	 *		General Purpose Buffer
	 */

#define GENBUFSIZE 1024

char *Buffer = NULL;


	/*
	 *		Passwd and Group File Notification
	 */

struct NotifyRequest __aligned PasswdNotifyReq;
struct NotifyRequest __aligned GroupNotifyReq;


	/*
	 *		Clear the General Purpose Buffer
	 */

static void __inline ClearBuffer(void)
{
	memset(Buffer, NULL, GENBUFSIZE);
}


	/*
	 *		Get a pointer to the User Definitions
	 */

struct muUserDef *GetUserDefs(void)
{
	if (!UserDef)
		InitDefs();
	return(UserDef);
}


	/*
	 *		Get a pointer to the Group Definitions
	 */

struct muGroupDef *GetGroupDefs(void)
{
	if (!GroupDef)
		InitDefs();
	return(GroupDef);
}


	/*
	 *		Initialise the User and Group Definitions
	 */

static void InitDefs(void)
{
	BPTR file;
	ULONG linenum;

	CurrentDir(PasswdDirLock);
	if (file = Open(PasswdFileName, MODE_OLDFILE)) {
		struct muUserDef *def1, *def2 = NULL;

		for (linenum = 1; FGets(file, Buffer, GENBUFSIZE-1); linenum++)
					  /* V36/37: use GENBUFSIZE-1 */
					  /* V39: use GENBUFSIZE */
			if (Buffer[0] && (Buffer[0] != '\n'))
				if (def1 = ParseUserLine(Buffer, linenum)) {
					if (def2)
						def2->Next = def1;
					else
						UserDef = def1;
					def2 = def1;
				}
		Close(file);
	}
	CurrentDir(ConfigDirLock);
	if (UserDef && (file = Open(GroupFileName, MODE_OLDFILE))) {
		struct muGroupDef *def1, *def2 = NULL;

		for (linenum = 1; FGets(file, Buffer, GENBUFSIZE-1) && (Buffer[0] != '\n'); linenum++)
					  /* V36/37: use GENBUFSIZE-1 */
					  /* V39: use GENBUFSIZE */
			if (Buffer[0])
				if (def1 = ParseGroupLine(Buffer, linenum)) {
					if (def2)
						def2->Next = def1;
					else
						GroupDef = def1;
					def2 = def1;
				}
		if (Buffer[0] == '\n')
			for (linenum++; FGets(file, Buffer, GENBUFSIZE-1); linenum++)
						  /* V36/37: use GENBUFSIZE-1 */
						  /* V39: use GENBUFSIZE */
				if (Buffer[0] && (Buffer[0] != '\n'))
					ParseRelationLine(Buffer, linenum);
		Close(file);
	}
	ClearBuffer();

	if (!UserDef || !GroupDef)
		FreeDefs();
}


	/*
	 *		Parse a User Entry
	 */

static struct muUserDef *ParseUserLine(STRPTR line, ULONG linenum)
{
	int i, j, len;
	LONG uid, gid;
	struct muUserDef *def;
	STRPTR part[7];
	STRPTR ptr;

	#define UPART_USERID		0
	#define UPART_PASSWORD	1
	#define UPART_UID			2
	#define UPART_GID			3
	#define UPART_USERNAME	4
	#define UPART_HOMEDIR	5
	#define UPART_SHELL		6

	i = 0;
	for (j = 0; j < 7; j++) {
		part[j] = &line[i];
		while ((line[i]) && (line[i] != '\n') && (line[i] != '|'))
			i++;
		if (j == 6)
			if (line[i] && (line[i] != '\n'))
				goto Fail;
			else {}
		else if (line[i] != '|')
			goto Fail;
		line[i++] = '\0';
		len = strlen(part[j]);
		switch(j) {
			case UPART_USERID:
				if (!len)
					goto Fail;
				break;

			case UPART_PASSWORD:
				if (len && (len != 11))
					goto Fail;
				break;

			case UPART_UID:
				if (!len || (StrToLong(part[j], &uid) == -1) || (uid < 1) || (uid > 65535))
					goto Fail;
				break;

			case UPART_GID:
				if (!len || (StrToLong(part[j], &gid) == -1) || (gid < 0) || (gid > 65535))
					goto Fail;
				break;

			case UPART_USERNAME:
			case UPART_HOMEDIR:
			case UPART_SHELL:
				break;
		}
	}

	if (def = (struct muUserDef *)MAllocV(sizeof(struct muUserDef)+strlen(part[UPART_USERID])+
													  strlen(part[UPART_USERNAME])+strlen(part[UPART_HOMEDIR])+
													  strlen(part[UPART_SHELL])+16)) {
		ptr = &((STRPTR)def)[sizeof(struct muUserDef)];
		def->UserID = ptr;
		strcpy(ptr, part[UPART_USERID]);
		ptr = &ptr[strlen(part[UPART_USERID])+1];
		def->Password = ptr;
		strcpy(ptr, part[UPART_PASSWORD]);
		ptr = &ptr[12];
		def->uid = (UWORD)uid;
		def->gid = (UWORD)gid;
		def->UserName = ptr;
		strcpy(ptr, part[UPART_USERNAME]);
		ptr = &ptr[strlen(part[UPART_USERNAME])+1];
		def->HomeDir = ptr;
		strcpy(ptr, part[UPART_HOMEDIR]);
		ptr = &ptr[strlen(part[UPART_HOMEDIR])+1];
		def->Shell = ptr;
		strcpy(ptr, part[UPART_SHELL]);
	} else
		Die(NULL, AN_Unknown | AG_NoMemory);
	return(def);

Fail:
	Warn(GetLocStr(MSG_BADENTRY_PASSWD), &linenum);
	return(NULL);
}


	/*
	 *		Parse a Group Entry
	 */

static struct muGroupDef *ParseGroupLine(STRPTR line, ULONG linenum)
{
	int i, j, len;
	LONG gid, mgruid;
	struct muGroupDef *def;
	STRPTR part[4];
	STRPTR ptr;

	#define GPART_GROUPID	0
	#define GPART_GID			1
	#define GPART_MGRUID		2
	#define GPART_GROUPNAME	3

	i = 0;
	for (j = 0; j < 4; j++) {
		part[j] = &line[i];
		while ((line[i]) && (line[i] != '\n') && (line[i] != '|'))
			i++;
		if (j == 3)
			if (line[i] && (line[i] != '\n'))
				goto Fail;
			else {}
		else if (line[i] != '|')
			goto Fail;
		line[i++] = '\0';
		len = strlen(part[j]);
		switch(j) {
			case GPART_GROUPID:
				if (!len)
					goto Fail;
				break;

			case GPART_GID:
				if (!len || (StrToLong(part[j], &gid) == -1) || (gid < 0) || (gid > 65535))
					goto Fail;
				break;

			case GPART_MGRUID:
				if (!len || (StrToLong(part[j], &mgruid) == -1) || (mgruid < 0) || (mgruid > 65535))
					goto Fail;
				break;

			case GPART_GROUPNAME:
				break;
		}
	}

	if (def = (struct muGroupDef *)MAllocV(sizeof(struct muGroupDef)+strlen(part[GPART_GROUPID])+
														strlen(part[GPART_GROUPNAME])+2)) {
		ptr = &((STRPTR)def)[sizeof(struct muGroupDef)];
		def->GroupID = ptr;
		strcpy(ptr, part[GPART_GROUPID]);
		ptr = &ptr[strlen(part[GPART_GROUPID])+1];
		def->gid = (UWORD)gid;
		def->MgrUid = (UWORD)mgruid;
		def->GroupName = ptr;
		strcpy(ptr, part[GPART_GROUPNAME]);
	} else
		Die(NULL, AN_Unknown | AG_NoMemory);
	return(def);

Fail:
	Warn(GetLocStr(MSG_BADENTRY_GROUP), &linenum);
	return(NULL);
}


	/*
	 *		Parse a Relation Entry
	 */

static void ParseRelationLine(STRPTR line, ULONG linenum)
{
	struct muUserDef *def;
	UWORD *groups;
	ULONG numgroups = 0;
	LONG uid, gid, len;
	ULONG i, j;

	if (((len = StrToLong(line, &uid)) == -1) || (uid < 1) || (uid > 65535))
		goto Fail;
	i = len;
	j = i+1;
	if (line[i] != ':')
		goto Fail;
	for (def = UserDef; def && (def->uid != uid); def = def->Next);
	if (def) {
		do {
			if (((len = StrToLong(&line[++i], &gid)) == -1) || (gid < 0) || (gid > 65535))
				goto Fail;
			i += len;
			numgroups++;
			if (line[i] && (line[i] != ',') && (line[i] != '\n'))
				goto Fail;
		} while (line[i] && (line[i] != '\n'));
		if (def->NumSecGroups+numgroups <= 65535) {
			if (def->NumSecGroups)
				if (groups = MAlloc((numgroups+def->NumSecGroups)*sizeof(UWORD))) {
					CopyMem(def->SecGroups, groups, def->NumSecGroups*sizeof(UWORD));
					Free(def->SecGroups, def->NumSecGroups*sizeof(UWORD));
					def->SecGroups = groups;
					groups += def->NumSecGroups;
					def->NumSecGroups += numgroups;
				} else
					Die(NULL, AN_Unknown | AG_NoMemory);
			else if (groups = MAlloc(numgroups*sizeof(UWORD))) {
				def->SecGroups = groups;
				def->NumSecGroups = numgroups;
			} else
				Die(NULL, AN_Unknown | AG_NoMemory);
			if (groups)
				for (i = 0; i < numgroups; i++) {
					j += StrToLong(&line[j++], &gid);
					groups[i] = gid;
				}
		} else {
			LONG args[2];

			args[0] = uid;
			args[1] = linenum;
			Warn(GetLocStr(MSG_TOOMANYSECGROUPS), args);
		}
	} else
Fail:
	Warn(GetLocStr(MSG_BADENTRY_GROUP), &linenum);
}


	/*
	 *		Free the User and Group Definitions
	 */

void FreeDefs(void)
{
	struct muUserDef *udef = UserDef;
	struct muGroupDef *gdef = GroupDef;
	APTR p;

	while(udef) {
		p = udef->Next;
		if (udef->NumSecGroups)
			Free(udef->SecGroups, udef->NumSecGroups*sizeof(UWORD));
		FreeV(udef);
		udef = p;
	}
	UserDef = NULL;
	while(gdef) {
		p = gdef->Next;
		FreeV(gdef);
		gdef = p;
	}
	GroupDef = NULL;
}


	/*
	 *		Update the User Definitions
	 *
	 *
	 *		OUT:	BOOL	Success
	 */

BOOL UpdateUserDefs(void)
{
	BPTR file;
	BOOL res;
	struct muUserDef *def = UserDef;
	LONG args[7];

	CurrentDir(PasswdDirLock);
	if (res = (file = Open(PasswdFileName, MODE_NEWFILE))) {
		while(def && res) {
			args[0] = (LONG)def->UserID;
			args[1] = (LONG)def->Password;
			args[2] = (LONG)def->uid;
			args[3] = (LONG)def->gid;
			args[4] = (LONG)def->UserName;
			args[5] = (LONG)def->HomeDir;
			args[6] = (LONG)def->Shell;
			res = (VFPrintf(file, "%s|%s|%ld|%ld|%s|%s|%s\n", args) != -1);
			def = def->Next;
		}
		res = Close(file) && res;
	}

	return(res);
}


	/*
	 *		Find all MultiUserFileSystem volumes
	 */

static BOOL FindVolumes(void)
{
	struct DosList *dl;
	struct FileSysStartupMsg *sm;
	struct DosEnvec *de;
	BOOL res = FALSE;
	struct muVolume **vollist = &muBase->Volumes;

	dl = LockDosList(LDF_DEVICES|LDF_READ);
	while (dl = NextDosEntry(dl,LDF_DEVICES))
		if ((dl->dol_Task) && ((LONG)(sm = BADDR(dl->dol_misc.dol_handler.dol_Startup)) > 1024) &&
			 (de = BADDR(sm->fssm_Environ)) && (de->de_TableSize >= DE_DOSTYPE) &&
			 (de->de_DosType == ID_MUFS_DISK))
			if (*vollist = (struct muVolume *)MAlloc(sizeof(struct muVolume))) {
				(*vollist)->DosList = dl;
				(*vollist)->Process = dl->dol_Task;
				vollist = &(*vollist)->Next;
				res = TRUE;
			} else
				Die(NULL, AN_Unknown | AG_NoMemory);
	UnLockDosList(LDF_DEVICES|LDF_READ);
	return(res);
}


	/*
	 *		Read and parse the Key Files
	 */

static BOOL ReadKeyFiles(void)
{
	struct muVolume *vol;
	BOOL res = FALSE;

		for (vol = muBase->Volumes; vol && (res = ReadKeyFile(vol->Process)); vol = vol->Next);
	return((BOOL)(res && PasswdDirLock && ConfigDirLock));
}


static BOOL ReadKeyFile(struct MsgPort *fs)
{
	BPTR dir, file;
	char buffer[12];
	BOOL res;
	static char __aligned name[] = "\1:";

	if (res = (dir = DoPkt(fs, ACTION_LOCATE_OBJECT, NULL, MKBADDR(name), ACCESS_READ, NULL, NULL))) {
		CurrentDir(dir);
		if (res = (file = Open(KeyFileName, MODE_OLDFILE))) {
			if (res = (BOOL)SafeFGets(file, Buffer, GENBUFSIZE-1)) {
								 /* V36/37: use GENBUFSIZE-1 */
								 /* V39: use GENBUFSIZE */
				RemTerminatingLF(Buffer);
				if (Encrypt(buffer, Buffer, "Alpha, PowerPC or R4400?")) {
					if (Key[0])
						res = !strcmp(Key, buffer);
					else
						strcpy(Key, buffer);
					if (res)
						res = ParseDirLockLine(fs, file, &PasswdDirLock) &&
								ParseDirLockLine(fs, file, &ConfigDirLock);
				}
			}
			Close(file);
			ClearBuffer();
		}
		UnLock(dir);
	}
	return(res);
}


	/*
	 *		Parse a Key File Line and Lock the appropriate Directory
	 */

static BOOL ParseDirLockLine(struct MsgPort *fs, BPTR file, BPTR *dir)
{
	BOOL res;

	if (res = (BOOL)FGets(file, Buffer+1, GENBUFSIZE-2)) {
						 /* V36/37: use GENBUFSIZE-2 */
						 /* V39: use GENBUFSIZE-1 */
		RemTerminatingLF(Buffer);
		if (Buffer[1])
			if (*dir)
				res = FALSE;
			else {
				Buffer[0] = strlen(Buffer+1);
				res = *dir = DoPkt(fs, ACTION_LOCATE_OBJECT, NULL, MKBADDR(Buffer), ACCESS_READ, NULL, NULL);
			}
	}
	return(res);
}


static void RemTerminatingLF(char *buffer)
{
	int i;

	for (i = 0; buffer[i]; i++);
	if (i && (buffer[i-1] == '\n'))
		buffer[i-1] = '\0';
}


	/*
	 *		Load the Configuration file
	 */

static void LoadConfig(void)
{
	BPTR file;
	LONG *argarray[12];

#define argLIMITDOSSETPROTECTION	0
#define argPROFILE					1
#define argLASTLOGINREQ				2
#define argLOGSTARTUP				3
#define argLOGLOGIN					4
#define argLOGLOGINFAIL				5
#define argLOGPASSWD					6
#define argLOGPASSWDFAIL			7
#define argLOGCHECKPASSWD			8
#define argLOGCHECKPASSWDFAIL		9
#define argPASSWDUIDLEVEL			10
#define argPASSWDGIDLEVEL			11

	struct RDArgs *rdargs;
	ULONG line;
	struct muConfig config;

	config.Flags = muCFGF_LimitDOSSetProtection | muCFGF_Profile | muCFGF_LastLoginReq;
	config.LogFlags = NULL;
	config.PasswduidLevel = 0;
	config.PasswdgidLevel = 0;

	if (rdargs = AllocDosObject(DOS_RDARGS, NULL)) {
		CurrentDir(ConfigDirLock);
		if (file = Open(ConfigFileName, MODE_OLDFILE)) {
			for (line = 1; FGets(file, Buffer, GENBUFSIZE-1); line++) {
				  			/* V36/37: use GENBUFSIZE-1 */
				  			/* V39: use GENBUFSIZE */
				rdargs->RDA_Source.CS_Buffer = Buffer;
				rdargs->RDA_Source.CS_Length = strlen(Buffer);
				rdargs->RDA_Source.CS_CurChr = 0;
				rdargs->RDA_DAList = NULL;
				rdargs->RDA_Buffer = NULL;
				rdargs->RDA_BufSiz = NULL;
				rdargs->RDA_ExtHelp = NULL;
				rdargs->RDA_Flags = RDAF_NOPROMPT;
				memset(argarray, NULL, sizeof(argarray));
				if (ReadArgs("LIMITDOSSETPROTECTION/K/N,PROFILE/K/N,LASTLOGINREQ/K/N,LOGSTARTUP/K/N,"
								 "LOGLOGIN/K/N,LOGLOGINFAIL/K/N,LOGPASSWD/K/N,LOGPASSWDFAIL/K/N,"
								 "LOGCHECKPASSWD/K/N,LOGCHECKPASSWDFAIL/K/N,PASSWDUIDLEVEL/K/N,"
								 "PASSWDGIDLEVEL/K/N", (LONG *)argarray, rdargs)) {
					if (argarray[argLIMITDOSSETPROTECTION])
						if (*argarray[argLIMITDOSSETPROTECTION])
							config.Flags |= muCFGF_LimitDOSSetProtection;
						else
							config.Flags &= ~muCFGF_LimitDOSSetProtection;
					if (argarray[argPROFILE])
						if (*argarray[argPROFILE])
							config.Flags |= muCFGF_Profile;
						else
							config.Flags &= ~muCFGF_Profile;
					if (argarray[argLASTLOGINREQ])
						if (*argarray[argLASTLOGINREQ])
							config.Flags |= muCFGF_LastLoginReq;
						else
							config.Flags &= ~muCFGF_LastLoginReq;
					if (argarray[argLOGSTARTUP])
						if (*argarray[argLOGSTARTUP])
							config.LogFlags |= muLogF_Startup;
						else
							config.LogFlags &= ~muLogF_Startup;
					if (argarray[argLOGLOGIN])
						if (*argarray[argLOGLOGIN])
							config.LogFlags |= muLogF_Login;
						else
							config.LogFlags &= ~muLogF_Login;
					if (argarray[argLOGLOGINFAIL])
						if (*argarray[argLOGLOGINFAIL])
							config.LogFlags |= muLogF_LoginFail;
						else
							config.LogFlags &= ~muLogF_LoginFail;
					if (argarray[argLOGPASSWD])
						if (*argarray[argLOGPASSWD])
							config.LogFlags |= muLogF_Passwd;
						else
							config.LogFlags &= ~muLogF_Passwd;
					if (argarray[argLOGPASSWDFAIL])
						if (*argarray[argLOGPASSWDFAIL])
							config.LogFlags |= muLogF_PasswdFail;
						else
							config.LogFlags &= ~muLogF_PasswdFail;
					if (argarray[argLOGCHECKPASSWD])
						if (*argarray[argLOGCHECKPASSWD])
							config.LogFlags |= muLogF_CheckPasswd;
						else
							config.LogFlags &= ~muLogF_CheckPasswd;
					if (argarray[argLOGCHECKPASSWDFAIL])
						if (*argarray[argLOGCHECKPASSWDFAIL])
							config.LogFlags |= muLogF_CheckPasswdFail;
						else
							config.LogFlags &= ~muLogF_CheckPasswdFail;
					if (argarray[argPASSWDUIDLEVEL])
						if (*argarray[argPASSWDUIDLEVEL] > 65535)
							Warn(GetLocStr(MSG_BADVALUE_CONFIG), &line);
						else
							config.PasswduidLevel = *argarray[argPASSWDUIDLEVEL];
					if (argarray[argPASSWDGIDLEVEL])
						if (*argarray[argPASSWDGIDLEVEL] > 65535)
							Warn(GetLocStr(MSG_BADVALUE_CONFIG), &line);
						else
							config.PasswdgidLevel = *argarray[argPASSWDGIDLEVEL];
				} else
					Warn(GetLocStr(MSG_BADOPTION_CONFIG), &line);
				FreeArgs(rdargs);
			}
			Close(file);
			ClearBuffer();
		} else
			Warn(GetLocStr(MSG_NOCONFIGFILE), NULL);
		FreeDosObject(DOS_RDARGS, rdargs);
	} else
		Die(NULL, AN_Unknown | AG_NoMemory);

	muBase->Config.Flags = config.Flags;
	muBase->Config.LogFlags = config.LogFlags;
	muBase->Config.PasswduidLevel = config.PasswduidLevel;
	muBase->Config.PasswdgidLevel = config.PasswdgidLevel;

	if (FirstStartup) {
		FirstStartup = FALSE;
		if (muBase->Config.LogFlags & muLogF_Startup)
			VLogF("Startup", NULL);
	}
}


	/*
	 *		Initialise the Volume Information
	 */

BOOL InitVolumes(void)
{
	if (!(Buffer = MAlloc(GENBUFSIZE)))
		Die(NULL, AN_Unknown | AG_NoMemory);
	ObtainSemaphore(&muBase->VolumesSem);
	if (!FindVolumes())
		Die(GetLocStr(MSG_NOMUFS), NULL);
	if (!ReadKeyFiles()) {
		muBase->SecurityViolation = TRUE;
		Die(GetLocStr(MSG_BADKEYFILE), NULL);
	}
	ReleaseSemaphore(&muBase->VolumesSem);
	LoadConfig();
	CurrentDir(PasswdDirLock);		
	memset(&PasswdNotifyReq, NULL, sizeof(PasswdNotifyReq));
	PasswdNotifyReq.nr_Name = PasswdFileName;
	PasswdNotifyReq.nr_Flags = NRF_SEND_SIGNAL;
	PasswdNotifyReq.nr_stuff.nr_Signal.nr_Task = (struct Task *)SysBase->ThisTask;
	PasswdNotifyReq.nr_stuff.nr_Signal.nr_SignalNum = muBase->NotifySig;
	StartNotify(&PasswdNotifyReq);
	CurrentDir(ConfigDirLock);		
	memset(&GroupNotifyReq, NULL, sizeof(GroupNotifyReq));
	GroupNotifyReq.nr_Name = GroupFileName;
	GroupNotifyReq.nr_Flags = NRF_SEND_SIGNAL;
	GroupNotifyReq.nr_stuff.nr_Signal.nr_Task = (struct Task *)SysBase->ThisTask;
	GroupNotifyReq.nr_stuff.nr_Signal.nr_SignalNum = muBase->NotifySig;
	StartNotify(&GroupNotifyReq);
	return(TRUE);
}


	/*
	 *		Free all Volume Information
	 */

void FreeVolumes(void)
{
	struct muVolume *vollist, *vl2;

	FreeDefs();
	EndNotify(&GroupNotifyReq);
	EndNotify(&PasswdNotifyReq);
	if (PasswdDirLock) {
		UnLock(PasswdDirLock);
		PasswdDirLock = NULL;
	}
	if (ConfigDirLock) {
		UnLock(ConfigDirLock);
		ConfigDirLock = NULL;
	}
	ObtainSemaphore(&muBase->VolumesSem);
	vollist = muBase->Volumes;
	while (vollist) {
		vl2 = vollist->Next;
		Free(vollist, sizeof(struct muVolume));
		vollist = vl2;
	}
	muBase->Volumes = NULL;
	ReleaseSemaphore(&muBase->VolumesSem);
	memset(Key, NULL, sizeof(Key));
	if (Buffer) {
		Free(Buffer, GENBUFSIZE);
		Buffer = NULL;
	}
}


	/*
	 *		Check whether a Process belongs to a muFS Volume
	 */

BOOL CheckmuFSVolume(struct MsgPort *port)
{
	struct muVolume *vollist;
	BOOL res = FALSE;

	ObtainSemaphoreShared(&muBase->VolumesSem);
	for (vollist = muBase->Volumes; vollist && !(res = (vollist->Process == port));
		  vollist = vollist->Next);
	ReleaseSemaphore(&muBase->VolumesSem);

	return(res);
}


   /*
    *		Format and dump a string to the Log File
    */

void VLogF(STRPTR fmt, LONG *argv)
{
	BPTR file;
	char date[LEN_DATSTRING];
	char time[LEN_DATSTRING];
	struct DateTime dt;
	LONG args[2];

	CurrentDir(ConfigDirLock);
	if (file = Open(LogFileName, MODE_READWRITE)) {
		if (Seek(file, 0, OFFSET_END) != -1) {
			DateStamp(&dt.dat_Stamp);
			dt.dat_Format = FORMAT_DOS;
			dt.dat_Flags = NULL;
			dt.dat_StrDay = NULL;
			dt.dat_StrDate = date;
			dt.dat_StrTime = time;
			DateToStr(&dt);
			args[0] = (LONG)date;
			args[1] = (LONG)time;
			VFPrintf(file, "%s, %s: ", args);
			VFPrintf(file, fmt, argv);
			FPutC(file, '\n');
		}
		Close(file);
	}
}


	/*
	 *		'Safe' FGets
	 *
	 *		Prevents synchronization problems on startup
	 */

static STRPTR SafeFGets(BPTR fh, STRPTR buf, ULONG len)
{
	STRPTR res;
	int i;

	if (!(res = FGets(fh, buf, len)))
		for (i = 1; !res && (i < 10); i++) {
			Delay(25);
			res = FGets(fh, buf, len);
		}
	return(res);
}
