Using COM and XMLRPC to get the time at Userland

Share your advanced PureBasic knowledge/code with the community.
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Using COM and XMLRPC to get the time at Userland

Post by FloHimself »

Code: Select all

; xmlrpc_time.pb:
;
; --------------------------------------------------------------------------------------------------------------
; This program demonstrates using the vbXMLRPC COM implementation from EnAppSys Ltd in PureBasic.
;
; In this example we are going to get the time from Userland
;
; To run this program you will need:
; - installed vbXML.dll and vbXMLRPC.dll   (see: http://www.enappsys.com/backend/vbXMLRPC_Index.jsp)
; - the very nice COMLIB from aXend        (get it here: http://home.planet.nl/~aXend/purebasic/COMLIB_demo.zip)
; - Variant_inc.res or Variant_inc.pb      (from aXends COMLIB archive)
; - vbXMLRPC.pb                            (provided with this example)
;
;
; Created by FloHimself, 2004.
; Questions? FloHimself@web.de
;
; --------------------------------------------------------------------------------------------------------------


IncludeFile "vbXMLRPC.pb"
;IncludeFile "Variant_inc.pb"


; Formats a variant containing named date and time information into a string.
; pvarIn 
;   [in] The variant containing the value To format. 
; iNamedFormat 
;   [in] The named date formats are: 0 = General Date, 1 = Long Date, 2 = Short Date, 3 = Long Time And 4 = Short Time. 
; dwFlags 
;   [in] 0 is the only flag that can be set. 
; *pbstrOut 
;   [out] Points To the formatted string that represents the 
;
; Return Value:
;   S_OK          = Success. 
;   E_INVALIDARG  = One Or more of the arguments is invalid. 
;
Procedure VarFormatDateTime(pvarIn.l, iNamedFormat.l, *pbStrOut)
  If OpenLibrary(0, "oleaut32.dll")
    ProcedureReturn CallFunction(0, "VarFormatDateTime", pvarIn.l, iNamedFormat.l, 0, *pbStrOut)
  EndIf
EndProcedure


; Create the vbXMLRPC.XMLRPCRequest object.
oRequest._XMLRPCRequest = CreateObject("vbXMLRPC.XMLRPCRequest")

; Set the connection properties.
oRequest\put_HostName(Ansi2Uni("time.xmlrpc.com"))
oRequest\put_HostPort(80)
oRequest\put_HostURI(Ansi2Uni("/RPC2"))
oRequest\put_MethodName(Ansi2Uni("currentTime.getCurrentTime"))

; Submit request to server and get a XMLRPCResponse object.
oRequest\Submit(@oResponse._XMLRPCResponse)

; Retrieve status from request.
oResponse\get_Status(@status.l)

; Debug the XML response returned from the server.
oResponse\get_XMLResponse(@rawData.l)
Debug "XML response returned from the remote server: " + Uni2Ansi(rawData)

; Check possible response states.
Select status 
  Case #XMLRPC_PARAMSRETURNED 
  ;The call to the remote XML-RPC server has been successful.
  
    ; Get XMLRPCParams object holding the parameters returned by the remote method call.
    oResponse\get_Params(@oParams._XMLRPCParams)

    ; Get XMLRPCValue Value object with return value.
    oParams\Item(1, @oValue._XMLRPCValue)

    ; Get and check Type of return value.
    oValue\get_ValueType(@type.l)
    If type = #XMLRPC_DATETIME
      Debug "Type is: DATE"


      dateTime.variant
      dateTime\vt = #VT_DATE
      
      ; Retrieve return value as Visual Basic DATE.
      oValue\get_DateTimeValue(@dateTime\value) 

      ; Use VarFormatDateTime() to get a date string.
      dateString.l = AllocateMemory(100) 
      VarFormatDateTime(@dateTime, 0, @dateString)
      Debug Uni2Ansi(dateString)
    EndIf  
    
  ; Handle XMLRPCResponse response errors. 
  Case #XMLRPC_FAULTRETURNED
    Debug "Server returned a fault."

  Case #XMLRPC_HTTPERROR
    Debug "HTTP error encountered."

  Case #XMLRPC_XMLPARSERERROR
    Debug "XML Parsing Error encountered."

  Case #XMLRPC_NOTINITIALISED
    Debug "Weird, the response claims not to be initialised !!!"
  
  Default
    Debug "Double Weird, unknown response status !!!"
    
EndSelect

; Release the XMLRPCResponse object.
ReleaseObject(oRequest)

Code: Select all

;vbXMLRPC.pb
;
; Interface generated by Interface Generator 1.0, Date 08/05/2004 
;
; vbXMLRPC, vbXMLRPC - XML-RPC Client

; Enumerations

Enumeration ; XMLRPC_ResponseStatus
  #XMLRPC_NOTINITIALISED = 0
  #XMLRPC_HTTPERROR = 1
  #XMLRPC_FAULTRETURNED = 2
  #XMLRPC_PARAMSRETURNED = 3
  #XMLRPC_XMLPARSERERROR = 4
EndEnumeration

Enumeration ; XMLRPC_ValueTypes
  #XMLRPC_INT_I4 = 1
  #XMLRPC_BOOLEAN = 2
  #XMLRPC_STRING = 3
  #XMLRPC_DOUBLE = 4
  #XMLRPC_DATETIME = 5
  #XMLRPC_BASE64 = 6
  #XMLRPC_STRUCT = 7
  #XMLRPC_ARRAY = 8
  #XMLRPC_NIL = 9
EndEnumeration

Enumeration ; XMLRPC_SSLTypes
  #XMLRPC_SSL_NONE = 0
  #XMLRPC_SSL_STRICT = 1
  #XMLRPC_SSL_IGNORE_CERT_CN_INVALID_AND_DATE_INVALID = 2
  #XMLRPC_SSL_IGNORE_CERT_CN_INVALID_ONLY = 3
  #XMLRPC_SSL_IGNORE_CERT_DATE_INVALID_ONLY = 4
EndEnumeration

Enumeration ; XMLRPC_Errors
  #vbXMLRPC_ERROR_BASE64ERROR = -2147098048
  #vbXMLRPC_ERROR_UNKNOWNVALUETYPE = -2147098047
  #vbXMLRPC_ERROR_UNEXPECTEDVALUETYPE = -2147098046
  #vbXMLRPC_ERROR_VALUETYPENOTSET = -2147098045
  #vbXMLRPC_ERROR_VALUENOTSET = -2147098044
  #vbXMLRPC_ERROR_MALFORMEDXML = -2147098043
  #vbXMLRPC_ERROR_HOSTNAMENOTSET = -2147098042
  #vbXMLRPC_ERROR_HOSTPORTNOTSET = -2147098041
  #vbXMLRPC_ERROR_HOSTURINOTSET = -2147098040
  #vbXMLRPC_ERROR_METHODNAMENOTSET = -2147098039
  #vbXMLRPC_ERROR_USERAGENTNOTSET = -2147098038
  #vbXMLRPC_ERROR_HTTPCALLFAILED = -2147098037
  #vbXMLRPC_ERROR_MEMBERNOTFOUND = -2147098036
EndEnumeration

; DispInterfaces

Interface _XMLRPCRequest Extends IDispatch
  get_Username(a)
  put_Username(a)
  get_Password(a)
  put_Password(a)
  get_HostName(a)
  put_HostName(a)
  get_HostPort(a)
  put_HostPort(a)
  get_HostURI(a)
  put_HostURI(a)
  get_MethodName(a)
  put_MethodName(a)
  get_SSLFlags(a)
  put_SSLFlags(a)
  get_Params(a)
  get_UserAgent(a)
  get_XMLToSend(a)
  Submit(a)
EndInterface

Interface _XMLRPCParams Extends IDispatch
  get_Count(a)
  AddNil()
  AddInteger(a)
  AddBoolean(a)
  AddString(a)
  AddDouble(a)
  AddDateTime(a)
  AddBase64(a)
  AddStruct(a)
  AddArray(a)
  Item(a,b)
  NewEnum(a)
EndInterface

Interface _XMLRPCResponse Extends IDispatch
  get_XMLResponse(a)
  put_XMLResponse(a)
  get_HTTPHeaders(a)
  get_Status(a)
  get_Params(a)
  get_Fault(a)
  get_XMLParseError(a)
  get_HTTPStatusCode(a)
EndInterface

Interface _XMLRPCUtility Extends IDispatch
  GetErrorCode(a,b)
  GetVariantType(a,b,c,d,e)
  GetXMLRPCType(a,b)
  GetHTTPError(a,b)
EndInterface

Interface _XMLRPCArray Extends IDispatch
  get_Count(a)
  AddNil()
  AddInteger(a)
  AddBoolean(a)
  AddString(a)
  AddDouble(a)
  AddDateTime(a)
  AddBase64(a)
  AddStruct(a)
  AddArray(a)
  Item(a,b)
  NewEnum(a)
EndInterface

Interface _XMLRPCMember Extends IDispatch
  get_Name(a)
  get_Value(a)
EndInterface

Interface _XMLRPCStruct Extends IDispatch
  get_Count(a)
  AddNil(a)
  AddInteger(a,b)
  AddBoolean(a,b)
  AddString(a,b)
  AddDouble(a,b)
  AddDateTime(a,b)
  AddBase64(a,b)
  AddStruct(a,b)
  AddArray(a,b)
  Item(a,b)
  GetValueByName(a,b)
  MemberExists(a,b)
  NewEnum(a)
EndInterface

Interface _XMLRPCFault Extends IDispatch
  get_faultCode(a)
  get_faultString(a)
EndInterface

Interface _XMLRPCValue Extends IDispatch
  get_ValueType(a)
  get_IntegerValue(a)
  get_BooleanValue(a)
  get_StringValue(a)
  get_DoubleValue(a)
  get_DateTimeValue(a)
  get_Base64Value(a)
  get_StructValue(a)
  get_ArrayValue(a)
EndInterface

Interface _XMLRPCBase64 Extends IDispatch
  put_UseLineTerminator(a)
  get_UseLineTerminator(a)
  put_LineLength(a)
  get_LineLength(a)
  put_LineTerminator(a)
  get_LineTerminator(a)
  put_Encoded(a)
  get_Encoded(a)
  put_Unencoded(a)
  get_Unencoded(a)
EndInterface
Leo
User
User
Posts: 21
Joined: Sat Aug 02, 2003 8:48 pm
Location: Netherlands, Wijk bij Duurstede

XMLRPC in PureBasic without COM

Post by Leo »

XMLRPC clients and servers can be easily written in purebasic without the use of components. The advantage is that only 1 executable has to be installed and no dll's or com's. I wrote a small OOish includefile that can be used to program XMLTPC clients or servers. The design is not fully OO and therefor the programmer should be aware of what he is doing (no errors are returned if methods are nit used in the intended order). The following files are in this message :
OO_XMLRPC.pb ---- The XIncludeFile
TimeServer.pb ---- a server that returns the current time
usage : TimeServer <portnr>
example : TimeServer 8000
TimeClient.pb ---- a client that retrieves the current time
usage : TimeClient <URL> <portnr>
example : TimeClient www.xmlrpc.com 80
example : TimeClient localhost 8000
TotalServer.pb ---- a server that totalises an array of floating point numbers
usage : TotalServer <portnr>
example : TotalServer 8000
TotalClient.pb ---- a client that receives the total
usage : TotalClient <URL> <portnr>
example : TotalClient localhost 8000
The software is not extensively tested against XMLRPC servers on the Web so that there still some shortcomings but the idea works.

The XIncludeFile

Code: Select all

;****************************************
;
; XML-RPC inclidefile OO_XMLRPC
; Author : Leo Mijnders
; Date : Nov 2004
;
;****************************************
; TAG Definitions
;****************************************
#CRF           = Chr(13)
#HTF           = Chr(9)
#LFF           = Chr(10)
#CRLF          = Chr(13)+Chr(10)
#MAXDEPTH      = 20
#POST          = "POST "
#HTTP          = "HTTP 1.0"
#USERAGENT     = "User-Agent: "
#HOST          = "Host: "
#CONTENTTYPE   = "Content-Type: text/html"
#CONTENTLENGTH = "Content-length: "

#PARAM_TAG     = "<param>"
#PARAM_ETAG    = "</param>"

#VALUE_TAG     = "<value>"
#VALUE_ETAG    = "</value>"

#BOOLEAN_TAG   = "<boolean>"
#BOOLEAN_ETAG  = "</boolean>"
#DOUBLE_TAG    = "<double>"
#DOUBLE_ETAG   = "</double>"
#INT_TAG       = "<int>"
#INT_ETAG      = "</int>"
#I4_TAG        = "<i4>"
#I4_ETAG       = "</i4>"
#STRING_TAG    = "<string>"
#STRING_ETAG    = "</string>"
#DATETIME_TAG  = "<dateTime.iso8601>"
#DATETIME_ETAG = "</dateTime.iso8601>"
#BASE64_TAG    = "<base64>"
#BASE64_ETAG   = "</base64>"
#NIL_TAG       = "<nil/>"

#ARRAY_TAG     = "<array>"
#DATA_TAG      = "<data>"
#DATA_ETAG     = "</data>"
#ARRAY_ETAG    = "</array>"

#STRUCT_TAG    = "<struct>"
#MEMBER_TAG    = "<member>"
#NAME_TAG      = "<name>"
#NAME_ETAG     = "</name>"
#MEMBER_ETAG   = "</member>"
#STRUCT_ETAG   = "</struct>"

;****************************************
; XMLRPCSend
;****************************************
Interface XMLRPCSend
  AddBase64(Addr.l, Long.l)
  AddBoolean(Boolean.b)
  AddDateTime(Seconds.l)
  AddDouble(Float.f, Decimals.l)
  AddInteger(Integer.l)
  AddString(String.s)
  AddNil()
  OpenArray()
  CloseArray()
  OpenStruct()
  CloseStruct()
  OpenMember(Name.s)
  CloseMember()
  Submit.s(Caddr.l)
EndInterface

Structure XMLRPCSendInstance
  vTable.l
  Functions.l[SizeOf(XMLRPCSend)/4] 
  HostName.s     
  HostPort.l     
  HostUri.s
  MethodName.s
  Server.s
  UserAgent.s     
  XMLBody.s
  Nested.b
  Fault.b     
EndStructure

Procedure AddBase64(*self.XMLRPCSendInstance, Addr.l, Long.l)
  *self\XMLBody+Space(2 * *self\Nested)
  *OutputBuffer=AllocateMemory(2*Long+64)
  Base64Encoder(Addr, Long, *OutputBuffer, 2*Long+64)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#BASE64_TAG
  *self\XMLBody+PeekS(*OutputBuffer)
  *self\XMLBody+#BASE64_ETAG+#VALUE_ETAG
  FreeMemory(*OutputBuffer)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddBoolean(*self.XMLRPCSendInstance, Boolean.b)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#BOOLEAN_TAG
  *self\XMLBody+Str(Boolean)
  *self\XMLBody+#BOOLEAN_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddDateTime(*self.XMLRPCSendInstance, Seconds.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#DATETIME_TAG
  *self\XMLBody+FormatDate("%yyyy%mm%ddT%hh:%ii:%ss",Seconds)
  *self\XMLBody+#DATETIME_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddDouble(*self.XMLRPCSendInstance, Float.f, Decimals.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#DOUBLE_TAG
  *self\XMLBody+StrF(Float, Decimals)
  *self\XMLBody+#DOUBLE_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddInteger(*self.XMLRPCSendInstance, Long.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#I4_TAG
  *self\XMLBody+Str(Long)
  *self\XMLBody+#I4_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddString(*self.XMLRPCSendInstance, String.s)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#STRING_TAG
  *self\XMLBody+String
  *self\XMLBody+#STRING_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure AddNil(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#NIL_TAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf
  *self\XMLBody+#CRLF   
EndProcedure

Procedure OpenArray(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#ARRAY_TAG+#DATA_TAG
  *self\XMLBody+#CRLF
  *self\Nested+1   
EndProcedure

Procedure CloseArray(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  *self\XMLBody+#DATA_ETAG+#ARRAY_ETAG+#VALUE_ETAG
  If *self\Nested = 1 And *self\Fault=0
    *self\XMLBody+#PARAM_ETAG
  EndIf   
  *self\XMLBody+#CRLF
  *self\Nested-1   
EndProcedure

Procedure OpenStruct(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody+#PARAM_TAG
  EndIf   
  *self\XMLBody+#VALUE_TAG+#STRUCT_TAG
  *self\XMLBody+#CRLF
  *self\Nested+1   
EndProcedure

Procedure CloseStruct(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  *self\XMLBody+#STRUCT_ETAG+#VALUE_ETAG
  If *self\Nested = 1 And *self\Fault = 0
    *self\XMLBody+#PARAM_ETAG
  EndIf   
  *self\XMLBody+#CRLF
  *self\Nested-1   
EndProcedure

Procedure OpenMember(*self.XMLRPCSendInstance, Name.s)
  *self\XMLBody+Space(2 * *self\Nested)
  *self\XMLBody+#MEMBER_TAG+#CRLF+Space(2* *self\Nested)+#NAME_TAG+Name+#NAME_ETAG
  *self\XMLBody+#CRLF
EndProcedure

Procedure CloseMember(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  *self\XMLBody+#MEMBER_ETAG
  *self\XMLBody+#CRLF
EndProcedure

Procedure.s Submit(*self.XMLRPCSendInstance, caddr.l)
  If *self\Fault
    *self\Nested=1
  EndIf  
  ReturnString.s=""
  If *self\Server=""
    naddr.l=OpenNetworkConnection(*self\HostName,*self\HostPort)
  Else
    naddr.l=caddr 
  EndIf   

  If naddr
    xml.s="<?xml version="+Chr(34)+"1.0"+Chr(34)+"?>"+#CRLF
    If *self\Server=""
      xml+"<methodCall>"
    Else
      xml+"<methodResponse>"
    EndIf
    xml+#CRLF
    If *self\Server=""
      xml+"<methodName>"+*self\MethodName+"</methodName>"+#CRLF
    EndIf
    If *self\Fault
      xml+"<fault>"+#CRLF
    Else  
      xml+"<params>"+#CRLF
    EndIf
    xml+*self\XMLBody
    If *self\Fault
      xml+"</fault>"+#CRLF
    Else  
      xml+"</params>"+#CRLF
    EndIf
    If *self\Server=""
      xml+"</methodCall>"
    Else
      xml+"</methodResponse>"
    EndIf
    xml+#CRLF
    If *self\Server=""
      com$="POST /RPC2 HTTP/1.0"+#CRLF 
      com$=com$+"User-Agent: HTTP-For-PureBasic"+#CRLF 
      com$=com$+"Host: "+*self\HostName+#CRLF 
      com$=com$+"Content-Type: text/html"+#CRLF 
      com$=com$+"Content-Length: "+Str(Len(xml))+#CRLF
      com$=com$+ #CRLF
      com$+xml
    Else
      com$="HTTP/1.1 200 OK"+#CRLF
      com$+"Connection: close"+#CRLF 
      com$+"Content-Length: "+Str(Len(xml))+#CRLF
      com$+"Content-Type: text/html"+#CRLF 
      com$+"Date: "+"To do"+#CRLF 
      com$+"Server: "+*self\Server+#CRLF 
      com$+ #CRLF
      com$+xml
    EndIf

    SendNetworkString(naddr,com$)
    
    If *self\Server=""
      Buffer.s=Space(4999)
      Repeat 
        result=ReceiveNetworkData(naddr,@Buffer,Len(Buffer))
        ReturnString+Left(PeekS(@Buffer),result)
      Until result=0
      CloseNetworkConnection(naddr)
    EndIf  
  EndIf  
  ProcedureReturn ReturnString
EndProcedure

Procedure.l CreateInstance_XMLRPCSend() 
  *Instance.XMLRPCSendInstance = AllocateMemory(SizeOf(XMLRPCSendInstance)) 
  *Instance\vTable = *Instance + OffsetOf(XMLRPCSendInstance, Functions) 
  *Instance\Functions[0] = @AddBase64() 
  *Instance\Functions[1] = @AddBoolean() 
  *Instance\Functions[2] = @AddDateTime() 
  *Instance\Functions[3] = @AddDouble() 
  *Instance\Functions[4] = @AddInteger()
  *Instance\Functions[5] = @AddString()
  *Instance\Functions[6] = @AddNil()
  *Instance\Functions[7] = @OpenArray()
  *Instance\Functions[8] = @CloseArray()
  *Instance\Functions[9] = @OpenStruct()
  *Instance\Functions[10] = @CloseStruct()
  *Instance\Functions[11] = @OpenMember()
  *Instance\Functions[12] = @CloseMember()
  *Instance\Functions[13] = @Submit() 
  *Instance\HostName = ""
  *Instance\HostPort = 0
  *Instance\HostUri = ""
  *Instance\MethodName = ""
  *Instance\XMLBody = ""
  *Instance\Server = ""
  *Instance\Nested = 0
  *Instance\Fault = 0
  ProcedureReturn *Instance
EndProcedure 

;****************************************
; XMLRPCRecieve
;****************************************
Interface XMLRPCRecieve
  FirstContent()
  NextContent()
  CheckWellFormed()
  GetCurrentPos.l()
EndInterface

Structure ParseStructure
  Tag.s
  Nr.l
EndStructure

Structure XMLRPCRecieveInstance
  vTable.l
  Functions.l[SizeOf(XMLRPCRecieve)/4] 
  Response.s     
  Header.s     
  XMLBody.s
  Content.s
  ContentPresent.b
  ContentLevel.l
  StringFieldNr.l
  Parse.ParseStructure[#MaxDepth]
  XMLVersion.s
  XMLWellFormed.b
EndStructure

Procedure Content(*self.XMLRPCRecieveInstance, StringFieldNr.l)
  i=StringFieldNr
  Repeat
    Tag.s=StringField(*self\XMLBody,i,#CRF)
    *self\ContentPresent=0
    *self\XMLWellFormed=1
    If Tag=""                               ; end of XML
      *self\StringFieldNr=i+1
      ProcedureReturn                     
    ElseIf Trim(Tag)=""                    ; space
      *self\StringFieldNr=i+1
    ElseIf Left(Tag,1)="<" And Right(Tag,1)<>">"
      *self\XMLWellFormed=0
      *self\StringFieldNr=i
      ProcedureReturn                     
    ElseIf Left(Tag,1)<>"<" And Right(Tag,1)=">"
      *self\XMLWellFormed=0
      *self\StringFieldNr=i
      ProcedureReturn                     
    ElseIf Left(Tag,13)="<?xml version"    ; set XML version  
      *self\XMLVersion=Tag 
    ElseIf Left(Tag,2)="<?"                ; ignore comment tags
    ElseIf Left(Tag,3)="</>"               ; handle the NIL Tag
      *self\ContentLevel-1
      *self\Content=""
      *self\ContentPresent=1
    ElseIf Left(Tag,2)="</"                ; handle end tag
      *self\ContentLevel-1
      If *self\Parse[*self\ContentLevel]\Tag <> ReplaceString(Tag,"</","<")
        *self\XMLWellFormed=0
        *self\StringFieldNr=i
        ProcedureReturn
      EndIf
    ElseIf Left(Tag,1)="<"                 ; handle tag
      *self\Parse[*self\ContentLevel]\Tag=Tag
      *self\Parse[*self\ContentLevel]\Nr+1
      *self\ContentLevel+1  
      *self\Parse[*self\ContentLevel]\Nr=0
    Else                                   ; handle content
      *self\Content=Tag
      *self\ContentPresent=1
    EndIf
    i+1
  Until *self\ContentPresent=1
  *self\StringFieldNr=i
EndProcedure

Procedure FirstContent(*self.XMLRPCRecieveInstance)
  Content(*self,1)
EndProcedure

Procedure NextContent(*self.XMLRPCRecieveInstance)
  Content(*self,*self\StringFieldNr)
EndProcedure

Procedure CheckWellFormed(*self.XMLRPCRecieveInstance)
  *self\StringFieldNr=1
  Repeat
    Content(*self,*self\StringFieldNr)
  Until *self\ContentPresent=0 Or *self\XMLWellFormed=0
EndProcedure

Procedure.l GetCurrentPos(*self.XMLRPCRecieveInstance)
  For i=1 To *self\StringFieldNr
    Tag.s =StringField(*self\XMLBody, i, #CRF)
    StartPos.l=1
    If Tag="</>"
    ElseIf Left(Tag,1)="<"
      StartPos=FindString(*self\Response, Tag, StartPos)
    EndIf
  Next
  ProcedureReturn StartPos
EndProcedure

Procedure.l CreateInstance_XMLRPCRecieve(Response.s) 
  *Instance.XMLRPCRecieveInstance = AllocateMemory(SizeOf(XMLRPCRecieveInstance)) 
  *Instance\vTable = *Instance + OffsetOf(XMLRPCRecieveInstance, Functions) 
  *Instance\Functions[0] = @FirstContent() 
  *Instance\Functions[1] = @NextContent() 
  *Instance\Functions[2] = @CheckWellFormed() 
  *Instance\Functions[3] = @GetCurrentPos() 
  *Instance\Response = Response
  ContentLength=Val(Mid(Response,FindString(Response,"Content-Length:",1)+15,10))
  *Instance\Header = Left(Response,Len(Response)-ContentLength)
  Temp.s=Right(Response, ContentLength)
  Temp=ReplaceString(Temp,"/>","></>")
  Temp=ReplaceString(Temp,#CRF,"")
  Temp=ReplaceString(Temp,#LFF,"")
  Temp=ReplaceString(Temp,#HTF,"")
  Temp=ReplaceString(Temp,"<",#CRF+"<")
  Temp=ReplaceString(Temp,">",">"+#CRF)
  Temp=ReplaceString(Temp,#CRF+#CRF,#CRF)
  
  *Instance\XMLBody=Right(Temp,Len(Temp)-FindString(Temp,"<",1)+1)
  *Instance\Content=""
  *Instance\ContentPresent=0
  *Instance\ContentLevel=0
  *Instance\StringFieldNr=0
  *Instance\XMLVersion=""
  *Instance\XMLWellFormed=0
  ProcedureReturn *Instance
EndProcedure 
The Examples

Code: Select all

;****************************************
;
; XML-RPC example TimeServer
; Author : Leo Mijnders
; Date : Nov 2004
;
;****************************************
XIncludeFile "OO_XMLRPC.pb"

Enumeration
  #NOTWELLFORMED
  #UNKNOWNMETHOD
  #NOMETHODNAMETAG
  #NOCONTENT
  #TOMUCHPARAMETERS
  #LASTENUM
EndEnumeration

; Global Errors.s
Dim Errors.s(#LASTENUM)
Errors(#NOTWELLFORMED)="XML not well formed"
Errors(#UNKNOWNMETHOD)="The methodName is unknown"
Errors(#NOMETHODNAMETAG)="There is no methodName tag"
Errors(#NOCONTENT)="There is no content"
Errors(#TOMUCHPARAMETERS)="This method has to much parameters"

Procedure FaultHandling(Error.l,ClientNaddr.l)
  Response.XMLRPCSend=CreateInstance_XMLRPCSend()
  *Response.XMLRPCSendInstance=Response
  *Response\Server="Leo's TimeServer"
  *Response\Fault=1
  Response\OpenStruct()
  Response\OpenMember("faultCode")
  Response\AddInteger(Error)
  Response\CloseMember()
  Response\OpenMember("faultString")
  Response\AddString(Errors(Error))
  Response\CloseMember()
  Response\CloseStruct()
  Response\Submit(ClientNaddr)
  FreeMemory(*Response)
  CloseNetworkConnection(ClientNaddr)
EndProcedure

Procedure ProcessRequest(Buffer.l, Length.l, ClientNaddr.l)
  Request.XMLRPCRecieve=CreateInstance_XMLRPCRecieve(Left(PeekS(Buffer),Length))
  *Request.XMLRPCRecieveInstance=Request
  Request\CheckWellFormed()
  If *Request\XMLWellFormed
  ;  MessageRequester("","Well Formed",0)
    Request\FirstContent()
    If *Request\ContentPresent
      If *Request\Parse[1]\Tag="<methodName>"
        If *Request\Content="currentTime.getCurrentTime"
        Else
          FaultHandling(#UNKNOWNMETHOD, ClientNaddr)
          FreeMemory(*Request)
          ProcedureReturn  
        EndIf
      Else
        FaultHandling(#NOMETHODNAMETAG, ClientNaddr)  
        FreeMemory(*Request)
        ProcedureReturn  
      EndIf
    Else
      FaultHandling(#NOCONTENT, ClientNaddr)  
      FreeMemory(*Request)
      ProcedureReturn  
    EndIf  
    
    Request\NextContent()
    If *Request\ContentPresent
      FaultHandling(#TOMUCHPARAMETERS, ClientNaddr)  
      FreeMemory(*Request)
      ProcedureReturn  
    Else
      FreeMemory(*Request)
      Response.XMLRPCSend=CreateInstance_XMLRPCSend()
      *Response.XMLRPCSendInstance=Response
      *Response\Server="Leo's TimeServer"
      Response\AddString(FormatDate("%yyyy%mm%ddT%hh:%ii:%ss",Date()))
      Response\Submit(ClientNaddr)
      FreeMemory(*Response)
      CloseNetworkConnection(ClientNaddr)
    EndIf  
  Else
    FaultHandling(#NOTWELLFORMED, ClientNaddr)
    FreeMemory(*Request)
    ProcedureReturn
  EndIf
EndProcedure

If InitNetwork() = 0
  MessageRequester("Error", "Can't initialize the network !", 0)
  End
EndIf

Port = Val(ProgramParameter())
Buffer = AllocateMemory(5000)

OpenConsole()                                  ; First we must open a console
ConsoleTitle ("PureBasic - XML-RPC Server at port "+Str(Port))  ; Now we can give the opened console a Titlename ;)                                                  
Print("Press Esc to shut down the server !")


If CreateNetworkServer(Port)
  Repeat
    SEvent.l = NetworkServerEvent()
    If SEvent
      ClientID = NetworkClientID()
      Select SEvent
        Case 1
;         MessageRequester("PureBasic - Server", "A new client has connected !", 0)
        Case 2
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has send a packet !", 0)
          Length.l=ReceiveNetworkData(ClientID, Buffer, 5000)
;          MessageRequester("Info", "String: "+PeekS(Buffer), 0)
          ProcessRequest(Buffer, Length, ClientID)

        Case 3
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has send a file via the network !", 0)
;          ReceiveNetworkFile(ClientID, "C:\TEST_Network.ftp3")
        Case 4
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has closed the connexion...", 0)
;          Quit = 1
      EndSelect
    EndIf
    EscPressed.s=Inkey()
    If Asc(EscPressed)=27
      Quit=1
    EndIf
  Until Quit = 1 
;  MessageRequester("PureBasic - Server", "Click to quit the server.", 0)
  CloseNetworkServer()
Else
  MessageRequester("Error", "Can't create the server (port in use ?).", 0)
EndIf
CloseConsole()
End   

Code: Select all

;****************************************
;
; XML-RPC example TimeClient
; Author : Leo Mijnders
; Date : Nov 2004
;
;****************************************
XIncludeFile "OO_XMLRPC.pb"

InitNetwork() 

Request.XMLRPCSend=CreateInstance_XMLRPCSend()
*Request.XMLRPCSendInstance=Request

;*Request\HostName="time.xmlrpc.com"
;*Request\HostPort=80
;*Request\HostName="localhost"
;*Request\HostPort=8000
*Request\HostName=ProgramParameter()
*Request\HostPort=Val(ProgramParameter())
*Request\HostUri="/RPC2"
*Request\MethodName="currentTime.getCurrentTime"
*Request\UserAgent="XML-RPC-For-PureBasic"
Result.s=Request\Submit(0)
If Result=""
  MessageRequester("","No Response, possibly network error",0)
  End
EndIf

Response.XMLRPCRecieve=CreateInstance_XMLRPCRecieve(Result)
*Response.XMLRPCRecieveInstance=Response

;MessageRequester("Response",*Response\Header,0)
;MessageRequester("Response",*Response\XMLBody,0)

Response\CheckWellFormed()
;MessageRequester("", Str(*Response\XMLWellFormed),0)

Response\FirstContent()
MessageRequester("Response",*Response\Content,0)
;MessageRequester("Response",Str(*Response\ContentLevel),0)
;MessageRequester("Response",*Response\Parse[*Response\ContentLevel-1]\Tag,0)

Repeat
  Response\NextContent()
  If *Response\ContentPresent=0
    Break
  EndIf  
  MessageRequester("Response", "Content : "+*Response\Content,0)
;  MessageRequester("Response","ContentLevel : "+Str(*Response\ContentLevel),0)
;  MessageRequester("Response","Content Tag : "+*Response\Parse[*Response\ContentLevel-1]\Tag,0)
Until #FALSE

MessageRequester("Response","No more Contents",0)

FreeMemory(*Request)
FreeMemory(*Response)

Code: Select all

;****************************************
;
; XML-RPC example TotalServer
; Author : Leo Mijnders
; Date : Nov 2004
;
;****************************************
XIncludeFile "OO_XMLRPC.pb"

Enumeration
  #NOTWELLFORMED
  #UNKNOWNMETHOD
  #NOMETHODNAMETAG
  #NOCONTENT
  #NONAMEPARAMETER
  #WRONGPARAMETER
  #NOTHINGTOTOTALIZE
  #LASTENUM
EndEnumeration

; Global Errors.s
Dim Errors.s(#LASTENUM)
Errors(#NOTWELLFORMED)="XML not well formed"
Errors(#UNKNOWNMETHOD)="The methodName is unknown"
Errors(#NOMETHODNAMETAG)="There is no methodName tag"
Errors(#NOCONTENT)="There is no content"
Errors(#NONAMEPARAMETER)="The name parameter is missing"
Errors(#WRONGPARAMETER)="The parameter is not allowed "
Errors(#NOTHINGTOTOTALIZE)="There is nothing to totalize"

Procedure FaultHandling(Error.l,ClientNaddr.l)
  Response.XMLRPCSend=CreateInstance_XMLRPCSend()
  *Response.XMLRPCSendInstance=Response
  *Response\Server="Leo's TimeServer"
  *Response\Fault=1
  Response\OpenStruct()
  Response\OpenMember("faultCode")
  Response\AddInteger(Error)
  Response\CloseMember()
  Response\OpenMember("faultString")
  Response\AddString(Errors(Error))
  Response\CloseMember()
  Response\CloseStruct()
  Response\Submit(ClientNaddr)
  FreeMemory(*Response)
  CloseNetworkConnection(ClientNaddr)
EndProcedure

Procedure ProcessRequest(Buffer.l, Length.l, ClientNaddr.l)
  Request.XMLRPCRecieve=CreateInstance_XMLRPCRecieve(Left(PeekS(Buffer),Length))
  *Request.XMLRPCRecieveInstance=Request
  Request\CheckWellFormed()
  If *Request\XMLWellFormed
    Request\FirstContent()
    If *Request\ContentPresent
      If *Request\Parse[1]\Tag="<methodName>"
        If *Request\Content="CalculateTotal"
        Else
          FaultHandling(#UNKNOWNMETHOD, ClientNaddr)
          FreeMemory(*Request)
          ProcedureReturn  
        EndIf
      Else
        FaultHandling(#NOMETHODNAMETAG, ClientNaddr)  
        FreeMemory(*Request)
        ProcedureReturn  
      EndIf
    Else
      FaultHandling(#NOCONTENT, ClientNaddr)  
      FreeMemory(*Request)
      ProcedureReturn  
    EndIf  

    Name.s=""
    Total.f=0.0
    Nr.l=0
    Repeat
      Request\NextContent()
      If *Request\ContentPresent=0
        Break
      ElseIf *Request\Parse[*Request\ContentLevel-1]\Tag="<name>"
        Name=*Request\Content
      ElseIf *Request\Parse[*Request\ContentLevel-1]\Tag="<double>"
        Total+ValF(*Request\Content)
        Nr+1
      Else
        FaultHandling(#WRONGPARAMETER, ClientNaddr)  
        FreeMemory(*Request)
        ProcedureReturn  
      EndIf
    Until #FALSE
    If Name=""
      FaultHandling(#NONAMEPARAMETER, ClientNaddr)  
      FreeMemory(*Request)
      ProcedureReturn  
    EndIf
    If Nr=0
      FaultHandling(#NOTHINGTOTOTALIZE, ClientNaddr)  
      FreeMemory(*Request)
      ProcedureReturn  
    EndIf
    FreeMemory(*Request)
    Response.XMLRPCSend=CreateInstance_XMLRPCSend()
    *Response.XMLRPCSendInstance=Response
    *Response\Server="Leo's TimeServer"
    Response\OpenStruct()
    Response\OpenMember("Total "+Name)
    Response\AddDouble(Total,2)
    Response\CloseMember()
    Response\CloseStruct()
    Response\Submit(ClientNaddr)
    FreeMemory(*Response)
    CloseNetworkConnection(ClientNaddr)
  Else
    FaultHandling(#NOTWELLFORMED, ClientNaddr)
    FreeMemory(*Request)
    ProcedureReturn
  EndIf
EndProcedure

If InitNetwork() = 0
  MessageRequester("Error", "Can't initialize the network !", 0)
  End
EndIf

Port = Val(ProgramParameter())
Buffer = AllocateMemory(5000)

OpenConsole()                                  ; First we must open a console
ConsoleTitle ("PureBasic - XML-RPC Server at port "+Str(Port))  ; Now we can give the opened console a Titlename ;)                                                  
Print("Press Esc to shut down the server !")


If CreateNetworkServer(Port)
  Repeat
    SEvent.l = NetworkServerEvent()
    If SEvent
      ClientID = NetworkClientID()
      Select SEvent
        Case 1
;         MessageRequester("PureBasic - Server", "A new client has connected !", 0)
        Case 2
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has send a packet !", 0)
          Length.l=ReceiveNetworkData(ClientID, Buffer, 5000)
;          MessageRequester("Info", "String: "+PeekS(Buffer), 0)
          ProcessRequest(Buffer, Length, ClientID)

        Case 3
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has send a file via the network !", 0)
;          ReceiveNetworkFile(ClientID, "C:\TEST_Network.ftp3")
        Case 4
;          MessageRequester("PureBasic - Server", "Client "+Str(ClientID)+" has closed the connexion...", 0)
;          Quit = 1
      EndSelect
    EndIf
    EscPressed.s=Inkey()
    If Asc(EscPressed)=27
      Quit=1
    EndIf
  Until Quit = 1 
;  MessageRequester("PureBasic - Server", "Click to quit the server.", 0)
  CloseNetworkServer()
Else
  MessageRequester("Error", "Can't create the server (port in use ?).", 0)
EndIf
CloseConsole()
End   

Code: Select all

;****************************************
;
; XML-RPC example TotalClient
; Author : Leo Mijnders
; Date : Nov 2004
;
;****************************************
XIncludeFile "OO_XMLRPC.pb"

InitNetwork() 

Request.XMLRPCSend=CreateInstance_XMLRPCSend()
*Request.XMLRPCSendInstance=Request

Request\OpenStruct()
Request\OpenMember("Prices")
Request\OpenArray()
Request\AddDouble(14.2,2)
Request\AddDouble(16.7,2)
Request\AddDouble(13.8,2)
Request\AddDouble(17.1,2)
Request\CloseArray()
Request\CloseMember()
Request\CloseStruct()


*Request\HostName=ProgramParameter()
*Request\HostPort=Val(ProgramParameter())
*Request\HostUri="/RPC2"
*Request\MethodName="CalculateTotal"
*Request\UserAgent="XML-RPC-For-PureBasic"
Result.s=Request\Submit(0)
If Result=""
  MessageRequester("","No Response, possibly network error",0)
  End
EndIf

Response.XMLRPCRecieve=CreateInstance_XMLRPCRecieve(Result)
*Response.XMLRPCRecieveInstance=Response

;MessageRequester("Response",*Response\Header,0)
;MessageRequester("Response",*Response\XMLBody,0)

Response\CheckWellFormed()
;MessageRequester("", Str(*Response\XMLWellFormed),0)

Response\FirstContent()
MessageRequester("Response",*Response\Content,0)
;MessageRequester("Response",Str(*Response\ContentLevel),0)
;MessageRequester("Response",*Response\Parse[*Response\ContentLevel-1]\Tag,0)

Repeat
  Response\NextContent()
  If *Response\ContentPresent=0
    Break
  EndIf  
  MessageRequester("Response", "Content : "+*Response\Content,0)
;  MessageRequester("Response","ContentLevel : "+Str(*Response\ContentLevel),0)
;  MessageRequester("Response","Content Tag : "+*Response\Parse[*Response\ContentLevel-1]\Tag,0)
Until #FALSE

MessageRequester("Response","No more Contents",0)

FreeMemory(*Request)
FreeMemory(*Response)
Leo
User
User
Posts: 21
Joined: Sat Aug 02, 2003 8:48 pm
Location: Netherlands, Wijk bij Duurstede

Post by Leo »

Another advantage of fully purebasic XMLRPC (without COM) is that it is running on Linux too.
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

very nice one leo!

sure you can implement all your needed stuff in purebasic yourself. but sometimes there is no time and installing a COM object isn't such a big problem.
User avatar
lgb-this
User
User
Posts: 32
Joined: Sat Aug 30, 2014 9:00 pm
Location: Switzerland
Contact:

Re: Using COM and XMLRPC to get the time at Userland

Post by lgb-this »

I would like to use the code from Leo with Purebasic 5.30. I get an error on this line:

Code: Select all

  *Instance\vTable = *Instance + OffsetOf(XMLRPCSendInstance, Functions)
The compiler says: syntax error.

Any idea how to change the code for 5.30 ?

Thanks and regards

Matthias
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Using COM and XMLRPC to get the time at Userland

Post by Demivec »

lgb-this wrote:I would like to use the code from Leo with Purebasic 5.30. I get an error on this line:

Code: Select all

  *Instance\vTable = *Instance + OffsetOf(XMLRPCSendInstance, Functions)
The compiler says: syntax error.

Any idea how to change the code for 5.30 ?
Use:

Code: Select all

  *Instance\vTable = *Instance + OffsetOf(XMLRPCSendInstance\Functions)
User avatar
lgb-this
User
User
Posts: 32
Joined: Sat Aug 30, 2014 9:00 pm
Location: Switzerland
Contact:

Re: Using COM and XMLRPC to get the time at Userland

Post by lgb-this »

Thanks for the answer. I changed the code and syntax-check is successful now. I tried to make the TimeClient example of Leo. The execution crashs:

Code: Select all

Result.s=Request\Submit(0)
The debugger tells me: not allowed memory access (ungültiger Speicherzugriff).

My testprogram runs under Win8.1 64bit and i create unicode-executable.

Thanks for any help.

Regards

Matthias
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Using COM and XMLRPC to get the time at Userland

Post by Demivec »

lgb-this wrote:Thanks for the answer. I changed the code and syntax-check is successful now. I tried to make the TimeClient example of Leo. The execution crashs:

Code: Select all

Result.s=Request\Submit(0)
The debugger tells me: not allowed memory access (ungültiger Speicherzugriff).

My testprogram runs under Win8.1 64bit and i create unicode-executable.
Here's my attempt at an update to the original include file 'OO_XMLRPC.pb':

Code: Select all

;****************************************
;
; XML-RPC includefile OO_XMLRPC
; Author : Leo Mijnders
; Date : Nov 2004
; Updated to v5.30: Demivec, converted for 64-bit compilation. lgb-this, modified for unicode compilation
;
;****************************************

; Posted in forum thread XMLRPC in PureBasic without COM
; 
; Ref: http://en.wikipedia.org/wiki/XML-RPC
;
; XMLRPC clients and servers can be easily written in PureBasic without the use of components.
; The advantage is that only 1 executable has To be installed and no DLL's or COM's. I wrote a
; small OOish IncludeFile that can be used To program XMLTPC clients or servers. The design is
; not fully OO And therefore the programmer should be aware of what he is doing (no errors are 
; returned if methods are not used in the intended order).
;
; The following files were also included in his forum post: 
;
; OO_XMLRPC.pb ---- The XIncludeFile
; TimeServer.pb ---- a server that returns the current time
;   usage : TimeServer <portnr> 
;   example : TimeServer 8000 
; TimeClient.pb ---- a client that retrieves the current time
;   usage : TimeClient <URL> <portnr>
;   example : TimeClient www.xmlrpc.com 80
;   example : TimeClient localhost 8000
; TotalServer.pb ---- a server that totalises an array of floating point numbers
;   usage : TotalServer <portnr> 
;   example : TotalServer 8000 
; TotalClient.pb ---- a client that receives the total
;   usage : TotalClient <URL> <portnr>
;   example : TotalClient localhost 8000
;
; The software is not extensively tested against XMLRPC servers on the Web so that there still
; some shortcomings but the idea works.


;****************************************
; TAG Definitions
;****************************************
#CRF           = Chr(13)
#HTF           = Chr(9)
#LFF           = Chr(10)
#CRLF          = Chr(13)+Chr(10)
#MAXDEPTH      = 20
#POST          = "POST "
#HTTP          = "HTTP 1.0"
#USERAGENT     = "User-Agent: "
#HOST          = "Host: "
#CONTENTTYPE   = "Content-Type: text/html"
#CONTENTLENGTH = "Content-length: "

#PARAM_TAG     = "<param>"
#PARAM_ETAG    = "</param>"

#VALUE_TAG     = "<value>"
#VALUE_ETAG    = "</value>"

#BOOLEAN_TAG   = "<boolean>"
#BOOLEAN_ETAG  = "</boolean>"
#DOUBLE_TAG    = "<double>"
#DOUBLE_ETAG   = "</double>"
#INT_TAG       = "<int>"
#INT_ETAG      = "</int>"
#I4_TAG        = "<i4>"
#I4_ETAG       = "</i4>"
#STRING_TAG    = "<string>"
#STRING_ETAG    = "</string>"
#DATETIME_TAG  = "<dateTime.iso8601>"
#DATETIME_ETAG = "</dateTime.iso8601>"
#BASE64_TAG    = "<base64>"
#BASE64_ETAG   = "</base64>"
#NIL_TAG       = "<nil/>"

#ARRAY_TAG     = "<array>"
#DATA_TAG      = "<data>"
#DATA_ETAG     = "</data>"
#ARRAY_ETAG    = "</array>"

#STRUCT_TAG    = "<struct>"
#MEMBER_TAG    = "<member>"
#NAME_TAG      = "<name>"
#NAME_ETAG     = "</name>"
#MEMBER_ETAG   = "</member>"
#STRUCT_ETAG   = "</struct>"

;****************************************
; XMLRPCSend
;****************************************
Interface XMLRPCSend
  AddBase64(Addr.i, Long.l)
  AddBoolean(Boolean.b)
  AddDateTime(Seconds.l)
  AddDouble(Double.f, Decimals.l)
  AddInteger(Integer.l)
  AddString(String.s)
  AddNil()
  OpenArray()
  CloseArray()
  OpenStruct()
  CloseStruct()
  OpenMember(Name.s)
  CloseMember()
  Submit.s(Caddr.i)
EndInterface

Structure XMLRPCSendInstance
  vTable.i
  Functions.i[SizeOf(XMLRPCSend)/4]
  HostName.s     
  HostPort.l     
  HostUri.s
  MethodName.s
  Server.s
  UserAgent.s     
  XMLBody.s
  Nested.b
  Fault.b     
EndStructure

Procedure AddBase64(*self.XMLRPCSendInstance, Addr.i, Long.l)
  Protected *OutputBuffer
  
  *self\XMLBody + Space(2 * *self\Nested)
  *OutputBuffer = AllocateMemory(2 * Long + 64)
  Base64Encoder(Addr, Long, *OutputBuffer, 2 * Long + 64)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #BASE64_TAG
  *self\XMLBody + PeekS(*OutputBuffer, - 1, #PB_Ascii) ;might need to use #PB_UTF
  *self\XMLBody + #BASE64_ETAG + #VALUE_ETAG
  FreeMemory(*OutputBuffer)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddBoolean(*self.XMLRPCSendInstance, Boolean.b)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #BOOLEAN_TAG
  *self\XMLBody + Str(Boolean)
  *self\XMLBody + #BOOLEAN_ETAG + #VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddDateTime(*self.XMLRPCSendInstance, Seconds.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #DATETIME_TAG
  *self\XMLBody + FormatDate("%yyyy%mm%ddT%hh:%ii:%ss", Seconds)
  *self\XMLBody + #DATETIME_ETAG+#VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddDouble(*self.XMLRPCSendInstance, Double.f, Decimals.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #DOUBLE_TAG
  *self\XMLBody + StrD(Double, Decimals)
  *self\XMLBody + #DOUBLE_ETAG + #VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddInteger(*self.XMLRPCSendInstance, Long.l)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #I4_TAG
  *self\XMLBody + Str(Long)
  *self\XMLBody + #I4_ETAG + #VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddString(*self.XMLRPCSendInstance, String.s)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #STRING_TAG
  *self\XMLBody + String
  *self\XMLBody + #STRING_ETAG + #VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure AddNil(*self.XMLRPCSendInstance)
  *self\XMLBody + Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #NIL_TAG + #VALUE_ETAG
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf
  *self\XMLBody + #CRLF   
EndProcedure

Procedure OpenArray(*self.XMLRPCSendInstance)
  *self\XMLBody+Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #ARRAY_TAG + #DATA_TAG
  *self\XMLBody + #CRLF
  *self\Nested + 1   
EndProcedure

Procedure CloseArray(*self.XMLRPCSendInstance)
  *self\XMLBody + Space(2 * *self\Nested)
  *self\XMLBody + #DATA_ETAG + #ARRAY_ETAG + #VALUE_ETAG
  If *self\Nested = 1 And *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf   
  *self\XMLBody + #CRLF
  *self\Nested - 1   
EndProcedure

Procedure OpenStruct(*self.XMLRPCSendInstance)
  *self\XMLBody + Space(2 * *self\Nested)
  If *self\Nested | *self\Fault = 0
    *self\XMLBody + #PARAM_TAG
  EndIf   
  *self\XMLBody + #VALUE_TAG + #STRUCT_TAG
  *self\XMLBody + #CRLF
  *self\Nested + 1   
EndProcedure

Procedure CloseStruct(*self.XMLRPCSendInstance)
  *self\XMLBody + Space(2 * *self\Nested)
  *self\XMLBody + #STRUCT_ETAG + #VALUE_ETAG
  If *self\Nested = 1 And *self\Fault = 0
    *self\XMLBody + #PARAM_ETAG
  EndIf   
  *self\XMLBody + #CRLF
  *self\Nested - 1   
EndProcedure

Procedure OpenMember(*self.XMLRPCSendInstance, Name.s)
  *self\XMLBody + Space(2 * *self\Nested)
  *self\XMLBody + #MEMBER_TAG + #CRLF + Space(2 * *self\Nested) + #NAME_TAG + Name + #NAME_ETAG
  *self\XMLBody + #CRLF
EndProcedure

Procedure CloseMember(*self.XMLRPCSendInstance)
  *self\XMLBody + Space(2 * *self\Nested)
  *self\XMLBody + #MEMBER_ETAG
  *self\XMLBody + #CRLF
EndProcedure

Procedure.s Submit(*self.XMLRPCSendInstance, caddr.i)
  Protected ReturnString.s, xml.s, com$, Buffer.s, naddr.i, result
  
  If *self\Fault
    *self\Nested = 1
  EndIf  
  
  ReturnString = ""
  
  If *self\Server = ""
    naddr = OpenNetworkConnection(*self\HostName, *self\HostPort)
  Else
    naddr = caddr 
  EndIf   

  If naddr
    xml = "<?xml version=" + Chr(34) + "1.0" + Chr(34) + "?>" + #CRLF
    If *self\Server = ""
      xml + "<methodCall>"
    Else
      xml + "<methodResponse>"
    EndIf
    xml + #CRLF
    If *self\Server = ""
      xml + "<methodName>" + *self\MethodName + "</methodName>" + #CRLF
    EndIf
    If *self\Fault
      xml + "<fault>" + #CRLF
    Else  
      xml + "<params>" + #CRLF
    EndIf
    xml + *self\XMLBody
    If *self\Fault
      xml + "</fault>" + #CRLF
    Else  
      xml + "</params>" + #CRLF
    EndIf
    If *self\Server = ""
      xml + "</methodCall>"
    Else
      xml + "</methodResponse>"
    EndIf
    xml + #CRLF
    If *self\Server = ""
      com$ = "POST /RPC2 HTTP/1.0" + #CRLF 
      com$ + "User-Agent: HTTP-For-PureBasic" + #CRLF 
      com$ + "Host: " + *self\HostName + #CRLF 
      com$ + "Content-Type: text/html" + #CRLF 
      com$ + "Content-Length: " + Str(Len(xml)) + #CRLF
      com$ +  #CRLF
      com$ + xml
    Else
      com$ = "HTTP/1.1 200 OK" + #CRLF
      com$ + "Connection: close" + #CRLF 
      com$ + "Content-Length: " + Str(Len(xml)) + #CRLF
      com$ + "Content-Type: text/html" + #CRLF 
      com$ + "Date: " + "To do" + #CRLF 
      com$ + "Server: " + *self\Server + #CRLF 
      com$ +  #CRLF
      com$ + xml
    EndIf

    SendNetworkString(naddr, com$, #PB_UTF8)
   
    If *self\Server = ""
      Buffer = Space(4999)
      Repeat
        result = ReceiveNetworkData(naddr, @Buffer, Len(Buffer))
        ReturnString + Left(PeekS(@Buffer,result,#PB_UTF8), result)
      Until result = 0
      CloseNetworkConnection(naddr)
    EndIf 
  EndIf 
  ProcedureReturn ReturnString
EndProcedure

Procedure.i CreateInstance_XMLRPCSend() 
  Protected *Instance.XMLRPCSendInstance
  
  *Instance = AllocateMemory(SizeOf(XMLRPCSendInstance)) 
  *Instance\vTable = *Instance + OffsetOf(XMLRPCSendInstance\Functions) 
  *Instance\Functions[0] = @AddBase64() 
  *Instance\Functions[1] = @AddBoolean() 
  *Instance\Functions[2] = @AddDateTime() 
  *Instance\Functions[3] = @AddDouble() 
  *Instance\Functions[4] = @AddInteger()
  *Instance\Functions[5] = @AddString()
  *Instance\Functions[6] = @AddNil()
  *Instance\Functions[7] = @OpenArray()
  *Instance\Functions[8] = @CloseArray()
  *Instance\Functions[9] = @OpenStruct()
  *Instance\Functions[10] = @CloseStruct()
  *Instance\Functions[11] = @OpenMember()
  *Instance\Functions[12] = @CloseMember()
  *Instance\Functions[13] = @Submit() 
  *Instance\HostName = ""
  *Instance\HostPort = 0
  *Instance\HostUri = ""
  *Instance\MethodName = ""
  *Instance\XMLBody = ""
  *Instance\Server = ""
  *Instance\Nested = 0
  *Instance\Fault = 0
  ProcedureReturn *Instance
EndProcedure 

;****************************************
; XMLRPCRecieve
;****************************************
Interface XMLRPCRecieve
  FirstContent()
  NextContent()
  CheckWellFormed()
  GetCurrentPos.l()
EndInterface

Structure ParseStructure
  Tag.s
  Nr.i
EndStructure

Structure XMLRPCRecieveInstance
  vTable.i
  Functions.i[SizeOf(XMLRPCRecieve)/4]
  Response.s     
  Header.s     
  XMLBody.s
  Content.s
  ContentPresent.b
  ContentLevel.i
  StringFieldNr.i
  Parse.ParseStructure[#MaxDepth]
  XMLVersion.s
  XMLWellFormed.b
EndStructure

Procedure Content(*self.XMLRPCRecieveInstance, StringFieldNr.i)
  Protected i, Tag.s
  
  i = StringFieldNr
  Repeat
    Tag = StringField(*self\XMLBody, i, #CRF)
    *self\ContentPresent = 0
    *self\XMLWellFormed = 1
    If Tag = ""                               ; end of XML
      *self\StringFieldNr = i + 1
      ProcedureReturn                     
    ElseIf Trim(Tag) = ""                     ; space
      *self\StringFieldNr = i + 1
    ElseIf Left(Tag, 1) = "<" And Right(Tag, 1) <> ">"
      *self\XMLWellFormed = 0
      *self\StringFieldNr = i
      ProcedureReturn                     
    ElseIf Left(Tag, 1) <> "<" And Right(Tag, 1) = ">"
      *self\XMLWellFormed = 0
      *self\StringFieldNr = i
      ProcedureReturn                     
    ElseIf Left(Tag, 13) = "<?xml version"    ; set XML version  
      *self\XMLVersion = Tag 
    ElseIf Left(Tag, 2) = "<?"                ; ignore comment tags
    ElseIf Left(Tag, 3) = "</>"               ; handle the NIL Tag
      *self\ContentLevel - 1
      *self\Content = ""
      *self\ContentPresent = 1
    ElseIf Left(Tag, 2) = "</"                ; handle end tag
      *self\ContentLevel - 1
      If *self\Parse[*self\ContentLevel]\Tag <> ReplaceString(Tag, "</", "<")
        *self\XMLWellFormed = 0
        *self\StringFieldNr = i
        ProcedureReturn
      EndIf
    ElseIf Left(Tag, 1) = "<"                 ; handle tag
      *self\Parse[*self\ContentLevel]\Tag = Tag
      *self\Parse[*self\ContentLevel]\Nr + 1
      *self\ContentLevel + 1  
      *self\Parse[*self\ContentLevel]\Nr = 0
    Else                                   ; handle content
      *self\Content = Tag
      *self\ContentPresent = 1
    EndIf
    i + 1
  Until *self\ContentPresent = 1
  *self\StringFieldNr = i
EndProcedure

Procedure FirstContent(*self.XMLRPCRecieveInstance)
  Content(*self, 1)
EndProcedure

Procedure NextContent(*self.XMLRPCRecieveInstance)
  Content(*self, *self\StringFieldNr)
EndProcedure

Procedure CheckWellFormed(*self.XMLRPCRecieveInstance)
  *self\StringFieldNr = 1
  Repeat
    Content(*self, *self\StringFieldNr)
  Until *self\ContentPresent = 0 Or *self\XMLWellFormed = 0
EndProcedure

Procedure.i GetCurrentPos(*self.XMLRPCRecieveInstance)
  Protected i, Tag.s, StartPos.i
  
  For i = 1 To *self\StringFieldNr
    Tag = StringField(*self\XMLBody, i, #CRF)
    StartPos = 1
    If Tag = "</>"
    ElseIf Left(Tag, 1) = "<"
      StartPos = FindString(*self\Response, Tag, StartPos)
    EndIf
  Next
  ProcedureReturn StartPos
EndProcedure

Procedure.i CreateInstance_XMLRPCRecieve(Response.s) 
  Protected *Instance.XMLRPCRecieveInstance, ContentLength, Temp.s
  
  *Instance = AllocateMemory(SizeOf(XMLRPCRecieveInstance)) 
  *Instance\vTable = *Instance + OffsetOf(XMLRPCRecieveInstance\Functions) 
  *Instance\Functions[0] = @FirstContent() 
  *Instance\Functions[1] = @NextContent() 
  *Instance\Functions[2] = @CheckWellFormed() 
  *Instance\Functions[3] = @GetCurrentPos() 
  *Instance\Response = Response
  ContentLength = Val(Mid(Response, FindString(Response, "Content-Length:", 1) + 15, 10))
  *Instance\Header = Left(Response, Len(Response) - ContentLength)
  Temp = Right(Response, ContentLength)
  Temp = ReplaceString(Temp, "/>", "></>")
  Temp = ReplaceString(Temp, #CRF, "")
  Temp = ReplaceString(Temp, #LFF, "")
  Temp = ReplaceString(Temp, #HTF, "")
  Temp = ReplaceString(Temp, "<", #CRF + "<")
  Temp = ReplaceString(Temp, ">", ">" + #CRF)
  Temp = ReplaceString(Temp, #CRF + #CRF, #CRF)
  
  *Instance\XMLBody = Right(Temp, Len(Temp) - FindString(Temp, "<", 1) + 1)
  *Instance\Content = ""
  *Instance\ContentPresent = 0
  *Instance\ContentLevel = 0
  *Instance\StringFieldNr = 0
  *Instance\XMLVersion = ""
  *Instance\XMLWellFormed = 0
  ProcedureReturn *Instance
EndProcedure
I think this modified file should work with 64 bit but I am not sure about unicode. Someone else may be able to point out any transitions that may need to be done for unicode. I'm actually not well aquainted with the networking features but I'm willing to keep at it for a while in an attempt help you and to learn as we go. Perhaps someone else will join in if they can't spot something that hasn't been addressed yet.

I'm currently stuck at finding a computer that is available to respond to the messages, I don't think the one from the original example exists anymore. :)


@Edit: added changes made by lgb-this for unicode compilation
Last edited by Demivec on Sun Aug 31, 2014 11:57 pm, edited 2 times in total.
User avatar
lgb-this
User
User
Posts: 32
Joined: Sat Aug 30, 2014 9:00 pm
Location: Switzerland
Contact:

Re: Using COM and XMLRPC to get the time at Userland

Post by lgb-this »

Fantastic help !

I had to add #PB_UTF8 twice in the procedure Submit:

Code: Select all

    SendNetworkString(naddr, com$, #PB_UTF8)
   
    If *self\Server = ""
      Buffer = Space(4999)
      Repeat
        result = ReceiveNetworkData(naddr, @Buffer, Len(Buffer))
        ReturnString + Left(PeekS(@Buffer,result,#PB_UTF8), result)
      Until result = 0
      CloseNetworkConnection(naddr)
    EndIf 
  EndIf 
  ProcedureReturn ReturnString
EndProcedure
Now the communication with my device over the network works.

Regards

Matthias
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Using COM and XMLRPC to get the time at Userland

Post by Demivec »

lgb-this wrote:Fantastic help !
Your welcome. :)
lgb-this wrote:I had to add #PB_UTF8 twice in the procedure Submit:
I added your changes to my previous post.
Post Reply