Sample code for 30+ languages & platforms
Tcl Requires Chilkat v11.0.0+

Xero API through an SSH Tunnel

This example demonstrates connecting through an SSH Tunnel w/ 2-legged OAuth for a Xero private application.

Chilkat Tcl Downloads

Tcl

load ./chilkat.dll

set success 0

#  This example requires the Chilkat API to have been previously unlocked.
#  See Global Unlock Sample for sample code.

set tunnel [new_CkSocket]

set sshHostname "sftp.example.com"
set sshPort 22

#  Connect to an SSH server and establish the SSH tunnel:
set success [CkSocket_SshOpenTunnel $tunnel $sshHostname $sshPort]
if {$success == 0} then {
    puts [CkSocket_lastErrorText $tunnel]
    delete_CkSocket $tunnel
    exit
}

#  Authenticate with the SSH server via a login/password
#  or with a public key.
#  This example demonstrates SSH password authentication.
set success [CkSocket_SshAuthenticatePw $tunnel "mySshLogin" "mySshPassword"]
if {$success == 0} then {
    puts [CkSocket_lastErrorText $tunnel]
    delete_CkSocket $tunnel
    exit
}

#   OK, the SSH tunnel is setup.  Now open a channel within the tunnel.
#   (Any number of channels may be created from the same SSH tunnel.
#   Multiple channels may coexist at the same time.)

#  This sample code would typically be placed in a subroutine or function
#  where the rest object is passed by reference.
#  It does the OAuth1 setup and makes the initial connection.
set rest [new_CkRest]

set bTls 1
set port 443
set maxWaitMs 7000

#  This returns a socket object that is a single channel within the SSH tunnel.
set channel [new_CkSocket]

set success [CkSocket_SshNewChannel $tunnel "api.xero.com" $port $bTls $maxWaitMs $channel]
if {$success == 0} then {
    puts [CkSocket_lastErrorText $tunnel]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    exit
}

#  Use the connection.  (This connection is a TLS running on an SSH channel through an SSH tunnel.
#  In other words, TLS is wrapped within the SSH tunnel.)
set success [CkRest_UseConnection $rest $channel 1]
if {$success == 0} then {
    puts [CkRest_lastErrorText $rest]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    exit
}

#  OK, we're connected.  
#  -----------------------------------------------------------------
#  Now setup the OAuth1 authenticator..

set consumerKey "XERO_PRIVATE_APP_KEY"
set consumerSecret "XERO_PRIVATE_APP_SECRET"

#  Let's get our private key from our PFX (password protected), or the PEM (unprotected).
#  You can decide which to use.  Either is OK, although I would recommend keeping your
#  private keys in a PFX and not in an unprotected PEM.
set pfx [new_CkPfx]

set success [CkPfx_LoadPfxFile $pfx "qa_data/certs/xero_private_app/public_privatekey.pfx" "PFX_PASSWORD"]
if {$success == 0} then {
    puts [CkPfx_lastErrorText $pfx]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    delete_CkPfx $pfx
    exit
}

set privKeyFromPfx [new_CkPrivateKey]

set success [CkPfx_PrivateKeyAt $pfx 0 $privKeyFromPfx]
if {$success == 0} then {
    puts [CkPfx_lastErrorText $pfx]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    delete_CkPfx $pfx
    delete_CkPrivateKey $privKeyFromPfx
    exit
}

#  Or we can load from a PEM..
set privKeyFromPem [new_CkPrivateKey]

set success [CkPrivateKey_LoadPemFile $privKeyFromPem "qa_data/certs/xero_private_app/privatekey.pem"]
if {$success == 0} then {
    puts [CkPrivateKey_lastErrorText $privKeyFromPem]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    delete_CkPfx $pfx
    delete_CkPrivateKey $privKeyFromPfx
    delete_CkPrivateKey $privKeyFromPem
    exit
}

#  Note: There are many other means for loading a private key, including
#  from other formats and directly from memory (i.e. not file-based).

set oauth1 [new_CkOAuth1]

CkOAuth1_put_ConsumerKey $oauth1 $consumerKey
CkOAuth1_put_ConsumerSecret $oauth1 $consumerSecret
CkOAuth1_put_Token $oauth1 $consumerKey
CkOAuth1_put_TokenSecret $oauth1 $consumerSecret
CkOAuth1_put_SignatureMethod $oauth1 "RSA-SHA1"
CkOAuth1_SetRsaKey $oauth1 $privKeyFromPfx

#  Install the OAuth1 authenticator.
CkRest_SetAuthOAuth1 $rest $oauth1 0

puts "OK, the Xero OAuth1 is initialized and the REST object is ready to make REST API calls.."

#  -----------------------------------------------------------------
#  Make a call to verify that OAuth1 through an HTTP proxy works..

#  Get the full list of contacts.
set sbXml [new_CkStringBuilder]

set success [CkRest_FullRequestNoBodySb $rest "GET" "/api.xro/2.0/Contacts" $sbXml]
if {$success == 0} then {
    puts [CkRest_lastErrorText $rest]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    delete_CkPfx $pfx
    delete_CkPrivateKey $privKeyFromPfx
    delete_CkPrivateKey $privKeyFromPem
    delete_CkOAuth1 $oauth1
    delete_CkStringBuilder $sbXml
    exit
}

#  A 200 response is expected for actual success.
if {[CkRest_get_ResponseStatusCode $rest] != 200} then {
    puts [CkStringBuilder_getAsString $sbXml]
    delete_CkSocket $tunnel
    delete_CkRest $rest
    delete_CkSocket $channel
    delete_CkPfx $pfx
    delete_CkPrivateKey $privKeyFromPfx
    delete_CkPrivateKey $privKeyFromPem
    delete_CkOAuth1 $oauth1
    delete_CkStringBuilder $sbXml
    exit
}

#  Iterate over the contacts..
set bAutoTrim 0
set xml [new_CkXml]

CkXml_LoadSb $xml $sbXml $bAutoTrim
CkXml_SaveXml $xml "qa_cache/xero_contacts.xml"

#  How many records exist?
set recordCount [CkXml_NumChildrenAt $xml "Contacts"]
puts "numRecords = $recordCount"

set i 0
while {$i < $recordCount} {
    CkXml_put_I $xml $i
    puts "ContactID: [CkXml_getChildContent $xml {Contacts|Contact[i]|ContactID}]"
    set i [expr $i + 1]
}

delete_CkSocket $tunnel
delete_CkRest $rest
delete_CkSocket $channel
delete_CkPfx $pfx
delete_CkPrivateKey $privKeyFromPfx
delete_CkPrivateKey $privKeyFromPem
delete_CkOAuth1 $oauth1
delete_CkStringBuilder $sbXml
delete_CkXml $xml