/*
	File:		PPCEntry.c

	Contains:	PPC Dispatcher of various calls

	Written by:	Sangam, Eric M. Trehus
	
	Copyright:	© 1989-1992 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<39>	 4/14/92	BBM		<JSM>: Remove unfinished PPC code that is under the contitional
									ÒTheFututeÓ, and remove the conditional ÒCubeEÓ since that is
									reality. Remove conditionals, since all they do is confuse.
		<38>	 10/4/91	JSM		Change PsychoticFarmerOrLater conditionals to TheFuture.
		<37>	 9/30/91	DTY		DeanÕs in the dog house. The System build got derailed with that
									last change.
		<36>	 9/29/91	DTY		Conditionalize <33> through <35> out of CubeE.
		<35>	 6/26/91	EMT		Add support to kill PPCInform calls
		<34>	 6/10/91	EMT		Check for duplicate completion.
		<33>	  6/4/91	EMT		Roll in StoreAndForward Revisions
		<32>	  1/7/91	EMT		<VC> Fix bug that causes machine to hang in PPCStart and
									IPCListPorts, related to GetMyZone call.
		<31>	12/13/90	EMT		<JSM> Change calling conventions to support new glue, and Inline
									Async/Sync parameterless calls.
		<30>	11/28/90	EMT		<VC> Fix/Work around bug that prevents my completion routine
									from being called.
		<29>	 11/8/90	EMT		Change how PPCStart and IPCListports determine if the session is
									local or not. Because we can't assume that the zone name in our
									globals is correct.
		<28>	 11/6/90	EMT		Changing CheckLocName to not used IUEqualString where it is not
									needed
		<27>	10/30/90	EMT		Fix usage and bug in PPCCommonPBEntry, that would not call
									completion routine if an error occured because we weren't
									initialized or had no globals.
		<26>	10/19/90	JSM		<EMT> Break out PPCEntry() into separate routines for new
									dispatching scheme.
		<25>	10/18/90	EMT		Change userName to machineName where needed
		<24>	10/11/90	EMT		Remove #define DEBUG
		<23>	 9/21/90	EMT		Update constants, types, and field names as dictated by
									PPCToolBox.h
		<22>	 9/16/90	EMT		Fix bug
		<21>	 9/15/90	EMT		Fix CheckLocName bug introduced.
		<20>	 9/14/90	EMT		Make changes to support only type specification in PPCOpen with
									location name.
		<19>	  9/5/90	EMT		Make this file C3.0 friendly
		<18>	  9/4/90	EMT		Eric's changes rolled in
		<17>	  8/6/90	S		Bug Fixes.
		<16>	 6/28/90	S		To Improve Dynamic allocation and fix other bugs.
		<15>	 4/24/90	S		To Un fix Jeff's code changes.
		<14>	 4/18/90	JSM		Add prototype for AvoidDuplicateCompletion, miscellaneous code
									size reductions.
		<13>	 4/10/90	S		To Support Network IPCList Port call when incomming sessions are
									prevented.
		<11>	  3/5/90	S		Don't Look for User &Group File at PPCOpen.
		<10>	 2/27/90	S		Don't allow BOTH for PPCOpen.
		 <9>	 2/13/90	S		Some Error Code Changes.
		 <8>	  2/7/90	S		Bug Fix with PPCOpen call.
		 <7>	  2/5/90	S		Included check to ignore duplicate check for locName for
									PPCOpen.
		 <6>	  2/5/90	S		Included Checking for netVisible Flag before making NBP
									registration.
		 <5>	 1/30/90	S		Bug Fixes
		<3+>	 1/12/90	S		Bug Fixes
		<2+>	  1/5/90	S		Some Error Codes
	   <1.3>	10/12/89	CVC		no change
	   <1.2>	10/12/89	CVC		Handling NoLocName in PortLocName
	   <1.1>	 9/25/89	ss		Support port types.
	   <1.0>	 9/18/89	CVC		Adding PPC toolbox for the first time.
	   
	   Old Revision History:

		05/30/89 	Sangam	    New Today
		07/20/89    Sangam      1.0d2 release today!
		07/25/89	Sangam      Nbp name is concatenated strings for setuser nbp name
		08/01/89	Sangam		Fixed PPOpen for not registering name when ADSP is not there
		08/09/89    Sangam      Reverted back to unpacked format for location name
		10/09/89	Sangam      Handling NoLocName in PortLocName.
								Returning noUserRecErr if user & group file is not there
								but user wants authentication
		1/4/90      Sangam      Some bug fixes and return errorcode changes.
*/

