/*
 * sr.c,v 1.1 1994/01/28 17:07:02 franktor Exp
 *
 * sr.c
 *
 * Copyright (c) 1992 Nordic SR-NET
 * Geir Pedersen, Geir.Pedersen@usit.uio.no
 *
 * Implementation of the low level SR-API for ISODE.
 *
 *
*/

/*
 * TODO
 *
 * o make sure that the proper element is removed from the "incomming" queue.
 *
*/


#include <sys/types.h>
#include <isode/acsap.h>
#include <isode/tsap.h>
#include "SR.h"
#include "ISO10163-SR-1-types.h"
#include <sr-api.h>
#include <sr-general.h>
#include <sr-util.h>
#include <sr-low.h>
#include <sr-address.h>

/*
 * qElem belongs to the low-level, had to move it here for the prototypes
 * to work.  -Frank.
 */

typedef struct qElem {		/* holding an incomming request */
   int		       	ref;
   PE		   	SR_APDU;
   PE			oprPE;
   SROperation		opr;
   struct qElem		*next;
} qElem;

#ifndef __CEXTRACT
#include "../tcpip/encdecproto.h"
#include "srproto.h"
#endif

/*
 * Utility functions
 *
*/

Boolean FD_ISZERO ( fd_set *x )
{
   if ( x )
   {
      int		i;
      int		size = sizeof ( x->fds_bits ) / sizeof ( *x->fds_bits );
      for ( i = 0; i < size; i++ )
	 if ( x->fds_bits[i] )
	    return False;
   }
   return True;
}
   

/*
 * Procedural interface to the ISODE low level module
 *
*/



char 			*qualifier		= "SR";
char			*applicationContext	= "SR BASIC";
char			*presentationContext	= "SR PCI";

OID			applicationContextOID;
OID			presentationContextOID;

qElem			*incomming		= (qElem *) NULL; /* queue of incomming unprocessed PEs */
Boolean 		(*AcceptFunc)(int, SR_protocolVersion, LowerLayerKind, presContext *);
void			(*AbortFunc)(int);



/*
 * OSI stuff 
*/

Boolean _OSI_Initialise ( LogLevel logLevel, FILE *log, void (*Abort)(int) )
{
   AbortFunc = Abort;
   
   return True;
}

Boolean _OSI_Verify ( void )
{
   return True;
}

void _OSI_Close ( void )
{
}

fd_set *_ISODE_GetSelectFD_SET ( void )
{
   Warn_UNIMPLEMENTED ( facLow, "_ISODE_GetSelectFD_SET()" );
}

char *_ISODE_LastError ( void )
{
   Warn_UNIMPLEMENTED ( facLow, "_ISODE_LastError()" );
}


Boolean SendPE ( int ref, PE pe, char *operation, char *kind )
{
   struct PSAPindication 	pis;
   struct PSAPabort		*pa;
   static PS			ps	= (PS) NULL;
   static FILE			*f;

   if ( !ps )
   {
      ps = ps_alloc ( std_open );
      
      if ( (f = fopen ( "PE-send.log", "w+" )) )
      {
	 fprintf ( f, "\n\n====================\n" );
	 fflush ( f );
      }

      /* I do not remember if the stream may be NULL */
      (void) std_setup ( ps, (f ? f : fopen ( "/dev/null", "w+" )) );
   }
   pe2pl ( ps, pe );
   if (f)
      fflush ( f );

   if ( PDataRequest ( ref, &pe, 1, &pis ) == NOTOK )
   {
      pa = &pis.pi_abort;
      LOG ( facLow, llevExceptions, "PDataRequest() failed for _ISODE_Send%s%s: %s", operation, kind, PErrString ( pa->pa_reason ) );
      if ( pa->pa_cc )
	 LOG ( facLow, llevExceptions, " [%*.*s]", pa->pa_cc, pa->pa_cc, pa->pa_data );

      return False;
   }

   return True;
}


/*
 * INTERNAL ROUTINES
 *
*/

