Files
harbour-core/harbour/contrib/xhb/trpccli.prg
Viktor Szakats f6c08520e4 2012-07-21 18:31 UTC+0200 Viktor Szakats (vszakats syenar.net)
+ tests/stripeol.hb
    + added script to strip spaces at EOL

  * contrib/gtalleg/tests/test.prg
  * contrib/gtwvg/tests/demoxbp.prg
  * contrib/gtwvg/tests/wvgactivex.prg
  * contrib/gtwvg/tests/wvgxbp.prg
  * contrib/gtwvg/wvgclass.prg
  * contrib/hbamf/tests/tstendin.prg
  * contrib/hbbz2/tests/test.prg
  * contrib/hbcairo/tests/fancytxt.prg
  * contrib/hbcairo/tests/table.prg
  * contrib/hbct/ctrand.prg
  * contrib/hbct/doc/en/addascii.txt
  * contrib/hbct/doc/en/atadjust.txt
  * contrib/hbct/doc/en/count.txt
  * contrib/hbct/doc/en/ct.txt
  * contrib/hbct/doc/en/ctc.txt
  * contrib/hbct/doc/en/ctmath.txt
  * contrib/hbct/doc/en/ctmath2.txt
  * contrib/hbct/doc/en/ctpad.txt
  * contrib/hbct/doc/en/ctstr.txt
  * contrib/hbct/doc/en/datetime.txt
  * contrib/hbct/doc/en/dattime2.txt
  * contrib/hbct/doc/en/exponent.txt
  * contrib/hbct/doc/en/finan.txt
  * contrib/hbct/doc/en/ftoc.txt
  * contrib/hbct/doc/en/justify.txt
  * contrib/hbct/doc/en/keyset.txt
  * contrib/hbct/doc/en/misc1.txt
  * contrib/hbct/doc/en/num1.txt
  * contrib/hbct/doc/en/numat.txt
  * contrib/hbct/doc/en/numconv.txt
  * contrib/hbct/doc/en/pos1.txt
  * contrib/hbct/doc/en/pos2.txt
  * contrib/hbct/doc/en/posdiff.txt
  * contrib/hbct/doc/en/print.txt
  * contrib/hbct/doc/en/range.txt
  * contrib/hbct/doc/en/relation.txt
  * contrib/hbct/doc/en/remove.txt
  * contrib/hbct/doc/en/replace.txt
  * contrib/hbct/doc/en/screen1.txt
  * contrib/hbct/doc/en/strdiff.txt
  * contrib/hbct/doc/en/strswap.txt
  * contrib/hbct/doc/en/token1.txt
  * contrib/hbct/doc/en/token2.txt
  * contrib/hbct/doc/en/trig.txt
  * contrib/hbct/doc/en/video.txt
  * contrib/hbct/doc/en/wordtoch.txt
  * contrib/hbct/getinfo.prg
  * contrib/hbfimage/tests/fitest.prg
  * contrib/hbgd/gdimage.prg
  * contrib/hbgd/tests/counter.prg
  * contrib/hbgd/tests/gdtest.prg
  * contrib/hbgd/tests/gdtestcl.prg
  * contrib/hbgd/tests/test_out.prg
  * contrib/hbide/idetags.prg
  * contrib/hblzf/tests/test2.prg
  * contrib/hbmisc/doc/en/ht_conv.txt
  * contrib/hbmisc/fcomma.prg
  * contrib/hbmisc/fileread.prg
  * contrib/hbmisc/hbedit.prg
  * contrib/hbnf/anomatch.prg
  * contrib/hbnf/calendar.prg
  * contrib/hbnf/clrsel.prg
  * contrib/hbnf/dispmsg.prg
  * contrib/hbnf/doc/en/aading.txt
  * contrib/hbnf/doc/en/aavg.txt
  * contrib/hbnf/doc/en/acctadj.txt
  * contrib/hbnf/doc/en/acctmnth.txt
  * contrib/hbnf/doc/en/acctqtr.txt
  * contrib/hbnf/doc/en/acctweek.txt
  * contrib/hbnf/doc/en/acctyear.txt
  * contrib/hbnf/doc/en/adessort.txt
  * contrib/hbnf/doc/en/aemaxlen.txt
  * contrib/hbnf/doc/en/aeminlen.txt
  * contrib/hbnf/doc/en/amedian.txt
  * contrib/hbnf/doc/en/anomatch.txt
  * contrib/hbnf/doc/en/any2any.txt
  * contrib/hbnf/doc/en/aredit.txt
  * contrib/hbnf/doc/en/asum.txt
  * contrib/hbnf/doc/en/at2.txt
  * contrib/hbnf/doc/en/bitclr.txt
  * contrib/hbnf/doc/en/bitset.txt
  * contrib/hbnf/doc/en/blink.txt
  * contrib/hbnf/doc/en/byt2bit.txt
  * contrib/hbnf/doc/en/byt2hex.txt
  * contrib/hbnf/doc/en/byteand.txt
  * contrib/hbnf/doc/en/byteneg.txt
  * contrib/hbnf/doc/en/bytenot.txt
  * contrib/hbnf/doc/en/bytexor.txt
  * contrib/hbnf/doc/en/calendar.txt
  * contrib/hbnf/doc/en/chdir.txt
  * contrib/hbnf/doc/en/cntryset.txt
  * contrib/hbnf/doc/en/color2n.txt
  * contrib/hbnf/doc/en/d2e.txt
  * contrib/hbnf/doc/en/datecnfg.txt
  * contrib/hbnf/doc/en/dayofyr.txt
  * contrib/hbnf/doc/en/daytobow.txt
  * contrib/hbnf/doc/en/dfile.txt
  * contrib/hbnf/doc/en/diskfunc.txt
  * contrib/hbnf/doc/en/dispc.txt
  * contrib/hbnf/doc/en/dispmsg.txt
  * contrib/hbnf/doc/en/dosver.txt
  * contrib/hbnf/doc/en/e2d.txt
  * contrib/hbnf/doc/en/easter.txt
  * contrib/hbnf/doc/en/elapsed.txt
  * contrib/hbnf/doc/en/findith.txt
  * contrib/hbnf/doc/en/floptst.txt
  * contrib/hbnf/doc/en/ftattr.txt
  * contrib/hbnf/doc/en/ftidle.txt
  * contrib/hbnf/doc/en/ftisprn.txt
  * contrib/hbnf/doc/en/ftround.txt
  * contrib/hbnf/doc/en/fttext.txt
  * contrib/hbnf/doc/en/gcd.txt
  * contrib/hbnf/doc/en/getenvrn.txt
  * contrib/hbnf/doc/en/hex2dec.txt
  * contrib/hbnf/doc/en/iamidle.txt
  * contrib/hbnf/doc/en/isbit.txt
  * contrib/hbnf/doc/en/isbiton.txt
  * contrib/hbnf/doc/en/isshare.txt
  * contrib/hbnf/doc/en/kspeed.txt
  * contrib/hbnf/doc/en/linked.txt
  * contrib/hbnf/doc/en/madd.txt
  * contrib/hbnf/doc/en/menutonf.txt
  * contrib/hbnf/doc/en/metaph.txt
  * contrib/hbnf/doc/en/miltime.txt
  * contrib/hbnf/doc/en/month.txt
  * contrib/hbnf/doc/en/netpv.txt
  * contrib/hbnf/doc/en/nooccur.txt
  * contrib/hbnf/doc/en/ntow.txt
  * contrib/hbnf/doc/en/nwlstat.txt
  * contrib/hbnf/doc/en/nwsem.txt
  * contrib/hbnf/doc/en/nwuid.txt
  * contrib/hbnf/doc/en/ontick.txt
  * contrib/hbnf/doc/en/origin.txt
  * contrib/hbnf/doc/en/page.txt
  * contrib/hbnf/doc/en/pchr.txt
  * contrib/hbnf/doc/en/peek.txt
  * contrib/hbnf/doc/en/pending.txt
  * contrib/hbnf/doc/en/poke.txt
  * contrib/hbnf/doc/en/popadder.txt
  * contrib/hbnf/doc/en/proper.txt
  * contrib/hbnf/doc/en/prtesc.txt
  * contrib/hbnf/doc/en/putkey.txt
  * contrib/hbnf/doc/en/qtr.txt
  * contrib/hbnf/doc/en/rand1.txt
  * contrib/hbnf/doc/en/savearr.txt
  * contrib/hbnf/doc/en/scregion.txt
  * contrib/hbnf/doc/en/setdate.txt
  * contrib/hbnf/doc/en/setkeys.txt
  * contrib/hbnf/doc/en/setlastk.txt
  * contrib/hbnf/doc/en/settime.txt
  * contrib/hbnf/doc/en/sinkey.txt
  * contrib/hbnf/doc/en/sleep.txt
  * contrib/hbnf/doc/en/sqzn.txt
  * contrib/hbnf/doc/en/sysmem.txt
  * contrib/hbnf/doc/en/tbwhile.txt
  * contrib/hbnf/doc/en/tempfile.txt
  * contrib/hbnf/doc/en/vertmenu.txt
  * contrib/hbnf/doc/en/vidcur.txt
  * contrib/hbnf/doc/en/vidmode.txt
  * contrib/hbnf/doc/en/wda.txt
  * contrib/hbnf/doc/en/week.txt
  * contrib/hbnf/doc/en/workdays.txt
  * contrib/hbnf/doc/en/woy.txt
  * contrib/hbnf/doc/en/xbox.txt
  * contrib/hbnf/doc/en/year.txt
  * contrib/hbnf/ftround.prg
  * contrib/hbnf/metaph.prg
  * contrib/hbnf/nwsem.prg
  * contrib/hbnf/nwuid.prg
  * contrib/hbnf/pending.prg
  * contrib/hbnf/popadder.prg
  * contrib/hbnf/scregion.prg
  * contrib/hbnf/tbwhile.prg
  * contrib/hbnf/workdays.prg
  * contrib/hbodbc/browodbc.prg
  * contrib/hbqt/tests/browqt.prg
  * contrib/hbqt/tests/dbfbrowserclass.prg
  * contrib/hbqt/tests/testbrow.prg
  * contrib/hbsqlit3/tests/blob.prg
  * contrib/hbsqlit3/tests/sl3_test.prg
  * contrib/hbxbp/tests/demoxbp.prg
  * contrib/hbxbp/tests/dialogqt.prg
  * contrib/hbxbp/xbpdataref.prg
  * contrib/hbxbp/xbpmenubar.prg
  * contrib/hbxbp/xbpprintdialog.prg
  * contrib/hbxbp/xbpprinter.prg
  * contrib/hbxbp/xbprtf.prg
  * contrib/hbxbp/xbpscrollbar.prg
  * contrib/xhb/cstruct.prg
  * contrib/xhb/hblog.prg
  * contrib/xhb/hterrsys.prg
  * contrib/xhb/htjlist.prg
  * contrib/xhb/sprintf.prg
  * contrib/xhb/stream.prg
  * contrib/xhb/tcgi.prg
  * contrib/xhb/tedit.prg
  * contrib/xhb/tfile.prg
  * contrib/xhb/thtm.prg
  * contrib/xhb/trpc.prg
  * contrib/xhb/trpccli.prg
  * contrib/xhb/ttable.prg
  * contrib/xhb/txml.prg
  * contrib/xhb/xcstr.prg
  * contrib/xhb/xhberr.prg
  * contrib/xhb/xhbmt.prg
  * doc/cmpopt.txt
  * doc/en/dbstrux.txt
  * examples/hbapollo/array.prg
  * examples/hbvpdf/hbvpdf.prg
  * examples/hbvpdf/hbvpdft.prg
  * examples/hbxlsxml/xlsxml_s.prg
  * examples/hbxlsxml/xlsxml_y.prg
  * examples/httpsrv/cgifunc.prg
  * examples/httpsrv/cookie.prg
  * examples/httpsrv/modules/cookie.prg
  * examples/httpsrv/modules/info.prg
  * examples/httpsrv/modules/showcounter.prg
  * examples/httpsrv/modules/tableservletdb.prg
  * examples/httpsrv/modules/testajax.prg
  * examples/httpsrv/session.prg
  * examples/httpsrv/uhttpd.prg
  * include/hbclass.ch
  * include/hbtrace.ch
  * src/debug/dbgtinp.prg
  * src/debug/debugger.prg
  * src/debug/tbrwtext.prg
  * src/rdd/usrrdd/rdds/hscdx.prg
  * src/rtl/hbi18n2.prg
  * src/rtl/valtoexp.prg
    * formatting, fixed indentation, cleanups, deleting system specific information,
      optimizing out platform dependent code, using hb_ntos()
