CGI


This page deals with CGI programming in Perl. If you don't know Perl, you would be best to go to the tutorial on this site, or find another tutorial, so you know the basics of Perl programming.

In order to run scripts you will need a web server running on your computer. The following information explains how to configure a web server and Perl on a Win32 computer in order to run CGI scripts. If you want information on setting up the same things on Unix/Linux there is a lot of information on the web, so I haven't written my own page about it.


Installing Apache and Perl on Win32


Introduction

This is a tutorial about how to run Perl CGI scripts with the Apache server on Win95/98. Remember our purpose is to install and configure these so the Windows environment mimics that of the Unix/Linux host computer, which is where your web pages are hosted; this way we won't have to re-edit scripts when we upload them.


Windows Version Warning

If your version of Windows 95 doesn't have a little "a" after its version number (go to Control Panel/System/General; you'll see your version number under System; something like 4.00.950a), you will have to include Perl's .exe extension in your shebang line:

!#/usr/local/bin/perl.exe
                      ^^^

This little "a" is the result of having installed MS's Server Pack 1, a free download (if you can find it).


Installing ActivePerl

Click the following link to download ActivePerl. Be sure to read this page's contents, since Win95 users will have to download DCOM for Windows 95.

  1. Double-click on the APi51?e.exe file. When you get to the part where you choose an install directory, make sure it's the same as the Perl location on your host site. For example, if Perl is located in /usr/bin/, create a directory c:\usr and install ActivePerl into that (it will create its own /bin directory). Your directory structure should look like this when you're done:
    c:\usr
       \bin
          perl.exe
          etc.
       \html
          Perl documentation
       \lib
          Perl's root library
       \site
          \lib
             Perl's site library 
    
    Note that Perl differs from ActivePerl in that it makes /site a subdirectory of /lib (Perl's root library).
  2. When it asks you which elements you want installed, you should be aware that mod_asp supplies PerlScript support to Apache (Thanks to Jerrad Pierce for bringing me up to date on this). So select Perl for ISAPI and PerlScript, if you're going to be working with these things.
  3. Next, choose to add perl.exe to your path, but don't associate .pl files with perl.exe. Unlike WinNT, you can't run Perl scripts in Win95/98 by double-clicking them; you have to open a command prompt window.
  4. That should be it. When ActivePerl is done installing, reboot your computer.

Here are two tests:

  1. Open your command prompt window (C:\WINDOWS\COMMAND.COM), type perl -v and hit Return. You should get a message saying This is Perl..., etc..
  2. Create the following script in a text editor like Textpad or Notepad:
    #!/usr/bin/perl -w
    print "Hello world, it works!\n";
    

    Save it somewhere, like c:\tmp\test.pl. Now open the command prompt window and type:

    perl c:\tmp\test.pl

    Hit the Return key. The message Hello world, it works! should be printed on your screen.


Installing Apache for(Win32)

The latest Apache Win32 binary is available from the following directory: http://www.apache.org/dist/. It's a typical self-installing .exe, but you might want to change the directory it installs itself into to one that doesn't have spaces: c:\apache would be fine. Be sure it is on the same hard drive as Perl. With Windows 95 you may also need to download Winsock 2 from Microsoft.


Configuring Apache (Win32)

For the following changes, we'll assume your base path on the Unix/Linux host is:
/usr/etc/htdocs/your_site
Therefore, you want to create this same directory structure on your PC. So make a directory called c:/usr/etc/htdocs/your_site and install your web pages there; thus c:/usr/etc/htdocs/your_site/index.html would be the document that is called when someone hits http://www.your_site.com.

Open Apache's configuration file: c:\apache\conf\httpd.conf in a plain text editor. The following instructions are for Apache 1.3.6; line numbers may change with later versions.

  1. At line 217, change:
    ServerAdmin you@your.address to:
    ServerAdmin webmaster@your_domain.com.
  2. At line 232, uncomment (erase the # symbol), and change it to:
    ServerName www.your_site.com.
  3. At line 239, change:
    DocumentRoot "C:/apache/htdocs" to:
    DocumentRoot "C:/usr/etc/htdocs/your_site"
  4. At line 264, change:
    <Directory "c:/apache/htdocs"> to:
    <Directory "c:/usr/etc/htdocs/your_site">
  5. At line 303, you might want to change:
    DirectoryIndex index.html to:
    DirectoryIndex  index.html index.htm index.cgi index.shtml
  6. At line 458, change:
    ScriptAlias /cgi-bin/ "c:/apache/cgi-bin/" to:
    ScriptAlias /cgi-bin/ "c:/usr/etc/htdocs/your_site/cgi-bin/"
  7. At lines 464, change: <Directory "c:/apache/cgi-bin"> to:
    <Directory "c:/usr/etc/htdocs/your_site/cgi-bin">
  8. Uncomment line 606 (erase the # symbol):
    AddHandler cgi-script .cgi

That's it. Double-click on the apache.exe binary and a command prompt window should open up and stay up. (You may get a message saying something like [Sun Jul 04 15:50:18 1999] [warn] pid file c:/apache2/logs/httpd.pid overwritten -- Unclean shutdown of previous Apache run?; this is a known annoyance and doesn't affect the running of Apache). Open your browser, write "localhost" in the GoTo window, cross your fingers, hit Return, and your homepage (the index.html page in whatever directory you assigned DocumentRoot to) should be displayed.

Here's a simple test: create the following Perl script:

#!/usr/bin/perl -w
print "Content-type: text/html\n\n";
print "This CGI script works!";
Save it as c:/usr/etc/htdocs/your_site/cgi-bin/test.pl. Now type localhost/cgi-bin/test.pl in your browser's GoTo window, hit Return, and you should see This CGI script works!.

Changing "localhost" to "www.your_site.com"

Open your c:/windows directory and look for a file called hosts (no extension). If it's called hosts.sam, rename it to hosts. The file should have a line in it something like:

127.0.0.1       localhost

Change this to:

127.0.0.1       www.your_site.com

That's it. Restart Apache and write in www.your_site.com. Your index page will be loaded.

IMPORTANT: To access your pages on the remote host server, you will have to rename hosts back to hosts.sam


Adding Virtual Hosts

To add a virtual host, edit the c:/windows/hosts file like this:

127.0.0.1       www.your_site.com
127.0.0.2       www.client.com
127.0.0.3       www.another_client.com
127.0.0.4       www.last_client.com
127.0.0.5       www.friend.com

Now open the httpd.conf file and add the following lines:

<VirtualHost 127.0.0.2>
ServerName www.client.com
ServerAdmin webmaster@client.com
DocumentRoot /usr/etc/htdocs/client
ScriptAlias /cgi-bin/ /usr/etc/htdocs/client/cgi-bin/
</VirtualHost>

<VirtualHost 127.0.0.3>
ServerName www.another_client.com
ServerAdmin webmaster@another_client.com
DocumentRoot /usr/etc/htdocs/another_client
ScriptAlias /cgi-bin/ /usr/etc/htdocs/another_client/cgi-bin/
</VirtualHost>

etc., etc.

And it's just that easy! Restart Apache and you can run through all your and your clients' pages with the same addresses that you use when you're on-line. For more info and cool stuff to play with, check the docs: C:\apache\htdocs\manual\vhosts.

Important note: You'll have to rename the hosts file (to hosts.sam, for example) when you go on-line, because the 127 series IP numbers will confuse your browser. There are two little Perl scripts, one to rename hosts to hosts.sam (for when you go on-line and want to see the versions of your pages that are on the host server) and another to do the reverse (when you want to run your pages off-line). Then there are two little .bat files that call the scripts from your desktop or wherever.


CGI Programming

Environmental Variables

Every time you run a CGI, your web server sends a whole host of variables to it. These detail information about your site and your server, and also the visitor who is using the CGI. Here is a complete list of those variables:

Variable Data
DOCUMENT_ROOT The root directory of the web server
HTTP_COOKIE The visitor's cookie, if one is set
HTTP_HOST The name of your server
HTTP_REFERER The page that called the script
HTTP_USER_AGENT The browser the visitor is using
HTTPS Set "on" if it is called through a secure server
PATH The system path your server is running on
QUERY_STRING See the GET method of receiving data below
REMOTE_ADDR The visitors IP address
REMOTE_HOST The hostname of the visitor
REMOTE_PORT The port on your web server the visitor is connected to
REMOTE_USER The visitors username
REQUEST_METHOD GET or POST - see below
REQUEST URI The interpreted pathname of the requested document or CGI
SCRIPT_FILENAME The full path name of the CGI
SCRIPT_NAME The interpreted pathname of the CGI
SERVER_ADMIN The email address of the webmaster
SERVER_NAME Your domain name
SERVER_PORT The port the server is set to
SERVER_SOFTWARE Your server type eg. Apache 1.3.11

Depending on the server you are using, you might receive other environmental variables as well. You will not necessarily receive every variable with every CGI either.

These variables are sent to each CGI in a hash called %ENV.

To print out the visitors IP address you would write :

print "IP Address = $ENV{'REMOTE_ADDR'}\n";

Here is my version of a completely working CGI that prints out all the variables in alphabetical order for the visitor in HTML. Call this env.pl (or env.cgi if you are using UNIX - but remember to take this name into account when we use this code below.) and place it in your cgi-bin.

#!/perl/bin/perl
#Remember : this path will vary depending on where Perl is located

print "Content-type:text/html\n\n";

print "<html><head><title>HELLO!</title></head>";
print "<body>\n";
print "<h2>Hello!</h2>\n";

foreach $key (sort(keys %ENV)) {
print "VARIABLE $key = $ENV{$key}<br>\n";
}
print "</body></html>\n";

If you need to debug a script (eg you get ERROR 500) the best way to do that is to run the script from the prompt rather than in the browser. Some things won't work like that of course, but it will still give you a good idea of where the problem is. The other thing I often do is comment out the lines I think are problematic and see if it works then. Keep doing this until you find the troublesome code.


The GET method

There are two ways to get data from an HTML page to your CGI - the GET and POST methods. With the GET method, values are actually sent as part of the URL to the CGI. This method is really only used if you have a small amount of data to pass. Many databases and search engines use this method.

You can see the data being sent - everything after the ? in the URL, plus it is saved in the environmental variable QUERY_STRING.

Create the following web page (eg copy and paste this code) - or make one similar if you see how it works :

<html>

<head>

<title>GET Method Demo</title>

</head>

<body>

<form action="http://localhost/cgi-bin/env.pl" method="GET">

Enter your name :&nbsp;

<p> <input type="text" name="Name"

size=30>

</p>

<p>

Enter your address : <p>

<input type="text" name="Address" size="30"><p>

<input type="submit" value="Submit" name="B1">

</form>

</body>

</html>

Obviously the form action should point to the env.pl CGI you just created, so you may need to change the path.

When you enter data and press submit, the env.pl CGI will be called. Have a look at the QUERY_STRING variable - it should contain the values you enter - amongst quite a bit of confusing information.

I entered Dane and New Zealand into the text boxes and received:

Name=Dane&Address=New+Zealand&B1=Submit

Basically, all words are separated by +'s and all fields are separated by &'s. Other none alphanumeric characters will also look strange, but don't worry about that yet.

Obviously you are going to want to convert this data, and this is one of the reasons you use Perl for CGI scripting. Perl is a very powerful string manipulation language.

Here is my rewriting of the env.pl script so that it prints out the string on the first line, and then the field names followed by their values on each successive line:

#!/perl/bin/perl

print "Content-type:text/html\n\n";

print "<html><head><title>HELLO!</title></head>";
print "<body>\n";
print "<h2>Hello!</h2>\n";

print "String = $ENV{'QUERY_STRING'}\n\n<p>";
@values = split(/&/, $ENV{'QUERY_STRING'});
foreach $i (@values) {
($varname, $mydata) = split(/=/, $i);
print "The value of $varname is $mydata\n\n<p>";
}
print "</body></html>\n";

What we have done if quite simple. Firstly we take advantage of the split function in Perl to break the QUERY_STRING up into its separate sections and store these in an array :

@values = split(/&/, $ENV{'QUERY_STRING'});

For instance, $values[1] will hold the name of the address field, and the address provided by the visitor. These will be separated from each other by the = sign still.

Next we want to print out all the values in the array, so we use the foreach loop.

Inside the loop, for each iteration we split the line apart at the = sign, and store the two sides in the variables $varname and $mydata:

($varname, $mydata) = split(/=/, $i);

We then print this data out:

print "The value of $varname is $mydata\n\n<p>";

To find out more about string manipulation functions, including split, see the Perl tutorial on this site.

GET is a very insecure method of sending data. For a start, it will be stored in the log file (since it is a URL) and anyone can often read logfiles.

Here is a simple CGI that looks at what type of browser the visitor is using and prints out what it is:

#!/perl/bin/perl

print "Content-type:text/html\n\n";

print "<html><head><title>Checking Browser Type</title></head>";
print "<body>\n";
print "<h2>What kind of Browser do you have?</h2>\n";

print "Browser = $ENV{'HTTP_USER_AGENT'}<p>";

if ($ENV{'HTTP_USER_AGENT'} =~ /MSIE/) {
print "You are using Internet Explorer<p>";
} elsif ($ENV{'HTTP_USER_AGENT'} =~ /Mozilla/) {
print "You are using Netscape<p>";
} else {
print "I don't know what you are using<p>";
}
print "</body></html>";

Note that you must test for Internet Explorer first, because it also has Mozilla in its description.

This is really all there is to simply using the GET method, and utilizing environmental variables.


The POST Method

You will usually want to use the POST method, rather than the GET method, in order to send the results of a form to a CGI. The POST method is ideal for all situations where you need to send more than a few words, or where the information needs to be semi-secure.

With the POST method all alphanumeric characters are sent as themselves. Aside from this, spaces are represented by +'s, and all other characters are represented by something like %23 - this is a percent sign followed by two hexadecimal numbers representing the ASCII position of the character. Don't worry about this too much, there are standard methods you can use to parse data. Perl is also a very easy language for doing substitutions in.

Try making a ,ore complex form than in the previous section :

<html>

<head>

<title>The Post Method</title>

</head>

<body>

<form action="http://localhost/cgi-bin/post.pl" method="POST">

Enter your name :&nbsp;

<p> <input type="text" name="Name"

size=30>

</p>

<p>

Enter your address : <p>

<input type="text" name="Address" size="30"> <p>

Enter Email : <p>

<input type="text" name="email" size="30"> <p>

Enter Phone Number: <p>

<input type="text" name="phone" size="30"><p>

<input type="submit" value="Submit" name="B1">

</form>

</body>

</html>

The only thing that has really changed is that the method has been changed from GET to POST.

Now create a CGI to accept the values and print them out. Mine is called post.pl:

#!/perl/bin/perl

read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
@values = split(/&/, $line);
print "Content-type:text/html\n\n";


print "<html><head><title>Parsing with POST method</title></head>";
print "<body>\n";
print "<h2>These were the values sent</h2>\n";
foreach $i (@values) {
($varname, $data) = split(/=/, $i);
print "The variable is $varname, the value is $data<p>";
}
print "</body></html>\n";

Notice that we read from standard input. We store the data in a variable (called $line) here, which is $ENV{'CONTENT_LENGTH'} long (remember, this was an environmental variable).

You may also want to change + signs into spaces before you print the data out. I did that with this line :

$data =~ tr/+/ /;

You will also want to get convert all the non-alphanumeric characters into their ASCII values - at the moment they will look like %26. To do that use this line:

$data =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

You can either accept this because it works, or work out why it works - it isn't all that difficult.

Instead of printing to the screen, you could also store the values in a hash table like this:

$FORM{$varname} = $data;

Once they are in a hash, you can print them out like this:

foreach $key (keys(%FORM)) {
print "$key = $FORM{$key} <p>";
}

This code can be reused over and over again to handle forms of any length.


More Advanced Forms

The forms in the previous section work, but they do not have the error checking necessary for real world CGI's. For instance, you might want to specify that some fields be filled in while others can be left blank. In fact, the CGI's in the previous section happily accept forms with nothing in them.

For instance, we could program the CGI to do something if a field equals one thing, otherwise do something else. Given the example in the previous section I could put this in the CGI:

if ($FORM{"Name"} eq "Dane"){
print "I'm glad to see you<p>";
} else {
print "Go away<p>";
}

Now if I enter my name (Dane) it says "I'm glad to see you", otherwise it says "Go away". Although this is a silly example, it is easy to see from this how some interesting CGI's could be written that test for important conditions.

For instance:

if ($FORM{"email"} eq ""){
print "Please go back and enter an email address<p>";
}

In order to do this you need to know the operators used in testing:

Test Numbers Strings
$a is equal to $b $a == $b $a eq $b
$a is not equal to $b $a != $b $a ne $b
$a is greater than $b $a > $b $a gt $b
$a is greater than or equal to $b $a >= $b $a ge $b
$a is less than $b $a < $b $a lt $b
$a is less than or equal to $b $a <= $b $a le $b

So far we have only dealt with text boxes. These are also several more advanced fields that data can be stored in such as checkboxes and radio-buttons.

Lets change the web page again so that the form includes checkboxes

<html>

<head>

<title>New Page 1</title>

</head>

<body>

<form action="http://localhost/cgi-bin/post.pl" method="POST">

Enter your name :&nbsp;

<p> <input type="text" name="Name"

size=30>

</p>

<p>

Enter your address : <p>

<input type="text" name="Address" size="30"> <p>

Enter Email : <p>

<input type="text" name="email" size="30"> <p>

Enter Phone Number: <p>

<input type="text" name="phone" size="30"> <p>

<input type="checkbox" name="Check1" value="ON">Send Product Information <p>

<input type="checkbox" name="Check2" value="ON">Phone Me with Product Information <p>

<input type="checkbox" name="Check3" value="ON">Add Me to your Database <p>

&nbsp;<p>

<input type="submit" value="Submit" name="B1">

</form>

</body>

</html>

The handler for this is very familiar:

#!/perl/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
@values = split(/&/, $line);
foreach $i (@values) {
($varname, $data) = split(/=/, $i);
$data =~ tr/+/ /;
$data =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$varname} = $data;
}
print "<html><head><title>Parsing with POST method</title></head>";
print "<body>\n";
print "<h2>These were the values sent</h2>\n";
foreach $key (keys(%FORM)) {
print "$key = $FORM{$key} <p>";
}
print "</body></html>\n";

If the checkboxes are ticked, you will receive data like Check1=ON.

The form we have been creating is getting a bit full, so I am going to start a new page.

Here is an example of using a list box. You choose a color, and it opens a page with that color as the background. Here is the calling page :

<html>

<head>

<title>New Page 1</title>

</head>

<body>

<form action="http://localhost/cgi-bin/color.pl" method="POST">

<p>

<select name="color" size="1">

<option value="red"> Red

<option value="green"> Green

<option value="blue"> Blue

<option value="gold"> Gold

</select>

<p>

<input type="submit" value="Submit" name="B1">

</form>

</body>

</html>

color.pl then looks like this:

#!/perl/bin/perl

read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
@values = split(/&/, $line);
print "Content-type:text/html\n\n";

%colors = ("red" => "#ff0000",
"green" => "#00ff00",
"blue" => "#0000ff",
"gold" => "#ffcc00");

foreach $i (@values) {
($varname, $data) = split(/=/, $i);
$data =~ tr/+/ /;
$data =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$varname} = $data;
}

