//  Sample Xb2.NET client interface to a web based Personal Information Manager
    //  This sample illustrates the following:
    //
    //  - how to write a web client app with Xb2.NET
    //  - how to connect through a proxy
    //  - how to maintain a persistent connection
    //  - how to create URL encoded forms
    //  - how to POST forms
    //  - how to GET files
    //  - how to handle cookies on the client side
    //  - how to handle a redirection
    //  - how to handle a 100-continue response
    //-----------------------------------------------------------------------------
    
    
    #include "xb2net.ch"
    #include "common.ch"
    #include "fileio.ch"
    
    #pragma  library ("xb2net.lib")
    #xtranslate NTrim() => LTrim(Str())
    #define CRLF (Chr(13)+Chr(10))
    
    //-----------------------------------------------------------------------------
    function Main()
       Local oPIM, oUser, cColor
    
    
       oPIM := PIMOnline():New()
    
       //oPIM:ProxyHost := 'MyProxyURL'
       //oPIM:ProxyPort := 8010
    
       if ! oPIM:Connect()
          MsgBox("Unable to connect to PIM server" + chr(10) +;
                 "Error code: " + NTrim(oPIM:Socket:ErrorCode) + " (" + oPIM:Socket:ErrorText(oPIM:Socket:ErrorCode) + ")" )
          oPIM:destroy()
          Return .f.
       endif
    
       ? "connected to PIM server"
    
       oUser := PIMUser():new()
       oUser:LoginID         := 'winter88'
       oUser:Password        := 'secret'
       oUser:FirstName       := 'Jack'
       oUser:LastName        := 'Frost'
       oUser:EMail1          := 'jfrost@hotmail.com'
       oUser:EMail2          := ''
       oUser:TimeZone        := '-300:Nassau'
       oUser:ZIPCode         := 'H0H 0H0'
       oUser:ShowHolidays    := .t.
       oUser:DaylightSavings := .t.
       oUser:Gender          := 'M'
       oUser:YearOfBirth     := '1956'
       oUser:Occupation      := '14'
    
       if oPIM:CreateNewUser( oUser )
          ? 'new user created, LoginID:',oUser:LoginID, ', password:', oUser:Password
       else
          ? 'error, creating new user'
       endif
    
       ?
       ? "Response from CreateNewUser:"
       cColor := SetColor("w/b")
    
       SaveToFile( oPIM:HTTPResponse:Content, "PIM-NewUser.htm" )
       MemoEdit(oPIM:HTTPResponse:Content, Row()+1, 0, MaxRow(), MaxCol())
    
       SetColor(cColor)
       cls
    
    
       ? "try to login..."
    
       if oPIM:Login(oUser:LoginID, oUser:Password, oUser:FirstName + " " + oUser:LastName)
          ? 'logged in successfully'
       else
          ? 'error, unable to login'
       endif
    
       ?
       ? 'Response from Login:'
       cColor := SetColor("w/b")
    
       SaveToFile( oPIM:HTTPResponse:Content, "PIM-Login.htm" )
       MemoEdit(oPIM:HTTPResponse:Content, Row()+1, 0, MaxRow(), MaxCol())
    
       oPIM:destroy()
    
       Return .t.
    
    //-----------------------------------------------------------------------------
    STATIC FUNCTION SaveToFile( cText, cFileName )
       Local i := 0, nHandle
    
       while (nHandle := FCreate(cFileName)) < 0
          if ++i > 9
             Return .f.
          endif
          Sleep(20)
       end
    
       FWrite(nHandle, cText + CRLF)
       FClose(nHandle)
    
       Return .t.
    
    //-----------------------------------------------------------------------------
    CLASS PIMUser
    EXPORTED:
       VAR LoginID
       VAR Password
       VAR FirstName
       VAR LastName
       VAR EMail1, EMail2
       VAR TimeZone
       VAR ZIPCode
       VAR ShowHolidays
       VAR DaylightSavings
       VAR Gender
       VAR YearOfBirth
       VAR Occupation
    
       INLINE METHOD Init()
       Return Self
    ENDCLASS
    
    //-----------------------------------------------------------------------------
    CLASS PIMOnline
       VAR Cookies
       METHOD Post, Get, SaveCookie
    
    EXPORTED:
       VAR Socket
       VAR HTTPRequest
       VAR HTTPResponse
       VAR ProxyHost, ProxyPort
       VAR Host, Port
    
       INLINE METHOD Init()
          ::Cookies      := {}
          ::Port         := 80
          ::Host         := 'www.SomeURL.com'
          ::Socket       := xbSocket():New(AF_INET, SOCK_STREAM, IPPROTO_IP)
          ::HTTPRequest  := xbHTTPRequest():new( ::Socket )
          ::HTTPResponse := xbHTTPResponse():new( ::Socket )
          Return self
    
       INLINE METHOD SetProxy( cHost, nPort )
          ::ProxyHost := cHost
          ::ProxyPort := iif(empty(nPort), 80, nPort)
          Return self
    
       INLINE METHOD Connect()
          if Empty(::ProxyHost)
             Return ::Socket:Connect(::Host, ::Port)
          endif
          Return ::Socket:Connect(::ProxyHost, ::ProxyPort)
    
       INLINE METHOD Destroy()
          Return ::Socket:Destroy()
    
       METHOD CreateNewUser
       METHOD Login
    
    ENDCLASS
    
    
    METHOD PIMOnline:Post( cPath, oForm )
       Local oRequest  := ::HTTPRequest
       Local oResponse := ::HTTPResponse
    
       if !::Socket:Connected .and. !::Connect()
          Return .f.
       endif
    
       // reset just in case these objects were used before
       oRequest:Reset()
       oResponse:Reset()
    
       oRequest:Command := "POST"
       // oRequest:HTTPVersion := '1.0'  // we can set the version to 1.0 so we don't get a 100-continue response
       oRequest:KeepAlive(.t.)
       oRequest:CacheControl('no-cache')
       oRequest:Host(::Host + ":" + NTrim(::Port))
       // note: this sends back *all* cookies
       // to be correct we should only send back the ones that belong this domain/path + have not expired
       oRequest:Cookies := ::Cookies
    
       if Empty(::ProxyHost)
          oRequest:Path(cPath)
       else
          oRequest:Path("http://" + ::Host + ":" + NTrim(::Port) + cPath)
          oRequest:SetHeader("Proxy-Connection", "keep-alive")
       endif
    
       oRequest:Content := oForm
    
       if oRequest:send() == SOCKET_ERROR
          Return .f.
       endif
    
       if ! oResponse:Recv()
          Return .f.
       endif
    
       // we already sent the whole request so if we receive a 100-continue status, just ignore it
       // do another :Recv to get the final response
       if oResponse:StatusCode == 100 .and. ! oResponse:Recv()
          Return .f.
       endif
    
       ::SaveCookie( oResponse:GetCookie() )
    
       if oResponse:StatusCode >= 300 .and. oResponse:StatusCode < 400
          // redirection
          Return ::Get("/" + oResponse:Location())
       endif
    
       Return .t.
    
    
    METHOD PIMOnline:Get( cPath )
       Local oRequest  := ::HTTPRequest
       Local oResponse := ::HTTPResponse
    
       if !::Socket:Connected .and. !::Connect()
          Return .f.
       endif
    
       // reset just in case these objects were used before
       oRequest:Reset()
       oResponse:Reset()
    
       oRequest:Command := "GET"
       // oRequest:HTTPVersion := '1.0'  // we can set the version to 1.0 so we don't get a 100-continue response
       oRequest:KeepAlive(.t.)
       oRequest:CacheControl('no-cache')
       oRequest:Host(::Host + ":" + NTrim(::Port))
       // note: this sends back *all* cookies
       // to be correct we should only send back the ones that belong this domain/path + have not expired
       oRequest:Cookies := ::Cookies
    
       // the path needs to be parsed because it may contain a query component
       if Empty(::ProxyHost)
          oRequest:xbURI:Parse(cPath)
       else
          oRequest:xbURI:Parse("http://" + ::Host + ":" + NTrim(::Port) + cPath)
          oRequest:SetHeader("Proxy-Connection", "keep-alive")
       endif
    
       if oRequest:send() == SOCKET_ERROR
          Return .f.
       endif
    
       if ! oResponse:Recv()
          Return .f.
       endif
    
       // we already sent the whole request so if we receive a 100-continue status, just ignore it
       // do another :Recv to get the final response
       if oResponse:StatusCode == 100 .and. ! oResponse:Recv()
          Return .f.
       endif
    
       ::SaveCookie( oResponse:GetCookie() )
    
       if oResponse:StatusCode >= 300 .and. oResponse:StatusCode < 400
          // redirection
          Return ::Get("/" + oResponse:Location())
       endif
    
       Return .t.
    
    
    METHOD PIMOnline:SaveCookie( aNewCookies )
       Local i, j, imax := len(aNewCookies)
    
       for i := 1 to imax
          // note: we should also be comparing the cookie path+domain and keeping track of it's expiry date
          if (j := AScan(::Cookies, {|a|a[1]=aNewCookies[i,1]})) > 0
             // new cookie replaces old one
             ::Cookies[j,2] := aNewCookies[i,2]
          else
             AAdd(::Cookies, {aNewCookies[i,1], aNewCookies[i,2]})
          endif
       next
    
       Return self
    
    
    METHOD PIMOnline:CreateNewUser( oUser )
       Local oForm := xbForm():New()
    
       oForm:SetVar('loginname'  , oUser:LoginID    )
       oForm:SetVar('pass'       , oUser:Password   )
       oForm:SetVar('pass2'      , oUser:Password   )
       oForm:SetVar('firstname'  , oUser:FirstName  )
       oForm:SetVar('lastname'   , oUser:LastName   )
       oForm:SetVar('email'      , oUser:EMail1     )
       oForm:SetVar('email2'     , oUser:EMail2     )
       oForm:SetVar('timezone'   , oUser:TimeZone   )
       oForm:SetVar('zip'        , oUser:ZIPCode    )
       if oUser:ShowHolidays   ; oForm:SetVar('U'       , 'on'); endif
       if oUser:DaylightSavings; oForm:SetVar('daylight', 'on'); endif
       oForm:SetVar('gender'     , oUser:Gender     )
       oForm:SetVar('yearofbirth', oUser:YearOfBirth)
       oForm:SetVar('occupations', oUser:Occupation )
       oForm:SetVar('submit1'    , ' I Accept ')
    
       if !::Post( '/newuserchk.asp', oForm )
          Return .f.
       endif
    
       // if this string is present in response then new user has been created
       Return "Congratulation " + oUser:FirstName + " " + oUser:LastName $ ::HTTPResponse:Content
    
    
    METHOD PIMOnline:Login( cUID, cPWD, cFullName )
       Local oForm := xbForm():New()
    
       oForm:SetVar('loginname'    , cUID )
       oForm:SetVar('pass'         , cPWD )
       oForm:SetVar('validateLogin', '998877')
       oForm:SetVar('submit1'      , 'Enter')
    
       if !::Post( '/checkuser.asp', oForm )
          Return .f.
       endif
    
       // if this string is present in response then we are logged in
       Return cFullName $ ::HTTPResponse:Content
    
    

    home     download     top of page