PAPServerSample.c

/*
 
    file: PAPServerSample.c
    
    This sample demonstrates the implementation of a PAP server using Open Transport.
    
    Issues:
    1. PostScript Query responses are default only
    To respond to the LaserWriter client, the PostScript query handler code is 
    designed to return the default responses.  A real PAP spooler would have to
    respond more appropriately for the printer that it is supporting.  This could mean
    delaying the response back to the client until the spooler can actually
    pass the query to the printer and get a real response.  The code for detecting
    a PostScript query is not very robust.  The purpose of this sample was not to
    demonstrate PostScript support, but to packet flow via OT.
    
    2. If the received data is not part of a PostScript query, the spooler sample
    assumes that all of the incoming data is part of the same job.  There is no check
    for the PostScript EOF indicator to distinguish different spooler jobs.  All of the 
    incoming data is saved to a temporary disk file at the root of the boot hard drive.
    The code does not try to preflight the amount of storage present, nor protect  
    some minimum hard drive storage in case the hard drive is close to full.
    
    3.  This sample requires OT 1.1.2 or greater.
    
    4. This sample support multiple handoff endpoints.  The constant kMaxHandoffEPs
    defines the maximum number of handoff endpoints that this sample supports.  I have
    tested that the server supports 2 endpoint connections at the same time, but have not
    gone into testing more simultaneous connections.    
    
    Change history
    RRK 5/28/99 modified code to look for eof and respond to client with an empty packet with
        the eof bit set.
    
    RRK 3/9/99 add new code to protect against multiple unbind calls on an endpoint
    *   Renamed DoDisconnect to DoRcvDisconnect and implemented
    *   Implemented DoSndDisconnect
    *   Check for errors in hanlder
    *   implemented OTLookHandler function
    *   implemented the ActivateHandoffEndpoint function where the handoff endpoint is initialized, and bound
    *   fixed bug to OTRcvDisconnect where we were passing unit'd &discon parameters.
    *   set flags before making OT calls instead of after the call success to fix timing issues.
    
    by:     Rich Kubota
            Developer Technical Support
            
*/
 
#define  qDebug             1
#define  HANDOFF_EP         1   // set to to to indicate use of handoff endpoint
 
#include <Types.h>
#include <Errors.h>
#include <String.h>
#include <ToolUtils.h>
#include "StringUtils.h"
#include <AppleTalk.h>
#include <DiskInit.h>
#include <TextUtils.h>
#include <Gestalt.h>
#include <Files.h>
#include <NumberFormatting.h>
#include "OpenTransport.h"
#include "OpenTptAppleTalk.h"
#include "ATalkSampleUtils.h"
#include "PAPServerUtilities.h"
#include "PAPServerSample.h"
#include "PAPPostScriptStuff.h"
#include "OTDebug.h"
#include "StrOpts.h"
 
extern  OSStatus OTSetMemoryLimits(size_t growSize, size_t maxSize);
 
/********************/
/*  globals         */
/********************/
DDPAddress      gAddr;
MyEndpointRef   gEp;
 
#if HANDOFF_EP
MyEndpointRef   gHandoffEp[kMaxHandoffEPs];
#endif
 
OTLIFO*         gFreeQ;         // note that an OTLIFO structure must be on a 4-byte boundary
                                // instead of trying to force it to be on a 4-byte boundary in
                                // global memory, we use a pointer, then allocate memory
                                // which will force the global to be on a 4-byte boundary.
UInt32          gFlags  = 0;
UInt32          gBufsAvail;
PacketPtr       gTempPackPtr;
short           gFRefNumToClose = 0;
Boolean         gDone   = false;
Str255          gIdleStr;
Str255          gBusyStr;
Str255          gServerNBPNameStr;
UInt32          gOptionCompleted; // used to flag completion of Option Management calls
extern char     gEOFStr[8];
extern char     gBeginPSStr[8];
extern char     qBeginQueryStr[8];
extern char     gEndStr[8];
extern char     gQueryStr[8];
 
OSType          gOTVersionSelector  = 'otvr';
UInt32          gOTVersion;
 
 
/*******************************************************************************
** Prototypes
********************************************************************************/
 
pascal  void    EventHandler(void*, OTEventCode, OTResult, void*);
OSStatus        HandleOTLookErr(MyEndpointRef *myEp);
static void     EnterListenAccept(MyEndpointRef *myServerEp);
void            DoListenAccept(MyEndpointRef *myServerEp);
Boolean         ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr);
void            DoReceiveData(MyEndpointRef *myEp);
OSStatus        DoSndDisconnect(MyEndpointRef *myEp);
OSStatus        DoDisconnect(MyEndpointRef *myEp);
OSStatus        DoRcvDisconnect(MyEndpointRef *myEp);
OSStatus        DoRcvOrderlyDisconnect(MyEndpointRef *myEp);
OSStatus        PollQueueList(void);
OSErr           ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr);
Boolean         TestDataIsPSQuery(PacketPtr packetPtr);
OSStatus        ProcessPSQuery(PacketPtr packetPtr);
Boolean         DoProcessPSQuery(PacketPtr packetPtr);
OSErr           CheckFileToClose(void);
void            DoServer(void);
void            DoEvent(EventRecord *event);
OSStatus        DoOTUnbind(MyEndpointRef *theEp);
OSStatus        DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, 
                        char *nbpName, UInt32 nbpNameLen);
OSStatus        StartAbortiveDisconnect(MyEndpointRef *myEp);
OSStatus        ActivatePAPEndpoint(MyEndpointRef *myEp);
OSStatus        ActivateHandoffEndpoint(MyEndpointRef *myEp);
OSStatus        InitPAPServerStuff(void);
void            ClosePAPServerStuff(void);
OSStatus            InitMyEndpointRef(MyEndpointRef *myEp);
OSStatus        InitPAPBuffers(void);
void            ReleasePAPMemory(void);
extern OSStatus DoNegotiateEOMOption(EndpointRef ep, Boolean enableEOM);
extern OSStatus DoSetServerStatusOption(EndpointRef ep, char *statusStr);
extern OSStatus DoNegotiateSelfSendOption(EndpointRef ep, long enableSelfSend);
void            DoValueBreak(long value, const char* message);
OSStatus        BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr);
UInt32          GetYesNoOption(void);
Boolean         EndpointsAllBusy(void);
#if HANDOFF_EP
MyEndpointRef*  FindFreeHandoffEp(void);
#endif
 
 
static pascal void OTDebugStr(const char *msg)
{
    UInt8 pMsg[256];
    
    pMsg[0] = OTStrLength(msg);
    OTMemcpy(&pMsg[1], msg, pMsg[0]);
    DebugStr(pMsg);
}
 
 
/*******************************************************************************
** EventHandler
**  The event handler can be called at times when it is not safe to do console I/O,
**  so this routine takes the event and jams it on a list to be handled when
**  we know it's safe to print informative messages
********************************************************************************/
 