static int SR_init ( vecp, vec )
      int		vecp;
      char		**vec;
{
   struct AcSAPstart  		acss;
   struct AcSAPstart		*acs			= &acss;
   struct PSAPctxlist		*ctxl			= &acs->acs_start.ps_ctxlist;
   struct AcSAPindication 	acis;
   struct AcSAPindication 	*aci			= &acis;
   struct AcSAPabort		*aca			= &aci->aci_abort;
   struct PSAPstart		*ps			= &acs->acs_start;
   int				remoteRef;
   presContext			*proposedPresContext	= (presContext *) NULL;
   presContext			*pc;
   int				i;
   SR_protocolVersion 		protoVersion;
   Oid				acnOid;
   
   if ( AcInit ( vecp, vec, acs, aci ) == NOTOK )
   {
      LOG ( facLow, llevExceptions, "SR_init(): Initialization of association failed: %s", AcErrString ( aca->aca_reason ) );
      return NOTOK;
   }

   acnOid = OID_str2Oid ( sprintoid ( acs->acs_context ) );
   if ( OID_EQ ( acnOid, Oid_SR_BASIC ) )
      protoVersion = proto_SR_V2;
   else if ( OID_EQ ( acnOid, Oid_Z3950_BASIC ) )
      protoVersion = proto_Z3950_V2;
   else
   {
      LOG ( facLow, llevExceptions, "Received Association Indication from %s with unknown application context %s - aborting association", OID_Oid2name ( acnOid ) );
      return NOTOK;
   }

   LOG ( facLow, llevNotice, "Received ACSE Association Indication from %s protocol %s",
	 paddr2str ( &ps->ps_calling, NULLNA ), protoVersion == proto_SR_V2 ? "SR" : "Z3950" );
   
   remoteRef = acs->acs_sd;

   /* generate presentation context description to be handed over to user for inspection */
   if ( ctxl )
      for ( i = 0;  i < ctxl->pc_nctx; i++ )
	 if ( ctxl->pc_ctx[i].pc_result == PC_ACCEPT )
	 {
	    Oid	asn_oid = OID_str2Oid ( sprintoid ( ctxl->pc_ctx[i].pc_asn ) );
	    Oid	atn_oid = OID_str2Oid ( sprintoid ( ctxl->pc_ctx[i].pc_atn ) );
	    proposedPresContext = addPC ( proposedPresContext,
					  pc = newPC ( asn_oid, atn_oid, i ) );

	    /* XXX: should be updated to take application context into consideration */
	    if ( OID_isNull ( pc->abstractSyntax ) )
	       pc->abstractSyntax = Oid_SR_ASN1;
	    if ( OID_isNull ( pc->transferSyntax ) )
	       pc->transferSyntax = Oid_SR_BER;
	 }

   if ( (*AcceptFunc)( remoteRef, protoVersion, llkISODE, proposedPresContext ) )
   {
      /* update presentation context list to be returned to initiator */
      presContext 		*p;

      for ( p = proposedPresContext; p; p = p->next )
	 if ( !p->accepted )
	    ctxl->pc_ctx[p->PCI].pc_result = PC_REJECTED;

      freePClist ( proposedPresContext );
      
      if ( AcAssocResponse ( remoteRef, ACS_ACCEPT, ACS_USER_NULL, NULLOID, NULLAEI, &ps->ps_called, NULLPC,
			     ps->ps_defctxresult, ps->ps_prequirements, ps->ps_srequirements, 
			     SERIAL_NONE, ps->ps_settings, &ps->ps_connect, NULLPEP, 0, aci ) == NOTOK )
      {
	 LOG ( facLow, llevExceptions, "warning: A-ASSOCIATE.RESPONSE: %s", AcErrString ( aca->aca_reason ) );
	 return NOTOK;
      }
   }
   else
   {
      if ( AcAssocResponse ( remoteRef, ACS_CONGEST, ACS_USER_NULL, NULLOID, NULLAEI, &ps->ps_called, NULLPC,
			     ps->ps_defctxresult, ps->ps_prequirements, ps->ps_srequirements, 
			     SERIAL_NONE, ps->ps_settings, &ps->ps_connect, NULLPEP, 0, aci ) == NOTOK )
      {
	 LOG ( facLow, llevExceptions, "warning: A-ASSOCIATE.RESPONSE: %s", AcErrString ( aca->aca_reason ) );
	 return NOTOK;
      }
   }

   ACSFREE ( acs );

   return remoteRef;
}

