BigEasy/BigEasyPPC.c

/*
    File:       BigEasyPPC.c
 
    Contains:   xxx put contents here xxx
 
    Written by: xxx put writers here xxx
 
    Copyright:  © 1992 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
         <2>      1/7/93    dvb     Compile under mpw.
         <1>     1/20/92    dvb     first checked in
 
*/
 
/* file: BigEasyPPC.c
 *
 * Started Mid 1991, more or less.
 *
 * A set of routines to quickly
 * access some simple uses of PPC.
 *
 */
 
/*--------------------------
    Protocol Notes
--------------------------*/
/*
 
Port creator type is always 'BigE', and the type is specified by
the application.
 
When the BrowseAndConnect is used, the PPCBrowser checks port creator and
type to match the connecting port (and only shows those that match), and
also attempts to connect to the port with a userdata of 'test'. The server
port rejects the connection attempt if the userdata is 'test', and
returns 'test' as the Reject-reason.
 
*/
 
 
/*--------------------------
    Inclusions
--------------------------*/
 
#include "BigEasyPPC.h"
#include <Errors.h>
#include <Events.h>
 
/*--------------------------
    Constants
--------------------------*/
 
#define kEasyPPCPortCreator 'BigE'
 
enum
/*
 * For the filterproc to work, there must
 * be an inform pending. The filterproc tests
 * this by actually connecting to the proposed
 * target, with userdata 'test'. The target sees this,
 * and ignores the connection, and posts another 'Inform'
 * just to make sure.
 */
    {
    testConnection = 'test',
    realConnection = 'real'
    };
 
/*--------------------------
    Globals
--------------------------*/
 
Boolean gEverBrowsed = 0;           /* to remember where we are in the browser */
LocationNameRec gTheLocation;
PortInfoRec gThePortInfo;
 
 
static EasyPPCSession gEasyFilterSession;       /* communicate with Filter Proc */
 
/*--------------------------
    Prototypes
--------------------------*/
 
static short BumpStringNumber(StringPtr s);
static void HandleSomeErrors(EasyPPCSession es,EasyPPCPollResult *pollResult);
static void InitializeClient(EasyPPCSession es,EasyPPCClient *clientPtr);
static EasyPPCClient *FindClient(EasyPPCSession es,long sessRefNum);
static void RemoveClientEntry(EasyPPCSession es,EasyPPCClient *clientPtr);
static void PollEasyPPCClient(EasyPPCSession es,EasyPPCClient *clientPtr,
        EasyPPCPollResult *pollResult);
static EasyPPCClient *FindFreeClient(EasyPPCSession es);
 
static pascal void InformCompletion(PPCParamBlockPtr pb);
static pascal Boolean EasyBrowserFilter (LocationNamePtr location, PortInfoPtr port);
 
 
#define MoveString(src,dst) BlockMove(src,dst,src[0]+1)
 
/*--------------------------
    Agedashi Dofu
--------------------------*/
 
short BumpStringNumber(StringPtr s)
/*
 * Starting from string "string" make strings "string 00" through
 * "string 99". Return false when that isn't possible.
 */
    {
    unsigned char *c;
 
    c = s + s[0];       /* c points to very last character */
 
    if(*c < '0' || *c > '9' || *(c-1) < '0' || *(c-1) > '9')    /* need to append 00? */
        {
        s[0] += 3;
        *++c = ' ';
        *++c = '0';
        *++c = '0';
        }
    else
        {
        *c += 1;
        if(*c > '9')
            {
            *c-- = '0';
            *c += 1;
            if(*c > '9')
                return 0;
            }
        }
    return 1;
    }
 
 
EasyPPCSession NewEasyPPCSession(StringPtr name,OSType type)
    {
    EasyPPCSession es;
    PPCOpenPBRec opbr;
    PPCPortRec portName;
    OSErr thisError;
    short i;
 
    es = (void *)NewPtr(sizeof(EasyPPCSessionRecord));
 
    portName.nameScript = 0;
    MoveString(name,portName.name);
    portName.portKindSelector = ppcByCreatorAndType;
    portName.u.port.creator = kEasyPPCPortCreator;
    portName.u.port.type = type;
 
    opbr.ioCompletion = 0;
    opbr.serviceType = ppcServiceRealTime;
    opbr.resFlag = 0;
    opbr.portName = &portName;
    opbr.locationName = 0;
    opbr.networkVisible = 1;
 
tryOpen:
    thisError = PPCOpenSync(&opbr);
 
    if(thisError == portNameExistsErr)
        if(BumpStringNumber(portName.name))
            goto tryOpen;
 
    if(thisError)
        goto fail;
 
    es->portRefNum = opbr.portRefNum;
    es->type = type;
    es->serverType = type;
    thisError = GetDefaultUser(0,es->userName);
 
    es->waitingToWrite = 0;
    for(i = 0; i < kEasyPPCWritePacketCount; i++)
        es->wpbr[i].used = 0;
 
    es->connected = 0;
    for(i = 0; i < kEasyPPCClientCount; i++)
        es->client[i].used = 0;
 
    goto goHome;
fail:
    DisposePtr((Ptr)es);
    es = 0;
goHome:
    return es;
    }
 
 