print "<html><head><title>Choosing your color</title></head>";
print "<body bgcolor=\"$colors{$FORM{'color'}}\">\n";
print "<h2>This was the color choosen</h2>\n";

print "</body></html>";


Using Databases on Win32

This information is mainly of relevance if you are programming CGI on Windows95/98.

It isn't particularly difficult to let users on your web page query a database, and then have the results printed out in HTML format. As long as you have a version of Perl that includes the Win32::ODBC library (the Active State version includes it), and as long as you have ODBC in your Control Panel folder (which you almost certainly have if you have any Microsoft products), then this information is relevant.

The first thing you need to do is create a database. I used Access for this, but, as you will see, you can build the database in anything that supports ODBC. 

For this I am using the Nwind.mdb database that comes with my version of Access. It contains quite a number of tables, but this initial demonstration only uses the table called Customers. This is what it looks like in design view:

FIELD NAME DATA TYPE
CustomerID Text
CompanyName Text
ContactName Text
ContactTitle Text
Address Text
City Text
Region Text
PostalCode Text
Country Text
Phone Text
Fax Text

If you don't have a ready made version you can make this and enter data in it, or you can use a database you already have.

The next thing you need to do is open ODBC in your Control Panel. Click on the type of database you are using eg Microsoft Access Driver(*.mbd). Give it a data source name eg. customer_data. Now press select and find the database. Once you select the database this process is complete. You can now access that database from within a program that supports ODBC by using ODBC commands. This saves you the trouble of needing to know all the different commands for all the different databases out there.