static int SR_work ( remoteRef )
      int			remoteRef;
{
   struct PSAPindication 	pis;
   int				result;
   struct PSAPdata		pxs;
   struct AcSAPindication 	acis;
   int				i;
   struct AcSAPindication	aci;
   /* struct _InitialiseRequest	*irq = (struct _InitialiseRequest *) NULL; */
   struct PSAPabort		*pa;
   
   LOG ( facLow, llevTrace, "SR_work ( %d )", remoteRef );
   
   switch ( result = PReadRequest ( remoteRef, &pxs, 60, &pis ) )
   {
      qElem			*e;
      qElem			*x, *p;

    case NOTOK:
      pa = &pis.pi_abort;
      LOG ( facLow, llevExceptions, "PReadRequest() failed: %s", PErrString ( pa->pa_reason ) );
      if ( pa->pa_cc )
	 LOG ( facLow, llevExceptions, " [%*.*s]", pa->pa_cc, pa->pa_cc, pa->pa_data );
      LOG ( facLow, llevExceptions, "Aborting association %d", remoteRef );
      (void) AcABORTser ( remoteRef, pa, &aci );
      (*AbortFunc)(remoteRef);

      /* pass abort indication to user */
      e = (qElem *) smalloc ( sizeof ( qElem ) );
      e->ref = remoteRef;
      e->SR_APDU = NULLPE;
      e->oprPE = NULLPE;
      e->opr = oprAbort;

      for ( x = incomming, p = (qElem *) NULL; x; p = x, x = x->next );
      if ( !p )
	 incomming = e;
      else
	 p->next = e;
      e->next = (qElem *) NULL;
      return OK;

    case OK:
      for ( i = 0; i < pxs.px_ninfo; i++ )
      {
	 qElem						*e	= (qElem *) smalloc ( sizeof ( qElem ) );
	 qElem						*x, *p;
	 struct _SR_APDU				*sr_apdu;
	 static PS			ps	= (PS) NULL;
	 static FILE *f;

	 if ( !ps )
	   {
	     ps = ps_alloc ( std_open );
      
	     if ( (f = fopen ( "PE-recv.log", "w+" )) )
	       {
		 fprintf ( f, "\n\n====================\n" );
		 fflush ( f );
	       }

	     /* I do not remember if the stream may be NULL */
	     (void) std_setup ( ps, (f ? f : fopen ( "/dev/null", "w+" )) );
	   }
	 pe2pl ( ps, pxs.px_info[i] );
	 if (f)
	   fflush ( f );
	 
	 e->ref = remoteRef;
	 e->SR_APDU = pxs.px_info[i];
	 e->oprPE = NULLPE;
	 e->opr = oprNone;

	 if ( decode_ISO10163__SR__1_SR__APDU ( e->SR_APDU, 1, 0, NULLCP, &sr_apdu ) == NOTOK )
	 {
	    LOG ( facLow, llevExceptions, "_ISODE_Work() failed to decode SR-APDU" );

	    /* XXX Should free APDU */
	    continue;
	 }

	 /* Hack to get around stripped SEQUENCE */
	 if ( e->SR_APDU->pe_un1.un_pe_cons->pe_id != PE_CONS_SEQ )
         {
	   PE                seq;
	   PE                p1, p2;

	   seq = pe_alloc ( PE_CLASS_UNIV, PE_FORM_CONS, PE_CONS_SEQ );
	   p1 = sr_apdu->pe, p2 = p1->pe_next, p1->pe_next = NULLPE;
	   do {
	     p1 = p2;
	     p2 = p2->pe_next;
	     if ( p1 )
	       p1->pe_next = NULLPE;

	     seq_add ( seq, p1, -1 );
	   }
	   while ( p2 );

	   sr_apdu->pe = seq;
         }

	 switch ( sr_apdu->offset )
	 {
	  case 1: e->opr = oprInitialiseRequest;       break;
	  case 2: e->opr = oprInitialiseResponse;      break;
	  case 3: e->opr = oprSearchRequest;           break;
	  case 4: e->opr = oprSearchResponse;          break;
	  case 5: e->opr = oprPresentRequest;          break;
	  case 6: e->opr = oprPresentResponse;         break;
	  case 7: e->opr = oprDeleteResultSetRequest;  break;
	  case 8: e->opr = oprDeleteResultSetResponse; break;
	  default:
	    LOG ( facLow, llevExceptions, "_ISODE_work(): unknown operation" );
	    continue;
	 }

	 e->oprPE = sr_apdu->pe;
	 
	 /* append the new APDU to the queue of incomming APDUs */
	 for ( x = incomming, p = (qElem *) NULL; x; p = x, x = x->next );
	 if ( !p )
	    incomming = e;
	 else
	    p->next = e;
	 e->next = (qElem *) NULL;
      }
      return OK;

    case DONE:
      switch ( pis.pi_type )
      {
	 qElem				*e;
	 qElem				*x, *p;

       case PI_TOKEN:
	 LOG ( facLow, llevTrace, "PI_TOKEN received" );
	 break;
	 
       case PI_SYNC:
	 LOG ( facLow, llevTrace, "PI_SYNC received" );
	 break;
	 
       case PI_ACTIVITY:
	 LOG ( facLow, llevTrace, "PI_ACTIVITY received" );
	 break;
	 
       case PI_REPORT:
	 LOG ( facLow, llevTrace, "PI_REPORT received" );
	 break;
	 
       case PI_FINISH:
	 LOG ( facLow, llevTrace, "PI_FINISH received" );

	 if ( AcFINISHser ( remoteRef, &pis.pi_finish, &acis ) == NOTOK )
	 {
	    LOG ( facLow, llevTrace, "AcFINISHser() failed" );
	    return NOTOK;
	 }

	 /* fake close request APDU for user */
	 e = (qElem *) smalloc ( sizeof ( qElem ) );
	 e->opr = oprCloseRequest;
	 e->ref = remoteRef;
	 e->SR_APDU = NULLPE;
	 e->oprPE = NULLPE;

	 /* append the new APDU to the queue of incomming APDUs */
	 for ( x = incomming, p = (qElem *) NULL; x; p = x, x = x->next );
	 if ( !p )
	    incomming = e;
	 else
	    p->next = e;
	 e->next = (qElem *) NULL;
	 break;

       default:
	 LOG ( facLow, llevExceptions, "Unknown indication (%d) received", pis.pi_type );
	 return NOTOK;
      }
      return OK;

    default:
      LOG ( facLow, llevExceptions, "Unexpected return from PReadRequest()=%d", result );
      return NOTOK;
   }
}