void DisposeEasyPPCSession(EasyPPCSession es)
    {
    EasyPPCWritePB *ewpb;
    PPCClosePBRec cpbr;
    short i;
 
    if(es)
        {
        cpbr.ioCompletion = 0;
        cpbr.portRefNum = es->portRefNum;
    
        PPCCloseSync(&cpbr);
 
        for(i = 0; i < kEasyPPCWritePacketCount; i++)
            {
            ewpb = &es->wpbr[i];
 
            if(ewpb->used && ewpb->buffer)
                DisposePtr(ewpb->buffer);           /* easymanage */
            }
        }
    }
 
 
OSErr ConnectEasyPPCSession(EasyPPCSession es,
        LocationNameRec *locationName,PortInfoRec *pir,long *sessRefNum)
    {
    PPCStartPBRec spbr;
    OSErr thisError;
    EasyPPCClient *clientPtr;
 
    if(!es || es->connected >= kEasyPPCClientCount)         /* already enough connected */
        {
        thisError = -1;
        goto goHome;
        }
 
    spbr.ioCompletion = 0;
    spbr.portRefNum = es->portRefNum;
    spbr.serviceType = ppcServiceRealTime;
    spbr.resFlag = 0;
    spbr.portName = &pir->name;
    spbr.locationName = locationName;
    spbr.userData = 0;
    spbr.userRefNum = 0;
 
    thisError = PPCStartSync(&spbr);
 
    if(thisError == guestNotAllowedErr)
        {
        Boolean asGuest;
 
        thisError = StartSecureSession(&spbr,
                (StringPtr)0x910,
                true,               /* use default? */
                false,              /* don't allow guest, it didn't work */
                &asGuest,
                "\p");
        }
 
    if(!thisError)
        {
        clientPtr = FindFreeClient(es);
 
        InitializeClient(es,clientPtr);
        clientPtr->userName[0] = 0;
        clientPtr->locationName = *locationName;
        clientPtr->portName = pir->name;
        clientPtr->sessRefNum = spbr.sessRefNum;
        }
goHome:
    if(sessRefNum)
        *sessRefNum = spbr.sessRefNum;
    return thisError;
    }
 
 
/* ---------------------------------------------------------- */
 
 
OSErr DisconnectEasyPPCSession(EasyPPCSession es,long sessRefNum)
    {
    PPCEndPBRec epbr;
    OSErr thisError;
    EasyPPCClient *clientPtr;
 
    if(!es)
        return -1;
 
    clientPtr = FindClient(es,sessRefNum);
    if(!clientPtr)
        return -1;
 
    epbr.ioCompletion = 0;
    epbr.sessRefNum = clientPtr->sessRefNum;
    thisError = PPCEndSync(&epbr);
 
    RemoveClientEntry(es,clientPtr);
 
    HUnlock((void *)es->client);
 
    return thisError;
    }
 
 
/* ---------------------------------------------------------- */
 
 
OSErr LetConnectEasyPPCSession(EasyPPCSession es)
    {
    OSErr thisError;
    EasyPPCClient *clientPtr;
 
    /*
     * Enough connected?
     */
    if(!es || es->connected >= kEasyPPCClientCount)
        {
        thisError = -1;
        goto goHome;
        }
 
    clientPtr = FindFreeClient(es);
 
    InitializeClient(es,clientPtr);
    clientPtr->sessRefNum = 0;
    clientPtr->waitingToConnect = 1;
 
    clientPtr->ipbr.ioCompletion = (ProcPtr)InformCompletion;
    clientPtr->ipbr.portRefNum = es->portRefNum;
    clientPtr->ipbr.autoAccept = 0;
    clientPtr->ipbr.portName = &clientPtr->portName;
    clientPtr->ipbr.locationName = &clientPtr->locationName;
    clientPtr->ipbr.userName = clientPtr->userName;
 
    thisError = PPCInformAsync(&clientPtr->ipbr);
 
goHome:
    return thisError;
    }
 
