Writing Programs with CGIRef

If you already know how CGI works, shoot on down to the Real-Life Fake Example. Otherwise take a second to read the very basics

The very basics of how CGI works

CGI programs create HTML on the fly in a request-response fashion.The user requests the CGI app with an URL, i.e., http://www.foobar.com/cgi-bin/foobar.cgi

The CGI app then responds with a generated HTML document. As a general rule, with Unix webservers, the webserver forks and executes the CGI app, and the CGI app writes the HTML code on standard output. The webserver captures the standard output of the app, and sends it over the TCP/IP channel to the user's browser. This is the case in almost all CGI apps under Unix.

Additionally, the user can send special fields of data to the CGI app in the request, such as a User ID, Part Number, etc. These fields are URL-Encoded(making the characters legal for use in an URL) and transmitted to the server. Precisely how they are transmitted depends upon the Request Method used.  CGIRef supports the two most common methods, GET and POST.

The GET Method

This method puts the entire request on the URL line. An example might be: You would use this method for non-sensitive submissions, and submissions that are referenced frequently, such as web-page counters, and generated graphic images, as browsers frequently cache on an URL-by-URL basis. Passwords and sensitive data should not be submitted with the GET method(IMHO) because these URLs can be looked at on the browser later by another user(unless you clear the cache). Also, since the entire request is on a single URL, you can place the URL as a link on a web page without having to use a form. Note that if you have sensitive data to send back and forth, you should send it over a secure connection such as SSL. Also, the entire URL cannot exceed 255 characters, so this can impact your decision to use GET.

The POST Method

This method "opens a channel" to the CGI app and sends the URL-Encoded data to the CGI app directly. The CGI app on the server side "sees" the request string as standard input, reads it in and generates the document. The POST method is generally used when you might have a lot of data in your request, such as forms with one or more TEXTAREA fields. Generally, the POST method can only be used with a form. Unfortunately, POST requests are usually not cached in the browser, because the request string is separated from the URL. 

A Real-Life Fake Example

OK, on with the show. CGIRef is most useful when you have fields to process. If your program doesn't take fields from the user, CGIRef really won't help you that much. Just code your program in C, and send your headers and HTML code to standard output. That having been said, lets look at a real-life fake example(the actual code for these is located in the /example directory in the distibution): As head of the math department, you want to give your students a chance to 'work out' at simple arithmatic. You want students to get a question, then answer it. Although technically, there is a more efficient way of doing this, I want to illustrate both writing HTML and interpreting forms. Later, as an exercise for you, try to combine this into one program. OK? OK.

Well, we need programs for math questions and math answers. So, let's call the programs mathq.c and matha.c. Let's start with mathq.c , which will select two random numbers and challenge the user to add them and input a correct sum.  



#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_Q_NUM 100             /* Do not define to be 0 - program will fail*/