static int SR_lose ( struct TSAPdisconnect *td )
{
   LOG ( facLow, llevExceptions, "SR_lose ( %s )", TErrString ( td->td_reason ) );
   
   return OK;
}



/*
 * OPEN AND CLOSE
*/

Boolean _ISODE_OpenAssociation ( SR_Address *to, presContext **pci, int *remoteRef )
{
   struct PSAPaddr		*presentationAddress;
   struct PSAPctxlist		presentationContextList;
   struct SSAPref		*sessionConnectionIdPtr, sessionConnectionId;

   struct AcSAPconnect		accs;
   struct AcSAPconnect		*acc			= &accs; 
   struct AcSAPindication 	acis;
   struct AcSAPindication 	*aci			= &acis;
   struct AcSAPabort		*aca			= &aci->aci_abort;

   presContext			*pc			= 0;
   int				i;

   if ( !to || to->addressType != addrOSI )
   {
      LOG ( facLow, llevExceptions, "Unspported addresstype for ISODE" );
      return False;
   }

   if ( (presentationAddress = str2paddr ( to->addr.osi_address )) == NULLPA )
   {
      LOG ( facLow, llevExceptions, "failed to parse address \"%s\"", to->addr.osi_address );
      return False;
   }
   
   if ( (applicationContextOID = ode2oid ( applicationContext )) == NULLOID )
   {
      LOG ( facLow, llevExceptions, "unknown object descriptor for application context: \"%s\"", applicationContext );
      return False;
   }

   if ( (presentationContextOID = ode2oid ( presentationContext )) == NULLOID )
   {
      LOG ( facLow, llevExceptions, "unknown object descriptor for presentation context: \"%s\"", presentationContext );
      return False;
   }

   /* presentation contexts */
   /* ASN.1/BER */
   presentationContextList.pc_nctx = 1;
   presentationContextList.pc_ctx[0].pc_id = 1;
   presentationContextList.pc_ctx[0].pc_asn = presentationContextOID;
   presentationContextList.pc_ctx[0].pc_atn = NULLOID;

   if ( pci && *pci )
      for ( pc = *pci, i = 1; pc && i < NPCTX; pc = pc->next, i++ )
      {
	 presentationContextList.pc_ctx[i].pc_id = (i*2)+1;
	 pc->PCI = (i*2)+1;
	 presentationContextList.pc_ctx[i].pc_asn = oid_cpy ( OID_GET ( pc->abstractSyntax ) );
	 presentationContextList.pc_ctx[i].pc_atn = oid_cpy ( OID_GET ( pc->transferSyntax ) );
	 presentationContextList.pc_nctx++;
      }

   if ( pc )
   {
      LOG ( facLow, llevExceptions, "Too many Presentation Contexts - can't handle more than %d", NPCTX-1 );

      /* mark the other PCs as unaccepted */
      for ( ; pc; pc = pc->next )
	 pc->accepted = False;
   }

   if ( (sessionConnectionIdPtr = addr2ref ( PLocalHostName() )) == NULL )
   {
      sessionConnectionIdPtr = &sessionConnectionId;
      (void) bzero ( (char *) sessionConnectionIdPtr, sizeof ( sessionConnectionId ) );
   }
   
   if ( AcAssocRequest ( applicationContextOID, NULLAEI, NULLAEI, NULLPA, presentationAddress,
			 &presentationContextList, NULLOID, 0, SR_DUPLEX, SERIAL_NONE, 0,
			 sessionConnectionIdPtr, NULLPEP, 0, NULLQOS, acc, aci )
        == NOTOK )
   {
      
      LOG ( facLow, llevExceptions, "association rejected: %s", AcErrString ( aca->aca_reason ) );
      return False;
   }
   if ( acc->acc_result != ACS_ACCEPT )
   {
      LOG ( facLow, llevExceptions, "association rejected: %s", AcErrString ( aca->aca_reason ) );
      return False;
   }
      
   *remoteRef = acc->acc_sd;

   Iserver_add_fd ( acc->acc_sd );

   if ( pci )
      for ( i = 0; i < acc->acc_connect.pc_ctxlist.pc_nctx; i++ )
	 if ( (pc = findPCI ( *pci, acc->acc_connect.pc_ctxlist.pc_ctx[i].pc_id )) )
	    pc->accepted = acc->acc_connect.pc_ctxlist.pc_ctx[i].pc_result == PC_ACCEPT ? True : False;

   ACCFREE ( acc );

   return True;
}