pascal void EventHandler(void* contextPtr, OTEventCode code,
                                        OTResult result, void* cookie)
{
#pragma unused (cookie)
    
    
    switch (code)
    {
        case T_LISTEN:              /* An connection request is available   */
            SetListenPendFlag(((MyEndpointRef*)contextPtr)->flags);
            DoListenAccept(contextPtr);
            break;
            
        //  T_DATA:
        //
        //  The main rule for processing T_DATA's is to remember that once you have
        //  a T_DATA, you won't get another one until you have read to a kOTNoDataErr.
        //  The advanced rule is to remember that you could get another T_DATA
        //  during an OTRcv() which will eventually return kOTNoDataErr, presenting
        //  the application with a synchronization issue to be most careful about.
        //  
        //  In this application, since an OTRcv() calls are made from inside the notifier,
        //  this particular synchronization issue doesn't become a problem.
        //
        case T_DATA:                /* Standard data is available           */
                                    // check that we are in the DATAXFER state
            SetHasDataFlag(((MyEndpointRef*)contextPtr)->flags);
            if (TstPassconFlag(((MyEndpointRef*)contextPtr)->flags))
                DoReceiveData(contextPtr);
            break;
 
        case T_DISCONNECT:          /* A disconnect is available            */
                // check whether there is a file open on this endpoint
            DoRcvDisconnect(contextPtr);
            break;
            
        case T_ERROR:               /* obsolete/unused in library           */
        case T_UDERR:               /* A Unit Data Error has occurred       */
            OTDebugStr((const char *)"T_ERROR called;g");
            break;
 
        case T_ORDREL:              /* An orderly release is available      */
            DoRcvOrderlyDisconnect(contextPtr);
            break;
 
        case T_GODATA:              /* Flow control lifted on standard data */
                                    // for this sample, I do not expect this event to occur.
            OTDebugStr((const char *)"T_GODATA called;g");
            break;
 
        case T_GOEXDATA:            /* Flow control lifted on expedited data*/
                                    // for this sample, I do not expect this event to occur.
            OTDebugStr((const char *)"T_GOEXDATA called;g");
            break;
 
        case T_PASSCON:             /* State is now case T_DATAXFER         */
                    // set the passcon flag
                // set flag to indicate that the endpoint is busy
            SetEPBusyFlag(((MyEndpointRef*)contextPtr)->flags);
                // Set flag to indicate that the endpoint is bound
                // RRK addition 3/10/99
            SetEPBoundFlag(((MyEndpointRef*)contextPtr)->flags);
                // set flag to tell system to set server status flag appropriately
                // for multiple handoff endpoint servers, this would need to be
                // handled differently.
            SetCheckOptFlag(gFlags);
 
            SetFirstPktFlag(((MyEndpointRef*)contextPtr)->flags);
 
            SetPassconFlag(((MyEndpointRef*)contextPtr)->flags);
 
                        // Set the timer for this connection
            OTGetTimeStamp(&((MyEndpointRef*)contextPtr)->timerDog);
 
                    // check to see if there is any data to process
            DoReceiveData(contextPtr);
            break;
            
        case T_RESET:               /* Protocol has been reset              */
            OTDebugStr((const char *)"T_RESET called;g");
            break;
 
        case T_ACCEPTCOMPLETE:      /* Accept call is complete              */
            OTDebugStr((const char *)"T_ACCEPTCOMPLETE called;g");
            ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags);
            SetFirstPktFlag(((MyEndpointRef*)contextPtr)->flags);
            
                // RRK 3/9/99
                // check for an error result
            if (result != kOTNoError)
            {
                OTDebugStr("Error occured with T_ACCEPTCOMPLETE event");
                if (result == kOTLookErr )
                {
                    HandleOTLookErr(contextPtr);
                }
            }
            break;
 
        case T_DISCONNECTCOMPLETE:  /* Disconnect call is complete          */
            OTDebugStr("T_DISCONNECTCOMPLETE event occurred;g");
            ClrDisconPendFlag(((MyEndpointRef*)contextPtr)->flags); // is the server endpoint bound
            ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags);
            ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags);
            ClrListenPendFlag(((MyEndpointRef*)contextPtr)->flags);
 
            break;
        
        case T_UNBINDCOMPLETE:                                      // RRK 3/9/99 add code to protect against multiple unbind calls
//          OTDebugStr((const char *)"T_UNBINDCOMPLETE called;g");
            DoValueBreak((long)contextPtr, "T_UNBINDCOMPLETE called #");
            SetCheckOptFlag(gFlags);                                // set flag to force the server to determine
                                                                    // whether to change the server message
            ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags);     // no longer waiting for T_UNBINDCOMPLETE message
            ClrUnbindPendFlag(((MyEndpointRef*)contextPtr)->flags);
            ClrEPBoundFlag(((MyEndpointRef*)contextPtr)->flags);    // is the server endpoint bound
 
            SetStatusIdleFlag(gFlags);
            break;
            
        //
        //  kStreamIoctlEvent:
        //
        //  This event is returned when an I_FLUSH ioctl has completed.
        //  The flush was done in an attempt to get back all T_MEMORYRELEASED events
        //  for outstanding OTSnd() calls with Ack Sends.   Errors are ignored at this point since it is
        //  possible that the connection will already be gone, etc.
        //
        case kStreamIoctlEvent:     // the flush is complete
            DoSndDisconnect (contextPtr); /* safe to disconnect */
            break;
        
        case T_OPTMGMTCOMPLETE:
            if (result != kOTNoError)
            {
                OTDebugStr("Error occured with T_OPTMGMTCOMPLETE event");
            }
            
            gOptionCompleted = 1;
            break;
 
        default:
            break;
    }
}
 
/***********************************************************
    RRK 3/9/99 addition
    HandleOTLookErr implemented to handle OTLookErr in a
    central place - only call this function in response
    to a OTLookErr for a function call
    
************************************************************/
OSStatus HandleOTLookErr(MyEndpointRef *myEp)
{
    OSStatus        err = kOTNoError;
    OTResult        result;
    
    result = OTLook(myEp->ep);
    
    switch (result)
    {
        case T_DISCONNECT:
            err = DoRcvDisconnect(myEp);
            if (err == kOTNoError)
            {
                ClrInPSQueryFlag(myEp->flags);
            }
            else
            {
                DoValueBreak(err, "Error calling DoRcvDisconnect from HandleOTLookErr #");
                SetForceCloseFlag(myEp->flags);
            }
 
            break;
        
        case T_ORDREL:
            err = DoRcvOrderlyDisconnect(myEp);
                // if successful, then we enter T_INREL state
            if (err != kOTNoError)
            {
                DoValueBreak(err, "Error calling DoRcvOrderlyDisconnect from HandleOTLookErr #");
                SetForceCloseFlag(myEp->flags);
            }
            break;
            
        
        default:
                // The use of this function in this instance is to handle
                // T_DISCONNECT events that can happen.  This call will
                // need to be customized for other use, like handling 
                // T_DATA events which is not implemented in this sample.
                // Pass a debugStr call to show what event was not handled here
            DoValueBreak((long)result, "HandleOTLookErr passed unprocessed event - #");
            
            break;
        
    }
    
    return err;
}
 
//
//  EnterListenAccept
//
//  This is a front end to DoListenAccept() which is used whenever 
//  it is not being called from inside the listener endpoint's notifier.
//  We do this for syncrhonization.   If we were processing an OTListen()
//  or an OTAccept() and we were interrupted at the listener endpoint's
//  notifier with a T_LISTEN, etc, it would be inconvenient and would require
//  some more sophisticated synchronization code to deal with the problem.
//  The easy way to avoid this is to do an OTEnterNotifier() on the listener's
//  endpoint.   
//
//  Important note - doing OTEnterNotifier on one endpoint only prevents that
//  endpoint's notifier for interrupting us.   Since the same notifier code
//  is used for lots of endpoints here, remember that the one endpoint's 
//  notifier can interrupt another.   Doing an OTEnterNotifier() on the
//  listener endpoint prevents the listener from interrupting us, but it
//  does not prevent the Notifier() routine from interrupting us via 
//  another endpoint which also uses the same routine.
//
//  Important note #2 - Don't ever do an OTEnterNotifier on an acceptor endpoint
//  before doing the OTAccept().   This confuses OT and creates problems.
//
static void EnterListenAccept(MyEndpointRef *myServerEp)
{
    Boolean doLeave;
    
    doLeave = OTEnterNotifier(myServerEp->ep);
    DoListenAccept(myServerEp);
    if (doLeave)
        OTLeaveNotifier(myServerEp->ep);
}
 
