// IvyApplication.cpp : implementation file // #include "IvyStdAfx.h" #include "IvyApplication.h" #include "IvyBinding.h" #include "IvySynchroWnd.h" //#define IVY_DEBUG #define ARG_START 2 #define ARG_END 3 #define MAXPORT(a,b) ((a>b) ? a : b) static char * firstArg( char *s, const char separator ) { char *ptr = s; while ( *ptr && *ptr != separator ) ptr++; if ( *ptr == separator ) return ptr++ ; else return NULL; } /* function like strok but do not eat consecutive separator */ static char * nextArg( char **s, const char separator ) { char *start = *s; char *end = *s; while ( *end && *end != separator ) end++; if ( *end == separator ) *end++ = '\0'; if ( end == start ) return NULL; *s = end; return start; } ///////////////////////////////////////////////////////////////////////////// // IvyApplication IvyApplication::IvyApplication(Ivy * bus) { this->bus = bus; remoteService = 0; /* unknown or unconnected application */ appname = "Unknown"; AppConnectedCallbackCalled = false; readyToSend = 0; regexp_in.clear(); InitializeCriticalSection(&m_SendReadySection ); } IvyApplication::~IvyApplication() { // bus->RemoveApplication( this ); for ( Bindings::iterator iter = regexp_in.begin( ); iter != regexp_in.end( ); iter++ ) delete iter->second; regexp_in.clear(); if ( m_hSocket != INVALID_SOCKET ) Close(); // Release resources used by the critical section object. DeleteCriticalSection(&m_SendReadySection); } ///////////////////////////////////////////////////////////////////////////// // IvyApplication member functions void IvyApplication::Create(const char* host, UINT & port, const char* name) { appname = name; // Exception to catch CBufferedSocket::Create(); Connect(host, port); } UINT IvyApplication::Create() { ivy::string host; UINT port; CBufferedSocket::Create(); // Max Listen Connexion Listen( SOMAXCONN ); GetSockName( host, port ); TRACE(" TCP %s:%d\n", host.c_str(), port ); #ifdef IVY_DEBUG TRACE( "IvyApplication::Create server socket %d\n", m_hSocket ); #endif return port; } void IvyApplication::SendReadyToPeer() { EnterCriticalSection (&m_SendReadySection); readyToSend++; if ( !bus->ready_message.empty() && readyToSend == 2 ) SendMsg( bus->ready_message.c_str() ); LeaveCriticalSection (&m_SendReadySection); } void IvyApplication::OnReceive(char * line) { int err; unsigned int id; int kind_of_msg = Bye; char *arg; int argc = 0; char *arg_ptr; static const int max_subexp = 200; const char *argv[max_subexp]; IvyBinding *exp; int erroffset; const char *errmsg; #ifdef IVY_DEBUG TRACE("Receive %s\n",line); #endif //IVY_DEBUG err = sscanf_s( line ,"%d %d", &kind_of_msg, &id); arg = firstArg( line , ARG_START ); if ( (err != 2) || (arg == NULL) ) { TRACE("Quitting bad format %s\n", line); SendMsg(Error, Error, "bad format request expected 'type id ...'"); Close(); return; } arg++; switch( kind_of_msg ) { case Bye: #ifdef IVY_DEBUG TRACE("Quitting %s\n", line); #endif //IVY_DEBUG OnClose(0); break; case Error: #ifdef IVY_DEBUG TRACE("Receive error %d %s\n", id, arg); #endif //IVY_DEBUG break; case AddRegexp: #ifdef IVY_DEBUG TRACE("Regexp id=%d exp='%s'\n", id, arg); #endif //IVY_DEBUG if ( !IvyBinding::Filter( arg ) ) { #ifdef IVY_DEBUG TRACE("Warning exp='%s' can't match removing from %s\n",arg,appname.c_str()); #endif //IVY_DEBUG bus->CallBindingFilterCallback( this, id, arg ); return; } exp = new IvyBinding(); if ( !exp->Compile(arg, &erroffset, &errmsg ) ) { ivy::string errstr( "Error can't compile regexp '" ); errstr += arg; errstr += "' error "; errstr += errmsg; SendMsg( Error, Error, errstr.c_str() ); TRACE("IvyApplication %s\n",errstr.c_str()); delete exp; return; } /*if ( regexp_in.size() < (id + 1) ) { regexp_in.resize( id + 1 ); regexp_str_in.resize( id + 1 ); }*/ regexp_in[ id ] = exp; #ifdef IVY_DEBUG TRACE("Adding regexp[%d]='%s' size: %d\n",id,arg,regexp_in.size()); #endif //IVY_DEBUG bus->CallBindingAddCallback( this, id, arg ); break; case DelRegexp: #ifdef IVY_DEBUG TRACE("Regexp Delete id=%d\n", id); #endif //IVY_DEBUG if ( regexp_in[id] ) { exp = regexp_in[ id ]; bus->CallBindingRemoveCallback( this, id, exp->getExpression() ); delete exp; regexp_in[ id ] = NULL; } break; case StartRegexp: { #ifdef IVY_DEBUG TRACE("Regexp Start id=%d\n", id); #endif //IVY_DEBUG appname = arg; /* remote socket port */ remoteService = id; IvyApplication *other = bus->CheckConnected( this ); if ( other ) { // Dilemma choose the rigth client to close // the symetric processing will try to close each other // only one side may be closed IvyApplication *target; if (MAXPORT(other->GetLocalPort(), other->GetRemotePort()) > MAXPORT( this->GetLocalPort(), this->GetRemotePort() )) { target = other; TRACE("choose %s other ports %d,%d\n", appname.c_str(), target->GetLocalPort(), target->GetRemotePort()); } else { target = this; TRACE("choose %s this ports %d,%d\n", appname.c_str(), target->GetRemotePort(), target->GetLocalPort()); } TRACE("Quitting already connected %s\n", appname.c_str()); target->SendMsg( Error, Error, "already connected" ); target->Close(); } } break; case EndRegexp: #ifdef IVY_DEBUG TRACE("Regexp End id=%d\n", id); #endif //IVY_DEBUG bus->CallApplicationConnectedCallback( this ); AppConnectedCallbackCalled = true; SendReadyToPeer(); break; case Msg: #ifdef IVY_DEBUG TRACE("Message id=%d msg='%s'\n", id, arg); #endif //IVY_DEBUG arg_ptr = arg; arg = nextArg( &arg_ptr, ARG_END); while ( arg ) { argv[argc++] = arg; arg = nextArg( &arg_ptr, ARG_END ); } bus->CallMessageCallback( this, id, argc, argv ); break; case DirectMsg: #ifdef IVY_DEBUG TRACE("Direct Message id=%d msg='%s'\n", id, arg); #endif //IVY_DEBUG bus->CallDirectMessageCallback( this, id, arg ); break; case Die: #ifdef IVY_DEBUG TRACE("Die Message id=%d msg='%s'\n", id, arg); #endif //IVY_DEBUG if ( bus->CallDieCallback( this, id, arg ) ) { IvySynchroWnd::PostQuit(); } break; case Ping: #ifdef IVY_DEBUG TRACE("Ping Message\n"); #endif //IVY_DEBUG this->SendMsg( Pong, id, arg ); //reply with same id and arg break; case Pong: #ifdef IVY_DEBUG TRACE("Pong Message\n"); #endif //IVY_DEBUG TRACE("Receive unhandled Pong message (ivy-c++ not able to send ping)\n"); break; default: TRACE("Receive unhandled message %s\n", line); break; } } void IvyApplication::SendMsg(MsgType msg, int id, const char * arg) { char buffer[4096]; if ( arg ) _snprintf_s( buffer, sizeof( buffer ),sizeof( buffer )-1, "%d %d%c%s\n", msg, id, ARG_START, arg ); else sprintf_s( buffer,sizeof( buffer ), "%d %d%c\n", msg, id, ARG_START); #ifdef IVY_DEBUG TRACE("SendMsg %s\n",buffer); #endif //IVY_DEBUG Send( buffer ); } void IvyApplication::OnAccept(int nErrorCode) { ivy::string remotehost; UINT remoteport; // construct a new, empty socket IvyApplication *newapp = new IvyApplication(bus); // accept connection Accept( *newapp ); newapp->GetPeerName( remotehost, remoteport ); TRACE("Connexion de %s:%u\n", remotehost.c_str(), remoteport ); bus->AddApplication( newapp ); } void IvyApplication::OnClose(int nErrorCode) { ivy::string remotehost; UINT remoteport; GetPeerName( remotehost, remoteport ); TRACE("Deconnexion de %s:%u\n", remotehost.c_str(), remoteport ); if ( AppConnectedCallbackCalled ) bus->CallApplicationDisconnectedCallback( this ); for ( Bindings::iterator iter = regexp_in.begin( ); iter != regexp_in.end( ); iter++) delete iter->second; regexp_in.clear(); Close(); } int IvyApplication::SendMsg(const char *message) { int count = 0; IvyBinding *exp; /* send to already connected */ for ( Bindings::iterator iter = regexp_in.begin( ); iter != regexp_in.end( ); iter++ ) { exp = iter->second; if ( !exp ) continue; int match_count = exp->Exec( message ); if ( match_count > 0 ) { ivy::string buffer; int arglen; const char *arg; for ( int j = 1; j < match_count; j++ ) { exp->Match(message, j, &arglen, &arg); buffer += ivy::string(arg,arglen ); buffer += ARG_END; } SendMsg( Msg, iter->first, buffer.c_str() ); count++; } } return count; } const char *IvyApplication::GetName(void) { return appname.c_str(); } BOOL IvyApplication::SameApplication(IvyApplication * app) { ivy::string host1; UINT port1; ivy::string host2; UINT port2; if ( (remoteService != 0) && (remoteService == app->remoteService) ) { GetPeerName( host1, port1 ); app->GetPeerName( host2, port2 ); TRACE( "IvyApplication::SameApplication %s:%d %s:%d\n", host1.c_str(),port1, host2.c_str(),port2); return ( host1 == host2 ); } return false; }