/*
 * pm.c,v 1.1 1994/01/28 17:21:40 franktor Exp
 *
 * pm.c  -  Protocol Machine
 *
 * Copyright (c) 1992, 1993 Nordic SR-NET
 * Geir Pedersen, Geir.Pedersen<@usit.uio.no
 *
 * Implements the procedural interface to the protocol machine
 * as seen through the SR API.
 *
*/

/*
 * TODO
 *
 * o Should perhaps flush input queue when an operation is written
 *
*/

#define LOW_ISODE		/* Use ISODE-based lower layer */
/*#define LOW_TCPIP		 * Use TCP/IP-based lower layer */

#include <sys/types.h>
#include <sys/time.h>
#include "pm.h"
#include "sr-low.h"
#include <sr-parser.h>
#include <malloc.h>
#include <sr-address.h>		/* Used in LOG messages */

extern struct AttributeSet *attributeSets;
extern struct DiagSet	*diagnosticSets;

void SR_AbortLow ( int );



Association 		**associations		= (Association **) NULL;
int			allocAssociations	= 0;
struct timeval		Infinity		= { 99999999, 0 };
Boolean 		(*PMAcceptFunc)(int COMMA_PM_LLK_ARG(LowerLayerKind), presContext *pci);

SR_protocolVersion	currentProtocolVersion	= (SR_protocolVersion) -1;
				/* This variable is set before each call to *
				 * lower layer (_ISODE/_TCPIP) functions. */

/*
 * Utility functions
*/

char *state2str ( State s )
{
   switch ( s )
   {
    case sClosed:
      return "sClosed";

    case osInitSent:
      return "osInitSent";

    case sOpen:
      return "sOpen";

    case osSearchSent:
      return "osSearchSent";

    case osPresentSent:
      return "osPresentSent";

    case osDeleteResultSetSent:
      return "osDeleteResultSetSent";

    case osReleaseSent:
      return "osReleaseSent";

    case sAborted:
      return "sAborted";

    case tsInitRcvd:
      return "tsInitRcvd";

    case tsSearchRcvd:
      return "tsSearchRcvd";

    case tsPresentRcvd:
      return "tsPresentRcvd";

    case tsDeleteResultSetRecvd:
      return "tsDeleteResultSetRecvd";

    case tsReleaseRcvd:
      return "tsReleaseRcvd";

    case tsReject:
      return "tsReject";
   }
   return (char *) NULL;	/* Should not happen */
}

char *sro2str ( SROperation sro )
{
   switch ( sro )
   {
    case oprNone:
      return "None";

    case oprInitialiseRequest:
      return "InitialiseRequest";

    case oprInitialiseResponse:
      return "InitialiseResponse";

    case oprSearchRequest:
      return "SearchRequest";

    case oprSearchResponse:
      return "SearchResponse";

    case oprPresentRequest:
      return "PresentRequest";

    case oprPresentResponse:
      return "PresentResponse";

    case oprDeleteResultSetRequest:
      return "DeleteResultSetRequest";

    case oprDeleteResultSetResponse:
      return "DeleteResultSetResponse";

    case oprCloseRequest:
      return "CloseRequest";

    case oprCloseResponse:
      return "CloseResponse";

    default:
      return "Unknown APDU";
   }
}


int getFreeAssocDesc ( void )
{
   int				i;

   if ( !allocAssociations )
   {
      associations = (Association **) malloc ( sizeof ( Association * ) * (allocAssociations = 25) );
      bzero ( associations, sizeof ( Association * ) * allocAssociations );
   }

   /* find a free association decriptor */
   for ( i = 0; i < allocAssociations; i++ )
   {
      if ( allocAssociations == i-1 )
      {
	 int		j;

	 associations = (Association **) realloc ( (char *) associations, sizeof ( Association * ) * (allocAssociations += 25) );
	 for ( j = i+1; j < allocAssociations; j++ )
	    associations[j] = 0;
      }

      if ( !associations[i] )
      {
	 associations[i] = (Association *) malloc ( sizeof ( Association ) );
	 return i;
      }
   }

   /* if ( i == allocAssociations ) / * always true * / */
   {
      LOG ( facPM, llevExceptions, "Failed to find free associaiton descriptor" );
      return -1;
   }
}