/*
    DoListenAccept is designed to handle an incoming connect request.  This module demonstrates a
    very complex task that all X/OPEN style acceptors must implement.  The problem stems from
    fact that an endpoint may be hit by a number of simultaneous connect requests.  In addition
    if there is a connection already established and the endpoint handles multiple
    handoff endpoints, then there could also be an incoming disconnect request to process before
    one can issue either an Accept or Disconnect response, all pending Connect requests must
    be consumed from the streamhead first.  This places a burden on the accept routine to have
    a place to temporarily stash all of the addresses to which discon messages will have to be
    sent.
    
    Refer to Tech Note 1059 for the 8 steps to handling an incoming connection request.
    
    Note that once all of the connect request have been consumed and we are in the process of
    sending discon responses, it could be that there will again be an kOTLookErr, which
    indicates that another incoming connect request heeds to be consumed before
    resuming with the discon responses.
    
    In this sample we consume the first listen request.  We then check whether we can accept
    the incoming request, If so, then call OTAccept.  If OTAccept fails with a lookErr, then 
    there is another connect request to be consumed.
    
    processed.
    
*/
void DoListenAccept(MyEndpointRef *myServerEp)
{
    MyEndpointRef   *myAcceptEp;
    OSStatus    err;
    TCall       call;
    Boolean     done = false;
    
        // check we have already entered DoListenAccept from the main event loop and are 
        // trying to do so again from the secondary interrupt
    if (OTAtomicSetBit(&myServerEp->semaphore, kInListenLoop))
        return; // if the bit was previous set, then we already have entered, but not exitted
                // this proc from the main event loop
 
    if (TstAcceptPendFlag(myServerEp->flags))
    {
            // we're waiting for an Accept call to complete, so don't accept a new
            // connection request as we will get an out of state error
            // set the ListenPendFlag so we will check the server endpoint later
        
        SetListenPendFlag(myServerEp->flags);
        done = true;
    }
    else
        ClrListenPendFlag(myServerEp->flags);
    
    if (done == false)
    {
            // clear out the TCall structure
        memset(&call, 0, sizeof(TCall));
        
            // set up the TCall structure
        call.addr.maxlen = sizeof(struct DDPAddress);
        call.addr.len = sizeof(struct DDPAddress);
        call.addr.buf = (unsigned char *) &gAddr;
                
        if (OTIsSynchronous(myServerEp->ep) == true)
            OTSetAsynchronous(myServerEp->ep);
            
            // step 1 for handling an incoming connection request
        err = OTListen(myServerEp->ep, &call);
        
        if (err == kOTNoDataErr)
        {
            OTDebugStr((const char*)"kOTNoDataErr returned by OTListen");
                // don't need to do anything
        }
        else if (err == kOTLookErr)
        {
                // step 2 for handling incoming connection requests
                // handle disconnect indications for a pending connection request
                // the only look indication allowed on an OTListen call is
                // T_DISCONNECT
            HandleOTLookErr(myServerEp);
            done = true;
             
        }
        else if (err != kOTNoError) // there was an unknown error in doing the listen
        {
            DoValueBreak((long)err, "error occured on OTListen #");
            done = true;
 
        }
    }
    
    if (done == false)
    {
#if HANDOFF_EP
        myAcceptEp = FindFreeHandoffEp();
#else
        if (TstEPBusyFlag(myServerEp.flags) == false)
            myAcceptEp = myServerEp;
        else
            myAcceptEp = nil;
#endif       
    }
    
    if (myAcceptEp == nil)
    {
            OTDebugStr((const char *)"rejecting T_LISTEN - no free handoff endpoints");
            // if there are no available handoff endpoints, then send the disconnect
        OTSndDisconnect(myServerEp->ep, &call);
        done = true;
    }
    
    if (done == false)
    {
    
            // set a flag to indicate that an accept call is pending
            // if we try to accept another connection before this
            // accept call is completed, an out of state error results 
            // need to do this before we make the OTAccept call
        SetAcceptPendFlag(myServerEp->flags);
        
            // make the accept call and handoff the connection
        err = OTAccept(myServerEp->ep, myAcceptEp->ep, &call);
 
        if (err == kOTNoError)
        {
            SetEPBusyFlag(myAcceptEp->flags);
 
                // set flag to have server check the serverstatus
            TstCheckOptFlag(gFlags);
                        
        }
        else if (err == kOTLookErr)
        {
            HandleOTLookErr(myServerEp);
        }
        else
        {
#if HANDOFF_EP
            DoValueBreak((long)err, "error occured on OTAccept for handoff #");
#else
            DoValueBreak((long)err, "error occured on OTAccept #");
#endif
 
        }
    }
    
        
    OTAtomicClearBit(&myServerEp->semaphore, kInListenLoop);    // clear the bit that indicates we're in this loop
    
}
 
/*******************************************************************************
** ReceiveOnePacket - makes the call to receive just one packet
** If there is data, then the data is placed into the packetPtr
** and the incoming byte count is incremented
** if the kOTNoDataErr occurs, then the HasDataFlag is cleared.
** 
**   
********************************************************************************/
Boolean ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr)
{
    OTResult    result;
    Boolean     done = false;
                    
    result = OTRcv(myEp->ep, &packetPtr->data, kPAPDataSize, &packetPtr->flags);
    if (result < 0)
    {
            // requeue the buffer to the freeq
        OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
        done = true;
        
        if (result == kOTLookErr)
        {
            HandleOTLookErr(myEp->ep);
            
        }
        else if (result == kOTOutStateErr)
        {
                // client could have issued a disconnect call while this call was about to
                // to be processed
            ClrHasDataFlag(myEp->flags);
        }
        
        else if (result == kOTNoDataErr)
        {
                // no more data so clr the flag
            ClrHasDataFlag(myEp->flags);
        }
        else 
        {
            DoValueBreak(result, "Unknown error occurred in OTRcv #");
        }
    }
    else if (result == 0)
    {
        OTDebugStr((const char *)"OTRcv got nothing ;g");
        
        if (TstInPSQueryFlag(myEp->flags))  // check if the endpoint is already processing a postscript
        {
            OTDebugStr((const char *)"0 byte packet while processing ps query ;g");
 
            if (packetPtr->flags & T_MORE == 0)
                OTDebugStr((const char *)"T_MORE was not set;g");
            else
                OTDebugStr((const char *)"T_MORE was set;g");
                
                // send an empty response with EOF flags set since it appears that this is what the Laserwriter 
                // client wants to see. At this point, we have already processed any preceding 
                // ps queries.  It is important to not respond with an empty packet and EOF bit set if
                // there is still a ps query response outstanding.
            packetPtr->theEp = myEp;
            SendEmptyPacket(packetPtr);
                // clear bit that indicates we are processing a postscript query
            ClrInPSQueryFlag(myEp->flags);
    
            OTDebugStr((const char *)"Have finished ps query;g");
            
                // queue the buffer to the freeQ associated with the endpoint
            OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
        }
        else
        {
            
                // pass this packet on to the event loop so that we can send a null response
                // save the endpoint ref associated with this data.
                // While we could send the empty packet here, this example waits until the
                // data is processed in the main event loop to send the null packet.
            packetPtr->theEp = myEp;
            
                        // save the amount of data read 
            packetPtr->numBytes = result;
            
                        // intialize the lastPos field
            packetPtr->lastPos = 0;
            
                        // get the timestamp for this packet
            OTGetTimeStamp(&packetPtr->timeStamp);
            
                        // Set the timer for this connection
            OTGetTimeStamp(&myEp->timerDog);
 
                // queue the buffer to the usedQ associated with the endpoint
            OTLIFOEnqueue(myEp->usedQ, &(packetPtr->fLink));
        }
            
        
    }
    else
    {
            // check if this is the first packet for this endpoint and set the time in timestamp
        if (TstFirstPktFlag(myEp->flags))
        {
            ClrFirstPktFlag(myEp->flags);
                
                // set the time Data In timestamp field
            OTGetTimeStamp(&myEp->timeDataIn);
            
                // zero out the numByteIn field
            myEp->numBytesIn = 0;
            
        }
            // save the number of bytes read
        myEp->numBytesIn += result;
 
            // set the time data end timestamp field
        OTGetTimeStamp(&myEp->timeDataEnd);
 
        if (TstdumpAllPktsFlag(gFlags))
        {
            OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
        }
        else
        {
            
                        // save the endpoint ref associated with this data
            packetPtr->theEp = myEp;
            
                        // save the amount of data read 
            packetPtr->numBytes = result;
            
                        // intialize the lastPos field
            packetPtr->lastPos = 0;
            
                        // get the timestamp for this packet
            OTGetTimeStamp(&packetPtr->timeStamp);
            
                        // Set the timer for this connection
            OTGetTimeStamp(&myEp->timerDog);
            
            if (!DoProcessPSQuery(packetPtr))
            {
                    // packet is not a postscript query so queue to the endpoint usedQ
                    // queue the buffer to the usedQ associated with the endpoint
                OTLIFOEnqueue(myEp->usedQ, &(packetPtr->fLink));
                
            }
        
        }
 
    }
    
    return done;
        
}
 
 
/*******************************************************************************
** DoReceiveData
**   
********************************************************************************/
void DoReceiveData(MyEndpointRef *myEp)
{
    PacketPtr   packetPtr;
    OTFlags     flags = 0;
    Boolean     done;
    
        // check we have already entered DoReceiveData from the main event loop
        // or from the event handler and are 
        // trying to do so again a second time
    if (OTAtomicSetBit(&myEp->semaphore, kInRcvDataFlag))
        return; // if the bit was previous set, then we already have entered, but not exitted
                // this proc from the main event loop
 
    done = gDone;
    while (done == false)
    {
        if (gFreeQ->fHead != nil)
        {
            packetPtr = (PacketPtr)OTLIFODequeue(gFreeQ);
            done = ReceiveOnePacket(myEp, packetPtr);
        }
        else
        {
            
            done = true;
        }
            
        
    }
 
    OTAtomicClearBit(&myEp->semaphore, kInRcvDataFlag); 
                // clear the bit that indicates we're in this loop
                        
}
 