Boolean _ISODE_CreateListener ( SR_Address *where,
			        Boolean (*accept)( int, SR_protocolVersion, LowerLayerKind, presContext *pci ) )
{
   struct PSAPaddr		*paddr;
   struct TSAPdisconnect	tds;
   struct TSAPdisconnect 	*td = &tds;

   if ( !where || where->addressType != addrOSI )
   {
      LOG ( facLow, llevExceptions, "Unspported addresstype for ISODE" );
      return False;
   }
   
   AcceptFunc = accept;

   if ( (paddr = str2paddr ( where->addr.osi_address )) == NULLPA )
   {
      fprintf ( stderr, "unable to translate OSI address: %s", where->addr.osi_address );
      return False;
   }
   
   if ( Iserver_init ( 0, (char **) NULL, paddr, SR_init, td ) == NOTOK )
   {
      if ( td->td_cc > 0 )
	 LOG ( facLow, llevExceptions,
	       "Iserver_init: [%s] %*.*s", TErrString ( td->td_reason), td->td_cc, td->td_cc, td->td_data );
      else
	 LOG ( facLow, llevExceptions,
	       "Iserver_init: [%s]", TErrString(td->td_reason) );

      return False;
   }

   return True;
}

void _ISODE_AbortAssociation ( int ref )
{
   struct AcSAPindication		aci;
   
   (void) AcUAbortRequest ( ref, (PE *) NULL, 0, &aci );

   Iserver_sub_fd ( ref );
}