pascal void InformCompletion(PPCParamBlockPtr pb)
/*
 * Here, we only accept the connection if
 * it's not a 'test' connection.
 * If it _is_ a test, we return 'test' as the
 * Reject-reason.
 */
    {
    PPCInformPBRec *ipbr;
    PPCAcceptPBRec apbr;
    PPCRejectPBRec rpbr;
 
    ipbr = (PPCInformPBRec *)pb;
 
    if(ipbr->ioResult)          /* problem? maybe the port's being closed? */
        return;                 /* just quit it. */
 
    if(ipbr->userData != testConnection)
        {
        apbr.ioCompletion = 0;
        apbr.sessRefNum = ipbr->sessRefNum;
        PPCAcceptSync(&apbr);
        }
    else
        {
        rpbr.ioCompletion = 0;
        rpbr.sessRefNum = ipbr->sessRefNum;
        rpbr.rejectInfo = testConnection;
        PPCRejectSync(&rpbr);
        PPCInformAsync(ipbr);
        }
    }
 
 
OSErr WriteEasyPPCSession(EasyPPCSession es,long sessRefNum,
        long packetType,void *buffer,long length,Boolean easyManage)
/*
 * If 'easyManage' is true, then BigEasyPPC makes a local copy
 * of the data, and the caller is free to forget about his copy.
 */
    {
    OSErr result;
    PPCWritePBRec *wpb;
    EasyPPCWritePB *ewpb;
    short i;
    EasyPPCClient *clientPtr;
 
    if(!es || (es->waitingToWrite >= kEasyPPCWritePacketCount) )
        {
        result = 1;
        goto goHome;
        }
 
    clientPtr = FindClient(es,sessRefNum);
    if(!clientPtr)
        {
        result = 1;
        goto goHome;
        }
 
    es->waitingToWrite ++;
 
    for(i = 0; i < kEasyPPCWritePacketCount; i++)
        {
        ewpb = &es->wpbr[i];
        if(!ewpb->used)
            goto gotOne;
        }
 
    result = 1;             /* this should never happen! */
    Debugger();
    goto goHome;
    
gotOne:
    ewpb->used = 1;
 
    if(easyManage)
        {
        ewpb->buffer = NewPtr(length);
        if(!ewpb->buffer)
            DebugStr("\pCouldn't allocate ppc buffer copy! Whoa!");
        BlockMove(buffer,ewpb->buffer,length);
        buffer = ewpb->buffer;
        }
    else
        ewpb->buffer = 0;
 
    wpb = &ewpb->wpbr;
 
    wpb->ioCompletion = 0;
    wpb->sessRefNum = clientPtr->sessRefNum;
    wpb->bufferLength = length;
    wpb->bufferPtr = buffer;
    wpb->more = 0;
    wpb->userData = length;
    wpb->blockCreator = 0;
    wpb->blockType = packetType;
 
    result = PPCWriteAsync(wpb);
goHome:
    return result;
    }
 
 
OSErr ReadEasyPPCSession(EasyPPCSession es,long sessRefNum,void *buffer,long length)
    {
    OSErr result;
    EasyPPCClient *clientPtr;
 
    if(!es)
        {
        result = 1;
        goto goHome;
        }
 
    clientPtr = FindClient(es,sessRefNum);
 
    if(!clientPtr || clientPtr->waitingToReadLength || clientPtr->waitingToReadData)
        {
        result = 1;
        goto goHome;
        }
 
    clientPtr->waitingToReadData = 1;
 
    *(long *)buffer = clientPtr->tinyBuffer;
 
    if(clientPtr->waitingDataSize > 4)
        {
        clientPtr->rpbr.ioCompletion = 0;
        clientPtr->rpbr.sessRefNum = clientPtr->sessRefNum;
        clientPtr->rpbr.bufferLength = length - 4;
        clientPtr->rpbr.bufferPtr = (void *) ( (long)buffer + 4 );
 
        if(clientPtr->rpbr.bufferLength < 0)
            clientPtr->rpbr.bufferLength = 0;
 
        result = PPCReadAsync(&clientPtr->rpbr);
        }
goHome:
    return result;
    }
 
 