OSStatus DoSndDisconnect(MyEndpointRef *myEp)
{
    OSStatus        err;
 
    err = OTSndDisconnect(myEp->ep, NULL);
    if (err != kOTNoError)
    {
        DoValueBreak((long)err, "error occured on OTSndDisconnect #");
    }
    else
    {
        ClrAcceptPendFlag(myEp->flags);
    }
 
    return err;
}
 
 
OSStatus DoRcvDisconnect(MyEndpointRef *myEp)
{
    OSStatus        err = kOTNoError;
    
    if (TstDisconPendFlag(myEp->flags))
    {
            // set discon flag before making the call and clear it if the call
            // fails so that we don't have a race condition with the handler routine
        SetDisconPendFlag(myEp->flags);
        err = OTRcvDisconnect(myEp->ep, NULL);
        if (err != kOTNoError)
        {
            DoValueBreak((long)err, "error occured on OTRcvDisconnect #");
            ClrDisconPendFlag(myEp->flags);
        }
    }
 
    return err;
}
 
OSStatus DoRcvOrderlyDisconnect(MyEndpointRef *myEp)
{
    OSStatus        err;
    
    err = OTRcvOrderlyDisconnect(myEp->ep);
    if (err < kOTNoError)
    {
        DoValueBreak((long)err, "error occured on OTRcvOrderlyDisconnect #");
    }
    else if (err > 0)
    {           
        OTDebugStr((const char *)"OTRcvOrderlyDisconnect returned positive result;g");
    }
    
    return err;
}
 
/*******************************************************************************
** PollEventList
**  This routine checks the used queue to see whether the handler has processed
    incoming PAP data from the client.
**  If the used queue is not empty, there are two different options to deal with.
    First we check whether we are in the middle of processing a postscript data
    file.  If so, we continue to read the data into a file which we have created.
    If not currently processing postscript data, we then check to see if the incoming
    packet is part of a postscript query in progress or is the beginning of a 
    postscript query.  If so, we continue to read the query and respond as appropriate.
    If not a query, then this must be the first packet of postscript data being sent.
    
********************************************************************************/
 
OSStatus PollQueueList(void)
{
    PacketPtr   packetPtr, nextPacketPtr;
    MyEndpointRef *theEp;
    OSStatus    err = kOTNoError;
    short       i = 0;
    Boolean     done = false;
    
#if HANDOFF_EP
    for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
    {
        theEp = &gHandoffEp[i];
#else
        theEp = &gEp;
#endif
 
        packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ);
        while (packetPtr != nil)
        {
 
            packetPtr = (PacketPtr)OTReverseList((OTLink*)packetPtr);
            do
            {
                nextPacketPtr = (PacketPtr)packetPtr->fLink.fNext;
                
                err = ProcessIncomingDataFile(theEp, packetPtr);
        
                    // queue the packetPtr back to the freeQ
                OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
 
                packetPtr = nextPacketPtr;
                
            } while((packetPtr != nil) && (err == kOTNoError));
            
            packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ);
        }
 
#if HANDOFF_EP
    }
#endif
                    
    return err;
}
 
OSErr ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr)
{
    OSStatus        err = noErr;
    short           fRefNum;
    Boolean         eofFlag = false;
    
    
        // For each incoming packet, we need to check whether the EOF
        // bit was set by looking at whether the T_MORE flag was clear
 
    if ((packetPtr->flags & T_MORE) == 0)
    {
        fprintf(stdout, "\nT_MORE flag not set \n");
        if (TstEOMOnFlag(theEp->flags))
        {
                // send empty packet response to the sender to acknowledge that we
                // have received the last packet
                // this is 
            fprintf(stdout, "Sending empty response packet \n");
            SendEmptyPacket(packetPtr);
            eofFlag = true;
        }
        else
        {
            fprintf(stdout, "EOM option not enabled - cannot tell if EOF message sent \n");
        }
    }
        
    if (!TstdumpAllPktsFlag(gFlags) && !TstDumpPktsFlag(theEp->flags))  // check if we are dumping the packets
    {
        if (!TstTempFileFlag(theEp->flags)) // is there a temp file opened for this endpoint
        {
                // no, so create one
            err = OpenTempFile(&fRefNum);
            if (err == noErr)
            {
                fprintf(stdout, "\nprocessing incoming file\n");
                theEp->fRefNum = fRefNum;       // save the refnum of the temp file;
                SetTempFileFlag(theEp->flags);  // indicate that there is a temp file associated
                                                // with this endpoint.
            }
            else
            {
                fprintf(stdout, "Error occurred opening a temp file %d\n", err);
                fprintf(stdout, "The packets will be dumped%d\n", err);
            }
            
        }
 
        if (err == noErr)
        {
            
                // the temp file has been created or already exists, so read the data and write it
                // to the temp file
            err = WriteDataToTempFile(theEp->fRefNum, packetPtr->data, packetPtr->numBytes);
            if (err != noErr)
            {
                fprintf(stdout, "Error occurred writing to temp file %d\n", err);
                fprintf(stdout, "All remaining packets will be dumped for this endpoint\n");
                SetDumpPktsFlag(theEp->flags);
                    // set err to noErr otherwise the error would force the close of the
                    // program
                err = noErr;    
            }
                // check if the eof bit was set, and close the file if so
            if (eofFlag == true)
            {
                err = CloseTempFile(theEp->fRefNum);
                if (err != noErr)
                    fprintf(stdout, "\n\nError occurred closing file - error %ld.\n", err);     
                else
                    ClrTempFileFlag(theEp->flags);
            }
        }
    }
    
    return err;
}
 
 
OSErr CheckFileToClose(void)
{
    OSErr   err = noErr;
    
    if (gFRefNumToClose != 0)
    {
        fprintf(stdout, "Am about to close file with fRefNum %ld.\n", gFRefNumToClose);
        
        err = CloseTempFile(gFRefNumToClose);
        if (err != noErr)
            fprintf(stdout, "\n\nError occurred closing file - error %ld.\n", err);     
        
            // reset the tempfile to close so that we don't close this file again
        gFRefNumToClose = 0;
    }
    
    return err;
}
 
 
/*******************************************************************************
** DoServer
********************************************************************************/
 