Boolean _ISODE_VerifyAssociation ( int ref )
{
   return False;
}

void _ISODE_ClearAssociation ( int ref )
{
   qElem			*x, *p;

   for ( x = incomming, p = (qElem *) NULL; x; p = x, x = x->next )
      if ( x->ref == ref )
      {
	 if ( p )
	    p->next = x->next;
	 else
	    incomming = x->next;

	 if ( x->SR_APDU )
	    pe_free ( x->SR_APDU );
	 free ( x );
      }
}


Boolean _ISODE_CheckIncommingQ ( int *ref, qElem **qelem )
{
   qElem			*x, *prev;

   for ( x = incomming, prev = (qElem *) NULL; x; prev = x, x = x->next )
      if ( (*ref == -1) || (*ref == x->ref) )
      {
	 *ref = x->ref;
	 *qelem = x;

	 /* move found element first in queue */
	 if ( prev )
	 {
	    prev->next = x->next;
	    x->next = incomming;
	    incomming = x;
	 }
	 
	 return True;
      }

   return False;
}

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

      /*

   Call this routine to wait for a new APDU. You can either wait for any APDU
   to arrive on any association, or specify which association the APDU should
   be related to. To specify that an APDU from any association is acceptabel,
   specify -1 in the ref parameter, or, to indicate that you are only
   interested in an APDU from a specific association specify its reference
   number in the ref parameter.

   If activity is spotted on one of the indicated file discriptors, oprNone is
   returned.

      */

{
   int				Timeout		= 0;
   struct TSAPdisconnect	tds;
   struct TSAPdisconnect 	*td 		= &tds;
   qElem			*qe;
   fd_set			ifds, ofds, xfds;

   if ( _ISODE_CheckIncommingQ ( ref, &qe ) )
   {
      SROperation		opr 		= qe->opr;
      
      *ref = qe->ref;

      if ( qe->opr == oprAbort )
      {
	 /* this is just a notification - remove qElem */
	 qElem			*x;

	 x = incomming;
	 incomming = incomming->next;
	 
	 if ( x->SR_APDU )
	    pe_free ( x->SR_APDU );
	 free ( x );
      }

      return opr;
   }
   
   /* wait for more data to arrive */
   if ( timeout )
   {
      if ( timeout->tv_sec > 0 )
	 Timeout = timeout->tv_sec;
      else if ( timeout->tv_usec > 0 )
	 Timeout = 1;
   }
   else
      Timeout = -1;

   if ( rfds )
      ifds = *rfds;
   else
      FD_ZERO ( &ifds );
   if ( wfds )
      ofds = *wfds;
   else
      FD_ZERO ( &ofds );
   if ( efds )
      xfds = *efds;
   else
      FD_ZERO ( &xfds );

 again:
   switch ( Iserver_wait ( SR_init, SR_work, SR_lose, nfds, &ifds, &ofds, &xfds, Timeout, td ) )
   {
    case DONE:
      LOG ( facLow, llevExceptions, "Iserver_wait() retruned DONE" );
      return oprNone;

    case NOTOK:
      /* error */
      if ( td->td_cc > 0 )
	 LOG ( facLow, llevExceptions,
	       "Iserver_wait: [%s] %*.*s",
	       TErrString ( td->td_reason), td->td_cc, td->td_cc, td->td_data );
      else
	 LOG ( facLow, llevExceptions,
 	       "Iserver_wait: [%s]", TErrString(td->td_reason) );
      return oprNone;

    case OK:
      break;
   }

   if ( _ISODE_CheckIncommingQ ( ref, &qe ) )
   {
      SROperation		opr 		= qe->opr;

      *ref = qe->ref;

      if ( rfds )
	 *rfds = ifds;
      if ( wfds )
	 *wfds = ofds;
      if ( efds )
	 *efds = xfds;

      if ( qe->opr == oprAbort )
      {
	 /* this is just a notification - remove qElem */
	 qElem			*x;

	 x = incomming;
	 incomming = incomming->next;
	 
	 if ( x->SR_APDU )
	    pe_free ( x->SR_APDU );
	 free ( x );
      }
      
      return opr;
   }

   if ( Timeout == -1 && FD_ISZERO(&ifds) && FD_ISZERO(&ofds) && FD_ISZERO(&xfds) )
      goto again;
   
   if ( rfds )
      *rfds = ifds;
   if ( wfds )
      *wfds = ofds;
   if ( efds )
      *efds = xfds;

   return oprNone;
}

   
   