/* ---------------------------------------------------------- */
 
 
void PollEasyPPCClient(EasyPPCSession es,EasyPPCClient *clientPtr,
        EasyPPCPollResult *pollResult)
    {
    /*
     * Should we start up the read process?
     */
    if(!clientPtr->waitingToReadLength && !clientPtr->waitingToReadData &&
            clientPtr->sessRefNum)      /* neither? */
        {
        clientPtr->waitingToReadLength = 1;
 
        clientPtr->rpbr.ioCompletion = 0;
        clientPtr->rpbr.sessRefNum = clientPtr->sessRefNum;
        clientPtr->rpbr.bufferLength = 4;
        clientPtr->rpbr.bufferPtr = (void *)&clientPtr->tinyBuffer;
 
        pollResult->error = PPCReadAsync(&clientPtr->rpbr);
        if(pollResult->error)
            {
            pollResult->operation = 1;
            pollResult->sessRefNum = clientPtr->sessRefNum;
            pollResult->length = 0;
            pollResult->packetType = 0;
            pollResult->buffer = 0;
            goto goHome;
            }
        }
 
    /*
     * Received a length datum?
     */
    if(clientPtr->waitingToReadLength && clientPtr->rpbr.ioResult != 1)
        {
        clientPtr->waitingToReadLength = 0;
        clientPtr->waitingDataSize = clientPtr->rpbr.userData;
 
        pollResult->operation = easyPPCSessionMessageWaiting;
        pollResult->error = clientPtr->rpbr.ioResult;
        pollResult->sessRefNum = clientPtr->rpbr.sessRefNum;
        pollResult->length = clientPtr->rpbr.userData;
        pollResult->packetType = clientPtr->rpbr.blockType;
        pollResult->buffer = 0;
        goto goHome;
        }
 
    /*
     * Received a message?
     */
    if(clientPtr->waitingToReadData &&
            (clientPtr->waitingDataSize < 4 || clientPtr->rpbr.ioResult != 1) )
        {
        clientPtr->waitingToReadData = 0;
        clientPtr->waitingDataSize = 0;
 
        pollResult->operation = easyPPCSessionMessageRead;
        pollResult->error = clientPtr->rpbr.ioResult;
        pollResult->sessRefNum = clientPtr->rpbr.sessRefNum;
        pollResult->length = clientPtr->rpbr.userData;
        pollResult->packetType = clientPtr->rpbr.blockType;
        pollResult->buffer = clientPtr->rpbr.bufferPtr;
 
        if(clientPtr->rpbr.more && !pollResult->error)      /* don't handle part-reads very well. */
            Debugger();
        goto goHome;
        }
 
    /*
     * Someone connect to us?
     */
    if(clientPtr->waitingToConnect && clientPtr->ipbr.ioResult != 1)
        {
        if(clientPtr->ipbr.userData != testConnection)
            {
            clientPtr->waitingToConnect = 0;
            clientPtr->sessRefNum = clientPtr->ipbr.sessRefNum;
    
            pollResult->operation = easyPPCSessionJustConnected;
            pollResult->error = clientPtr->ipbr.ioResult;
            pollResult->sessRefNum = clientPtr->ipbr.sessRefNum;
            pollResult->length = 0;
            pollResult->packetType = 0;
            pollResult->buffer = 0;
            goto goHome;
            }
        }
 
    /*
     * No activity?
     */
    pollResult->operation = easyPPCSessionIdle;
    pollResult->error = 0;
    pollResult->sessRefNum = 0;
    pollResult->length = 0;
    pollResult->packetType = 0;
    pollResult->buffer = 0;
 
goHome:
    HandleSomeErrors(es,pollResult);
    }
 
 