void DoServer(void)
{
    EventRecord     theEvent;
    OTTimeStamp     eTime;
    OTResult        state;
    UInt32          eTimeMSec, sec, msec, rate;
    MyEndpointRef   *theEp;
    OSStatus        err1, err = kOTNoError;
#if HANDOFF_EP
    short           i = 0;
#endif
    
    while (gDone == false)
    {
        if (!WaitNextEvent(everyEvent, &theEvent, 15, nil))
            theEvent.what = nullEvent;
            
        DoEvent(&theEvent);
        
        if (TstListenPendFlag(gEp.flags))       // are there incoming connect requests to process
        {
            fprintf(stdout, "\n processing EnterListenAccept from even tloop");
            EnterListenAccept(&gEp);
        }
            
        err = PollQueueList();
        if (err != kOTNoError)      // we're outta here if an error occurred.
            gDone = true;
 
#if HANDOFF_EP
        theEp = &gHandoffEp[i];
        i++;
        if (i >= kMaxHandoffEPs)
            i = 0;
#else
        theEp = &gEp;
#endif
        if (TstForceCloseFlag(theEp->flags))
        {
            state = kForceClose;
        }
        else
        {
                // get the current endpoint state
            state = OTGetEndpointState(theEp->ep);
        }
        
        switch (state)
        {
            case T_DATAXFER:
                    
                    // check if we haven't completed reading data from OT.  
                DoReceiveData(theEp);
                break;
                
            case T_INREL:   // we've processed an incoming Orderly Release request
                            // but have not issued our own orderly disconnect
                
 
                    // note that since we have already processed an incoming orderly disconnect
                    // we are in the INREL state.  After making the OTSndOrderlyDisconnect
                    // call, there will not be a T_DISCONNECTCOMPLETE call, since we complete
                    // the orderly release with the above call.  We go ahead and reset our flags
                    // and issue the OTUnbind call if we are using a handoff endpoint.
                    // if we did get the orderly release indication, then make sure we have
                    // drained the receive queue - PollQueueList does this
                    
                    // A PAPServer does not normally issue an OrderlyDisconnect request.  Typically,
                    // the client issues the orderly release, at which point the server must accept the
                    // T_ORDREL event. At this point the server is 
 
 
                do
                {
                    fprintf(stdout, "processing some last data\n");
                    PollQueueList();
                
                }while( (TstHasDataFlag(theEp->flags)) || (theEp->usedQ->fHead != nil));
 
                fprintf(stdout, "Calling OTSndOrderlyDisconnect\n");
                    // clear the flag and send a corresponding disconnect
                err = OTSndOrderlyDisconnect(theEp->ep);
                if (err)
                {
                    // the only error that makes sense here is the kOTLookErr
                    //  which the only event is the T_DISCONNECT event.  This should not happen,
                    // however, if there are multiple handoffs, this may be an issue.  Given the way
                    // incoming requests are handled, I don't expect that there would be an 
                    // unresponded listen request here that would warrant a possible T_DISCONNECT
                    // however some implementation will need to consider this possibility.
                    DoValueBreak((long)err, "error occured on OTSndOrderlyDisconnect #");
                    
                    gDone = true;
 
                }
                                        
                if (TstTempFileFlag(theEp->flags))
                {   
                                                        // is there an open spool file associated with this endpoint
                    gFRefNumToClose = theEp->fRefNum;   // set the global to have this file closed
                                                    // note that this method of signalling for a file to
                                                    // get closed is a quick and dirty method and assumes th
                                                    // that only one file at a time is being processed here.
                    CheckFileToClose();
                }
                
                ClrTempFileFlag(theEp->flags);
                ClrPassconFlag(theEp->flags);
 
#if HANDOFF_EP
                    // in order to re-use a handoff endpoint, it must be unbound.  Unbinding the 
                    // endpoint clears the connection information that allows to to know which
                    // endpoint, incoming PAP data is routed to.
                if (theEp->ep != gEp.ep)
                {
                    if (TstEPBoundFlag(theEp->flags))
                    {
                        OTDebugStr("Entered DoOTUnbind for T_INREL");
                        DoOTUnbind(theEp);      // RRK 3/9/99 change to protect against multiple unbinds
                    }
                }
#else
                    // indicate that we are no longer busy
                ClrEPBusyFlag(theEp->flags);
                    // set flag so that we will check the status
                SetCheckOptFlag(gFlags);
                SetStatusIdleFlag(gFlags);
                    
#endif
                    
                
                    // calculate the elapsed time
                OTSubtractTimeStamps(&eTime, &theEp->timeDataIn, &theEp->timeDataEnd);
                    // convert to millisecs
                eTimeMSec = OTTimeStampInMilliseconds(&eTime);
                sec = eTimeMSec / (UInt32)1000;
                msec = eTimeMSec %(UInt32)1000;
                fprintf(stdout, "Time to transfer file was %ld . %ld seconds.\n", sec, msec);
                fprintf(stdout, "Bytes transferred was - %ld.\n", theEp->numBytesIn);
 
                if (sec != 0)
                {
                    rate = (theEp->numBytesIn / sec) / 1024;
                    fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate);
                }
                else 
                {
                    rate = (theEp->numBytesIn *1000 / msec) / 1024;
                    fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate);
                }
                break;
                
            case T_IDLE:    // we're either really idle, or the we've processed a disconnect event
                            // if we processed a disconnect event then the busy flag for the ep would still be set
                fprintf(stdout, "i");
                fflush(stdout);
                
                if (TstEPBusyFlag(theEp->flags))    // we're we previously connected
                {
                    ClrPassconFlag(theEp->flags);   // clear the flag so that we can accept a new connection
                    
                    ClrTempFileFlag(theEp->flags);
                    ClrPassconFlag(theEp->flags);
 
                    if (TstTempFileFlag(theEp->flags))  
                                                            // is there an open spool file associated with this endpoint
                        gFRefNumToClose = theEp->fRefNum;   // set the global to have this file closed
                                                        // note that this method of signalling for a file to
                                                        // get closed is a quick and dirty method and assumes th
                                                        // that only one file at a time is being processed here.
 
#if HANDOFF_EP
                        // in order to re-use a handoff endpoint, it must be unbound.  Unbinding the 
                        // endpoint clears the connection information that allows to to know which
                        // endpoint, incoming PAP data is routed to.
                    if (theEp->ep != gEp.ep)
                    {
                        DoOTUnbind(theEp->ep);  // RRK 3/9/99 change to protect against multiple unbinds
        
                    }
#else
                        // set flag so that we will check the status
                    SetCheckOptFlag(gFlags);
                        // indicate that we are no longer busy
                    ClrEPBusyFlag(theEp->flags);
                    SetStatusIdleFlag(gFlags);
 
#endif
                }
                break;
            
            case kForceClose:   // our endpoint is really hosed so close it an reopen it
                ClrForceCloseFlag(theEp->flags);
                OTCloseProvider(theEp->ep);
#if HANDOFF_EP
                err = ActivateHandoffEndpoint(theEp);
#else
                err = ActivatePAPEndpoint(theEp);
#endif
                if (err != kOTNoError)
                {
                    fprintf(stdout, "\n Error activating endpoint - %ld.\n", err);
                    fprintf(stdout, "\n Quitting.\n");
                    gDone = true;
                }
                break;
                
        }
        
        
        err1 = CheckFileToClose();
        if (err1 != noErr)
            fprintf(stdout, "\n\nCheckFileToClose error - %ld.\n", err1);
    
        if (TstCheckOptFlag(gFlags))
        {
            
            if (EndpointsAllBusy())
            {
                if (TstStatusBusyFlag(gFlags) == false)
                {
                    BeginSetServerStatusOption(gEp.ep, kSetBusyStr);
                    SetStatusBusyFlag(gFlags);
                }
            }
            else
            {
                if (TstStatusBusyFlag(gFlags) == true)
                {
                    BeginSetServerStatusOption(gEp.ep, kSetIdleStr);
                    ClrStatusBusyFlag(gFlags);
                }
            }
            ClrCheckOptFlag(gFlags);
        }
                    
 
    }
 
}
 
/* Do the right thing for an event.  Determine what kind of event it is, and
** call the appropriate routines. */
 