/*
 * Include generated macroes which contain all the SR_Send* and
 * SR_Read* functions.
 */

#include "sr_macroes_cpp.h"

Boolean _ISODE_SendCloseRequest ( int ref, SRCloseRequest *crq )
{
   struct AcSAPrelease			acr;
   struct AcSAPindication		aci;
   qElem				*e;
   qElem				*x, *p;

   if ( AcRelRequest ( ref, ACF_NORMAL, NULLPEP, 0, 10, &acr, &aci ) != OK )
   {
      LOG ( facLow, llevExceptions, "Failed to close association %d, P-RELEASE.REQUEST: %s", ref,
	   PErrString ( aci.aci_abort.aca_reason ) );

      LOG ( facLow, llevExceptions, "Aborting association %d", ref );
      (void) AcUAbortRequest ( ref, NULLPEP, 0, &aci );
   }
   else
   {
      if ( !acr.acr_affirmative ) 
	 (void) AcUAbortRequest ( ref, NULLPEP, 0, &aci );

      ACRFREE ( &acr );
   }

   /* connection is now closed, fake close confirm indication to user */
   e = (qElem *) smalloc ( sizeof ( qElem ) );
   e->opr = oprCloseResponse;
   e->ref = ref;
   e->SR_APDU = NULLPE;
   e->oprPE = NULLPE;

   /* append the new APDU to the queue of incomming APDUs */
   for ( x = incomming, p = (qElem *) NULL; x; p = x, x = x->next );
   if ( !p )
      incomming = e;
   else
      p->next = e;
   e->next = (qElem *) NULL;

   return True;
}


Boolean _ISODE_SendCloseResponse ( int ref, SRCloseResponse *crp )
{
   struct AcSAPindication 	acis;

   if ( AcRelResponse ( ref, ACS_ACCEPT, ACR_NORMAL, NULLPEP, 0, &acis ) == NOTOK )
      LOG ( facLow, llevExceptions, "AcRelResponse() failed" );

   Iserver_sub_fd ( ref );	

   return True;
}


SRCloseRequest *_ISODE_ReadCloseRequest ( int ref )
{
   /* free _irs */
   incomming = incomming->next;

   return (SRCloseRequest *) smalloc ( sizeof ( struct SRCloseRequest ) );
}


SRCloseResponse *_ISODE_ReadCloseResponse ( int ref )
{
   Iserver_sub_fd ( ref );

   /* free _irs */
   incomming = incomming->next;

   return (SRCloseResponse *) smalloc ( sizeof ( struct SRCloseResponse ) );
}