Now you need to create the HTML form that the user can enter their search criteria into. This is my version:

<html>
<head>
<title>This is the database search engine</title>
</head>
<body>
<p><b>This is the database search engine:</b></p>
<form action="/cgi-bin/open.pl" method="POST">
<p><b>Name :</b></p>
<p><input type="text" name="name" size="20"></p>
<p><b>Position:</b></p>
<p><input type="text" name="position" size="20"></p>
<p><input type="submit" value="Submit" name="B1"></p>
</form>
<p>&nbsp;</p>
</body>
</html>

It contains two text boxes - one to enter a name to search for, the other to enter a job position to search for. When the user hits submit a CGI called open.pl is called. That CGI looks like this:

1:  #!/perl/bin/perl
2:
3:  use Win32::ODBC;
4:
5:  print "Content-type:text/html\n\n";
6:
7:  read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
8:  @values = split(/&/, $line);
9:  foreach $i (@values) {
10:  ($name, $value) = split(/=/, $i);
11:  $value =~ tr/+/ /;
12:  $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
13:  $FORM{$name} = $value;
14:  }
15:  print "<html><head><title>Database</title></head>";
16:  print "<body>\n";
17:  print "<h2>These are the results</h2>\n";
18:
19:  $DSN="customer_data";
20:  if (!($db=new Win32::ODBC($DSN))) {
21:  print "ERROR CONNECTING";
22:  }
23:
24:  $db->Sql("SELECT * FROM Customers");
25:
26:  while ($db->FetchRow()){
27:  my(%data) = $db->DataHash();
28:
29:  if ($data{'ContactName'} =~ /$FORM{'name'}/) {
30:  print "<b>Name =</b> $data{'ContactName'} <b>Company =</b> $data{'CompanyName'}<p>";
32:  } 
33:  elsif ($FORM{'position'} eq $data{'ContactTitle'}) {
34:  print "<b>Name =</b> $data{'ContactName'} <b>Company =</b> $data{'CompanyName'}<p>";
36:  }
37:  }