/* ---------------------------------------------------------- */
 
 
void PollEasyPPCSession(EasyPPCSession es,EasyPPCPollResult *pollResult)
    {
    unsigned short i,j;
    EasyPPCWritePB *ewpb;
    PPCWritePBRec *wpb;
    EasyPPCClient *clientPtr;
 
    es->fairSeed++;
    if(es->fairSeed & 1)
        goto checkOutgoing;
 
    /*
     * Every poll, switch which 
     * client gets checked first.
     */
    es->fairClient++;
    j = es->fairClient;
 
    for(i = 0; i < kEasyPPCClientCount; i++)
        {
        clientPtr = &es->client[ (i + j) % kEasyPPCClientCount];
 
        if(clientPtr->used)
            {
            PollEasyPPCClient(es,clientPtr,pollResult);
            if(pollResult->operation != easyPPCSessionIdle)
                goto goHome;
            }
        }
 
 
checkOutgoing:
    /*
     * Sent a message?
     */
    if(es->waitingToWrite)
        {
        es->fairWrite ++;
        j = es->fairWrite + TickCount();
        for(i = 0; i < kEasyPPCWritePacketCount; i++)
            {
            ewpb = &es->wpbr[(i + j) % kEasyPPCWritePacketCount];
            wpb = &ewpb->wpbr;
            if(ewpb->used && wpb->ioResult != 1)
                {
                es->waitingToWrite --;
                ewpb->used = 0;
 
 
                pollResult->operation = easyPPCSessionMessageWritten;
                pollResult->error = wpb->ioResult;
                pollResult->sessRefNum = wpb->sessRefNum;
                pollResult->packetType = wpb->blockType;
                pollResult->length = wpb->userData;
 
                if(ewpb->buffer)
                    {
                    DisposePtr(ewpb->buffer);       /* easymanage */
                    pollResult->buffer = 0;
                    }
                else
                    pollResult->buffer = wpb->bufferPtr;                    
 
                goto goHome;
                }
            }
        }
 
    pollResult->operation = easyPPCSessionIdle;
    pollResult->error = 0;
    pollResult->sessRefNum = 0;
    pollResult->length = 0;
    pollResult->packetType = 0;
    pollResult->buffer = 0;
 
goHome:
    HandleSomeErrors(es,pollResult);
    }
 
void HandleSomeErrors(EasyPPCSession es,EasyPPCPollResult *pollResult)
/*
 * Handle some types of PPC errors, and modify
 * the result accordingly.
 */
    {
    EasyPPCClient *clientPtr;
 
    switch(pollResult->error)
        {
        case sessClosedErr:
        case noSessionErr:
            pollResult->error = easyPPCSessionOtherPortGone;
            /*
             * There may be no client, if we're just
             * clearing out the stale outgoing-messages.
             */
            clientPtr = FindClient(es,pollResult->sessRefNum);
            if(clientPtr)
                RemoveClientEntry(es,clientPtr);
            break;
        default:
            if(pollResult->error < 0)
                Debugger();
            break;
        }
    }
 
/* ---------------------------------------------------------- */
 
OSErr BrowseAndConnectEasyPPCSession(EasyPPCSession es,
        StringPtr prompt, StringPtr applListLabel,long *sessRefNum,
        LocationNameRec *location,PortInfoRec *portInfo)
    {
    OSErr thisError;
 
    if(es->connected >= kEasyPPCClientCount)        /* enough connections? */
        {
        thisError = easyPPCSessionTooManyConnections;
        goto goHome;
        }
 
    gEasyFilterSession = es;            /* great, pass arguments in global */
 
    thisError = PPCBrowser(prompt,
            applListLabel,
            gEverBrowsed,
            &gTheLocation,
            &gThePortInfo,
            EasyBrowserFilter,
            0);
 
    gEverBrowsed = true;
 
    if(thisError)
        goto goHome;
 
    thisError = ConnectEasyPPCSession(es,&gTheLocation,&gThePortInfo,sessRefNum);
 
    if(location)
        *location = gTheLocation;
    if(portInfo)
        *portInfo = gThePortInfo;
 
goHome:
    return thisError;
    }
 
pascal Boolean EasyBrowserFilter (LocationNamePtr location, PortInfoPtr port)
/*
 * If the port has the same type as us, and our servertype is our own type,
 * then guest access must be enabled, and a test connection is attempted.
 *
 * If the servertype is different, and we find one of that kind, then a
 * connection is attempted, but, if "guest not allowed" is returned, we
 * assume that it's okay to try, later, with the password, and allow it in
 * the browser.
 */
    {
    OSErr thisError;
 
    if (port->name.u.port.creator == kEasyPPCPortCreator
            && port->name.u.port.type == gEasyFilterSession->serverType)
        {
        /*
         * Try to connect to it, to see if it's available
         */
        PPCStartPBRec spbr;
        PPCEndPBRec epbr;
 
        spbr.ioCompletion = 0;
        spbr.portRefNum = gEasyFilterSession->portRefNum;
        spbr.serviceType = ppcServiceRealTime;
        spbr.resFlag = 0;
        spbr.portName = &port->name;
        spbr.locationName = location;
        spbr.userData = testConnection;
        spbr.userRefNum = 0;
 
        thisError = PPCStartSync(&spbr);
        if(!thisError)                      /* shouldn't accept, but... */
            {
            epbr.ioCompletion = 0;
            epbr.sessRefNum = spbr.sessRefNum;
            PPCEndSync(&epbr);
            return false;
            }
        else if(thisError == guestNotAllowedErr)
            return true;
        else
            {
            if(thisError == userRejectErr && spbr.rejectInfo == testConnection)
                return true;
            else
                return false;
            }
        }
    else
        return false;
    }
 
 