2012-07-21 16:48:00 +00:00

1060 lines
27 KiB
Plaintext

/*
* $Id$
*/
/*
* xHarbour Project source code:
* Remote Procedure Call code
* Client class
*
* Copyright 2003 Giancarlo Niccolai <giancarlo@niccolai.ws>
* www - http://www.xharbour.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbclass.ch"
#include "hbrpc.ch"
CLASS tRPCClient
DATA aServers
DATA aFunctions
DATA nUdpPort
DATA nTcpPort
/* asyncrhonous mode */
DATA lAsyncMode
/* block to be called at scan completion */
DATA bOnScanComplete
/*block called when there is a progress in the scan */
DATA bOnScanServersProgress
DATA bOnScanFunctionsProgress
/* block to be called at function error */
DATA bOnFunctionProgress
/* block to be called at function success */
DATA bOnFunctionReturn
/* block to be called at function failure */
DATA bOnFunctionFail
METHOD New( cNetwork, nTcpPort, nUdpPort ) CONSTRUCTOR
METHOD Destroy()
/* Connection */
METHOD Connect( cServer, cUserId, cPassword )
METHOD Disconnect()
/* Network scan functions */
METHOD ScanServers( cName )
METHOD ScanFunctions( cFunc, cSerial )
METHOD ScanAgain() INLINE ::UDPAccept()
METHOD StopScan()
/* Function call */
METHOD CheckServer( cRemote ) //Checks if a server is ready on tcp
METHOD SetLoopMode( nMethod, xData, nEnd, nStep )
METHOD Call( ... ) // variable parameters
METHOD CallAgain() INLINE ::TCPAccept()
METHOD StopCall()
METHOD SetPeriodCallback( ... )
METHOD ClearPeriodCallback()
/* Accessors */
METHOD SetEncryption( cKey )
METHOD IsEncrypted() INLINE ::cCryptKey != NIL
METHOD GetStatus() INLINE ::nStatus
METHOD SetTimeout( nTime )
METHOD GetTimeout()
METHOD GetResult() INLINE ::oResult
METHOD FoundServers() INLINE Len( ::aServers ) != 0
METHOD FoundFunctions() INLINE Len( ::aFunctions ) != 0
METHOD HasError() INLINE ::nErrorCode != 0 .or. ::TcpHasError() .or. ::UdpHasError()
METHOD GetErrorCode() INLINE ::nErrorCode
METHOD TcpHasError() INLINE iif( Empty( ::skTCP ), .F., hb_inetErrorCode( ::skTCP ) > 0 )
METHOD GetTcpErrorCode() INLINE iif( Empty( ::skTCP ), 0, hb_inetErrorCode( ::skTCP ) )
METHOD GetTcpErrorDesc() INLINE iif( Empty( ::skTCP ), "", hb_inetErrorDesc( ::skTCP ) )
METHOD UdpHasError() INLINE iif( Empty( ::skUDP ), .F., hb_inetErrorCode( ::skUDP ) > 0 )
METHOD UdpGetErrorCode() INLINE iif( Empty( ::skUDP ), 0, hb_inetErrorCode( ::skUDP ) )
METHOD UdpGetErrorDesc() INLINE iif( Empty( ::skUDP ), "", hb_inetErrorDesc( ::skUDP ) )
/* Used to retreive data from scans */
METHOD GetFunctionName( xId )
METHOD GetServerName( xId )
METHOD GetServerAddress( xId )
HIDDEN:
// Automatic initialization of inet support
CLASSDATA lInit INIT hb_inetInit()
DATA mtxBusy INIT HB_MutexCreate()
DATA nStatus
// This RPC protocol breaking error code
DATA nErrorCode
/* Network data */
DATA cServer
DATA cNetwork
DATA skUdp
DATA skTcp
/* Timeout system */
DATA nTimeout INIT -1
DATA nTimeLimit INIT -1
DATA caPerCall
DATA nUdpTimeBegin INIT 0
DATA thUdpAccept INIT NIL
DATA nTcpTimeBegin INIT 0
DATA thTcpAccept INIT NIL
/* XHB RPC Loop system */
DATA nLoopMode
DATA aLoopData
DATA nLoopStart
DATA nLoopEnd
DATA nLoopStep
/* Encryption data */
DATA bEncrypted
DATA cCryptKey
/* Last connection result */
DATA oResult
/* Encryption system */
METHOD Encrypt( cDataIn )
METHOD Decrypt( cDataIn )
METHOD BuildChallengePwd( cPassword )
METHOD ManageChallenge()
/* Network negotiation system */
METHOD StartScan()
METHOD UDPAccept()
METHOD UDPParse( cData, nLen )
METHOD TCPAccept()
METHOD TCPParse( cCode )
METHOD clearTCPBuffer()
/* internal network send call */
METHOD SendCall(cFunction,aParams )
/* event handlers */
METHOD OnScanComplete()
METHOD OnScanServersProgress( aLoc )
METHOD OnScanFunctionsProgress( aLoc )
METHOD OnFunctionFail( nReason, cReason )
METHOD OnFunctionReturn( oReturn )
METHOD OnFunctionProgress( nProgress, oData )
ENDCLASS
METHOD New( cNetwork, nTcpPort, nUdpPort ) CLASS tRPCClient
::nStatus := RPC_STATUS_NONE // not connected
::nErrorCode := 0 // no RPC error
::cServer := NIL // no server
::nUdpPort := iif( nUdpPort == NIL, 1139, nUdpPort )
::nTcpPort := iif( nTcpPort == NIL, 1140, nTcpPort )
::skTcp := hb_inetCreate()
::skUdp := hb_inetDGram( .T. )
::lAsyncMode := .F.
::aServers := {}
::aFunctions := {}
::cNetwork := cNetwork
::bEncrypted := .F.
::nLoopMode := RPC_LOOP_NONE
RETURN Self
METHOD Destroy() CLASS tRPCClient
HB_MutexLock( ::mtxBusy )
::Disconnect()
IF hb_threadId( ::thUdpAccept ) != 0
hb_threadQuitRequest( ::thUdpAccept )
::thUdpAccept := NIL
ENDIF
IF hb_threadId( ::thTcpAccept ) != 0
hb_threadQuitRequest( ::thTcpAccept )
::thTcpAccept := NIL
ENDIF
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD SetEncryption( cKey )
IF ! Empty( cKey )
::bEncrypted := .T.
::cCryptKey := cKey
ELSE
::bEncrypted := .F.
ENDIF
RETURN .T.
METHOD ScanServers(cName) CLASS tRPCClient
// do not allow asynchronous mode without timeout
IF ! ::lAsyncMode .and. ( ::nTimeout == NIL .or. ::nTimeOut <= 0 )
RETURN .F.
ENDIF
HB_MutexLock( ::mtxBusy )
::aServers := {}
HB_MutexUnlock( ::mtxBusy )
hb_inetDGramSend( ::skUDP, ::cNetwork , ::nUdpPort, "XHBR00" + HB_Serialize( cName ) )
::StartScan()
RETURN .F.
METHOD CheckServer( cRemote )
LOCAL cData, skRemote, nLen, cData2
cData := "XHBR00"
IF cRemote == NIL
cRemote := ::cNetwork
ENDIF
skRemote := hb_inetConnect( cRemote, ::nTcpPort )
IF hb_inetErrorCode( skRemote ) == 0
hb_InetTimeout(skRemote, 10000)
hb_inetSendAll( skRemote, cData )
cData := space(256)
hb_inetRecvAll( skRemote, @cData, 6+9 )
IF hb_inetErrorCode( skRemote ) == 0
cData2 := Space(256)
nLen := HB_GetLen8( substr( cData, 8, 8 ) )
hb_inetRecvAll( skRemote, @cData2, nLen )
IF hb_inetErrorCode( skRemote ) == 0
cData := Substr( cData + cData2, 7 )
cData2 := HB_Deserialize( cData )
AAdd(::aServers, {hb_inetAddress( skRemote ), cData2} )
RETURN .T.
ENDIF
ENDIF
ENDIF
RETURN .F.
METHOD ScanFunctions( cFunc, cSerial ) CLASS tRPCClient
// do not allow asynchronous mode without timeout
IF ! ::lAsyncMode .and. ( ::nTimeOut == NIL .or. ::nTimeOut <= 0 )
RETURN .F.
ENDIF
IF cSerial == NIL
cSerial := "00000000.0"
ENDIF
HB_MutexLock( ::mtxBusy )
::aFunctions := {}
::aServers := {}
HB_MutexUnlock( ::mtxBusy )
hb_inetDGramSend( ::skUDP, ::cNetwork, ::nUdpPort,;
"XHBR01" + HB_Serialize( cFunc ) + HB_Serialize( cSerial ))
::StartScan()
RETURN .F.
METHOD StartScan()
// We don't accept sync call without timeout
IF ::lAsyncMode
// in async mode, force scanning stop
::StopScan()
ENDIF
::nUDPTimeBegin := INT( Seconds() * 1000 )
// in async mode, just launch the listener
IF ::lAsyncMode
HB_MutexLock( ::mtxBusy )
::thUdpAccept := StartThread( Self, "UDPAccept" )
HB_MutexUnlock( ::mtxBusy )
ELSE
::UDPAccept()
ENDIF
RETURN .T.
METHOD UDPAccept() CLASS tRPCClient
LOCAL nTime, nDatalen, cData
cData := Space( 1400 )
// set default socket timeout
IF ::nTimeout >= 0
hb_inetTimeout( ::skUDP, ::nTimeout )
ELSE
hb_inetClearTimeout( ::skUdp )
ENDIF
DO WHILE .T.
nDatalen := hb_inetDGramRecv( ::skUDP, @cData, 1400 )
IF nDataLen > 0 .and. ::UDPParse( cData, nDatalen )
EXIT
ENDIF
IF ::nTimeout >= 0
nTime := Int( Seconds() * 1000 )
// a little tollerance must be added for double roundings
// in the double INT() functions
IF nTime - ::nUDPTimeBegin >= ::nTimeout - 5
EXIT
ENDIF
ENDIF
ENDDO
::OnScanComplete()
// signal that this thread is no longer active
HB_MutexLock( ::mtxBusy )
::thUdpAccept := NIL
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD UDPParse( cData, nLen ) CLASS tRPCClient
LOCAL cCode, cSer, cFunc, cName
LOCAL aLoc
IF nLen < 12
RETURN .F.
ENDIF
cCode := Substr( cData, 1, 6 )
DO CASE
/* XHRB00 - server scan */
CASE cCode == "XHBR10"
cData := Substr( cData, 7 )
cData := HB_Deserialize( cData, 512 )
// deserialization error checking
IF cData != NIL
aLoc := { hb_inetAddress( ::skUDP ), cData }
AAdd( ::aServers, aLoc )
RETURN ::OnScanServersProgress( aLoc )
ELSE
RETURN .F.
ENDIF
CASE cCode == "XHBR11"
cData := Substr( cData, 7 )
cSer := HB_DeserialBegin( cData )
cName := HB_DeserialNext( @cSer, 64 )
cFunc := HB_DeserialNext( @cSer, 64 )
IF cName != NIL .and. cFunc != NIL
aLoc := { hb_inetAddress( ::skUDP ), cName, cFunc }
AAdd( ::aFunctions, aLoc )
RETURN ::OnScanFunctionsProgress( aLoc )
ELSE
RETURN .F.
ENDIF
ENDCASE
RETURN .F.
METHOD StopScan() CLASS tRPCClient
HB_MutexLock( ::mtxBusy )
IF hb_threadId( ::thUDPAccept ) != 0
hb_threadQuitRequest( ::thUDPAccept )
::thUDPAccept := NIL
HB_MutexUnlock( ::mtxBusy )
::OnScanComplete()
ELSE
HB_MutexUnlock( ::mtxBusy )
ENDIF
RETURN .T.
METHOD Connect( cServer, cUserId, cPassword ) CLASS tRPCClient
LOCAL cAuth, cReply := Space(8)
hb_inetConnect( cServer, ::nTcpPort, ::skTcp )
IF hb_inetErrorCode( ::skTcp ) == 0
::nStatus := RPC_STATUS_CONNECTED // Connected
IF ::bEncrypted
cAuth := ::BuildChallengePwd( cPassword )
cAuth := cUserId + ":" + cAuth
hb_inetSendAll( ::skTcp, "XHBR93" + HB_CreateLen8( Len( cAuth ) ) + cAuth )
ELSE
cAuth := cUserId + ":" + cPassword
hb_inetSendAll( ::skTcp, "XHBR90" + HB_CreateLen8( Len( cAuth ) ) + cAuth )
ENDIF
IF hb_inetErrorCode( ::skTcp ) == 0
IF ! ::bEncrypted
hb_inetRecvAll( ::skTcp, @cReply )
IF hb_inetErrorCode( ::skTcp ) == 0 .and. cReply == "XHBR91OK"
::nStatus := RPC_STATUS_LOGGED // Logged in
RETURN .T.
ENDIF
ELSE
RETURN ::ManageChallenge()
ENDIF
ENDIF
ENDIF
::skTcp := NIL
::nStatus := RPC_STATUS_NONE
RETURN .F.
METHOD BuildChallengePwd( cPassword ) CLASS tRPCClient
LOCAL nLen, nCount, cRet
nLen := 10 + INT( HB_Random( 1, 60 ) )
cRet := ""
FOR nCount := 1 TO nLen
cRet += Chr( Int( HB_Random( 2, 254 ) ) )
NEXT
cRet += "PASSWORD:" + cPassword + ":"
DO WHILE Len( cRet ) < 100
cRet += Chr( Int( HB_Random( 2, 254 ) ) )
ENDDO
cRet := ::Encrypt( cRet )
RETURN cRet
METHOD ManageChallenge() CLASS tRPCClient
LOCAL cCode, cLen, nLen
LOCAL cData, nChallenge
cCode := Space( 6 )
IF hb_inetRecvAll( ::skTCP, @cCode ) != 6
RETURN .F.
ENDIF
IF !( cCode == "XHBR94" )
RETURN .F.
ENDIF
cLen := Space( 8 )
IF hb_inetRecvAll( ::skTCP, @cLen ) != 8
RETURN .F.
ENDIF
nLen := HB_GetLen8( cLen )
cData := Space( nLen )
IF hb_inetRecvAll( ::skTCP, @cData, nLen ) != nLen
RETURN .F.
ENDIF
cData := HB_Decrypt( cData, ::cCryptKey )
nChallenge := HB_Checksum( cData )
hb_inetSendAll( ::skTCP, "XHBR95" + HB_CreateLen8( nChallenge ) )
//IF hb_inetErrorCode( ::skTCP ) != 0
// RETURN .F.
//ENDIF
cCode := Space( 8 )
hb_inetRecvAll( ::skTCP, @cCode )
IF hb_inetErrorCode( ::skTCP ) != 0 .OR. !( cCode == "XHBR91OK" )
RETURN .F.
ENDIF
/* SUCCESS! */
::nStatus := RPC_STATUS_LOGGED
RETURN .T.
METHOD Disconnect() CLASS tRPCClient
IF ::nStatus >= RPC_STATUS_LOGGED
HB_MutexLock( ::mtxBusy )
::nStatus := RPC_STATUS_NONE
hb_inetSendAll( ::skTcp, "XHBR92" )
hb_inetClose( ::skTcp )
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
ENDIF
RETURN .F.
METHOD SetLoopMode( nMethod, xData, nEnd, nStep ) CLASS tRPCClient
IF nMethod == RPC_LOOP_NONE
::nLoopMode := RPC_LOOP_NONE
::aLoopData := NIL
RETURN .T.
ENDIF
IF HB_ISARRAY( xData )
::aLoopData := xData
ELSE
IF ValType( xData ) == "NI"
// this is to allow garbage collecting
::aLoopData := NIL
::nLoopStart := xData
::nLoopEnd := nEnd
IF ValType( nStep ) == "NI"
::nLoopStep := nStep
ELSE
::nLoopStep := 1
ENDIF
ELSE
RETURN .F.
ENDIF
ENDIF
::nLoopMode := nMethod
RETURN .T.
METHOD ClearTCPBuffer() CLASS tRPCClient
LOCAL cDummy := Space( 512 )
IF ::skTCP == NIL .or. ::nStatus < RPC_STATUS_LOGGED
RETURN .F.
ENDIF
DO WHILE hb_inetDataReady( ::skTCP ) > 0
// hb_inetRecv reads only the available data
hb_inetRecv( ::skTCP, @cDummy )
ENDDO
RETURN .T.
METHOD Call( ... ) CLASS tRPCClient
LOCAL oCalling
LOCAL cFunction, aParams
LOCAL nCount
IF Pcount() == 0
RETURN NIL
ENDIF
::oResult := NIL
// do not allow asynchronous mode without timeout
IF ! ::lAsyncMode .and. ( ::nTimeOut == NIL .or. ::nTimeOut <= 0 )
RETURN NIL
ENDIF
oCalling := hb_PValue( 1 )
IF HB_ISARRAY( oCalling )
cFunction := oCalling[1]
ADel( oCalling, 1 )
ASize( oCalling, Len( oCalling ) -1 )
aParams := oCalling
ELSE
cFunction := oCalling
aParams := Array( Pcount() -1 )
FOR nCount := 2 TO Pcount()
aParams[nCount - 1] := hb_PValue( nCount )
NEXT
ENDIF
// clear eventual pending data
::ClearTcpBuffer()
// The real call
HB_MutexLock( ::mtxBusy )
// already active or not already connected
IF hb_threadId( ::thTcpAccept ) != 0 .or. ::skTCP == NIL .or. ::nStatus < RPC_STATUS_LOGGED
HB_MutexUnlock( ::mtxBusy )
RETURN NIL
ENDIF
HB_MutexUnlock( ::mtxBusy )
::nStatus := RPC_STATUS_WAITING // waiting for a reply
// send the call through the socket
IF ! ::SendCall( cFunction, aParams )
RETURN .F.
ENDIF
// in async mode, just launch the listener
IF ::lAsyncMode
HB_MutexLock( ::mtxBusy )
::thTCPAccept := StartThread( Self, "TCPAccept" )
HB_MutexUnlock( ::mtxBusy )
ELSE
::TCPAccept()
ENDIF
RETURN ::oResult
METHOD SetPeriodCallback( ... ) CLASS tRPCClient
LOCAL caCalling
LOCAL nCount
IF Pcount() < 3
//TODO set an error
RETURN .F.
ENDIF
HB_MutexLock( ::mtxBusy )
::nTimeout := hb_PValue( 1 )
::nTimeLimit := hb_PValue( 2 )
caCalling := hb_PValue( 3 )
IF ! HB_ISARRAY( caCalling )
caCalling := Array( Pcount() -2 )
FOR nCount := 3 TO Pcount()
caCalling[nCount - 2] := hb_PValue( nCount )
NEXT
ENDIF
::caPerCall := caCalling
IF ::skTCP != NIL
hb_inetTimeout( ::skTCP, ::nTimeout )
hb_inetTimeLimit( ::skTCP, ::nTimeLimit )
hb_inetPeriodCallback( ::skTCP, caCalling )
ENDIF
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD ClearPeriodCallback() CLASS tRPCClient
HB_MutexLock( ::mtxBusy )
::nTimeout := -1
::nTimeLimit := -1
::caPerCall := NIL
IF ::skTCP != NIL
hb_inetClearTimeout( ::skTCP )
hb_inetClearTimeLimit( ::skTCP )
hb_inetClearPeriodCallback( ::skTCP )
ENDIF
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD SetTimeout( nTime ) CLASS tRPCClient
HB_MutexLock( ::mtxBusy )
::nTimeout := nTime
hb_InetTimeout( ::skTCP, ::nTimeout )
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD GetTimeout()
LOCAL nRet
HB_MutexLock( ::mtxBusy )
nRet := ::nTimeout
HB_MutexUnlock( ::mtxBusy )
RETURN nRet
METHOD StopCall() CLASS tRPCClient
IF ::nStatus != RPC_STATUS_WAITING
RETURN .F.
ENDIF
// clear eventual pending data
::ClearTcpBuffer()
// send cancelation request
hb_inetSendAll( ::skTCP, "XHBR29" );
//Stops waiting for a result
HB_MutexLock( ::mtxBusy )
IF hb_threadId( ::thTCPAccept ) != 0
hb_threadQuitRequest( ::thTCPAccept )
::thTCPAccept := NIL
::nStatus := RPC_STATUS_LOGGED
HB_MutexUnlock( ::mtxBusy )
::OnFunctionReturn( NIL )
ELSE
HB_MutexUnlock( ::mtxBusy )
ENDIF
RETURN .T.
METHOD SendCall( cFunction, aParams ) CLASS tRPCClient
LOCAL cData := "", nLen
LOCAL nReq, cType
SWITCH ::nLoopMode
CASE RPC_LOOP_NONE
nReq := 0
cType := ""
EXIT
CASE RPC_LOOP_ALLDATA
nReq := 2
cType := "A"
EXIT
CASE RPC_LOOP_SUMMARY
nReq := 2
cType := "C"
EXIT
CASE RPC_LOOP_CONFIRMATION
nReq := 2
cType := "E"
EXIT
END
IF ::aLoopData == NIL .and. ::nLoopMode > RPC_LOOP_NONE
cData := HB_Serialize( ::nLoopStart ) + HB_Serialize( ::nLoopEnd ) +;
HB_Serialize( ::nLoopStep )
ENDIF
cData += HB_Serialize( cFunction ) + HB_Serialize( aParams )
IF ::aLoopData != NIL
cData += HB_Serialize( ::aLoopData )
nReq += 2
ENDIF
nLen := Len( cData )
IF nLen > 512
cData := HB_Compress( cData )
cData := "XHBR2" + AllTrim( Str( nReq + 1 ) ) + ;
HB_CreateLen8( nLen ) + HB_CreateLen8( Len( cData ) ) +;
cType + ::Encrypt( cData )
ELSE
cData := "XHBR2" + AllTrim( Str( nReq ) ) + HB_CreateLen8( nLen ) +;
cType + ::Encrypt( cData)
ENDIF
hb_inetSendAll( ::skTCP, cData )
RETURN hb_inetErrorCode( ::skTCP ) == 0
METHOD TCPAccept() CLASS tRPCClient
LOCAL nTime := 0
LOCAL cCode
LOCAL nTimeLimit
// TcpAccept can also be called standalone, without the
// support of call(). So, we must set the waiting state.
HB_MutexLock( ::mtxBusy )
::nErrorCode := 0
::nStatus := RPC_STATUS_WAITING
HB_MutexUnlock( ::mtxBusy )
cCode := Space(6)
::nTCPTimeBegin := INT( Seconds() * 1000 )
nTimeLimit := Max( ::nTimeout, ::nTimeLimit )
DO WHILE .T.
IF hb_inetRecvAll( ::skTCP, @cCode, 6 ) <= 0
EXIT
ENDIF
IF ! ::TCPParse( cCode )
EXIT
ENDIF
IF nTimeLimit >= 0
nTime := Int( Seconds() * 1000 )
// a little tollerance must be added for double roundings
// in the double INT() functions
IF nTime - ::nTCPTimeBegin >= nTimeLimit - 5
EXIT
ENDIF
ENDIF
ENDDO
HB_MutexLock( ::mtxBusy )
// NOT waiting anymore
::nStatus := RPC_STATUS_LOGGED
::thTcpAccept := NIL
IF ::caPerCall == NIL .and. hb_inetErrorCode( ::skTCP ) != -1 .and.;
nTime - nTimeLimit < nTimeLimit - 5
IF hb_inetErrorCode( ::skTCP ) != 0
::nStatus := RPC_STATUS_ERROR
ENDIF
ENDIF
HB_MutexUnlock( ::mtxBusy )
RETURN .T.
METHOD TCPParse( cCode ) CLASS tRPCClient
LOCAL nDataLen, cData, nOrigLen
LOCAL cDataLen := Space( 8 ), cOrigLen := Space( 8 )
LOCAL cProgress := Space( 10 ), nProgress
LOCAL lContinue := .F.
::nErrorCode := 0
DO CASE
/* Warn error codes */
CASE cCode == "XHBR40"
cData := Space(2)
hb_inetRecvAll( ::skTCP, @cData, 2 )
::nErrorCode := Val( cData )
::OnFunctionFail( ::nErrorCode, "No description for now" )
/* We have a reply */
CASE cCode == "XHBR30"
IF hb_inetRecvAll( ::skTCP, @cDataLen ) == Len( cDataLen )
nDataLen := HB_GetLen8( cDataLen )
cData := Space( nDataLen )
IF hb_inetRecvAll( ::skTCP, @cData, nDataLen ) == nDataLen
::oResult := HB_Deserialize( ::Decrypt( cData ), nDataLen )
IF ::oResult != NIL
::OnFunctionReturn( ::oResult )
ENDIF
// todo: rise an error if ::oResult is nil
ENDIF
ENDIF
/* We have a reply */
CASE cCode == "XHBR31"
IF hb_inetRecvAll( ::skTCP, @cOrigLen ) == Len( cOrigLen )
nOrigLen := HB_GetLen8( cOrigLen )
IF hb_inetRecvAll( ::skTCP, @cDataLen ) == Len( cDataLen )
nDataLen := HB_GetLen8( cDataLen )
cData := Space( nDataLen )
IF hb_inetRecvAll( ::skTCP, @cData ) == nDataLen
cData := HB_Uncompress( nOrigLen, ::Decrypt( cData ) )
IF ! Empty( cData )
::oResult := HB_Deserialize( cData, nDataLen )
IF ::oResult != NIL
::OnFunctionReturn( ::oResult )
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
/* We have a progress */
CASE cCode == "XHBR33"
IF hb_inetRecvAll( ::skTCP, @cProgress, 10 ) == 10
nProgress := HB_Deserialize( cProgress, 10 )
IF nProgress != NIL
lContinue := .T.
::OnFunctionProgress( nProgress )
ENDIF
ENDIF
/* We have a progress with data*/
CASE cCode == "XHBR34"
IF hb_inetRecvAll( ::skTCP, @cProgress ) == Len( cProgress )
nProgress := HB_Deserialize( cProgress, Len( cProgress) )
IF nProgress != NIL .and. hb_inetRecvAll( ::skTCP, @cDataLen ) == Len( cDataLen )
nDataLen := HB_GetLen8( cDataLen )
cData := Space( nDataLen )
IF hb_inetRecvAll( ::skTCP, @cData ) == nDataLen
::oResult := HB_Deserialize(::Decrypt( cData), nDataLen )
IF ::oResult != NIL
lContinue := .T.
::OnFunctionProgress( nProgress, ::oResult )
ENDIF
ENDIF
ENDIF
ENDIF
/* We have a progress with compressed data*/
CASE cCode == "XHBR35"
IF hb_inetRecvAll( ::skTCP, @cProgress ) == Len( cProgress )
nProgress := HB_Deserialize( cProgress, Len( cProgress ) )
IF nProgress != NIL .and. hb_inetRecvAll( ::skTCP, @cOrigLen ) == Len( cOrigLen )
nOrigLen := HB_GetLen8( cOrigLen )
IF hb_inetRecvAll( ::skTCP, @cDataLen ) == Len( cDataLen )
nDataLen := HB_GetLen8( cDataLen )
cData := Space( nDataLen )
IF hb_inetRecvAll( ::skTCP, @cData ) == nDataLen
cData := HB_Uncompress( nOrigLen, cData )
IF ! Empty( cData )
::oResult := HB_Deserialize( ::Decrypt( cData), nDataLen )
IF ::oResult != NIL
lContinue := .T.
::OnFunctionProgress( nProgress, ::oResult )
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDCASE
RETURN lContinue
/***********************************
* Utility functions
************************************/
METHOD GetFunctionName( xId ) CLASS tRpcClient
LOCAL cData, nPos
IF HB_ISARRAY( xID )
cData := xId[3]
ELSEIF Len( ::aFunctions ) > 0
cData := ::aFunctions[xId][3]
ELSE
cData := ""
ENDIF
IF ! Empty(cData)
nPos := At( "(", cData )
cData := Substr( cData, 1, nPos-1 )
ENDIF
RETURN cData
METHOD GetServerName( xId ) CLASS tRpcClient
LOCAL cData
IF HB_ISARRAY( xID )
cData := xId[2]
ELSE
IF Len( ::aFunctions ) > 0
cData := ::aFunctions[xId][2]
ELSEIF Len( ::aServers ) > 0
cData := ::aServers[xId][2]
ELSE
cData := ""
ENDIF
ENDIF
RETURN cData
METHOD GetServerAddress( xId ) CLASS tRpcClient
LOCAL cData
IF HB_ISARRAY( xID )
cData := xId[1]
ELSE
IF ! Empty( ::aFunctions )
cData := ::aFunctions[xId][1]
ELSEIF ! Empty( ::aServers )
cData := ::aServers[xId][1]
ELSE
cData := ""
ENDIF
ENDIF
RETURN cData
METHOD Encrypt(cDataIn) CLASS tRPCClient
IF ::bEncrypted
RETURN HB_Crypt( cDataIn, ::cCryptKey )
ENDIF
RETURN cDataIn
METHOD Decrypt(cDataIn) CLASS tRPCClient
IF ::bEncrypted
RETURN HB_Decrypt( cDataIn, ::cCryptKey )
ENDIF
RETURN cDataIn
/***********************************
* Event handlers
************************************/
METHOD OnScanComplete() CLASS tRPCClient
IF ::bOnScanComplete != NIL
RETURN Eval( ::bOnScanComplete )
ENDIF
RETURN .T.
METHOD OnScanServersProgress( aLoc ) CLASS tRPCClient
IF ::bOnScanServersProgress != NIL
RETURN Eval( ::bOnScanServersProgress, aLoc )
ENDIF
RETURN .T.
METHOD OnScanFunctionsProgress( aLoc ) CLASS tRPCClient
IF ::bOnScanFunctionsProgress != NIL
RETURN Eval( ::bOnScanFunctionsProgress, aLoc )
ENDIF
RETURN .T.
METHOD OnFunctionFail( nReason, cReason ) CLASS tRPCClient
IF ::bOnFunctionFail != NIL
RETURN Eval( ::bOnFunctionFail, nReason, cReason )
ENDIF
RETURN .T.
METHOD OnFunctionReturn( oReturn ) CLASS tRPCClient
IF ::bOnFunctionReturn != NIL
RETURN Eval( ::bOnFunctionReturn, oReturn )
ENDIF
RETURN .T.
METHOD OnFunctionProgress( nProgress, oData ) CLASS tRPCClient
IF ::bOnFunctionProgress != NIL
RETURN Eval( ::bOnFunctionProgress, nProgress, oData )
ENDIF
RETURN .T.