void    DoEvent(EventRecord *event)
{
    Point   pt;
    char    key;
 
    switch(event->what) 
    {
 
        case nullEvent:
        case mouseDown:
        case activateEvt:
        case updateEvt:
        case kHighLevelEvent:
            break;
 
        case autoKey:
        case keyDown:
            key = event->message & charCodeMask;
            switch (key)
            {
                case 'q':
                case 'Q':
                    if (event->modifiers & cmdKey)
                    gDone = true;
                    break;
            }
            break;
 
        case osEvt:
            switch ((event->message >> 24) & 0xFF) 
            {
                    /* Must logical and with 0xFF to get only low byte. */
                    /* High byte of message. */
 
                case mouseMovedMessage:
                    break;
 
                case suspendResumeMessage:
                        /* Suspend/resume is also an activate/deactivate. */
                    if ((event->message) & resumeFlag)
                        ClrInBackGndFlag(gFlags);   // process has come to the foreground
                    else
                        SetInBackGndFlag(gFlags);   // process has gone into background
                    break;
            }
            break;
 
        case diskEvt:
            if (HiWord(event->message) != noErr) 
            {
                SetPt(&pt, kDILeft, kDITop);
                DIBadMount(pt, event->message);
            }       /* It is not a bad idea to at least call DIBadMount in response to */
            break;  /* a diskEvt, so that the user can format a floppy. */
    }
 
}
 
 
/*******************************************************************************
    DoOTUnbind
    Call to make the OTUnbind request and check for an error result.
    Once this call is made, we don't want to protect against the OTUnbind
    from being called a second time.  If the OTUnbind call is made a second time
    before the T_UNBINDCOMPLETE event has occurred, then the call results in an
    StateChangeErr
    
    // RRK 3/9/99 add new code to protect against multiple unbind calls
    
********************************************************************************/
 
OSStatus DoOTUnbind(MyEndpointRef *theEp)
{
    OSStatus    err = kOTNoError;
    Boolean     isSync;
    
//  OTDebugStr("Entered DoOTUnbind");
    
    if (TstEPBoundFlag(theEp->flags) && !TstUnbindPendFlag(theEp->flags))
    {
            // check whether the ep is asynch and set a flag to indicate we are waiting for the
            // unbind to complete.  We have to set the flag before we issue the call as
            // the T_UNBINDCOMPLETE call could occur before we could set the flag is done
            // after the OTUnbind call
        isSync = OTIsSynchronous(theEp->ep);
        
        if (isSync == false)
        {
            SetUnbindPendFlag(theEp->flags);
        }
            
        OTDebugStr("About to call OTUnbind");
        err = OTUnbind(theEp->ep);
        if (err != kOTNoError)
        {
            DoValueBreak(err, "Error calling OTUnbind #");
            SetForceCloseFlag(theEp->flags);
                // on err clear the UnbindPendFlag
            ClrUnbindPendFlag(theEp->flags);
        }
        else
        {
            if (isSync == true)
            {
                ClrEPBusyFlag(theEp->flags);
                ClrEPBoundFlag(theEp->flags);
            }
        }
    }
    return err;
}
 
/*******************************************************************************
** DoBind
**  Implements the option to set the qlen field and to bing with an nbpName
**  by setting the nbpName field with a pointer to an entity name.
**  If nbpName is not nil, you must also set the nbpNameLen to the size of the
**  nbpName field. Note, nbpName is not a pascal nor a C string.  
********************************************************************************/
 
OSStatus DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, 
                    char *nbpName, UInt32 nbpNameLen)
{
    TBind           req, ret;
    OSStatus        err = kOTNoError;
    DDPNBPAddress   addr;
    
 
    // init the DDPAddress variable 
    if (nbpName == nil)     // are we registering with no nbp name
    {
        addr.fAddressType = AF_ATALK_DDP;       // set the address type
        req.addr.len    = kDDPAddressLength;
    }
    else
    {
        addr.fAddressType = AF_ATALK_DDPNBP;    // registering with an nbp name
                                                // copy the name to the name buffer
        BlockMove(nbpName, &addr.fNBPNameBuffer, nbpNameLen);
                                                // set the request length to include the name len
        req.addr.len    = kDDPAddressLength + nbpNameLen;
    }
    
    addr.fNetwork = 0;
    addr.fNodeID = 0;
    addr.fSocket = socket;
    addr.fDDPType = type;
    
    // set up the request
    req.addr.buf    = (UInt8*)&addr;
    req.qlen        = qlen;
    
    // set up the response
    ret.addr.buf    = (UInt8*)&addr;
    ret.addr.maxlen = sizeof(addr);
 
    fprintf(stderr, "Doing Bind\n");
 
    //
    // Try to bind
    // 
    err = OTBind(ep, &req, &ret);
 
    if ( err != kOTNoError )
    {
        fprintf(stderr, "PAPSample: DoBind returns error %ld\n", err);
        return err;
    }
 
    fprintf(stderr, "Bound address = ");
    ShowDDPAddress((DDPAddress*)&addr);
    fprintf(stderr, "\n");
    fprintf(stderr, "Bound queue len is %ld\n", ret.qlen);
 
    fprintf(stderr, "After Bind, ");
    ShowEndpointState(ep, "");
 
    return err;
}
 
/*******************************************************************************
** StartAbortiveDisconnect      RRK 3/9/99 addition
**  starts an abortive disconnect of the endpoint by first forcing a flush of the
    cache, which results in a kStreamIoctlEvent handler event
********************************************************************************/
 
OSStatus StartAbortiveDisconnect(MyEndpointRef *myEp)
{
    OSStatus    err = kOTNoError;
    
    err = OTIoctl(myEp->ep, I_FLUSH, (void*) FLUSHRW);
    if (err) 
        DoOTUnbind(myEp->ep);
    
    return err;
}
 
/*******************************************************************************
** ActivatePAPEndpoint
** 
********************************************************************************/
 
OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp)
{
    OSStatus    err = kOTNoError;
    OTResult    result;
    
    //
    // Create a PAP endpoint
    // Open listener, using the tilisten module to make 
    // listen/accept/disconnect processing much simpler.
    //
    myEp->ep = OTOpenEndpoint(OTCreateConfiguration("tilisten, pap"), 0, NULL, &err);
 
    if ( myEp->ep == NULL || err != kOTNoError )
    {
        myEp->ep = NULL;
        
        if (err == kOTNoError)
            err = memFullErr;
            
        fprintf(stderr,"ERROR: OpenEndpoint(\"pap\") failed with %ld\n", err);
    }
    else
    {
        SetEPActiveFlag(myEp->flags);
    }
 
#if HANDOFF_EP
#else
    if (err == kOTNoError)
    {
 
            // turn on the EOM option
        err = DoNegotiateEOMOption(myEp->ep, true);
        if (err != kOTNoError)
        {
            ClrEOMOnFlag(myEp->flags);
            fprintf(stderr, "\n\nError setting EOM option!\n");
        }
        else
        {
            SetEOMOnFlag(myEp->flags);
        }
    }
#endif
    
    if (err == kOTNoError)
    {
        //
        // Install notifier we're going to use for testing - we do this before
        //      binding - at this point, we're still synchronous
        //
        err = OTInstallNotifier(myEp->ep, EventHandler, (void*)myEp);
        if ( err != kOTNoError )
        {
            fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for server endpoint", err);
        }
    }
        
    if (err == kOTNoError)
    {
 
        /*-------------------------------------------------------------------------
        Bind the endpoint endpoint using NBP
        ------------------------------------------------------------------------- */
 
#if HANDOFF_EP
        err = DoBind(myEp->ep, kDynamicSocket, kATPType, kMaxHandoffEPs, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr));
#else
        err = DoBind(myEp->ep, kDynamicSocket, kATPType, 1, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr));
#endif
 
        if ( err != kOTNoError )
        {
            fprintf(stderr, "PAPSample: bind of server endpoint failed.\n");
        }
        else
        {
            SetEPBoundFlag(myEp->flags);
        }
    }
 
    /*-------------------------------------------------------------------------
    Enable self send
        have to do this before we set the endpoint into async mode.
    ------------------------------------------------------------------------- */
    if (err == kOTNoError)
    {
        err = DoNegotiateSelfSendOption(myEp->ep, 1);
        
        if (err < kOTNoError)
        {
            fprintf(stderr, "error negotiating selfsend on main endpoint\n");
            err = kOTNoError;   // don't let this stop the app.
        }
        else if (err > kOTNoError)
        {
            fprintf(stderr, "selfsend previously enable\n");
            err = kOTNoError;   // don't let this stop the app.
        }
        else
        {
            fprintf(stderr, "selfsend enabled\n");
        }
    }
    
        // lastly, set endpoint into async modez
    if (err == kOTNoError)
        OTSetAsynchronous(myEp->ep);
    
 
    /*-------------------------------------------------------------------------
    Set the server status string
    ------------------------------------------------------------------------- */
    if (err == kOTNoError)
        BeginSetServerStatusOption(myEp->ep, kSetIdleStr);
    
    
    
    return err;
}
 