void freeAssocDesc ( int ref )
{
   if ( ref < 0 || ref >= allocAssociations )
   {
      LOG ( facPM, llevExceptions, "freeAssocDesc(): illegal reference %d", ref );
      return;
   }

   if ( associations[ref]->state != sClosed )
   {
      LOG ( facPM, llevExceptions, "freeAssocDesc(): %d is not in a closed state - not freed", ref );
      return;
   }

   free ( associations[ref] );
   associations[ref] = (Association *) NULL;
}


/*
 * OSI
 *
*/

Oid	Oid_SR_BER,	Oid_SR_ASN1;

Oid	Oid_SR_BASIC,	Oid_Z3950_BASIC;
Oid	Oid_SR_PCI,	Oid_Z3950_PCI;

Oid	Oid_RECORDSYNTAX,		Oid_RECORDSYNTAX_UNIMARC;
Oid	Oid_RECORDSYNTAX_INTERMARC,	Oid_RECORDSYNTAX_CCF;
Oid	Oid_RECORDSYNTAX_US_MARC,	Oid_RECORDSYNTAX_UK_MARC;
Oid	Oid_RECORDSYNTAX_NORMARC,	Oid_RECORDSYNTAX_LIBRISMARC;
Oid	Oid_RECORDSYNTAX_DANMARC,	Oid_RECORDSYNTAX_FINMARC;
Oid	Oid_RECORDSYNTAX_MAB1,		Oid_RECORDSYNTAX_CANMARC;
Oid	Oid_RECORDSYNTAX_SBN,		Oid_RECORDSYNTAX_PICAMARC;

Oid	Oid_DIAGSET_BIB1,		Oid_ATTRSET_BIB1;

Oid	Oid_ISO2709_TRANSFERSYNTAX_CHARENC;

Oid	Oid_NULL;

void Oid_Initialise ( void )
{
   static struct Oid_var {
      Oid		*var;		/* Ptr -> Oid variable to register */
      const char	*name;		/* Symbolic name for the Oid, and */
      const char	*oid1, *oid2;	/* OID strings for the variable   */
   } oid_vars[] = {
#define OID_1VARIABLE(name,txt,oid) { &Oid_ ## name, txt, oid, oid }
#define OID_2VARIABLE(name,txt) { &Oid_ ## name, txt, SR_ ## name, Z3950_ ## name }
      OID_1VARIABLE(SR_BER,	  "SR BER",	 SR_BER_OID),
      OID_1VARIABLE(SR_ASN1,	  "SR ASN1",	 SR_ASN1_OID),

      OID_1VARIABLE(SR_BASIC,    "SR BASIC",    SR_BASIC),
      OID_1VARIABLE(Z3950_BASIC, "Z3950 BASIC", Z3950_BASIC),

      OID_1VARIABLE(SR_PCI,      "SR PCI",      SR_PCI),
      OID_1VARIABLE(Z3950_PCI,   "Z3950 PCI",   Z3950_PCI),

      OID_2VARIABLE (RECORDSYNTAX,		"RECORDSYNTAX"),
      OID_2VARIABLE (RECORDSYNTAX_UNIMARC,	"RECORDSYNTAX UNIMARC"),
      OID_2VARIABLE (RECORDSYNTAX_INTERMARC,	"RECORDSYNTAX INTERMARC"),
      OID_2VARIABLE (RECORDSYNTAX_CCF,		"RECORDSYNTAX CCF"),
      OID_2VARIABLE (RECORDSYNTAX_US_MARC,	"RECORDSYNTAX US MARC"),
      OID_2VARIABLE (RECORDSYNTAX_UK_MARC,	"RECORDSYNTAX UK MARC"),
      OID_2VARIABLE (RECORDSYNTAX_NORMARC,	"RECORDSYNTAX NORMARC"),
      OID_2VARIABLE (RECORDSYNTAX_LIBRISMARC,	"RECORDSYNTAX LIBRISMARC"),
      OID_2VARIABLE (RECORDSYNTAX_DANMARC,	"RECORDSYNTAX DANMARC"),
      OID_2VARIABLE (RECORDSYNTAX_FINMARC,	"RECORDSYNTAX FINMARC"),
      OID_2VARIABLE (RECORDSYNTAX_MAB1,		"RECORDSYNTAX MAB1"),
      OID_2VARIABLE (RECORDSYNTAX_CANMARC,	"RECORDSYNTAX CANMARC"),
      OID_2VARIABLE (RECORDSYNTAX_SBN,		"RECORDSYNTAX SBN"),
      OID_2VARIABLE (RECORDSYNTAX_PICAMARC,	"RECORDSYNTAX PICAMARC"),

      OID_2VARIABLE (ATTRSET_BIB1,		"ATTRSET BIB1"),
      OID_2VARIABLE (DIAGSET_BIB1,		"DIAGSET BIB1"),

      OID_1VARIABLE(ISO2709_TRANSFERSYNTAX_CHARENC, "ISO2709 TRANSFERSYNTAX CHARENC", ISO2709_TRANSFERSYNTAX_CHARENC),

/*    { &Oid_NULL,   "NULLOID", "", "" }, */
      { 0, 0, 0, 0 }
   };

   struct Oid_var *vp;
   if ( !OID_contents(*oid_vars[0].var) ) /* Check if already initialised */
      for ( vp = oid_vars;  vp->var;  vp++ )
	 *vp->var = OID_register ( vp->name, vp->oid1, vp->oid2 );
}