38:  print "<p>THIS IS THE END OF THE RECORDS<p>";
39:  $db->Close();
40:  print "</body></html>\n";


The first 18 lines are very familiar except for line 3 - use Win32::ODBC; This tells the interpreter we want to use this library in our script. The rest of the first 18 lines just get the user input and place it in a hash table - the same as we have done in several other scripts.

In lines 19-22 we connect to the database. A string called $DSN is set to the name we gave the database to the ODBC contoller. The database is opened with the command:

$db=new Win32::ODBC($DSN)

but this has been written so that if the database is not successfully opened an error message is given.

Then in line 24 we give an SQL command to get all the data from the table called Customers:

 $db->Sql("SELECT * FROM Customers");

Next we set up a loop to read the data from the SQL command one row at a time.

while ($db->FetchRow()){

my(%data) = $db->DataHash();

You need to use two commands - one to fetch the data and another to put the data in a hash.

Still inside the while loop, we print the data out in HTML format. Firstly, if the name given matches a name in the ContactName field of the database, it is printed out, along with the company name of that person. (Actually, the data supplied by the user is in wildcard form - Th is enough inforamtion to match the names Thomas and Thompson).

if ($data{'ContactName'} =~ /$FORM{'name'}/) {
print "<b>Name =</b> $data{'ContactName'} <b>Company =</b> $data{'CompanyName'}<p>";
 } 

Remember - the $FORM{'name'} refers to the hash table we created with the users input.

Also inside the loop, if the position supplied by the user matches exactly with a position in the CompanyName field of the database, the name of the person and the company name are printed out.

elsif ($FORM{'position'} eq $data{'ContactTitle'}) {
print "<b>Name =</b> $data{'ContactName'} <b>Company =</b> $data{'CompanyName'}<p>";
 }

We then close the connection to the database, and finish the HTML page.

Getting a simple example like this to work is 90% of the effort. Once you reach this stage it is relatively easy to see how you can expand the CGI to deal with more complex situations.


Writing Data to File with Win32

Writing data straight to a text file is easier than using a database. Here is the HTML form the user will enter data into (it is exactly the same as the one above:

<html>

<head>

<title>This writes information to a file</title>

</head>

<body>

<p><b>Enter details below:</b></p>

<form action="/cgi-bin/fileop.pl" method="POST">

<p><b>Name :</b></p>

<p><input type="text" name="name" size="20"></p>

<p><b>Position:</b></p>

<p><input type="text" name="position" size="20"></p>

<p><input type="submit" value="Submit" name="B1"></p>

</form>

<p>&nbsp;</p>

</body>

</html>

The only change is that we are now calling a CGI form called fileop.pl. This CGI collects the data the user enters, opens a text file for appending, and writes each users entry on a newline in the following format:

name | position

This is called a flat database - the divider can be any character that won't be included in the input data.

The CGI looks like this:

#!/perl/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
@values = split(/&/, $line);
foreach $i (@values) {
($name, $value) = split(/=/, $i);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}

open(FP, ">>results.txt");
print FP "$FORM{'name'} | $FORM{'position'}\n";
close(FP);

print "<html><head><title>Writing to File</title></head>";
print "<body>\n";
print "<h2>Your Results have been written to file.</h2>\n";


print "<p>Thank You for your time.<p>";

print "</body></html>\n";


There are only three new lines here:

open(FP, ">>results.txt");
print FP "$FORM{'name'} | $FORM{'position'}\n";
close(FP);

Firstly we open a file for appending. Open uses the following format:

open(filehandle, filename);

I have called my filehandle FP for filepointer, but you can use any name. Next we open a file called results.txt which is in the CGI directory (you can put it where you want, but give a full pathname). The >> means open for appending. > would mean open for writing, while just giving the filename means open for reading. Create an empty text file called results.txt before you run this.

Next we print the information we want with print, but we give it the filehandle as its first parameter. Then we can write whatever we want to the file.

When you are finished close the file.

A potential problem arises if two people try to access the file at once. On Unix you can lock the file with flock so that only one person can use it at once. I am not sure what approach can be used with Windows. 

It is easy to see how you can also make a web counter with this approach. This is the same CGI as above, but it also opens a second file called counter.txt (initially created with 0 on the first line), increments it by one, and prints out the value:

#!/perl/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
@values = split(/&/, $line);
foreach $i (@values) {
($name, $value) = split(/=/, $i);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}

open(FP, ">>results.txt");
print FP "$FORM{'name'} | $FORM{'position'}\n";
close(FP);

print "<html><head><title>Writing to File</title></head>";
print "<body>\n";
print "<h2>Your Results have been written to file.</h2>\n";
print "<p>Thank You for your time.<p>";

open(FP2, "counter.txt");
$num = <FP2>;
close(FP2);

$num++;

open(FP3, ">counter.txt");
print FP3 "$num";
close(FP3);

print "<p>You are visitor number $num<p>";

print "</body></html>\n";


I have opened and read the data with:

open(FP2, "counter.txt");
$num = <FP2>;
close(FP2);

using $num reads one line. If I wanted to read more than one line, I would use something like:

@data = <FP2>;

This would store each new line at a different index in the array.

After incrementing the value of $num, I write it back to the file. This time I want to overwrite the previous value rather than append to it, so I use a single >.

open(FP3, ">counter.txt");
print FP3 "$num";
close(FP3);

I then print out what number visitor you were. I find working with files one of the easiest things with Perl, there is very little that can go wrong.


Setting up Server Side Includes

Server side includes allow you to do a variety of things by putting simple one line tages into HTML code. These files are then saved as .shtml files so that the server knows to scan the file for server side includes.

These are six different types of SSI, but before you can use any of them you need to configure your server so that you can use them. 

To configure the Apache server on a Win32 machine do the following (some of these things may have been done by default):

Open httpd.conf in notepad (or some other text editor - just make sure the editor has a Find facility so you can quickly find the code that needs changing).

Uncomment these two lines:

AddType text/html .shtml
AddHandler server-parsed .shtml

Find the line that says something like:

Options Indexes FollowSymLinks Multiviews

and change it to

Options Indexes FollowSymLinks Includes

This is all I needed to do to my httpd.conf file.


Using Server Side Includes

There are six commands available to you:

1. Include : allows you to have another file (e.g. .txt, .html) embedded in a .shtml file).

2. Echo : allows you to display environmental variables in the .shtml page.

3. Exec : executes a Unix command or CGI script.

4. Fsize : returns the size of a file.    

5. Flastmod : returns the date the file was last modified.

6. Confid : used to control things like the format of output.

I find that the Include and Exec are the most useful commands.

There are many uses for include. For instance, suppose each page on your site must include a disclaimer. You could write this into each page, but then if you wanted to change the disclaimer you would have to change each page. A far easier way to do this would be to have the disclaimer in a separate file (either .txt or .html), and then just include it in each page.

Supposing the file was called disclaimer.txt, and supposing it was in the same directory as the .shtml file, you would embed it like this.

<!--#include file="disclaimer.txt"-->

Of course the file doesn't have to be in the same directory, but this approach takes the directory the .shtml file in to be the root directory, so it can't be in a directory higher in the hierarchy than the .shtml file's directory.

It is possible to give the file address relative to the root directory with the virtual keyword:

<!--#include virtual="c:\apache\disclaimer.txt"-->

I have found lots of good uses for Include. For instance, you might want to keep Guest book comment in a .txt file, and then just include them in a page displaying the Guest book. 

The Exec option is very useful for calling CGI scripts. For instance, here is a simple CGI that counts the visitors to a site :

#!/perl/bin/perl

open(FP, "counter.txt");
$num = <FP>;
close(FP);

$num++;

open(FP, ">counter.txt");
print FP "$num";
close(FP);

Obviously every time this CGI is called the number in the file counter.txt is incremented by one. Since we don't actually want to display the CGI results to the user, we can just call it from a .shtml page. The user isn't even aware that the CGI is being called. Here is the tag to put in the .shtml page:

<!--#exec cgi="/cgi-bin/counter.pl"-->

Echo allows you to print out a whole assortment of Environmental Variables on the .shtml page. Foe instance, to print out the local time every time the page is loaded by a user, you would use the tag:

<!--#echo var="DATE_LOCAL"-->

Just change the variable name to suit. For instance:

<!--#echo var="HTTP_USER_AGENT"-->

This prints out the users browser.

Use Fsize to print out the size of a file. For instance

<!--#fsize virtual="comments.shtml"-->

This displays the size of the comments.shtml file that this tag is embedded in. Obviously this type of tag would be used for more meaningful tasks such as telling the user how big any downloads on your site are.

Flastmod is called in a very similar manner, except it prints out the date the specified file was last modified:

<!--#flastmod virtual="comments.shtml"-->

The final command, config, is used to specify how the other SSI's output their data. For instance, you may not like the way the date is being displayed by these SSI's, so you can change the format:

<!--#config timefmt="%d-%m-%y"-->

The date will now be printed out like 1-1-00.

Size's of files can be abbreviated to something like 25k with:

<!--#config sizefmt="abbrev"-->

or displayed as 25bytes with:

<!--#config sizefmt="bytes"-->

This is really all there is to SSI's. They are very simple, and can't really do much, but what they do, they do well. They can save you a lot of time and trouble if you use them right, and they aren't likely to cause too many problems.