int main(void)
{
 int N1,N2;
 srand(time(0));

 N1=(rand()%MAX_Q_NUM)+1;
 N2=(rand()%MAX_Q_NUM)+1;

 printf("Content-type: text/html%c%c",10,10);

 printf("<! DOCTYPE  HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">%c",10);
 printf("<HTML>%c",10);
 printf("<HEAD>%c",10);
 printf("<TITLE>Sample Math Question</TITLE>%c",10);
 printf("</HEAD>%c",10);

 printf("<BODY>%c",10);
 printf("<H1>A Sample Math Question</H1>%c",10);
 printf("<HR>%c",10);
 printf("<FORM ACTION=\"/cgi-bin/matha.cgi\" METHOD=\"POST\">");
 printf("<INPUT TYPE=\"Hidden\" NAME=\"N1\" VALUE=\"%d\" SIZE=0></TD></TR>%c",N1,10);
 printf("<INPUT TYPE=\"Hidden\" NAME=\"N2\" VALUE=\"%d\" SIZE=0></TD></TR>%c",N2,10);
 printf("<TABLE>%c",10);
 printf("<TR><TD>First Number</TD><TD ALIGN=\"RIGHT\">%d</TD></TR>%c",N1,10);
 printf("<TR><TD>Second Number</TD><TD ALIGN=\"RIGHT\">%d</TD></TR>%c",N2,10);
 printf("<TR><TD>Enter the sum</TD><TD ALIGN=\"RIGHT\">");
 printf("<INPUT TYPE=\"text\" NAME=\"A\" SIZE=8></TD></TR>%c",10);
 printf("</TABLE>%c",10);

 printf("<INPUT TYPE=\"submit\"><INPUT TYPE=\"reset\">%c",10);
 printf("</FORM>%c",10);

 printf("</BODY>%c",10);

 printf("</HTML>%c",10);

 return(0);
}



This is actual HTML. What this program prints is HTML that read by the user's browser.

Now here is the program to interpret it:



#include <stdio.h>
#include <stdlib.h>
#include <cgi/cgi.h>

char *FieldName_Answer = "A";
char *FieldName_N1     = "N1";
char *FieldName_N2     = "N2";

int ValidateFields(void);

int main(int argc, char *argv[])
{
 unsigned TheirAnswer=0;
 unsigned N1;
 unsigned N2;

 printf("Content-Type: text/html%c%c",10,10);
 
 printf("<! DOCTYPE  HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">%c",10);
 printf("<HEAD>%c",10);
 printf("<TITLE>Quiz results</TITLE>%c",10);
 printf("</HEAD>%c",10);
 

 printf("<BODY>%c",10);
 printf("<H1>Quiz Results</H1>%c",10);
 printf("<HR>%c",10);

 if(!cgiInit())
  {
   if(!ValidateFields())
    {
     if(strlen(cgiGetField(FieldName_Answer)))
      {
       TheirAnswer=atoi(cgiGetField(FieldName_Answer));
       N1         =atoi(cgiGetField(FieldName_N1    ));
       N2         =atoi(cgiGetField(FieldName_N2    ));
       if(TheirAnswer==N1+N2)
        printf("<B>Correct!</B> You answered: %d%c",TheirAnswer,10);
       else
        {
         if(TheirAnswer==0)
          printf("Zero or non-numeric response: %s%c",cgiGetField(FieldName_Answer),10);
         else
          printf("Incorrect! Try again!%c",10);
        }
      }
     else
      printf("Error: You left the answer field blank!%c",10);
    }
   else
    printf("Error: Required field(s) invalid or not present!%c",10);
  }
 else
  printf("Error: Cannot initialize CGI environment!%c",10);

 printf("<HR>%c",10);
 printf("Thank you for taking our quiz%c",10);
 printf("</BODY>%c",10);

 return(0);
}

int ValidateFields(void)
{
 int rv=-1;

 if(cgiFieldPresent(FieldName_Answer))
  if(cgiFieldPresent(FieldName_N1))
   if(cgiFieldPresent(FieldName_N1))
    rv=0;

 return(rv);
}



This program gets the information passed from the browser, and parses the query string. It reads the three variables passed(two addends and a sum), converts them to integers and checks that the one is the sum of the other two.

Basically the program works as follows: First, we call the cgiInit library routine, to initialize the the CGIRef library and read in the query. Then, we call ValidateFields() to make sure the user typed in the right stuff. If not, they get an error message. Then, we obtain character pointers to the passed variables, by calling cgiGetField with the name of the field we want to get. The string values stored in these variables are converted to integers, then added and checked.

Wait!  What's all this with the %c and so forth? Why not just put a "\n" at the end of the line? Because some implementations of printf() do an fflush() system call when they see a "\n" in the format string. Well, that's normally OK, but when we're working over a network socket, an fflush() will trigger a packet being sent. If we are outputting a 5-character line, that will trigger the sending of a packet with a data payload only 5 bytes long...very inefficient, as practically any Unix machine will trigger a send when the buffer fills up to the MTU size. Don't use "\n" with printf()s in CGI apps. Be lazy and let the system decide when to send packets.

NOTE: Stop for a second, look at this program and notice how no matter what happens, the user gets something back. Always design your CGI apps this way. Nothing is more frustrating than to hit the "Submit" button and get nothing back and have no idea what was done wrong.

Finally, we do a horizontal rule, and thank the user.

OK, so you've seen the basics! Now go to it!