Boolean OSI_Initialise ( LogLevel logLevel, FILE *log )
{
   Oid_Initialise ();
   return _OSI_Initialise ( logLevel, log, SR_AbortLow );
}

Boolean OSI_Verify ( void )
{
   return True;
}

void OSI_Close ( void )
{
   _OSI_Close();
}


fd_set *SR_GetSelectFD_SET ( void )
{
   Warn_UNIMPLEMENTED ( facPM, "SR_GetSelectFD_SET()" );
}


char *OSI_LastError ( void )
{
   return "Unknown error";
}


/*
 * Internal functions
 *
*/

void SR_AbortLow ( int ref )
{
   int			i;

   for ( i = 0; i < allocAssociations; i++ )
      if ( associations[i] && associations[i]->lowerLayerRef == ref )
      {
/*
	 currentProtocolVersion = associations[i]->protocolVersion;
	 associations[i]->state = sAborted;
	 associations[i]->lowerLayerRef = -2;
	 switch ( associations[i]->lowerLayerKind )
	 {
#ifdef LOW_ISODE
	  case llkISODE:
	    _ISODE_ClearAssociation ( ref );
	    break;
#endif
#ifdef LOW_TCPIP
	  case llkTCPIP:
	    _TCPIP_ClearAssociation ( ref );
	    break;
#endif
	 }
*/
	 break;
      }

   if ( i == allocAssociations )
      LOG ( facPM, llevExceptions, "SR_AbortLow() failed to find association (%d)", ref );
   else
      LOG ( facPM, llevExceptions, "SR_AbortLow: Lower layer aborting connection %d (lower layer ref=%d)",
	    ref, i );
}

Boolean SR_AcceptAssoc ( int ref, SR_protocolVersion protoVersion, LowerLayerKind llk, presContext *pci )
{
   Boolean	    	ok = False;
   int			i;

   if ( (i = getFreeAssocDesc ()) == -1 )
   {
      LOG ( facPM, llevExceptions, "SR_AcceptAssoc() failed to accept association, no more free association descriptors" );
      return False;
   }

   if ( PMAcceptFunc )
      ok = (*PMAcceptFunc) ( i COMMA_PM_LLK_ARG(llk), pci );

   if ( ok )
   {
      associations[i]->role = isTarget;
      associations[i]->state = sClosed;
      associations[i]->lowerLayerRef = ref;
      associations[i]->lowerLayerKind = llk;
      associations[i]->protocolVersion = protoVersion;
   }

   return ok;
}

Boolean SR_StillOpen ( int ref )
{
   /* Verify that the association is open */
   State		s	= associations[ref]->state;

   if ( s != sAborted && s != sClosed )
      return True;

   return False;
}



/*
 * Opening SR associations as origin or target
 *
*/