#include "PPCCommon.h"
#include <Packages.h>

/*----------------------------------------------------------------------------------------
	Prototypes used only in this file.
----------------------------------------------------------------------------------------*/

STATIC Boolean PPCCommonPBEntry(void *PB, PPCGlobalParamsPtr *ppcglobPtrPtr);
STATIC OSErr PPCCommonPBExit(void *PB, PPCGlobalParamsPtr ppcglobPtr);
STATIC void NetOpenCompletion(void);					// <36>
STATIC void CallCompletionRoutine(PPCParamBlockPtr PB,OSErr ioResult);
STATIC Boolean LocNameTaken(LocationNamePtr locationName,PPCGlobalParamsPtr ppcglobPtr);
STATIC void BeginLocalOrRemote(PPCGlobalParamsPtr ppcglobPtr,PPCParamBlockPtr PB,void *portPtr);
STATIC void LocalOrNot(void);
STATIC void DoGetMyZone(XCallParam *xpb,Boolean async,ProcPtr compRtn,Str32	zoneName);

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCOpen call.
----------------------------------------------------------------------------------------*/
OSErr ppcOpen(PPCOpenPBPtr openPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
 	PPCPortEntryPtr		portPtr;						// <36>
	LocationNamePtr theLocName;
	
	if (PPCCommonPBEntry(openPB, &ppcglobPtr))			// do common entry code for parameter block based calls
	{
		openPB->csCode = PPCOpenCall;					// save command code
		openPB->nbpRegistered = false;
		openPB->portRefNum = 0;
		theLocName = openPB->locationName;
		if(openPB->resFlag)								// <36> Enforce resFlag == 0
		{												// <36>
			CompleteWithResult(openPB,badReqErr);		// <36>
			goto Done;									// <36>
		}												// <36>
	
		if (!ValidPortName(openPB->portName))			// <36> make sure the Port name looks good
		{												// <36>
			CompleteWithResult(openPB,badPortNameErr);	// <36>
			goto Done;									// <36>
		}
		
		if (CheckPortName(openPB->portName, ppcglobPtr))	// make sure the Port name is not already in use
		{
			CompleteWithResult(openPB,portNameExistsErr);
			goto Done;
		}
		
		if (openPB->serviceType != ppcServiceRealTime)	// make sure we support this serviceType
		{
			CompleteWithResult(openPB,badServiceMethodErr);
			goto Done;
		}
		if (openPB->networkVisible && openPB->locationName)
		{
			if (theLocName->locationKindSelector != ppcNBPTypeLocation)
			{
				CompleteWithResult(openPB,nameTypeErr);	// <36>
				goto Done;								// <36>
			}
			
			if (theLocName->u.nbpType[0] > 32 || theLocName->u.nbpType[0] == 0)
			{
				CompleteWithResult(openPB,badLocNameErr);
				goto Done;
			}

			if(LocNameTaken(openPB->locationName,ppcglobPtr))
			{
				CompleteWithResult(openPB,nbpDuplicate);
				goto Done;
			}
		}
		
		if (!(portPtr = GetPortTable(openPB, ppcglobPtr)))	// make sure this port exists
		{
			CompleteWithResult(openPB,noPortErr);
			goto Done;
		}
		
		if (portPtr->locationInfo)		// If client wants a location name with the port
		{
			if(ppcglobPtr->allowIncommingRequests)	// Are we allowing incomming IAC.
			{
				PortLocationTablePtr locInfo;
				
				locInfo = portPtr->locationInfo;
				RegisterName(ppcglobPtr->machineName,		// use our machine name
							 locInfo->typeStr, 				// use the type specified.
							 "\p*",							// always *
							 ppcglobPtr->adspSocket,		// socket to register name on
							 true,							// Call ASyncronously 
							 (ProcPtr)NetOpenCompletion,	// NBPCompletion routine
							 (Ptr)&locInfo->nteQEle,		// ntePtr
							 &locInfo->nbpPB);				// PB for NBP call
							 
			}
			else	// Port open, location name not registered.
			{
				portPtr->openPB = nil;		// This is important
				CompleteWithResult(openPB,noErr);
				goto Done;
			}
		}
		else	// Port open without a location name.
		{
			portPtr->openPB = nil;		// This is important
			CompleteWithResult(openPB,noErr);
			goto Done;
		}
	}
Done:
	return PPCCommonPBExit(openPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCStart call.
----------------------------------------------------------------------------------------*/
OSErr ppcStart(PPCStartPBPtr startPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	OSErr				result;
 	PPCPortEntryPtr		portPtr;
	
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(startPB, &ppcglobPtr))
	{
		startPB->csCode = PPCStartCall;								// save command code
		if (!ValidPortName(startPB->portName))
		{
			CompleteWithResult(startPB,badPortNameErr);
		}
		else if (startPB->serviceType != ppcServiceRealTime)
		{
			CompleteWithResult(startPB,badServiceMethodErr);
		}
		else if ((portPtr = PortRefNumtoPtr(startPB->portRefNum, ppcglobPtr)) == nil)
		{
			CompleteWithResult(startPB,noPortErr);		// <36>
		}
		else if (result = VerifyLocNameFormat(startPB->locationName))
		{
			CompleteWithResult(startPB,result);
		}
		else
			BeginLocalOrRemote(ppcglobPtr,(PPCParamBlockPtr)startPB,portPtr);
	}
	return  PPCCommonPBExit(startPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCInformCall.
----------------------------------------------------------------------------------------*/
OSErr ppcInform(PPCInformPBPtr informPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
 	PPCPortEntryPtr		portPtr;
	
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(informPB, &ppcglobPtr))
	{
		informPB->csCode = PPCInformCall;							// save command code
		if ((portPtr = PortRefNumtoPtr(informPB->portRefNum, ppcglobPtr)) == nil)
		{
			CompleteWithResult(informPB,noPortErr);
		}
#ifdef notDefined										// <36>
		else											// <36>
		if(portPtr->serviceType == StoreAndForward || portPtr->serviceType == Both) // <36>
		{												// <36>
		}												// <36>
#endif													// <36>
		else											// <36>
		{												// <36>
			SetPortInformQue(informPB, portPtr);		// <36> Wait for somthing.
		}												// <36>
	}
	return PPCCommonPBExit(informPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCAccept call.
----------------------------------------------------------------------------------------*/
OSErr ppcAccept(PPCAcceptPBPtr acceptPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	CommonSessionParams	*sessPtr;
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(acceptPB, &ppcglobPtr))
	{
		acceptPB->csCode = PPCAcceptCall;							// save command code
		if ((sessPtr = SessRefNumtoPtr(acceptPB->sessRefNum, ppcglobPtr)) == nil)
		{
			CompleteWithResult(acceptPB,noSessionErr);
		}
		else if (sessPtr->sessState != AwaitAcceptReq)
		{
			if (sessPtr->sessState == AwaitAbortComp)
				CompleteWithResult(acceptPB,noSessionErr);
			else
				CompleteWithResult(acceptPB,badReqErr);
		}
		else if (sessPtr->sessUse == locUse)
		{
			AcceptLocalSession(acceptPB, sessPtr,ppcglobPtr);
		}
		else if (sessPtr->sessUse == netUse)
		{
			AcceptNetworkSession(acceptPB,(NetIPCParamsPtr)sessPtr);
		}
		else
		{
			CompleteWithResult(acceptPB,paramErr);	// <36>
		}
	}
	
	return PPCCommonPBExit(acceptPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCReject call.
----------------------------------------------------------------------------------------*/
OSErr ppcReject(PPCRejectPBPtr rejectPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	CommonSessionParams	*sessPtr;

	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(rejectPB, &ppcglobPtr))
	{
		rejectPB->csCode = PPCRejectCall;			// save command code
		if ((sessPtr = SessRefNumtoPtr(rejectPB->sessRefNum, ppcglobPtr)) == nil)
		{
			CompleteWithResult(rejectPB,noSessionErr);
		}
		else if (sessPtr->sessState != AwaitAcceptReq)
		{
		   if (sessPtr->sessState == AwaitAbortComp)
				CompleteWithResult(rejectPB,noSessionErr);
		   else
				CompleteWithResult(rejectPB,badReqErr);
		}
		else if (sessPtr->sessUse == locUse)
		{
			RejectLocalSession(rejectPB, sessPtr, ppcglobPtr);
		}
		else if (sessPtr->sessUse == netUse)
		{
			RejectNetworkSession(rejectPB,(NetIPCParamsPtr)sessPtr);
		}
		else
		{
			CompleteWithResult(rejectPB,paramErr);	// <36>
		}
	}
	
	return PPCCommonPBExit(rejectPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCWrite call.
----------------------------------------------------------------------------------------*/
OSErr ppcWrite(PPCWritePBPtr writePB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	CommonSessionParams	*sessPtr;
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(writePB, &ppcglobPtr))
	{
		writePB->csCode = PPCWriteCall;				// save command code
		writePB->actualLength = 0;
		if ((sessPtr = SessRefNumtoPtr(writePB->sessRefNum,  ppcglobPtr)) == nil)
		{
			CompleteWithResult(writePB,noSessionErr);	// <36>
		}
		else if (sessPtr->sessState != DataXferState)
		{
		   if (sessPtr->sessState == AwaitAbortComp)
				CompleteWithResult(writePB,noSessionErr);
		   else
				CompleteWithResult(writePB,badReqErr);
		}
		else if (sessPtr->sessUse == locUse)
		{
			WriteLocalData( writePB, sessPtr, ppcglobPtr);
		}
		else if (sessPtr->sessUse == netUse)
		{
			WriteNetworkData(writePB,(NetIPCParamsPtr)sessPtr);
		}
		else										// <36>
		{											// <36>
			CompleteWithResult(writePB,paramErr);	// <36>
		}
	}
	return PPCCommonPBExit(writePB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCRead call.
----------------------------------------------------------------------------------------*/
OSErr ppcRead(PPCReadPBPtr readPB)
{
	PPCGlobalParamsPtr	ppcglobPtr;
	CommonSessionParams	*sessPtr;

	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(readPB, &ppcglobPtr))
	{
		readPB->csCode = PPCReadCall;				// save command code
		readPB->actualLength = 0;
		if ((sessPtr = SessRefNumtoPtr(readPB->sessRefNum, ppcglobPtr)) == nil)
		{
			CompleteWithResult(readPB,noSessionErr);	// <36>
		}
		else if (sessPtr->sessState != DataXferState)
		{

		   if (sessPtr->sessState == AwaitAbortComp)
				CompleteWithResult(readPB,noSessionErr);
		   else
				CompleteWithResult(readPB,badReqErr);
		}
		else if (sessPtr->sessUse == locUse)
		{
			ReadLocalData( readPB, sessPtr, ppcglobPtr);
		}
		else if (sessPtr->sessUse == netUse)
		{
			ReadNetworkData( readPB,(NetIPCParamsPtr)sessPtr);
		}
		else
		{
			CompleteWithResult(readPB,paramErr);
		}
	}
	
	return PPCCommonPBExit(readPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCEnd call.
----------------------------------------------------------------------------------------*/
OSErr ppcEnd(PPCEndPBPtr endPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	CommonSessionParams	*sessPtr;
	
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(endPB, &ppcglobPtr))
	{
		endPB->csCode = PPCEndCall;									// save command code
		if (sessPtr = SessRefNumtoPtr(endPB->sessRefNum, ppcglobPtr))
		{
			if (sessPtr->sessState == DataXferState)
			{
				if (sessPtr->sessUse == locUse)
				{
					sessPtr = DeleteSessByRefNum(endPB->sessRefNum, ppcglobPtr);
					EndLocalSession( endPB, sessPtr, ppcglobPtr);
				}
				else if (sessPtr->sessUse == netUse)
				{
					sessPtr = DeleteSessByRefNum(endPB->sessRefNum, ppcglobPtr);
					EndNetworkSession(endPB,(NetIPCParamsPtr) sessPtr);
				}
				else
				{
					CompleteWithResult(endPB,noSessionErr);
				}
			}
			else
			{
				CompleteWithResult(endPB,noSessionErr);
			}
		}
		else
		{
			CompleteWithResult(endPB,noSessionErr);
		}
	}
	
	return PPCCommonPBExit(endPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the PPCClose call.
----------------------------------------------------------------------------------------*/
OSErr ppcClose(PPCClosePBPtr closePB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
 	PPCPortEntryPtr		portPtr;
	
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(closePB, &ppcglobPtr))
	{
		closePB->csCode = PPCCloseCall;								// save command code
		if (portPtr = DeletePortByRefNum(closePB->portRefNum, ppcglobPtr))
		{
			ClosePortTable(closePB, portPtr, ppcglobPtr);
		}
		else
		{
			CompleteWithResult(closePB,noPortErr);
		}
	}
	
	return PPCCommonPBExit(closePB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	Main entry point to the IPCListPorts call.
----------------------------------------------------------------------------------------*/
OSErr ipcListPorts(IPCListPortsPBPtr listPB)
{
    PPCGlobalParamsPtr	ppcglobPtr;
	OSErr				result;
	
	
	// do common entry code for parameter block based calls
	if (PPCCommonPBEntry(listPB, &ppcglobPtr))
	{
		listPB->csCode = IPCListPortsCall;				// save command code
		if (!ValidPortName(listPB->portName))
		{

			CompleteWithResult(listPB,badPortNameErr);	// <36>
		}
		else if (result = VerifyLocNameFormat(listPB->locationName))
		{
			CompleteWithResult(listPB,result);
		}
		else
			BeginLocalOrRemote(ppcglobPtr,(PPCParamBlockPtr)listPB,NULL);
	}
	
	return PPCCommonPBExit(listPB, ppcglobPtr);
}

/*----------------------------------------------------------------------------------------
	PPCCommonPBEntry is called at the beginning of all parameter block based calls.  It returns true
	if PPC can even deal with PB calls right now.  We make sure we have globals, and that we are
	initialized first.  We then prepare ourself to process the request.
	
	WARNING:  This routine has strange calling conventions.  It depends upon register D1 having the
	the PPCToolBox trap word in register D1.  C
----------------------------------------------------------------------------------------*/
STATIC Boolean PPCCommonPBEntry(void *PB, PPCGlobalParamsPtr *ppcglobPtrPtr)
PPCParamBlockPtr PB;
{
	PPCGlobalParamsPtr	ppcglobPtr;
	PBWriteResPtr		res;							// <36>
	unsigned short		PPCTrap;

	PPCTrap = GetD1();
#ifdef DEBUG
	if(PPCTrap != 0xA0DD && PPCTrap != 0xA4DD)
		DebugStr("\pRegister D1 did not have our trap word");
#endif
	if(*ppcglobPtrPtr = ppcglobPtr = getGlobal())	// get globals
	{
		// save application's A5

		res = (PBWriteResPtr)(PB->startParam.Reserved);	// <36>
		res->ApplA5 = GetA5();							// <36>
		res->cmdMode = PPCTrap & 0x0400?true:false;		// <36>

		if (!ppcglobPtr->inited)
		{
			CompleteWithResult(PB,notInitErr);
			return(false);
		}
	
		// mark PB as busy
		PB->startParam.ioResult = 1;
	
		// increment nesting level
		++ppcglobPtr->inEntry;
	
		
		// replenish resources if call made synchronously
		if(!res->cmdMode)								// <36>
			Replenish(ppcglobPtr);
		
		return(true);	// We are prepared to process this request.
	}
	else	// No Globals, return false and let PPCCommonPBExit handle it.
	{
		return false;
	}
}
		
/*----------------------------------------------------------------------------------------
	PPCCommonPBExit is called at the end of all parameter block based calls.  Note if
	PPCCommonPBEntry returned false because we don't have globals, we will no this here
	since the ppcglobPtr will be NULL.  So we have to complete the request differently in
	this because CompleteWithResult depends upon globals being present.
----------------------------------------------------------------------------------------*/
STATIC OSErr PPCCommonPBExit(void *PB, PPCGlobalParamsPtr ppcglobPtr)
PPCParamBlockPtr PB;
{
	short result = PB->startParam.ioResult;
	
	if(ppcglobPtr)	// Should always be no zero, but one never knows!
	{
		if(ppcglobPtr->inEntry == 1)	// If at outermost Entry in stack frame
		{
			PPCParamBlockPtr tempPB;	// complete all pending calls.
			
			do
			{
				short p;
				
				p = spl(kNoInterrupts);	// Extremely Critical Code.
				if(!ppcglobPtr->CompletedPBQueue.qSize)	// if the queue is empty
				{
					--ppcglobPtr->inEntry;	// We will allow direct call of comp routines.
					spl(p);					// restore the interrupt level.
					break;					// Done with this loop.
				}
				else	// Somthing is in the queue!
				{
					spl(p);	// Restore interrupts to the way they were.
					tempPB = ServeQueue(&ppcglobPtr->CompletedPBQueue);	// Get user PB.
					CallCompletionRoutine(tempPB,tempPB->startParam.ioResult);	// Call comp routine
				}
			}
			while(true);	// Repeat until the queue is empty.

			if (!((PBWriteResPtr)(PB->startParam.Reserved))->cmdMode)	// <36> if Synchronous
			{
				while (PB->startParam.ioResult == 1);	// Wait for the call to complete.
				return PB->startParam.ioResult;
			}
		}
		else	// Not at top level.
			--ppcglobPtr->inEntry;	// Decrease our level indicator.
		
		// if call still processing, return noErr, else return result
		return(result == 1 ? noErr : result);
	}
	else
	{
		CallCompletionRoutine(PB,noGlobalsErr);
		return(noGlobalsErr);
	}
}

STATIC void NetOpenCompletion(void)
{
	PPCPortEntryPtr			portPtr;
	PortLocationTablePtr	locInfo;
	PPCOpenPBPtr			openPB;
	
	locInfo = GetA0();
	portPtr = locInfo->portPtr;
	openPB = (PPCOpenPBPtr)(portPtr->openPB); /* get back the openPB */
	
	if (locInfo->nbpPB.ioResult == noErr)
		portPtr->nbpRegistered = openPB->nbpRegistered = true; // we did register with nbp
	portPtr->openPB = nil; // This is important
	CompleteWithResult(openPB,noErr);
} // NetNBPCompletion

STATIC void CallCompletionRoutine(PPCParamBlockPtr PB,OSErr ioResult)
{
	PPCReservedPtr res = (PPCReservedPtr)(&(PB->startParam.Reserved[0]));
	
	if (res->cmdMode)  // if Asynchronous 
	{
		callCompletion(PB, ioResult); // Save AppA5 and call completion
	}
}

/*----------------------------------------------------------------------------------------
	CompleteWithResult prepares and completes the usage of the client's parameter block
	with the specified result code.  If we are not inEntry, then the call is complete
	immediatly, otherwise it is place on the CompletedPBQueue to be served later when the
	stack unwinds within PPCCommonPBExit.
----------------------------------------------------------------------------------------*/
OSErr CompleteWithResult(void *PB,OSErr Result)
PPCParamBlockPtr PB;
{
	PPCGlobalParamsPtr   	ppcglobPtr;

	PB->closeParam.ioResult = Result;
	ppcglobPtr = getGlobal();
	if(ppcglobPtr->inEntry)
	{
		EnQueue(PB,&ppcglobPtr->CompletedPBQueue);
	}
	else
		CallCompletionRoutine(PB,Result);
	return(Result);	// For callers convience!
}

/*----------------------------------------------------------------------------------------
	VerifyLocNameFormat is used to verify that a location name can be used to start a
	session.  It just checks the structure.
----------------------------------------------------------------------------------------*/
OSErr VerifyLocNameFormat(LocationNamePtr locName)
{
	if(locName)
	{
		if (locName->locationKindSelector == ppcNoLocation)
			return noErr; // NoLocation implies local.

		if (locName->locationKindSelector != ppcNBPLocation)
			return nameTypeErr; // we don't understand any other type
		
		if (locName->u.nbpEntity.objStr[0] > 32 || locName->u.nbpEntity.typeStr[0] > 32 ||
			locName->u.nbpEntity.zoneStr[0] > 32)
			return badLocNameErr;
		
		// NOTE: little known AppleTalk fact, a zero length zone string indicates this zone.
		// That is why I won't check for minimum length on the zone string here.
		if (locName->u.nbpEntity.objStr[0] == 0 || locName->u.nbpEntity.typeStr[0] == 0)
			return badLocNameErr;
		
	}
	return(noErr);
}

/*----------------------------------------------------------------------------------------
	LocationNameLocal Determines if the given location name exists on this machine.
----------------------------------------------------------------------------------------*/
Boolean LocationNameLocal(LocationNamePtr locName)
{
	PortLocationTablePtr locInfo;
	PPCGlobalParamsPtr ppcglobPtr;
	PPCPortEntryPtr  portPtr;					// <36>

	if(!locName)
		return(true);
	if (locName->locationKindSelector == ppcNoLocation)
 		return(true);

	ppcglobPtr = getGlobal();
	if(locName->u.nbpEntity.zoneStr[0] == 0 ||
	  (locName->u.nbpEntity.zoneStr[0] == 1 && locName->u.nbpEntity.zoneStr[1] == '*') ||
	  EqualString(locName->u.nbpEntity.zoneStr,ppcglobPtr->zoneName,false,true))
	{
		// At this point the zone is our zone, so we will check the object.
		if(EqualString(locName->u.nbpEntity.objStr,ppcglobPtr->machineName,false,true))
		{
			if(EqualString(locName->u.nbpEntity.typeStr, ppcglobPtr->configData.ppctoolboxName,false,true))
				return(true);
			else
			{
				short sr;
				
				sr = spl(kNoInterrupts);		// Make sure we don't have any problems.

				FOREACHELEMENT(portPtr,&ppcglobPtr->OpenPortQueue)
				{
					if (locInfo = portPtr->locationInfo)
					{
						if (EqualString(locName->u.nbpEntity.typeStr, locInfo->typeStr,false,true))
						{
							spl(sr);
							return(true); 		// Name is locally registered already
						}
					}
				}
				spl(sr);
			}
		}
	}
NotLocal:
	return(false);
}

/*----------------------------------------------------------------------------------------
	LocNameTaken determines if the typeStr is currently in use in this machine for some
	port.
----------------------------------------------------------------------------------------*/
STATIC Boolean LocNameTaken(LocationNamePtr locationName,PPCGlobalParamsPtr ppcglobPtr)
{
	short sr;
	Boolean isTaken;
	PPCPortEntryPtr  portPtr;					// <36>

	isTaken = false;
	sr = spl(kNoInterrupts);

	FOREACHELEMENT(portPtr,&ppcglobPtr->OpenPortQueue)
	{
		if(portPtr->locationInfo)
		{
			if(EqualString(locationName->u.nbpType,
						   portPtr->locationInfo->typeStr,
						   false,true))	/* case insensitive, diacritical sensitive */
			{
				isTaken = true;
				break;
			}
		}
	}
	spl(sr);
	return(isTaken);
}

/*----------------------------------------------------------------------------------------
	BeginLocalOrRemote begins a local or remote request for IPCListPorts or PPCStart.  It has a
	side effect of updating our global down-below with our current zone name, keeping it fresh.  NOTE
	there exists an interesting, but harmless race condition, worst case we perform a GetMyZone call
	and when it completes have nothing to processes, casue it was already taken care of.
----------------------------------------------------------------------------------------*/
STATIC void BeginLocalOrRemote(PPCGlobalParamsPtr ppcglobPtr,PPCParamBlockPtr PB,void *portPtr)
{
	short sr;
	
	PB->startParam.intUsePtr = portPtr;		// Remember this for later in case of PPCStart.
	EnQueue(PB,&ppcglobPtr->BeginLRQueue);
	sr = spl(kNoInterrupts);
	if(ppcglobPtr->BeginLRQueue.qSize == 1)	// Not currently performing a GetMyZone.
	{
		spl(sr);
		DoGetMyZone(&ppcglobPtr->xpb,		// Use this parameter block
					true,					// do this asyncrhonously
					(ProcPtr)LocalOrNot,	// Completion Routine.
					&ppcglobPtr->zoneName);	// here is where to put the zone name.
	}
	else
		spl(sr);
}


/*----------------------------------------------------------------------------------------
	LocalOrNot is chained from BeginLocalOrRemote.  It processes the list pending IPCListPorts
	and PPCStart requests by branching to the appropriate routine, or completing the request with
	an error if we don't support remote request.
----------------------------------------------------------------------------------------*/
STATIC void LocalOrNot(void)
{
	PPCGlobalParamsPtr ppcglobPtr;
	Boolean Local;
	PPCParamBlockPtr PB;
	
	ppcglobPtr = getGlobal();
	
	if(ppcglobPtr->xpb.ioResult)
		BlockMove("\p*",ppcglobPtr->zoneName,2);
		
	while(PB = ServeQueue(&ppcglobPtr->BeginLRQueue))	// Batch process the requests.
	{
		Local = LocationNameLocal(PB->startParam.locationName);
		if (!Local && !ppcglobPtr->canRemote)	// If its remote, and we can't, then
		{
			CompleteWithResult(PB,localOnlyErr);
			continue;
		}

		if(PB->startParam.csCode == PPCStartCall)
		{
			if(Local)
				StartLocalSession((PPCStartPBPtr)PB,(PPCPortEntryPtr)PB->startParam.intUsePtr, ppcglobPtr);
			else
				StartNetworkSession((PPCStartPBPtr)PB,(PPCPortEntryPtr)PB->startParam.intUsePtr, ppcglobPtr);
		}
		else	// Must be an IPCListPorts request
		{
			if(Local)
				ListLocalPorts((IPCListPortsPBPtr)PB, ppcglobPtr);
			else
				ListNetworkPorts((IPCListPortsPBPtr)PB, ppcglobPtr);
		}
	}
}

/*----------------------------------------------------------------------------------------
	DoGetMyZone performs the task of a GetMyZone call, its just a nice interface to perform such
	a boring task.
----------------------------------------------------------------------------------------*/
STATIC void DoGetMyZone(XCallParam *xpb,
						Boolean async,
						ProcPtr compRtn,
						Str32	zoneName)
{
	OSErr result;

	xpb->ioCompletion = compRtn;
	xpb->zipBuffPtr = (Ptr)zoneName;
	xpb->zipInfoField[0] = 0;
	xpb->zipInfoField[1] = 0;
	xpb->xppTimeout = 1;
	xpb->xppRetry = 3;
	if (getGlobal()->mppOpen)
	{
#ifdef JimFixedTheGlue
		result = DMFix(GetMyZone((XPPParmBlkPtr)xpb, async),xpb);
#else
		xpb->csCode = xCall;
		xpb->xppSubCode = zipGetMyZone;
		xpb->ioRefNum = xppRefNum;
		result = DMFix(PBControl((ParmBlkPtr)xpb, async),xpb);
#endif
	}
	else	// pretend we are .XPP for a moment.
	{
		BlockMove("\p*",zoneName,2);
		xpb->ioResult = noErr;
		FakeCompletion(xpb);
	}

}

/*----------------------------------------------------------------------------------------
	DMFix checks to see if the error code in a parameter block was returned
	by the Device Manager.  The Device Manger fails to call the completion routine specified in
	the parameter block.  So I will check for the known cases when this happens, and if its one of
	them, call the completion routine as if I were the device manager doing its job properly.
----------------------------------------------------------------------------------------*/
OSErr DMFix(OSErr Result,void *thePB)
MPPparms *thePB;
{
	
	if(Result == notOpenErr || Result == badUnitErr)
	{
		if(thePB->ioCompletion)	// Assume if completion routine specified, call is async.
		{
				FakeCompletion(thePB);	// Calls the completion routine with Device Manager calling conventions.
		}
	}
	return(Result);
}