#if HANDOFF_EP
/*******************************************************************************
** ActivateHandoffEndpoint function - Open prepare a handoff endpoint for the
   PAP server
********************************************************************************/
 
OSStatus ActivateHandoffEndpoint(MyEndpointRef *myEp)
{
    OSStatus    err;
    
    myEp->ep = OTOpenEndpoint(OTCreateConfiguration(kPAPName), (OTOpenFlags)NULL, NULL, &err);
    if (err == kOTNoError)
    {
        SetEPActiveFlag(myEp->flags);
            // turn on the EOM option
        err = DoNegotiateEOMOption(myEp->ep, true);
        if (err != kOTNoError)
        {
            ClrEOMOnFlag(myEp->flags);
            fprintf(stderr, "\nError setting EOM option %d\n", err);
        }
        else
        {
            SetEOMOnFlag(myEp->flags);
        }
    }
 
    if (err == kOTNoError)
    {
        err = OTInstallNotifier(myEp->ep, EventHandler, (void*)myEp);
        if ( err != kOTNoError )
        {
            fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for handoff endpoint &d\n", err);
        }
        else
            OTSetAsynchronous(myEp->ep);
 
    }
 
    return err;     
}
#endif
 
/*******************************************************************************
** InitMyEndpointRef function - zero out the MyEndpointRef structure
**  Course we could use the memset function here.
********************************************************************************/
 
OSStatus InitMyEndpointRef(MyEndpointRef *myEp)
{
    myEp->ep = nil;
    myEp->flags = 0;
    myEp->usedQ = OTAllocMem(sizeof(OTLIFO));
    if (myEp->usedQ == nil)
        return memFullErr;
    
    myEp->usedQ->fHead = nil;       // no packets with this endpoint.
    
    return kOTNoError;
}
 
 
OSStatus InitPAPBuffers(void)
{
    PacketPtr       packetPtr;
    OSStatus        err = kOTNoError;
    short           i;
    
        // allocate memory for the freeQ
    gFreeQ = OTAllocMem(sizeof(OTLIFO));
    if (gFreeQ == nil)
        return memFullErr;
        
    /* set up the free queue */
    gFreeQ->fHead = nil;
    
        // allocate a temp buffer to be used when packet data overlaps.
    gTempPackPtr = OTAllocMem(sizeof(PacketBuffer) + kPAPDataSize); 
    if (gTempPackPtr == nil)
        err = memFullErr;
 
    /* enqueue the packet buffer records to the free queue */
    for (i=0; (i<kNumBuffers) && (err == kOTNoError); i++)
    {
            // before calling OTAllocMem, we must have already have called
            // InitOpenTransport
            
        packetPtr = OTAllocMem(sizeof(PacketBuffer));
        if (packetPtr != nil)
        {
            packetPtr->flags = 0;
            packetPtr->fLink.fNext = nil;
            OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); // first field is fLink field
            
        }
        else
            err = memFullErr;
    }
    
    if (err == kOTNoError)
    {
        /* show that the buffers have been init'd */
        SetBufsInitdFlag(gFlags);
 
        /* note that we don't need to worry about holding memory in the
            presence of VM.  The notifier routine will be called at
            deferred task time when it is safe to page memory
         */
        
    }
    else
    {
        fprintf(stderr, "\n\nError allocating receive buffers!\n");
        fprintf(stderr, "\nBye Bye.\n");
    }
        
    return(err);
 
}
 
    
 
/*******************************************************************************
** Initialize OpenTransport and call DoTest function
********************************************************************************/
OSStatus InitPAPServerStuff(void)
{
    OSStatus    err = kOTNoError;
    UInt32      growAmt, selection;
    UInt32      i;
    char        mystr[255]; 
                
        // register ourselves with OT
    OTRegisterAsClient((OTClientName) "PAPServerSample", nil);
 
        // ask whether to alter current options
    fprintf(stdout, "\nDo you want to change the current options?");
    fprintf(stdout, "\n  Options which can be changed:");
    fprintf(stdout, "\n                    1. SetMemoryLimits");
    fprintf(stdout, "\n                    2. Dump packets");
    fprintf(stdout, "\n    Enter - n to use default settings");
    fprintf(stdout, "\n    Enter - y to alter above settings");
 
    selection = GetYesNoOption();
    if (selection == kQuitTest)
        err = -1;
    else if (selection == kAcceptOption)
    {
            // ask whether to use the set memory limits option or not
        fprintf(stdout, "\nDo you want to set memory limits?");
        selection = GetYesNoOption();
        
        if (selection == kQuitTest)
            err = -1;
        else if (selection == kAcceptOption)
        {
            fprintf(stderr, "How much memory to grow heap ?\n");
            fprintf(stdout, "\n  Enter - n to use default setting");
            fprintf(stdout, "\n  Enter - y to set heap growth");
            if (gets(mystr) != 0)
            {
                stringtonum(mystr, (long*)&growAmt);
                OTSetMemoryLimits(growAmt, 0);
                
            }
        }
        
        if (err == kOTNoError)
        {
                // ask whether to use the set memory limits option or not
            fprintf(stdout, "\nDo you want to just dump packets?");
            fprintf(stdout, "\n  Enter - n to save packets to default file");
            fprintf(stdout, "\n  Enter - y to dump packets");
            selection = GetYesNoOption();
            if (selection == kQuitTest)
                err = -1;
            else if (selection == kAcceptOption)
            {
                SetdumpAllPktsFlag(gFlags);
            }
        }
    }
 
    if (err == kOTNoError)
    {
 
            // load in the various strings
        GetIndString((unsigned char*)&gIdleStr, kServerStrID, kIdleStrID);
        GetIndString((unsigned char*)&gBusyStr, kServerStrID, kBusyStrID);
        GetIndString((unsigned char*)&gServerNBPNameStr, kServerStrID, kServerNBPStrID);
            // covert each string to c
        p2c(gIdleStr);
        p2c(gBusyStr);
        p2c(gServerNBPNameStr);
    
        // initialize the endpoint ref
        err = InitMyEndpointRef(&gEp);
    }
    
    if (err == kOTNoError)
    {
#if HANDOFF_EP
        for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError) ; i++)
            err = InitMyEndpointRef(&gHandoffEp[i]);
#endif
    }
 
    if (err == kOTNoError)
    {
        //
        // Initialize the buffers
        //
        err = InitPAPBuffers();
    }
            
 
    if (err == kOTNoError)
    {
        //
        //  Init the server endpoint
        //
        err = ActivatePAPEndpoint(&gEp);
    }
 
#if HANDOFF_EP
    if (err == kOTNoError)
    {
        //
        //  Init the handoff endpoint
        //
        for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
            err = ActivateHandoffEndpoint(&gHandoffEp[i]);      // RRK 3/10/99 call ActivateHandoffEndpoint
 
        if (err != kOTNoError)
        {
            fprintf(stderr, "\n\nError opening the handoff endpoint!\n");
            fprintf(stderr, "\nBye Bye.\n");
        }
    }
 
#endif      // HANDOFF_EP   
        
    return err;
    
}
 
 
/*******************************************************************************
** ReleasePAPMemory
********************************************************************************/
 
void ReleasePAPMemory(void)
{
    PacketPtr   packetPtr;
    OTLIFO      *otLIFOPtr;
    short       i;
    
    if (gTempPackPtr)
        OTFreeMem(gTempPackPtr);
        
        // first release the freeQ memory
    otLIFOPtr = gFreeQ;
    packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);
    
    while (packetPtr != nil)    // while the queue is not empty
    {
                // free the packetbuffer
        OTFreeMem(packetPtr);
        packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);            
    }
 
#if HANDOFF_EP
    for (i = 0; i < kMaxHandoffEPs; i++)        // 
    {
        otLIFOPtr = gHandoffEp[i].usedQ;
#else
        otLIFOPtr = gEp.usedQ;
#endif
        if (otLIFOPtr->fHead)
            fprintf(stderr, "Am releasing buffer from the used queue.\n\n");
        
        packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);
        
        while (packetPtr != nil)    // while the queue is not empty
        {
                    // free the packetbuffer
            OTFreeMem(packetPtr);
            packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);            
        }
    
