Book HomeApache: The Definitive GuideSearch this book

4.4. Useful Scripts

When we fill in an order form and hit the Submit Query button, we simply get the heartening message:

Have a nice day

because the ACTION specified at the top of the form is to run the script mycgi.cgi and all it does is to echo that friendly phrase to the screen.

We can make mycgi.cgi more interesting by making it show us what is going on between Apache and the CGI script. Let's add the line env, which calls the Unix utility that prints out all the environment variables, or add the Win32 equivalent, set. Remember that you can't use echo to produce a blank line in Win32, so you have to produce a file, called new1 here, that contains just a RETURN and then type it:

Figure 4.4

#!/bin/sh
echo "content-type: text/plain"
echo
env

Figure 4.4

echo "content-type: text/plain"
type newl
echo
set

Now on the client side we see a screen full of data:

GATEWAY_INTERFACE=CGI/1.1
CONTENT_TYPE=application/x-www-form-urlencoded
REMOTE_HOST=192.168.123.1
REMOTE_ADDR=192.168.123.1
QUERY_STRING=
DOCUMENT_ROOT=/usr/www/site.cgi/htdocs
HTTP_USER_AGENT=Mozilla/3.0b7 (Win95; I)
HTTP_ACCEPT=image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
HTTP_ACCEPT_LANGUAGE=
CONTENT_LENGTH=74
SCRIPT_FILENAME=/usr/www/cgi-bin/mycgi
HTTP_HOST=www.butterthlies.com
SERVER_SOFTWARE=Apache/1.3
HTTP_PRAGMA=no-cache
HTTP_CONNECTION=Keep-Alive
HTTP_COOKIE=Apache=192257840095649803[37] 
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
HTTP_REFERER=http://www.butterthlies.com/form_summer.html
SERVER_PROTOCOL=HTTP/1.0
REQUEST_METHOD=POST
SERVER_ADMIN=[no address given]
SERVER_PORT=80
SCRIPT_NAME=/cgi-bin/mycgi
SERVER_NAME=www.butterthlies.com

If we have included the module mod_unique_id, we also have the environment variable UNIQUE_ID, which has attached to it a unique number for each hit:

UNIQUE_ID==NWG7@QoAAAIBkwAADYY

The script mycgi.cgi has become a tool we shall keep up our sleeves for the future.

Of course, a CGI script can send any valid header it likes. A particularly useful one is Location , which redirects the client to somewhere else -- which might be anywhere from a file up to another URL. In this case, we can pretend that we have run some sort of program that collects information; having done that, we return the client to the starting URL. The script .../cgi-bin/location.cgi is as follows:

#!/bin/sh
echo "content-type: text/plain"
# run some program to gather information
echo "Location: http://192.168.123.2"
echo

Once the form has been changed to run this file rather than mycgi.cgi, clicking on the Submit button shoots us straight back to the original screen.

Now we can set about writing a C version of mycgi that does something useful. Let's think now what we want to do. A customer fills in a form to order some cards. His browser extracts the useful data and sends it back to us. We need to echo it back to him to make sure it is correct. This echo needs to be an HTML form itself so that he can indicate his consent. If he's happy, we need to take his data and process it; if he isn't, we need to resend him the original form. We will write a demonstration program that gets the incoming data, builds a skeleton HTML form around it, and sends it back. You should find it easy enough to fiddle around with the program to make it do what you want. Happily, we don't even have to bother writing this program, because we can find what we want among the Netscape forms documentation: the program echo.c, with helper functions in echo2.c. This program is reproduced with the permission of Netscape Corporation and can be found in Appendix B, "The echo Program ".

4.4.1. echo.c

echo receives incoming data from an HTML form and returns an HTML document listing the field names and the values entered into the fields by the customer. To avoid any confusion with the Unix utility echo, we renamed ours to myecho. It is worth looking at myecho.c, because it shows that the process is easier than it sounds:

#include <stdio.h>
#include <stdlib.h>
#define MAX_ENTRIES 10000
typedef struct
    {
    char *name;
    char *val;
    } entry;

char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);

int main(int argc, char *argv[])
    {
    entry entries[MAX_ENTRIES];
    register int x,m=0;
    int cl;
    char mbuf[200];

The next line:

printf("Content-type: text/html\n\n");

supplies the HTML header. We can have any MIME type here. It must be followed by a blank line, hence the \n\n. The line:

if(strcmp(getenv("REQUEST_METHOD"),"POST"))

checks that we have the right sort of input method. There are normally only two possibilities in a CGI script: GET and POST. In both cases the data is formatted very simply:

fieldname1=value&fieldname2=value&...

If the method is GET, the data is written to the environment variable QUERY_STRING. If the method is POST, the data is written to the standard input and can be read character by character with fgetc( ) (see echo2.c in Appendix B, "The echo Program ").

The next section returns the length of date to come:

{
        printf("This script should be referenced with a METHOD of POST.\n");
        exit(1);
        }
    if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) 
        {
        printf("This script can only be used to decode form results. \n");
        exit(1);
        }
cl = atoi(getenv("CONTENT_LENGTH"));

The following snippet reads in the data, breaking at the & symbols:

for(x=0;cl && (!feof(stdin));x++) 
    {
    m=x;
    entries[x].val = fmakeword(stdin,'&',&cl);
    plustospace(entries[x].val);
    unescape_url(entries[x].val);
    entries[x].name = makeword(entries[x].val,'=');
    }

The next line displays the top of the return HTML document:

printf("<H1>Query Results</H1>");

The final section lists the fields in the original form with the values filled in by the customer:

printf("You submitted the following name/value pairs:<p>%c",10);
    printf("<ul>%c",10);

    for(x=0; x <= m; x++)
        printf("<li> <code>%s = %s</code>%c",entries[x].name,
               entries[x].val,10);
    printf("</ul>%c",10);
    }

We compile myecho.c and copy the result to mycgi [38] to see it in action next time we run the form. The result on the client machine is something like this (depending on how the form was filled in):

[38]Of course, we could have changed the form to use myecho instead.

QUERY RESULTS
You submitted the following name/value pairs:
* 2315_order=20
* 2316_order=10
* 2317_order=
* 2318_order=
* card_type=Amex
* card_num=1234567

Clearly, it's not difficult to modify myecho.c to return another form, presenting the data in a more user-friendly fashion and asking the customer to hit a button to signify agreement. The second form activates another script/program, process_orders, which turns the order into delivered business. However, we will leave these pleasures as an exercise for the reader.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.