/* ---------------------------------------------------------- */
void FindNamesEasyPPCSession(EasyPPCSession es,long sessRefNum,
        StringPtr zoneName,StringPtr macName,StringPtr portName)
/*
 * Return the names of the Macintosh we're connected to.
 */
    {
    EasyPPCClient *clientPtr;
    StringPtr s;
 
    clientPtr = FindClient(es,sessRefNum);
    if(clientPtr)
        {
        if(zoneName)
            {
            s = clientPtr->locationName.u.nbpEntity.zoneStr;
            if(s[0] && clientPtr->locationName.locationKindSelector
                    == ppcNBPLocation)
                MoveString(s,zoneName);
            else
                MoveString("\p<Local>",zoneName);
            }
 
        if(macName)
            {
            s = clientPtr->locationName.u.nbpEntity.objStr;
            if(s[0] && clientPtr->locationName.locationKindSelector
                    == ppcNBPLocation)
                MoveString(s,macName);
            else
                MoveString("\p<This Macintosh>",macName);
            }
 
        if(portName)
            MoveString(clientPtr->portName.name,portName);
        }
    else
        {
        if(zoneName)
            zoneName[0] = 0;
        if(macName)
            macName[0] = 0;
        if(portName)
            portName[0] = 0;
        }
    }
 
 
 
/* ---------------------------------------------------------- */
 
void InitializeClient(EasyPPCSession es,EasyPPCClient *clientPtr)
/*
 * Initialize just this client, as if it's
 * just sitting there.
 */
    {
    #pragma unused (es)
 
    clientPtr->sessRefNum = 0;
 
    clientPtr->waitingToConnect = 0;
    clientPtr->waitingToReadData = 0;
    clientPtr->waitingToReadLength = 0;
    }
 
/* ---------------------------------------------------------- */
 
EasyPPCClient *FindClient(EasyPPCSession es,long sessRefNum)
/*
 * Do a linear search of the EasyPPCSession's
 * clients, and return a pointer to the
 * client specified.
 *
 * sessRefNum nil==0 matches to the first client.
 * So, an app that only has one connection need not remember
 * its refNum.
 *
 * If there isn't a match, return nil.
 */
    {
    short i;
    EasyPPCClient *w;
 
    if(!es->connected)
        {
        w = 0;
        goto goHome;
        }
 
    if(sessRefNum == 0)
        {
        w = &es->client[0];
        if(!w->sessRefNum)      /* Not a good choice, if not really connected */
            w = 0;
        goto goHome;
        }
 
    for(i = 0; i < kEasyPPCClientCount; i++)
        {
        w = &es->client[i];
        if(w->used && w->sessRefNum == sessRefNum)
            goto goHome;
        }
    w = 0;
 
goHome:
    return w;
    }
 
 
/* ---------------------------------------------------------- */
 
void RemoveClientEntry(EasyPPCSession es,EasyPPCClient *clientPtr)
/*
 * Remove client number 'n' from the list.
 * Assume the list is locked, already.
 * Also, unmark any outgoing packets for that session.
 */
    {
    /*
     * Remove any waiting outgoing packets
     */
 
    clientPtr->used = 0;
    es->connected --;
    }
 
 
/* ---------------------------------------------------------- */
 
EasyPPCClient *FindFreeClient(EasyPPCSession es)
    {
    short i;
    EasyPPCClient *clientPtr;
 
    for(i = 0; i < kEasyPPCClientCount; i++)
        {
        clientPtr = &es->client[i];
 
        if(!clientPtr->used)
            {
            clientPtr->used = 1;
            es->connected ++;
            goto goHome;
            }
        }
 
    clientPtr = 0;
    Debugger(); //!!! should _never_ happen.
goHome:
    return clientPtr;
    }
 
/* ---------------------------------------------------------- */
 
 
void SetServerTypeEasyPPCSession(EasyPPCSession es,OSType serverType)
    {
    es->serverType = serverType;
    }