#if HANDOFF_EP
    }
#endif
    
    OTFreeMem(gFreeQ);
    
}
 
/*******************************************************************************
** Close endpoints, and OpenTransport 
********************************************************************************/
 
void ClosePAPServerStuff(void)
{
 
    short i;
 
    if (gEp.ep)
        OTSetSynchronous(gEp.ep);
 
    // note that we don't check whether the endpoint is still connected.  Since
    // we are leving anyway, let the UnBind call clear things up.  If we did do a
    // disconnect here, we would meed to also implement a timer mechanism. so that
    // we didn't get hung awaiting a DisconnectComplete event that might not come.
    
#if HANDOFF_EP
    for (i = 0; i < kMaxHandoffEPs; i++)
    {
 
        if (gHandoffEp[i].ep)
            OTSetSynchronous(gHandoffEp[i].ep);
        
        if (TstEPBoundFlag(gHandoffEp[i].flags))    // is the handoff endpoint bound
            DoOTUnbind(gHandoffEp[i].ep);           // unbind the endpoint
                                                    // RRK 3/9/99 change to protect against multiple unbinds
        
        if (gHandoffEp[i].usedQ != nil)         // free allocated memory with usedQ.
            OTFreeMem(gHandoffEp[i].usedQ);
            
        if (TstEPActiveFlag(gHandoffEp[i].flags))   // is the handoff endpoint open
            OTCloseProvider(gHandoffEp[i].ep);      // close the endpoint
            
    }
 
#endif
            
    if (TstEPBoundFlag(gEp.flags))          // is the server endpoint bound
        DoOTUnbind(gEp.ep);                 // unbind the endpoint
                                            // RRK 3/9/99 change to protect against multiple unbinds
        
    if (gEp.usedQ != nil)                   // free allocated memory with usedQ.
        OTFreeMem(gEp.usedQ);
        
    if (TstEPActiveFlag(gEp.flags))         // is the server endpoint open
        OTCloseProvider(gEp.ep);            // close the endpoint
        
    // ReleasePAPMemory();                      // release memory before OT goes away
                                            // not really necessary since we are just going to 
                                            // exit after this.
 
    if (gFreeQ != nil)
        OTFreeMem(gFreeQ);
    
    if (TstOTActiveFlag(gFlags))            // is OpenTransport active
        CloseOpenTransport();               // shutdown OT services
    
}
 
//
//  NetInit:
//
//  This routine does various networking related startup tasks:
//
//  (1) it does InitOpenTransport
//  (2) it records the OT version for us.
//
//  result - true - OpenTransport init'd and the correct version is present
//           false - problem opening OT or OT version not correct
//
static Boolean NetInit()
{
    OSStatus err;
 
    err = Gestalt(gOTVersionSelector, (long*) &gOTVersion);
    if (err || (gOTVersion < kOTVersion111))
    {
        fprintf(stderr, "Please install Open Transport 1.1.1 or later");
        return false;
    }
    
    err = InitOpenTransport();
    if (err)
    {
        fprintf(stderr, "NetInit: InitOpenTransport error %d", err);
        return false;
    }
    return true;
}
 
 
/*******************************************************************************
** main function
********************************************************************************/
 
main(void) 
{
    OSStatus    osstatus;
    
//  InitGraf(&qd.thePort);                  // initialize quickdraw so we can use regions
 
    fprintf(stderr, "PAPServerSample showing implementation of PAP server.\n\n");
 
    // init the flags variable
    gFlags = 0;
 
 
    if (NetInit() == false)
        return 0;
    
    //
    // Initialize the OT and the global Endpoints
    //
    osstatus = InitPAPServerStuff();
    
    if (osstatus == kOTNoError)
    {
    
 
        //
        // Run the PAPServer code
        //
        DoServer();
        //
    }
    
    //
    // Close things down
    //
    ClosePAPServerStuff();
    
    CheckFileToClose();
    
    fprintf(stderr, "\n\nDone\n");
        
    return 0;
}
 
void DoValueBreak(long value, const char* message)
{
    static short    sDoErrorBreak = 0;
 
    {
        Str255  s,
                n = "\p";
 
        s[0] = strlen(message);
        BlockMoveData(message,&s[1],s[0]);
        if (value < 0)
        {
            s[0] += 1;
            s[s[0]] = '-';
            value = -value;
        }
        while (value)
        {
            if (n[0])
                BlockMoveData(&n[1],&n[2],n[0]);
            n[0]++;
            n[1] = 48 + (value % 10);
            value /= 10;
        }
        BlockMoveData(&n[1],&s[s[0]+1],n[0]);
        s[0] += n[0];
 
        sDoErrorBreak++;
        {
            short   cnt = sDoErrorBreak;
 
            s[0]++;
            s[s[0]] = ',';
            s[0]++;
            s[s[0]] = ' ';
            n[0] = 0;
            while (cnt)
            {
                if (n[0])
                    BlockMoveData(&n[1],&n[2],n[0]);
                n[0]++;
                n[1] = 48 + (cnt % 10);
                cnt /= 10;
            }
            BlockMoveData(&n[1],&s[s[0]+1],n[0]);
            s[0] += n[0];
        }
        OTDebugStr((const char *)s);
    }
}
 
 
OSStatus BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr)
{
    OSStatus    err;
    unsigned char   *in = "\p._in";
    
    if (whichStr == kSetIdleStr)
    {
        err = DoSetServerStatusOption(ep, (char*)&gIdleStr);
    }
    else
    {
        err = DoSetServerStatusOption(ep, (char*)&gBusyStr);
    }
    
    if (err != kOTNoError)
    {
        fprintf(stderr, "ServerStatus option call failed\n");
        fprintf(stderr, "however, this will not keep the server from running\n");
        err = kOTNoError;
    }
    
    return err;
}
 
 
UInt32 GetYesNoOption(void)
{
    UInt32  result;
    char    selection[32];
    Boolean done;
    
    fprintf(stdout, "\n    Enter Q - To quit");
    fprintf(stdout, "\nYour selection -> ");
    fflush(stdout);
    done = false;
 
    do
    {
        if (gets(selection) != 0)
        {
            switch (selection[0])
            {
                case 'y':
                case 'Y':
                    result = kAcceptOption;
                    done = true;
                    break;
                
                case 'n':
                case 'N':
                    result = kDeclineOption;
                    done = true;
                    break;
 
                case 'q':
                case 'Q':
                    result = kQuitTest;
                    done = true;
                    break;
                    
                default:
                    fprintf(stdout, "\nInvalid entry - %c, try again -> ", selection);
                    fflush(stdout);
                    break;
 
            }
        }
    } while (!done);
    
    fflush (stdout);
    return result;
}
 
 
#if HANDOFF_EP
/*
    Iterate through the handoff endpoints and find one that doesn't have the busy flag
    set - return the pointer else return null
*/
MyEndpointRef*  FindFreeHandoffEp(void)
{
    short   i;
    
    for (i = 0; i < kMaxHandoffEPs; i++)
    {       // RRK 3/9/99 change to protect against the use of a handoff endpoint that is
            // not active, will be forced to close, or is busy
            // don't allow use of a handoff endpoint if the endpoint is
            // busy, or if it will be closed, or if it not active
        if ((TstEPBusyFlag(gHandoffEp[i].flags) == false) && 
            (TstEPActiveFlag(gHandoffEp[i].flags) == true) &&
            (TstOpenPendFlag(gHandoffEp[i].flags) == false) &&
            (TstForceCloseFlag(gHandoffEp[i].flags) == false))
            return &gHandoffEp[i];
    }
    
    return nil;
}
#endif
 
Boolean EndpointsAllBusy(void)
{
    Boolean     result = true;  
#if HANDOFF_EP
    short       i;
    
    for (i = 0; (i < kMaxHandoffEPs) && (result == true); i++)
    {
        if (TstEPBusyFlag(gHandoffEp[i].flags) == false)
            result = false; // any endpoint not busy means that not all eps are busy
    }
 
#else
    result = TstEPBusyFlag(gEp.flags);
        
#endif
    
    return result;
}