// ============================================================================
// This is a Servlet sample for the G-WAN Web Server (http://www.trustleap.com)
// ----------------------------------------------------------------------------
// request.c: get exchange rates from the European Central Bank (updated daily)
// to convert to/from 34 currencies (including the Euro).
//
// We cache rates during one day after the date they were generated.
// Doing so saves CPU and network resources but also preserves the
// availability of a (rare) *free* online source of exchange rates.
//
// Theorically, we should check the ETag: "c9c3f7-6ab-4655096678b80"
// header to verify if the rates we already have are the latest, and
// poll the ECB server until we get a new Etag.
// We prefer to check the "Last-Modified:" HTTP header that lets us
// find at which exact second the latest rates were issued (doing so
// will immensely reduce the load on both G-WAN and the ECB server
// because we can then check online rates one time per day at most).
//
// ============================================================================
#include "gwan.h" // exported G-WAN functions
// Title of our HTML page
static char title[]="Currency conversions";
// Top of our HTML page
static char top[]=""
"
%s"
""
"
%s
";
// ----------------------------------------------------------------------------
// imported functions:
// get_reply(): get a pointer on the 'reply' dynamic buffer from the server
// set_reply(): send back the 'reply' dynamic buffer's pointer to the server
// xbuf_reset(): (re)initiatize a dynamic buffer object
// xbuf_frfile(): load a file, and store its contents in a dynamic buffer
// xbuf_frurl(): make an Http request, and store results in a dynamic buffer
// xbuf_ncat(): like strncat(), but in the specified dynamic buffer
// xbuf_xcat(): formatted strcat() (a la printf) in a given dynamic buffer
// xbuf_free(): release the memory allocated for a dynamic buffer
// rfc2time(): convert an HTTP time string into a time_t value
// time2rfc(): convert a time_t value into an HTTP time string
// s_time(): like time(0), just much faster
// get_env(): get connection's 'environment' variables from the server:
// ----------------------------------------------------------------------------
// int xbuf_frurl(buf, host, port, HTTP_method, URI, ms_timeout, params);
// ----------------------------------------------------------------------------
// find the requested rate (ie: "USD") in the buffer and return it as a double
// ----------------------------------------------------------------------------
double get_rate(char *cache, char *currency)
{
char *p;
double rate=0.;
static char *szvalue="'???' rate='";
szvalue[1]=currency[0];
szvalue[2]=currency[1];
szvalue[3]=currency[2];
p=(char*)strstr(cache, szvalue);
if(p)
{
int rateM=0, ratem=0, scale=1;
p += 12; // "...currency='USD' rate='[1].2942'..."
while(*p!='.') // convert the rate's integral part (from chars to int)
rateM=rateM*10+(*p++-'0');
p++;
while(*p!='\'') // convert the rate's decimal part (from chars to int)
ratem=ratem*10+(*p++-'0'), scale*=10;
// convert the Major and minor integers into a double
rate=(double)rateM+((double)ratem/(double)(scale));
}
return rate;
}
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
xbuf_ctx cache;
char *csp_root, szfile[1024], *p;
long i, code=0; // assume cached file is not there, or not recent enough
// create a dynamic buffer and get a pointer on the server response buffer
xbuf_ctx reply; get_reply(argv, &reply);
// ---- format the top of our HTML page with a title
xbuf_xcat(&reply, top, title, title);
// ---- write static text
xbuf_cat(&reply, " Using the most recent exchange rates: ");
// ---- check and load the cached file, or make the Http request
get_env (argv, CSP_ROOT, &csp_root); // get the ".../csp/" path
s_snprintf (szfile, sizeof(szfile)-1, "%srates.xml", csp_root);
xbuf_xcat (&reply, "- Trying to load %s... ", szfile);
xbuf_reset (&cache);
xbuf_frfile(&cache, szfile);
// check the data creation date (use the "Last-Modified:" header)
if(cache.len)
{
static char sztag[]="Last-Modified:";
xbuf_cat(&reply, "- Found cached data ");
if(p=(char*)strstr(cache.ptr, sztag))
{
// convert the HTTP time string into a time_t (nbr sec since 1970)
unsigned int currtime=s_time();
unsigned int filetime=rfc2time(p+sizeof(sztag));
unsigned int difftime=currtime-filetime;
if(difftime<(3600*24)) // < 1 day
{
char s1[64], s2[64];
xbuf_xcat(&reply, "- Cached data up-to-date: %U<%U ",
difftime, 3600*24);
xbuf_xcat(&reply, " (today's time: %s) (cache's time: %s) ",
time2rfc(currtime, s1), time2rfc(filetime, s2));
code=200; // found cached file, and it is up-to-date
}
else
xbuf_cat(&reply, "- Cache too old, geting fresh data... ");
}
else
xbuf_cat(&reply, "- Could not find time-stamp ");
}
else
xbuf_cat(&reply, "- Could not find cached data ");
if(!code || code==20) // cached file not found (or not up-to-date)
{
xbuf_ctx ecb;
xbuf_reset(&ecb);
code=xbuf_frurl(&ecb, "www.ecb.int", 80, HTTP_GET,
"/stats/eurofxref/eurofxref-daily.xml", 500, 0);
// cache this valuable data for later use
if(code==200)
{
xbuf_xcat(&reply, "- The ECB server delivered new rates ");
if(cache.len) // if not empty, empty cache
{
cache.len=0;
*cache.ptr=0;
}
// copy the new ecb rates in memory cache
xbuf_ncat (&cache, ecb.ptr, ecb.len);
// save the new ecb rates in cached file on disk
xbuf_tofile(&ecb, szfile);
}
else
xbuf_cat(&reply, "- Could not get new rates from the ECB server ");
xbuf_free(&ecb);
}
// ---- success (we can now use this data to write into our html page)
if(cache.len)
{
// add any currency you may need (see at the bottom of the code, a whole
// list) but keep in mind that Payment Gateways will not support them all
double rEUR, rUSD, rCHF, rGPB;
// get the currency rates from the (cached or downloaded) xml file
rEUR=1.;
rUSD=get_rate(cache.ptr, "USD");
rCHF=get_rate(cache.ptr, "CHF");
rGPB=get_rate(cache.ptr, "GPB");
// use the rates to convert currencies
xbuf_xcat(&reply, " "
"100 EUR = USD %.02f ", 100.*rUSD);
xbuf_xcat(&reply, "100 EUR = CHF %.02f ", 100.*rCHF);
xbuf_xcat(&reply, "100 USD = CHF %.02f ", 100.*rCHF/rUSD);
}
else
xbuf_cat(&reply, "- No cached data found. ");
// ---- release memory
xbuf_free(&cache);
// ---- close our HTML page
xbuf_xcat(&reply, "");
// confirm the reply's dynamic buffer address and size to the server
// (they have changed when more memory is allocated during formatting)
set_reply(argv, &reply); return(200); // return an HTTP code (200:'OK')
}
// ----------------------------------------------------------------------------
// The European Central Bank's server reply
// ----------------------------------------------------------------------------
// "HTTP/1.1 200 OK\r\n"
// "Date: Wed, 18 Mar 2009 11:07:02 GMT\r\n"
// "Server: Apache/2.2.3 (Linux/SUSE)\r\n"
// "Last-Modified: Tue, 17 Mar 2009 13:31:42 GMT\r\n"
// "ETag: "c9c3f7-6ab-4655096678b80"\r\n"
// "Accept-Ranges: bytes\r\n"
// "Content-Length: 1707\r\n"
// "Connection: close\r\n"
// "Content-Type: text/xml\r\n"
// "X-Pad: avoid browser bug\r\n"
// "\r\n"
//
//
// Reference rates
//
// European Central Bank
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// ============================================================================
// End of Source Code
// ============================================================================