A CGI shell script that prints the current date
#!/bin/sh
# Print out necessary header and empty line
echo "Content-type: text/html"
echo ""
# Print out a title and text. Note the HTML tags in the strings.
echo "<TITLE>The current date!</TITLE>"
echo ""
echo "<H1 align=center>The current date</H1>"
echo "<hr>"
echo ""
echo "Today's date is <b>"
# Echo the date
date +"%a %b %e, %Y"
# Clean up HTML page. Note the ending bold tag.
echo "</b>"
echo "<hr>"
The current date as sent from a simple CGI shell script
A sample CGI program in Perl
The following Perl example prints a message about the browser being used, unless the browser is Netscape Navigator, in which case the example prints a different message. This shows how you can do different actions depending on the client browser. This example also shows how you get information from environment variables.
A CGI Perl example that checks the user's client browser
#!/bin/perl
# Remember that the above line must reflect where your perl really resides.
# sample2.pl: A simple Perl CGI program that displays a different message
# depending on the user's browser.
# Terminate headers
print "Content-type: text/htmlnn";
# Get the User-Agent (also known as client type)
$user_agent = $ENV{"HTTP_USER_AGENT"};
# Print a header with appropriate information
print " <TITLE>Which browser are you using?</TITLE>
<H1 align=center>Which browser are you using?</H1>";
# Print the browser that they're using.
print "<hr>I'm using <b>".$user_agent."</b><hr>n";
# If it's Mozilla (Netscape Navigator), tell them how cool they are.
if($user_agent =~ "Mozilla")
{ print "<i>Congratulations!</i>"; }
else
{ print "<i>To each his own, as they say.</i>"; }
print "<hr>";
A more detailed Perl example
The following example is a simple HTML form that sends feedback about a server. The Perl code following the HTML example is a simple form handler that takes the data from the HTML form and mails it to a hard-coded address.
The CGI form handler uses either the POST or GET method.
The HTML source for the form.
<TITLE>Give some feedback</TITLE>
<h1 align=center>Give some feedback!</h1>
Help us make this site better--give us some feedback to help us grow!
<hr>
<FORM method=POST action=/cgi-bin/sample3.pl>
<b>Your email address:</b> <INPUT type=text name=email size=40>
<p><b>Positive feedback:</b>
<TEXTAREA name=p_feed rows=5 cols=80>I loved it!
</TEXTAREA>
<p><b>Negative feedback:</b>
<TEXTAREA name=n_feed rows=5 cols=80>
You could really improve it if you...
</TEXTAREA>
<p><center><INPUT type=submit value="Send some feedback!">
<INPUT type=reset value="Restore original values">
</FORM>
The form as it appears in Netscape Navigator
#!/bin/perl
# Remember that the above line has to reflect where your perl really resides. This is a
# simple form handler that uses form values to send mail to a hard-coded user.
# Terminate headers
print "Content-type: text/htmlnn";
# Who should get the email and where is the email program?
$send_to = "user@yourserver.yourdomain.dom";
$mail_prog = "/usr/lib/sendmail";
# See which method they used to access this form. If they used POST, then
# read the input from STDIN. If they used GET, use the query string.
# Which method is used is determined by the HTML in the form.
if($ENV{'REQUEST_METHOD'} eq "GET") {
$buffer = $ENV{'QUERY_STRING'};
if($buffer eq "") {
print "<TITLE>Error - use HTML</TITLE>n";
print "<H1 align=center>Please use the HTML form provided</H1>n";
print "You accessed this program without a valid query string. Please ";
print "use the associated form to access it.n";
exit(1);
}
} else {
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}
# Split pairs by the ampersand that divides variables
@pairs = split(/&/, $buffer);
# Create an array, indexed by the variable name, that contains all the values
foreach $pair (@pairs)
{
# Each variable is structured "name1=value1", so split it on those lines
($name, $value) = split(/=/, $pair);
# Decode the value (+ is a space, and %xx in hex is an encoded character)
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# Create an array indexed by names and put the value in
$form{$name} = $value;
}
# The program should have the following values:
# email = the person's email address
# p_feed = the person's positive feedback
# n_feed = the person's negative feedback
#
# Next, put it into a usable form and mail it. Check to see if they left an
# email address. Don't check to see if it's valid, just to see if it's there.
if($form{"email"} eq "") {
print "<TITLE>Sorry</TITLE>n";
print "<h1 align=center>No email address given</h1>n";
print "<p align=center>Your request could not be sent because you ";
print "gave no return address. Please give a return address and ";
print "try again.</p>n";
exit(1);
}
# Open the mail command, or print an error.
open (MAIL, "|$mail_prog $send_to") || die "Could not open $mail_prog";
# Send the feedback. print MAIL "From: $form{'email'}n";
# Print the user's email address as a reply-to, and send the user a copy
print MAIL "Reply-to: $form{'email'}n";
print MAIL "Cc: $form{'email'}n";
# Terminate mail headers.
print MAIL "n";
# Create the document body
print MAIL "Feedback from ".$form{'email'}.":n";
print MAIL "--------------------------------------------------------------n";
print MAIL "n----Positive feedback----n";
print MAIL $form{'p_feed'};
print MAIL "n----Negative feedback----n";
print MAIL $form{'n_feed'};
print MAIL "n------------------------------------------------------------n";
# Close the command, and send the mail.
close (MAIL);
# Now print out a success story, so the user knows it was sent
print "<TITLE>Thanks for your feedback</TITLE>n";
print "<h1 align=center>Thanks for your feedback</h1>n";
print "Thanks for taking the time to give us your feedback. We hope that ";
print "with your help, we can make this an even better web site!n";
print "<hr>";
exit(0);
A sample CGI program as a Java Applet
This section describes a sample CGI program in the form of a Java Applet that gathers information about the remote client and displays it back to the client:
This example returns information about the client browser using a server-side Java
HttpApplet
import netscape.server.applet.*;
import java.io.PrintStream;
import java.net.Socket;
import java.net.InetAddress;
/**
* This class demonstrates how to get various pieces of information about
* the remote browser, such as its type, their IP address or host name,
* and what HTTP method they used to access this class. It also demonstrates
* getting various server configuration information.
*/
public class BrowserData extends HttpApplet {
BrowserData() {}
public void run() throws Exception {
Socket sock = getClientSocket();
String remoteHost = sock.getInetAddress().getHostName();
String remoteSW = getHeader("user-agent");
String method = getMethod();
String path = getServerProperty("path");
if (returnNormalResponse("text/plain")) {
PrintStream out = getOutputStream();
out.println("Hello " + remoteHost);
if (remoteSW != null)
out.println("You're using " + remoteSW);
else
out.println("Your browser didn't identify itself.");
out.println("You accessed " + path +
"nusing the " + method + " method");
out.println("The server is named "
+ getServer().getAddress().getHostName()
+ " (or "
+ InetAddress.getLocalHost().getHostName()
+ ")nand is listening to port "
+ getServer().getListeningPort());
}
}
}
A sample CGI program in ANSI C
This section describes a sample CGI program that handles the output of an HTML form. The following listing is the text of the HTML form:
An HTML form that uses CGI
<TITLE>Guest book</TITLE>
<H1>Guest book</H1>
Welcome to our guest book! Choose one of the following:<p>
<FORM ACTION=gb.cgi METHOD=POST>
<INPUT TYPE=radio NAME=action VALUE=log> Log your name to the
guest book: <INPUT TYPE=text NAME=email SIZE=40><p>
<INPUT TYPE=radio NAME=action VALUE=read> Read the guest book<p>
<INPUT TYPE=submit VALUE="Submit">
</FORM>
First the user accesses the HTML form as /guestbook/gb.html. Once the user selects an action and optionally types in a name for the guest book, they click the Submit button. Note that the name of the radio button is action
, and can have the values log
or read
. Also note that there is a text area called email
.
Sample guest-book/gb.html as it would appear in a client
gb.cgi
.
Note:
For brevity, the entire code necessary to actually update or maintain a guest
book is not provided. Only the code necessary to get information to and from
the server and client through CGI is shown.
This program takes the output of a form and processes it. If the user entered their name, then the program enters it into a guest book. If the user decided to view the guest book, they are redirected to a new URL. The function to update the guest book is not actually provided here, in order to keep the focus on CGI.
The comments to the right of the following header-file include
statements indicate which function prototypes are used from that header file.
#include <stdio.h> /* stdin, printf, fread */
#include <stdlib.h> /* malloc, getenv */
#include <ctype.h> /* isalpha */
#include <string.h> /* strchr, strcmp */
The program begins with some defines and prototypes. Code execution begins in the main function later in the code.
/* The biggest set of form data we'll accept. This is a small form. */
#define MAX_CONTENT_LEN 2048
/* Function defined below prints an error to the client and exits. */
void err(char *s);
/*
* Function defined below takes a pointer to some URL-encoded
* data and allocates a new string, copies and decodes the data into
* that string, and returns the new string.
*/
char *url_decode(char *encoded);
/* This function logs the given email address to the guest book. */
void log_address(char *email)
{
/* This code is not provided...
.
.
.
. */
}
int main(int argc, char *argv[]}
{
The previous functions and defines are used in the main routine where the program starts. Note that the command-line parameters to main are defined but not used. The main begins by defining and initializing its variables. The variables that aren't initialized here are initialized later as they are needed.
/*
* Get the method used to access the script from the environment. This
* form uses the POST method, and using any other results in an error.
*/
char *method = getenv("REQUEST_METHOD");
/* The length of the form results the browser sends the server (set below) */
char *clstr;
int clen;
/* The server holds the form contents in this buffer (+1 for the null) */
char content[MAX_CONTENT_LEN +1];
/* Return code from system or library calls */
int ret;
/* Pointers used to parse the form data */
char *name, *value;
/* A pointer for the remote person's email address */
char *email = NULL;
/*
* Which action the user selected. -1 is invalid and used to indicate
* that no action was selected.
*/
int action = -1;
The execution code begins in the following section. The first thing this program does is check the data the server provides in the environment variables to see if it is correct. If it isn't, the request can't proceed and a message is sent to the client navigation software.
/* First, see if they got the method right. */
if(strcmp(method, "POST") !=0)
err("you must submit a <a href=gb.html>form</a> to access this URL.");
/* Make sure of proper form data and check its length */
clstr = getenv("CONTENT_LENGTH");
if(!clstr)
err("your browser didn't send any content. Is it not POST capable?");
/* Change that string into a number */
clen = atoi(clstr);
/* Make sure it's really form data */
if(strcmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded")!=0)
err("your browser sent the wrong content type.");
/*
* Check for negative or outrageously large content lengths
* An upper limit is set to make sure they don't use system
* resources beyond a stated limit
*/
if((clen < 0) || (clen >= MAX_CONTENT_LEN))
err("your browser created too much data from that tiny form.");
If the variables appear to be set correctly, the program can then read the form data into a string variable.
/* Read in the data in one shot */
ret = fread(content, 1, clen, stdin);
/* Return of < 1 means either EOF or error */
if(ret <1)
err("an I/O error occurred before your form data could be read.");
/* Terminate it with a null char */
content[ret] = '0';
When the program has the string of data the client sent, the program parses that string of data. This involves splitting the string into name-value pairs, splitting those pairs into name
and value
strings, and then URL-decoding those strings.
/*
* Here's where the fun starts. Most of the time, you will want to create
* a generic function to decode form data, and then use that routine in
* your script.
*
* Form data looks like this:
* name1=value1&name2=value2...
*
* The nameN and valueN strings are URL-encoded, which means that many
* special characters such as spaces, semicolons, forward or back slashes,
* question marks, percent signs, newlines, plus signs, and colons will be
* changed from one character into three, of the form %xx, where x is a
* hexadecimal digit. These two digits are changed into a number from 0-255,
* which is interpreted as a character.
*
* Further, many browsers turn spaces into plus signs.
*/
/* Start the routine's name pointer at the beginning of the data */
name = content;
/* name will be set to NULL by some of the code below when the routine is done */
while(name && (*name != '0')) {
/* First find somewhere to put the value pointer */
value = strchr(name, '=');
/* However, check to make sure the right data came in*/
if(value == NULL)
err("the submitted form data was corrupt.");
/* Otherwise, mark the end of the name string. */
*value++ = '0';
/*
* The routine now has the name string by itself. See what it is.
* Note: do not URL-decode the name string, because the form
* is set up such that the names don't use any bad characters.
*/
if(strcmp(name, "action") == 0) {
/* First, find the next name string. NULL indicates the last one */
name = strchr(value, '&');
if(name != NULL)
*name++ = '0';
/*
* Action can have two values: "log" and "read". Again,
* avoid URL-decoding the value because no special chars
* should occur in the form.
*/
if(strcmp(value, "log") == 0)
action = 1;
else if(strcmp(value, "read") == 0)
action = 2;
else
err("your form results had an invalid action.");
}
else if(strcmp(name, "email") == 0) {
/* Find the next name string. NULL indicates the last one */
name = strchr(value, '&');
if(name != NULL)
*name++ = '0';
/*
* The email value will be an email address, which can have
* weird % signs and ! marks in them. The routine must URL decode
* this string. url_decode is defined below.
*/
email = url_decode(value);
}
}
After the program has the data in the email and action variables, it can verify that the form was filled out in its entirety.
/* Check to make sure they filled out the necessary data. */
if(action == -1)
err("you did not pick a button.");
if((action == 1) && ((email == NULL) || (*email == '0')))
err("you did not fill out your email address");
Finally, the program looks at the action the user selected and does what they asked it to do. Note that there is an example of returning a new document (a success page in HTML) and an example of redirecting the client to a new URL (the one that has the guest book for them to view).
/* Now that the data is collected, do something with it. */
if(action == 1) {
/* Log their email address */
log_address(email);
/* Now generate a success page */
printf("Content-type: text/htmlnn");
printf("<title>Congratualtions</title><h1>Congratulations</h1>n");
printf("Your name has been added to our guest book, %s!n", email);
}
else {
/* View the guest book */
/*
* Instead of printing a new HTML page, this function sends them to
* a new URL. This URL is hard-coded to a certain location, but uses
* CGI variables to get the server's hostname and port.
*/
printf("Location: http://%s:%s/guestbook/guests.htmlnn",
getenv("SERVER_NAME"), getenv("SERVER_PORT"));
}
return 0;
}
The main program calls the following supporting functions to perform some basic tasks.
/* -------------------- Function: err --------------------- */
/* Returns an error to the client */
void err(char *s)
{
/* Print the CGI header telling the client that this is HTML */
printf("Content-type: text/htmlnn");
/* This generates HTML for the client, telling them what went wrong. */
printf("<title>Guestbook error</title><h1>Guestbook error</h1>n");
printf("Your form results could not be processed because %s.n");
/* Now, just exit and let them try again */
exit(0);
}
The following functions perform URL decoding on the string.
/* -------------------- Function: url_decode --------------------- */
/* First, a function that verifies that a 2-char string is a hex digit */
int is_hex(char hex)
{
/* Make sure it's upper case */
if(isalpha(hex))
hex = toupper(hex);
/* This just checks the character to see if it's in the two ranges */
if(((hex < 'A') && (hex > 'F")) && ((hex < '0') && (hex > '9')))
return 0;
else
return 1;
}
char *url_decode(char *encoded)
{
/* Allocate space for the new string. The routine won't need more space than it
already has after decoding */
char *new = (char *) malloc((strlen(encoded) + 1) * sizeof(char));
/* Index register for string copy */
char *enc, *dec;
/* Digit is used to translate hex into character */
char digit;
if(new == NULL)
err("the program ran out of memory.");
/* Go through the string, looking for + signs or percents */
for(enc = encoded, dec = new; *enc; enc++, dec++) {
if(*enc != '%') {
/* Plus goes to space, but most chars are untouched */
if(*enc == '+')
*dec = '';
else
*dec = *enc;
}
else {
/* Another tricky part. First, make sure things are safe. */
if((!is_hex(enc[1])) || (!is_hex(enc[2])))
err("invalid escape sequence");
/* Now, advance over the % sign to the first digit, and decode. */
/* The Oxdf is to turn the character into upper case */
++enc;
if(*enc >= 'A')
digit = 16 * (((*enc & Oxdf) -'A') + 10);
else
digit = 16 * (*enc - '0');
/* Finally, transfer the digit to the new string. */
*dec = digit;
}
}
return new;
}