// ============================================================================ // 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 // ============================================================================