Boolean SR_OpenAssociation ( SR_Address *to, presContext **pci, int *remoteRef )
{
   int			i;

   if ( (i = getFreeAssocDesc ()) == -1 )
   {
      LOG ( facPM, llevExceptions, "Failed to open connection to %s: Failed to allocated association descriptor",
	    sr_address2str ( to ) );
      return False;
   }

   *remoteRef = i;
   associations[i]->role = isOrigin;
   associations[i]->state = sClosed;
   associations[i]->lowerLayerRef = -1;
   associations[i]->protocolVersion = to->protocolVersion;

   currentProtocolVersion = associations[i]->protocolVersion;

   for ( ; to; to = to->next )
   {
      switch ( to->addressType )
      {
#ifdef LOW_TCPIP
       case addrTCPIP:
	 /* Open TCP/IP-based connection */
	 associations[i]->lowerLayerKind = llkTCPIP;
	 if ( _TCPIP_OpenAssociation ( to, pci, &associations[i]->lowerLayerRef ) )
	    return True;
	 else
	    LOG ( facPM, llevNotice, "Failed to open TCP/IP based connection to %s: %s",
		  sr_address2str ( to ), _TCPIP_LastError () );
	 break;
#endif LOW_TCPIP

#ifdef LOW_ISODE
       case addrOSI:
	 /* Open ISODE-based connection */
	 associations[i]->lowerLayerKind = llkISODE;
	 if ( _ISODE_OpenAssociation ( to, pci, &associations[i]->lowerLayerRef ) )
	    return True;
	 else
	    LOG ( facPM, llevNotice, "Failed to open OSI based connection to %s: %s",
		  sr_address2str ( to ), _ISODE_LastError () );
#endif /* LOW_ISODE */
      }
   }

   freeAssocDesc ( i );

   return False;
}

Boolean SR_CreateListener ( SR_Address *where, Boolean (*accept)( int COMMA_PM_LLK_ARG(LowerLayerKind), presContext *pci ) )
{
   Boolean		succ;

   PMAcceptFunc = accept;

   for ( succ = False; where; where = where->next )
   {
      currentProtocolVersion = where->protocolVersion;

      switch ( where->addressType )
      {
#ifdef LOW_ISODE
       case addrOSI:
	 succ = succ || _ISODE_CreateListener ( where, SR_AcceptAssoc );
	 break;
#endif

#ifdef LOW_TCPIP
       case addrTCPIP:
	 succ = succ || _TCPIP_CreateListener ( where, SR_AcceptAssoc );
	 break;
#endif
      }

   }

   return succ;
}

Boolean SR_VerifyAssociation ( int remoteRef )
{
   if ( associations && associations[remoteRef] )
   {
      currentProtocolVersion = associations[remoteRef]->protocolVersion;
      switch ( associations[remoteRef]->lowerLayerKind )
      {
#ifdef LOW_ISODE
       case llkISODE:
	 return _ISODE_VerifyAssociation ( associations[remoteRef]->lowerLayerRef );
#endif
#ifdef LOW_TCPIP
       case llkTCPIP:
	 return _TCPIP_VerifyAssociation ( associations[remoteRef]->lowerLayerRef );
#endif
      }
   }
   return False;
}

SROperation SR_PollNetwork ( struct timeval *timeout,
			     int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds,
			     int *ref )

/* TODO: When returning make sure that only file descriptors passed down
   through the fd_set parameters are set.
*/

{
   int			lref;
   int			i;
   SROperation		opr;
#ifdef LOW_TCPIP
   fd_set		*tcpr, *tcpw;
#endif

   if ( *ref == -1 )
      lref = -1;
   else
   {
      if ( !associations[*ref] )
      {
	 LOG ( facPM, llevExceptions, "SR_PollNetwork(): Unknown Association %d", ref );
	 return oprNone;
      }

      lref = associations[*ref]->lowerLayerRef;
   }

#ifdef LOW_TCPIP
   /* Add filedescriptors in use for the tcp/ip based lower layer */
   /* to the set being checked by ISODE */
   _TCPIP_GetSelectFD_SET ( &tcpr, &tcpw);
   for ( i = 0; i < FD_SETSIZE; i++ )
   {
      if ( FD_ISSET(i, tcpr) )
	 FD_SET( i, rfds );
      if ( FD_ISSET(i, tcpw) )
	 FD_SET( i, wfds );
   }
#endif /* LOW_TCPIP */

#ifdef LOW_ISODE
   opr = _ISODE_PollNetwork ( timeout,
			      nfds > FD_SETSIZE ? nfds : FD_SETSIZE, rfds, wfds, efds,
			      &lref );
#elif LOW_TCPIP
   /* Do select on file descriptors */
This option is not yet implemented
#endif /* LOW_TCPIP */

#ifdef LOW_TCPIP
   if ( opr == oprNone )
   {
      /* Check if activity was reported on one of the tcp/ip related file descriptors */
      static struct timeval tout	= { 0, 0 };

      _TCPIP_HandleFileDescriptors ( rfds, wfds, efds );
      opr = _TCPIP_PollNetwork ( &tout, &lref );
   }
#endif /* LOW_TCPIP */

   if ( opr != oprNone )
      for ( i = 0; i < allocAssociations; i++ )
	 if ( associations[i] && associations[i]->lowerLayerRef == lref
	      /*&& associations[i]->state != sAborted */)
	 {
	    *ref = i;
	    break;
	 }

   return opr;
}


