// ============================================================================ // This is a Servlet sample for the G-WAN Web Server (http://www.trustleap.com) // ---------------------------------------------------------------------------- // loan.c: An AJAX Web application to help people calculate the cost of loans // // When clients use the loan.html form, we use the form fields to // build new content which is then inserted into the original page // (the javascript code in loan.html will tell you how AJAX works). // // GET and POST forms are processed with the SAME code (the server // does the URL/Entity parsing for servlets). // // POST is often prefered to GET because the parameters are passed // in the URL (so they are visible) while POST is using the request // entity. This difference is *not* relevant in our case because we // use AJAX to process the query (as a result, the URL is not seen // by end-users in the Web browser 'address' field). // // Tip: When using XMLHttpRequest (AJAX), Web browsers split POST in two // steps: they send headers and data separately. // As GET takes only one step then you can save a connection by not // using POST in your forms -enhancing your web server scalability. // (this is an efficient way to boost AJAX graphic user interfaces) // // Using AJAX here saves you from using mod_rewrite to have "normal" // search-friendly URLs while taking advantage of servlets. // // One of the most important things to keep in mind here is to make // sure that clients will not lock-up your code with bad input data. // The motto is 'filter'. You can also do it on the client side with // javascript -but don't count on it. Filter again in your servlets // because you just cannot trust what the network brings you (IPSec // and SSL make absolutely no difference in this matter). // // Note: xbuf_xcat() will insert ',' every 3 digits if you use uppercase // formatters like: "%D", "%U", "%F" (instead of "%d", "%u", "%f"). // To format (unsigned) __int64 values, use ("%llu" or) "%lld". // // ============================================================================ #include #include "xbuffer.h" // G-WAN dynamic buffers // Top of our HTML page (so it can be called directly rather than as an insert) static char top[]="" "Loan Calculator" "" "

Dear %s, your loan goes as follows:

"; // ---------------------------------------------------------------------------- // 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_ncat(): like strncat(), but in the specified dynamic buffer // xbuf_xcat(): formatted strcat() (a la printf) in a given dynamic buffer // get_arg(): get the specified form field value // escape_html(): translate HTML tags into harmless escaped sequences // ---------------------------------------------------------------------------- // like atof() -but here you can *filter* input strings // ---------------------------------------------------------------------------- double atod(char *p) { double val=0.; if(p && *p) { int valM=0, valm=0, scale=1, sign=0, i=11; while(*p && *p==' ') p++; // filter spaces, if any switch(*p) { case '-': sign=1; case '+': p++; } while(i && *p && *p>='0' && *p<='9') // convert the integral part valM=valM*10+(*p++-'0'), i--; if(*p=='.') // pass the decimal point { p++; i=2; while(i && *p && *p>='0' && *p<='9') // convert the decimal part valm=valm*10+(*p++-'0'), scale*=10, i--; } // convert the Major and minor integers into a double val=(double)valM+((double)valm/(double)(scale)); val=sign?-val:val; } return val; } // ---------------------------------------------------------------------------- // return the smallest integer greater than x (this one doesn't use modf) // ---------------------------------------------------------------------------- __inline unsigned int uceil(double x) // yes, you can use __inline { unsigned int ipart=(int)x; if(x-ipart) return(x<0.0)?ipart:ipart+1; else return(ipart); } // ---------------------------------------------------------------------------- // main() is receiving the query parameters ("csp?arg1&arg2&arg3...") in argv[] // ---------------------------------------------------------------------------- int main(int argc, char *argv[]) { // create a dynamic buffer and get a pointer on the server response buffer xbuf_ctx reply; get_reply(argv, &reply); // ---- no query parameters were provided, redirect client to "loan.html" if(argc<2) { static char redir[]= "HTTP/1.1 302 Found\r\n" "Content-type:text/html\r\n" "Location: loan.html\r\n\r\n" "" "Redirect" "Click HERE for redirect."; xbuf_ncat(&reply, redir, sizeof(redir)-1); // 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(302); // return an HTTP code (302:'Found') } else // ---- if we have query parameters, we process a GET/POST form { char Months[][10]={ "January","February","March","April","May","June","July", "August","September","October","November","December"}; char szName[400]={0}; double amount, rate, term, payment, interest, principle, cost; int month=0, year=1, lastpayment=1; // the form field "names" we want to find values for char *Name="", *Amount="0", *Rate="0", *Term="0"; __int64 start=cycles64(); // current CPU clock cycles' counter // get the form field values (note the ending '=' name delimiter) get_arg("name=", &Name, argc, argv); get_arg("amount=", &Amount, argc, argv); get_arg("rate=", &Rate, argc, argv); get_arg("term=", &Term, argc, argv); // all litteral strings provided by a client must be escaped this way // if you inject them into an HTML page escape_html(szName, Name, sizeof(szName)-1); // filter input data to avoid all the useless/nasty cases amount = atod(Amount); if(amount<1) amount=1; rate = atod(Rate); if(rate> 19) rate =19; else if(rate >1) rate /=100; else if(rate <1) rate =1./100.; term = atod(Term); if(term<0.1) term =1./12.; else if(term>100) term =30; // calculate the monthly payment amount payment = amount*rate/12*pow(1+rate/12, term*12) / (pow(1+rate/12, term*12)-1); cost = (term*12*payment)-amount; // build the top of our HTML page xbuf_xcat(&reply, top, (*szName && *szName!='-')?szName:"client"); xbuf_xcat(&reply, "
" "" "" "" "" "" "
loandetails
Amount%.2F
Rate%.2F%%
Term%u %s(s)
Cost%.2F (%.2F%%)
", amount, rate*100, ((unsigned int)term)?((unsigned int)term):uceil(12*term), ((unsigned int)term)?"year":"month", cost, 100/(amount/cost)); xbuf_xcat(&reply, "
" "
YEAR %u
" "" "" "", year); for(;;) // output monthly payments { month++; interest = (amount*rate)/12; if(amount>payment) { amount = (amount-payment)+interest; principle = payment-interest; } else // calculate last payment { if(lastpayment) { lastpayment = 0; payment = amount; principle = amount-interest; amount = 0; } else // all payments are done, just padd the table { amount = 0; payment = 0; interest = 0; principle = 0; } } xbuf_xcat(&reply, "" "", month&1, Months[month-1], payment, interest, principle, amount); if(month==12) { if(amount) { month=0; year++; xbuf_xcat(&reply, "
monthpaymentinterestprinciplebalance
%s%.2F%.2F%.2F%.2F

" "
YEAR %u
" "" "" "", year); } else break; } } // time the process and close the HTML page xbuf_xcat(&reply, "
monthpaymentinterestprinciplebalance

This page was generated in %llU cpu clock cycles." "
(on a 3GHz CPU 1 ms = 3,000,000 cycles)" "
", cycles64()-start); } // 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') } // ============================================================================ // End of Source Code // ============================================================================