/*
 * INITIALISE
 *
*/

Boolean SR_SendInitialiseRequest ( int ref, SRInitialiseRequest *irq )
{
   Boolean		res = False;

   /* assocumptions: we are in a closed state */
   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendInitialiseRequest: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != sClosed )
   {
      LOG ( facPM, llevExceptions, "SendInitialiseRequest: Illegal operation for this state: %s",
	   state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendInitialiseRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendInitialiseRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = osInitSent;
      return True;
   }
   else
      return False;
}


SRInitialiseRequest *SR_ReadInitialiseRequest ( int ref )
{
   SRInitialiseRequest			*req	= (SRInitialiseRequest *) NULL;
   SROperation				opr;

   /* asumptions: we are in a closed state */
   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions,
	    "ReadInitialiseRequest() unknown association %d", ref );
      return (SRInitialiseRequest *) NULL;
   }
   if ( !associations[ref]->state == sClosed )
   {
      LOG ( facPM, llevExceptions,
	    "ReadInitialiseRequest() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRInitialiseRequest *) NULL;
   }

   /* look for an initialise request on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	    == oprInitialiseRequest )
	 req = _ISODE_ReadInitialiseRequest ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprInitialiseRequest )
	 req = _TCPIP_ReadInitialiseRequest ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRInitialiseRequest() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( req )
      /* change state */
      associations[ref]->state = tsInitRcvd;

   if ( opr != oprInitialiseRequest )
      LOG ( facPM, llevExceptions,
	    "SR_ReadInitialiseRequest() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return req;
}

Boolean SR_SendInitialiseResponse ( int ref, SRInitialiseResponse *irq )
{
   Boolean				res = False;

   /* assocumptions: we are in a init received state */
   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendInitialiseResponse: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != tsInitRcvd )
   {
      LOG ( facPM, llevExceptions, "SendInitialiseResponse: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendInitialiseResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendInitialiseResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = sOpen;
      return True;
   }
   else
      return False;
}


SRInitialiseResponse *SR_ReadInitialiseResponse ( int ref )
{
   SRInitialiseResponse		*rsp	= (SRInitialiseResponse *) NULL;
   SROperation			opr;

   /* assumtions: we are in a closed state */
   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadInitialiseResponse() unknown association %d", ref );
      return (SRInitialiseResponse *) NULL;
   }
   if ( !associations[ref]->state == osInitSent )
   {
      LOG ( facPM, llevExceptions, "ReadInitialiseResponse() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRInitialiseResponse *) NULL;
   }

   /* look for an initialise Response on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprInitialiseResponse )
	 rsp = _ISODE_ReadInitialiseResponse ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprInitialiseResponse)
	 rsp = _TCPIP_ReadInitialiseResponse ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRInitialiseResponse() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( rsp )
      /* change state */
      associations[ref]->state = sOpen;

   if ( opr != oprInitialiseResponse )
      LOG ( facPM, llevExceptions, "SR_ReadInitialiseResponse() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return rsp;
}


/*
 * SEARCH
 *
*/

Boolean SR_SendSearchRequest ( int ref, SRSearchRequest *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendSearchRequest: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != sOpen )
   {
      LOG ( facPM, llevExceptions, "SendSearchRequest: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendSearchRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendSearchRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = osSearchSent;
      return True;
   }
   else
      return False;
}


SRSearchRequest *SR_ReadSearchRequest ( int ref )
{
   SRSearchRequest			*req	= (SRSearchRequest *) NULL;
   SROperation				opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadSearchRequest() unknown association %d", ref );
      return (SRSearchRequest *) NULL;
   }
   if ( !associations[ref]->state == sOpen )
   {
      LOG ( facPM, llevExceptions, "ReadSearchRequest() illegal state for this operation: %s",
	   state2str ( associations[ref]->state ) );
      return (SRSearchRequest *) NULL;
   }

   /* look for an Search request on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef )) == oprSearchRequest )
	 req = _ISODE_ReadSearchRequest ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef )) == oprSearchRequest )
	 req = _TCPIP_ReadSearchRequest ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRSearchRequest() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( req )
      /* change state */
      associations[ref]->state = tsSearchRcvd;

   if ( opr != oprSearchRequest )
      LOG ( facPM, llevExceptions, "SR_ReadSearchRequest() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return req;
}


Boolean SR_SendSearchResponse ( int ref, SRSearchResponse *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendSearchResponse: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != tsSearchRcvd )
   {
      LOG ( facPM, llevExceptions, "SendSearchResponse: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendSearchResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendSearchResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = sOpen;
      return True;
   }
   else
      return False;
}


SRSearchResponse *SR_ReadSearchResponse ( int ref )
{
   SRSearchResponse		*rsp		= (SRSearchResponse *) NULL;
   SROperation			opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadSearchResponse() unknown association %d", ref );
      return (SRSearchResponse *) NULL;
   }
   if ( !associations[ref]->state == osSearchSent )
   {
      LOG ( facPM, llevExceptions, "ReadSearchResponse() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRSearchResponse *) NULL;
   }

   /* look for an Search Response on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprSearchResponse )
	 rsp = _ISODE_ReadSearchResponse ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprSearchResponse )
	 rsp = _TCPIP_ReadSearchResponse ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRSearchResponse() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( rsp )
      /* change state */
      associations[ref]->state = sOpen;

   if ( opr != oprSearchResponse )
      LOG ( facPM, llevExceptions, "SR_ReadSearchResponse() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return rsp;
}



/*
 * PRESENT
 *
*/

Boolean SR_SendPresentRequest ( int ref, SRPresentRequest *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendPresentRequest: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != sOpen )
   {
      LOG ( facPM, llevExceptions, "SendPresentRequest: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendPresentRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendPresentRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = osPresentSent;
      return True;
   }
   else
      return False;
}


SRPresentRequest *SR_ReadPresentRequest ( int ref )
{
   SRPresentRequest			*req	= (SRPresentRequest *) NULL;
   SROperation				opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadPresentRequest() unknown association %d", ref );
      return (SRPresentRequest *) NULL;
   }
   if ( !associations[ref]->state == sOpen )
   {
      LOG ( facPM, llevExceptions, "ReadPresentRequest() illegal state for this operation: %s",
	   state2str ( associations[ref]->state ) );
      return (SRPresentRequest *) NULL;
   }

   /* look for an Present request on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprPresentRequest )
	 req = _ISODE_ReadPresentRequest ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	  == oprPresentRequest )
	 req = _TCPIP_ReadPresentRequest ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRPresentRequest() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( req )
      /* change state */
      associations[ref]->state = tsPresentRcvd;

   if ( opr != oprPresentRequest )
      LOG ( facPM, llevExceptions, "SR_ReadPresentRequest() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return req;
}

Boolean SR_SendPresentResponse ( int ref, SRPresentResponse *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendPresentResponse: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != tsPresentRcvd )
   {
      LOG ( facPM, llevExceptions, "SendPresentResponse: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendPresentResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendPresentResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = sOpen;
      return True;
   }
   else
      return False;
}


SRPresentResponse *SR_ReadPresentResponse ( int ref )
{
   SRPresentResponse		*rsp		= (SRPresentResponse *) NULL;
   SROperation			opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadPresentResponse() unknown association %d", ref );
      return (SRPresentResponse *) NULL;
   }
   if ( !associations[ref]->state == osPresentSent )
   {
      LOG ( facPM, llevExceptions, "ReadPresentResponse() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRPresentResponse *) NULL;
   }

   /* look for an Present Response on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprPresentResponse )
	 rsp = _ISODE_ReadPresentResponse ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprPresentResponse )
	 rsp = _TCPIP_ReadPresentResponse ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRPresentResponse() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( rsp )
      /* change state */
      associations[ref]->state = sOpen;

   if ( opr != oprPresentResponse )
      LOG ( facPM, llevExceptions, "SR_ReadPresentResponse() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return rsp;
}



/*
 * DELETE RESULT SET
 *
*/

Boolean SR_SendDeleteResultSetRequest ( int ref, SRDeleteResultSetRequest *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendDeleteResultSetRequest: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != sOpen )
   {
      LOG ( facPM, llevExceptions, "SendDeleteResultSetRequest: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendDeleteResultSetRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendDeleteResultSetRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = osDeleteResultSetSent;
      return True;
   }
   else
      return False;
}


SRDeleteResultSetRequest *SR_ReadDeleteResultSetRequest ( int ref )
{
   SRDeleteResultSetRequest		*req	= (SRDeleteResultSetRequest *) NULL;
   SROperation				opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadDeleteResultSetRequest() unknown association %d", ref );
      return (SRDeleteResultSetRequest *) NULL;
   }
   if ( !associations[ref]->state == sOpen )
   {
      LOG ( facPM, llevExceptions, "ReadDeleteResultSetRequest() illegal state for this operation: %s",
	   state2str ( associations[ref]->state ) );
      return (SRDeleteResultSetRequest *) NULL;
   }

   /* look for an DeleteResultSet request on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprDeleteResultSetRequest )
	 req = _ISODE_ReadDeleteResultSetRequest ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprDeleteResultSetRequest )
	 req = _TCPIP_ReadDeleteResultSetRequest ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRDeleteResultSetRequest() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( req )
      /* change state */
      associations[ref]->state = tsDeleteResultSetRecvd;

   if ( opr != oprDeleteResultSetRequest )
      LOG ( facPM, llevExceptions, "SR_ReadDeleteResultSetRequest() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return req;
}

Boolean SR_SendDeleteResultSetResponse ( int ref, SRDeleteResultSetResponse *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendDeleteResultSetResponse: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != tsDeleteResultSetRecvd )
   {
      LOG ( facPM, llevExceptions, "SendDeleteResultSetResponse: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendDeleteResultSetResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendDeleteResultSetResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = sOpen;
      return True;
   }
   else
      return False;
}


SRDeleteResultSetResponse *SR_ReadDeleteResultSetResponse ( int ref )
{
   SRDeleteResultSetResponse	*rsp	 = (SRDeleteResultSetResponse *) NULL;
   SROperation			opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadDeleteResultSetResponse() unknown association %d", ref );
      return (SRDeleteResultSetResponse *) NULL;
   }
   if ( !associations[ref]->state == osDeleteResultSetSent )
   {
      LOG ( facPM, llevExceptions, "ReadDeleteResultSetResponse() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRDeleteResultSetResponse *) NULL;
   }

   /* look for an DeleteResultSet Response on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprDeleteResultSetResponse )
	 rsp = _ISODE_ReadDeleteResultSetResponse ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprDeleteResultSetResponse )
	 rsp = _TCPIP_ReadDeleteResultSetResponse ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SRDeleteResultSetResponse() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( rsp )
      /* change state */
      associations[ref]->state = sOpen;

   if ( opr != oprDeleteResultSetResponse )
      LOG ( facPM, llevExceptions, "SR_ReadDeleteResultSetResponse() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return rsp;
}



/*
 * CLOSE
 *
*/

Boolean SR_AbortAssociation ( int ref )
{
   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "SR_AbortAssociation() unknown association %d", ref );
      return False;
   }

   /* XXX */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      _ISODE_ClearAssociation ( associations[ref]->lowerLayerRef );
      _ISODE_AbortAssociation ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      _TCPIP_ClearAssociation ( associations[ref]->lowerLayerRef );
      _TCPIP_AbortAssociation ( associations[ref]->lowerLayerRef );
      break;
#endif
   }

   associations[ref]->state = sClosed;
   freeAssocDesc ( ref );

   return True;
}



Boolean SR_SendCloseRequest ( int ref, SRCloseRequest *irq )
{
   Boolean				res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendCloseRequest: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != sOpen )
   {
      LOG ( facPM, llevExceptions, "SendCloseRequest: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendCloseRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendCloseRequest ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = osReleaseSent;
      return True;
   }
   else
      return False;
}


SRCloseRequest *SR_ReadCloseRequest ( int ref )
{
   SRCloseRequest			*req	= (SRCloseRequest *) NULL;
   SROperation				opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadCloseRequest() unknown association %d", ref );
      return (SRCloseRequest *) NULL;
   }
   if ( !associations[ref]->state == sOpen )
   {
      LOG ( facPM, llevExceptions, "ReadCloseRequest() illegal state for this operation: %s",
	   state2str ( associations[ref]->state ) );
      return (SRCloseRequest *) NULL;
   }

   /* look for an Close request on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
    case llkISODE:
#ifdef LOW_ISODE
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprCloseRequest )
	 req = _ISODE_ReadCloseRequest ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprCloseRequest )
	 req = _TCPIP_ReadCloseRequest ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SR_ReadCloseRequest() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( req )
      /* change state */
      associations[ref]->state = tsReleaseRcvd;

   if ( opr != oprCloseRequest )
      LOG ( facPM, llevExceptions, "SR_ReadCloseRequest() unexpected APDU received: %s",
	    sro2str ( opr ) );

   return req;
}

Boolean SR_SendCloseResponse ( int ref, SRCloseResponse *irq )
{
   Boolean					res = False;

   if ( !associations[ref] )
   {
      LOG ( facPM, llevExceptions, "SendCloseResponse: Unknown association" );
      return False;
   }
   else if ( associations[ref]->state != tsReleaseRcvd )
   {
      LOG ( facPM, llevExceptions, "SendCloseResponse: Illegal operation for this state: %s",
	    state2str ( associations[ref]->state ) );
      return False;
   }

   /* send it and if successfull update state */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      res = _ISODE_SendCloseResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      res = _TCPIP_SendCloseResponse ( associations[ref]->lowerLayerRef, irq );
      break;
#endif
   }

   if ( res )
   {
      associations[ref]->state = sClosed;

      freeAssocDesc ( ref );

      return True;
   }
   else
      return False;
}


SRCloseResponse *SR_ReadCloseResponse ( int ref )
{
   SRCloseResponse		*rsp	 = (SRCloseResponse *) NULL;
   SROperation			opr;

   if ( ref < 0 || (!associations[ref]) )
   {
      LOG ( facPM, llevExceptions, "ReadCloseResponse() unknown association %d", ref );
      return (SRCloseResponse *) NULL;
   }
   if ( !associations[ref]->state == osReleaseSent )
   {
      LOG ( facPM, llevExceptions, "ReadCloseResponse() illegal state for this operation: %s",
	    state2str ( associations[ref]->state ) );
      return (SRCloseResponse *) NULL;
   }

   /* look for an Close Response on the specified reference */
   currentProtocolVersion = associations[ref]->protocolVersion;
   switch ( associations[ref]->lowerLayerKind )
   {
#ifdef LOW_ISODE
    case llkISODE:
      if ( (opr = _ISODE_PollNetwork ( &Infinity, 0, 0, 0, 0,
				       &associations[ref]->lowerLayerRef ))
	   == oprCloseResponse )
	 rsp = _ISODE_ReadCloseResponse ( associations[ref]->lowerLayerRef );
      break;
#endif
#ifdef LOW_TCPIP
    case llkTCPIP:
      if ( (opr = _TCPIP_PollNetwork ( &Infinity, &associations[ref]->lowerLayerRef ))
	   == oprCloseRequest )
	 rsp = _TCPIP_ReadCloseResponse ( associations[ref]->lowerLayerRef );
      break;
#endif

    default:
      opr = oprNone;
      LOG (facPM, llevExceptions, "SR_ReadCloseResponse() unimplemented layer %d", associations[ref]->lowerLayerKind);
      break;
   }

   if ( rsp )
      /* change state */
      associations[ref]->state = sClosed;

   if ( opr != oprCloseRequest )
      LOG ( facPM, llevExceptions, "SR_ReadCloseResponse() unexpected APDU received: %s",
	    sro2str ( opr ) );

   freeAssocDesc ( ref );

   return rsp;
}
