Sunday, May 1, 2011

Proprietary Extensions to Severs:Chapter 13


Chapter 13

Proprietary Extensions to Severs





CONTENTS


Behind every good CGI program is a good server, usually an HTTP server. An HTTP server is a program that intermediates between the end user (the browser) and the CGI program itself. It is the server that actually spawns the CGI program, passing along information from the browser, and it is the server that collects the output of the CGI program and sends it back to the end user who requested it.
At its most basic, that is all a Web server has to do: Be a sort of halfway house for information between the client and the machine storing the data. It didn't take long before people started to realize that the more tricks a server had up its sleeve, the less work they would have to do with external CGI programs. As a result, server extensions were born.
Server extensions are added bits of functionality that are either built straight into the server or dynamically added to the server as needed. Either way, they are part of the server. This means that when a client requests a function from a server, the server extension is right there. Unlike CGI programs, a server extension does not require an extra process to be run on the host machine.
A downside to server extensions is that they are largely proprietary, meaning that a nifty add-on to the ncSA server might not be present in the CERN server or vice versa. However, several server extensions have proven useful enough to be added to many of the most popular Web servers (which at the time of this writing means Apache, ncSA, and Netscape).
I'll start by looking at the two server extensions that are currently used the most: server push and cookies.

Server Push

Server push was first envisioned as a method for displaying simple animations over the Web. Unfortunately, it's not very good at it. At the time server push was implemented, it was the only option, but today Web animations are better served by Java or GIf89a extensions. Other applications have been found for server push, and its cousin, client pull, so it remains a useful extension supported by almost all servers.
To understand server push, you must first look at how the Web server handles data before it passes it on to the client. When asked to retrieve a file from the host, the server first looks at the extension of the file and based on that, assigns it a MIME type. MIME (Multipurpose Internet Mail Extensions) types tell the browser what type of file it is receiving. This is usually put in the initial header of the document, which the end user never sees. For example, if you were to Telnet to www.yahoo.com on port 80 (the HTTP port), and type GET / HTTP/1.0, you would receive Yahoo's home page with the following information attached to the beginning:
HTTP/1.0 200 OK
Last-Modified: Thu, 25 Jul 1996 21:45:35 GMT
Content-Type: text/html
Content-Length: 5490
The first line tells the browser what version of the HTTP protocol Yahoo's server is using; the OK indicates it found the requested document. The second line gives the date the document was last modified, and the last line gives the size of the document in bytes. The third line, Content-type: text/html, tells the browser to expect an HTML document and treat it accordingly. It is this content type header that makes server push possible.
As intimated by the acronym, MIME was originally designed as a set of protocols to enable electronic mail to contain full multimedia enhancements. In fact, most modern mail clients do support MIME, but its usefulness has grown beyond that. One of the necessities of transferring multimedia mail is the capability to enclose several different types of documents in one message. MIME does this through a content type called multipart/mixed. A document of this type has a string of characters given in the header known as the boundary. This boundary can occur any number of times in the message, each time preceded by --. Each section of document between boundaries is considered a separate message with its own MIME type.
In 1995, some smart folks at Netscape, Inc., thought of a way to put a twist on the multipart/mixed MIME type that would allow animations to be displayed over the Web. A new MIME type was created: multipart/x-mixed-replace. The x means that it's an extra type that is not part of the official MIME specification. The replace part is the key. This means that instead of one message with lots of different types, a multipart/x-mixed-replace message is really lots of separate messages all with the same type. As each message is displayed, it replaces the one before it. When applied to a MIME type such as image/gif, this multipart/x-mixed-replace causes one GIF to be displayed and then replaced by another and so on. Voilà! Instant "poor man's animation."
Because the key to server push lies in the header that appears before the document is sent, you can't use server push within a plain HTML document. You must use a CGI program to generate the document and send each part along as needed.
One of the great advantages of server push is that as long the browser remains on the page, the server can keep the connection open as long as it wants. Suppose you want to keep users around the world up-to-date on the local softball game. Using server push, a connection is kept open between your server and each viewer, and you could send an updated score whenever necessary.

Note
The advantage of a persistent connection can also be a disadvantage. Having a server keep the connection open keeps the server's process alive, which could cause added load to your system. This is usually not a problem, but if you get thousands of hits a day, it does add up.

For a simple example, you can go back to the original purpose of server push and make a poor man's animation. I'll use Perl for these examples, although this first one is simple enough for Bourne Shell. I start by cycling through a series of GIFs (assumed to be in the current directory and named 0.gif through 9.gif):
#!/usr/local/bin/perl -Tw

use strict; # The -Tw flags and "use strict" are always useful for
# keeping CGIs safe.

print <<EOP; # Print the HTTP header
Content-type: multipart/x-mixed-replace;boundary=I_Like_Snow

EOP
for $i (0..9) {
    print "--I_Like_Snow\n"; # Print boundary to start new image.
    print "Content-type: image/gif\n\n"; # Tell client that it's a GIF
    open (GIF, "$i.gif");      #
    while (<GIF>) { print; }   #  Send the GIF.
    close GIF;             #
}

print "--I_Like_Snow--\n"; # Tell client that you're done.

Tip
It might be necessary to read the documentation that came with your server before running these examples. Some servers have trouble with certain aspects of server push. Notably, ncSA httpd has been known to have fits if the HTTP header isn't exactly right.

Label this script as a CGI program (for example, cycle.cgi) and then insert the tag <IMG SRC="cycle.cgi"> into any HTML file in the same directory, and suddenly you have a full-blown animation accessible from the Web. As it stands, this script pushes the GIFs to the client as fast as it can. Because most people use 14.4Kbps modems or live far away from your server, this rapid pushing guarantees that the end user receives the animations as quickly as possible. This also means that people using a good connection might receive the images too fast to display and could skip images. You can avoid this by using sleep statements in strategic parts of the script to pause between images. Using sleep statements is also a good way to create slide-show-type animations. You could cause each image to stay on the screen for however long is necessary before the next image is called up.
As mentioned previously, you can also use server push to create an HTML document that updates itself as needed. As long as the client stays on the page, it receives the new information; as soon as the client leaves the page, the connection is broken and no more information is sent. For an example of this, consider the script in Listing 13.1, which displays the users currently logged into a UNIX machine and updates the display every time someone logs in or out.

Note
Listing 13.1 makes heavy use of the system() function so it must be run from a UNIX machine with the necessary commands. You probably need to change it somewhat for your system to reflect the location and differences in syntax of the commands.


Listing 13.1. Dynamically display current users.
#!/usr/local/bin/perl -Tw

print <<EOP; # Print the HTTP header
Content-type: multipart/x-mixed-replace;boundary=I_Like_Snow

EOP

LOOP:
while (1) {     # Perform an endless loop. We leave it up to the client
                # to break the connection.
         open(WHO, "w|");  # Run the 'w' command to list users.
         my @who = <WHO>;  # Collect the result in an array.
         close WHO;  # Close the command.

         my $i; # Dummy variable
         foreach $i (2..$#who) { # Start at the third line of output.
                                 # The first two are other information.
                my @fields = split(/ +/, $who[$i]); # Get the user name.
        push(@users2, $fields[0]); # Add it to list of
                       # current users.
    }
    foreach $user2 (@users2) {
        # If any of the current users were not present last
        # time through, add them to a list of new users.
        if (grep(!/$user2/,@users)) { push(@newusers,$user2); }
    }
    foreach $user (@users) {
        # If any of the users last time through are not
        # present this time, add them to a list of old
        # users.
        if (grep(!/$user/,@users2)) { push(@oldusers,$user); }
    }
    # If no one has logged in or out since last time, don't do
    # anything. This way a new HTML page is only sent when a
    # change is made.
    if (@newusers || @oldusers) {
        $who = join("<br>\n",@who); # Translate linefeeds to <BR>s.

        # Print the boundary, headers, and beginning of message.
        print <<EOX;
--I_Like_Snow
Content-type: text/html

<HTML><HEAD><TITLE>Current Users</title></head><BODY>
Current Users:<br>
$who
<br>
EOX
        # List everyone who has logged on since last time.
        foreach (@newusers) {
            print "$_ has logged on!<br>\n";
        }
        # List everyone who has logged out since last time.
        foreach (@oldusers) {
            print "$_ has logged off!<br>\n";
        }
        print "</body></html>\n";
    }
    # Wait 10 seconds then do it again.
    sleep 10;
}

This script is just a sample of what you can do with server push. Even though it has lost the spotlight to newer and flashier technologies, server push can still yield great results when applied creatively.

HTTP Cookies

Aside from being great with milk, cookies have become one of the most discussed server extensions ever. HTTP cookies are an attempt to solve one of the great hurdles of working with CGI: state retention.
Transfer over the Web is transient. Once the server sends the document to the client, it forgets that the client ever existed. If a user browses through a hundred pages on the same site, they make a hundred separate connections to the same server.
This presents a big problem for CGI programs. Almost all CGI-based applications, from games to databases, need to store information about the person using them between calls to the program. Traditionally, this requires saving information in temporary files or hidden variables in HTML forms. For instance, a series of CGI programs that interact with a database might ask for a username on the first page, save the data to a temporary file, and then pass the username and temporary filename from CGI to CGI by using hidden variables.
If this all sounds complicated, that's because it is. Cookies are an attempt to simplify this by allowing the browser to store information sent to it by the server. The browser then sends the information back each time it accesses that server. To avoid filling up the hard drive of the end user, cookies are generally very short pieces of information: usually an ID number or filename of a file on the server that contains more information.

Note
Although called server extensions, almost everything discussed in this chapter must also have support on the client side. This is especially true for HTTP cookies. Unless the browser has the capability to store the cookie information, your CGI can't do anything with it. At the time of this writing, several major browsers still have not implemented HTTP cookies, including Lynx-FM and ncSA Mosaic. If your pages absolutely depend on cookies, it's a good idea to provide an alternate mechanism for people who have no access to a cookie-capable browser.

Like server push, HTTP cookies are implemented through the HTTP header that is sent
before the document itself. The value of a cookie is passed to the client through the header Set-Cookie. The full syntax, as it would appear to a browser receiving a cookie, is as follows:
Set-Cookie: name=value; expires=date; path=path; domain=domain; secure
Everything except for name=value is optional. The following list outlines each part in more detail:
  • name=value-This is the actual cookie. name and value can be anything as long as they don't contain semicolons, commas, or white space. (You can use URL-encoded equivalents in their place.)
  • expires=date-date must be formatted as follows:
    Wdy, DD-Mon-YYYY HH:MM:SS GMT. If the browser supports it, the cookie is deleted after the expires date has been reached.
  • path=path-path (on the right-hand side) is the uppermost directory for which the cookie is valid. If path is a file, the cookie is only valid for that file. path is the URL path, not the path on the server file system. (For example, the path for http://foo.bar.com/my/dir/ is /my/dir.) The path / encompasses all URLs within the server machine.
  • domain=domain-domain (on the right-hand side) is the subset of all Internet domains for which the cookie is valid. For example, a domain of .msu.edu creates a cookie that is activated for all hosts that end in msu.edu. The domain must have at least two dots to prevent someone from giving out cookies that activate for any domain.
  • secure-If the secure flag is present, the cookie is only transmitted through HTTPS servers. This only has meaning to servers that support the SSL protocol, which are very few (mainly the Netscape Secure Server).
Every time a URL is entered into a cookie-aware browser, the browser checks to see if it has any cookies that belong to the domain of the URL. If it finds any, it checks them to see if the URL's path contains the path of any of the cookies. If any do, it sends the following as a header back to the server:
Cookie: name=value
If more than one cookie matches, the client returns
Cookie: name1=value1; name2=value2
for as many cookies as are valid.
If two or more cookies have the same name (which is possible if they have different paths), the browser sends them all. If the server is cookie-aware, it sets the environment variable HTTP_COOKIE once it receives the cookie header. It is through this environment variable that CGI programs retrieve the cookie information.
Listing 13.2 is a simple Perl script that displays any cookies that are sent from the browser.

Listing 13.2. Display cookies sent from the browser.
#!/usr/local/bin/perl -Tw

print <<EOP; # Print headers and beginning of message.
Content-type: text/html

<HTML><HEAD><TITLE>Mmmmm.... Cookies</title></head><BODY>
EOP

if (!$ENV{'HTTP_COOKIE'}) {
    # The server puts cookie information into the environment
    # variable 'HTTP_COOKIE. If there are no cookies, print a
    # message then leave.

    print <<EOP;
Sorry, the browser didn't send you any cookies!
</body></html>
EOP
    die;
}

print "<H1>Your Cookies</h1>\n";

$cookies = $ENV{'HTTP_COOKIE'};

# Split the cookie string up into individual cookies.
@cookies = split(/; */, $cookies);

foreach (@cookies) {
    # For each of the cookies, split the cookie up into two parts:
    # The name (key) and the value.
    ( $key, $val ) = split(/=/, $_);

    # If more than one cookie has the same name, combine their
    # values, separated by commas. (The syntax of the HTTP header
    # already guarantees that there are no commas in the cookie.)
    if ($COOKIE{$key}) { $COOKIE{$key} .= ",$val"; }
    else { $COOKIE{$key} = $val; }
}

# Now go through each of the cookies in alphabetical order.
foreach (sort keys %COOKIE) {

    # If the cookie's value has a comma in it, it must be a multiple
    # cookie.
    if ($COOKIE{$_} =~ /,/) {
        # Split the multiple cookie up in to each value...
        @vals = split(/,/,$COOKIE{$_});
        foreach $valu (@vals) {
            # ...and print each value as a separate cookie.
            print "$_ => $valu<br>\n";
        }
    } else {
        print "$_ => $COOKIE{$_}<br>\n";
    }
}
# Say bye bye.
print "</body></html>\n";

You could easily turn the core code of the preceding script into a subroutine that returns the hash %COOKIE containing all the cookie values. It is useful to do this if you use cookies often. Including this subroutine in an external package allows you to check for cookies easily in any CGI script.
Now that you can store and retrieve cookies, what good are they? Originally, they were mainly used for storing user preferences and to facilitate "shopping cart" systems. The ability to store data on the client side has proven to be very versatile. If you have access to a Netscape browser (version 2.0 or greater), turn on the option that brings up a requester every time you are presented with a cookie. It is amazing to see the number of sites using cookies for one reason or another.
To some people, it is also frightening. Of the two main reasons for using cookies, storing user preferences has come under great scrutiny as a possible danger to privacy. Soon after cookies were developed, many sites advertised the capability to alter their pages to suit a user's preference, which is done using cookies. Every page a user visits on one of these sites sends the browser a cookie. The home page is then altered to include links to pages that the user visits frequently. Some users feel that having their movements tracked in that way is a violation of their privacy-despite the fact that all the information gathered from the cookies is also available, albeit harder to interpret, from the logs of the Web server. It's generally a safe plan, if you really want to track a user's preferences, to inform the user of this up front and allow him the option of not having his information logged.
For an example of HTTP cookies, turn to their other major application: the "shopping cart." A Web shopping cart is simply a method for allowing the end user to browse a number of pages, selecting any number of items from HTML forms on those pages and storing the selections for later use. There need not be any actual shopping involved. In fact, the next example would be impractical for real commerce because no security is involved.

Tip
In this example, a module called cgi_head.pm is used to gather the information from the form. This module places a form entry with the name 'foo' into an associative array entry with name $FORM{'foo'}. Included in this module is the subroutine you saw previously, which loads all cookie information into an associative array called %COOKIE. There are several freely available programs for several languages to accomplish this, including CGI.pm for Perl at http://www.perl.com/perl/CPAN/.

For simplicity's sake, the shopping cart is contained in one CGI program that produces one HTML page. The real power of shopping carts is that they can span any number of pages, remembering the user's items throughout. Listing 13.3 displays the simple shopping cart.

Listing 13.3. Simple shopping cart.
#!/usr/local/bin/perl -Tw

require cgi_head; # Get %FORM and %COOKIE hashes.

# Now we create an associative array that contains information about our
# product. This information could be gathered in any method: from a flat file,
# from a database, from another process, etc. This information isn't even
# necessary for the actual program to run, it just provides a more realistic
# simulation.
%whatzit = (
    Blue => '14.95',
    Red => '12.50',
    Big => '19.95',
    Tiny => '4.95',
    Paisely => '99.95'
);

# We set a variable with the date as it will be 3 hours from now (10800
# seconds is 3 hours).
$later = time + 10800;
# We now convert the time into the format required by the Cookie syntax.
$date = gmtime($later);

# The browser didn't send us any cookies, this must be its first trip
# here. All we need to do is assign it an ID number and print out the
# introductory HTML form.
if (!%COOKIE) {

    # The user's ID number is a combination of the current time and
    # the current process id number. Since any two processes running
    # at the same time will have different id numbers, this creates a
    # pretty much unique number.
    $id = time . $$;

###########################IMPORTANT###################################
# The second line below is the actual 'Set-cookie' HTTP header.       #
# The name of the cookie is the ID number, and the value is '0'.      #
# (The ID number is preceded by 'mycartid' just in case someone       #
# else sends the user a cookie with the same ID number. Of course     #
# someone could send them a cookie that begins with 'mycartid' as     #
# well, but the more complex your cookie name is, the less likely     #
# it is to be accidentally duplicated.  The value will be replaced    #
# by whatever items the user chooses. The ''expires'' field is set to #
# the $date variable we create above which is 3 hours from now.       #
# This can be changed to whatever time is needed. The path should     #
# be changed from '/~me/' to whatever URL path points to the          #
# directory where this CGI is located. The domain should be changed   #
# from 'my.isp.com' to whatever your machine name is.                 #
#######################################################################

    print <<EOP;
Content-type: text/html
Set-cookie: mycartid$id=0; expires=$date; path=/~me/; domain=my.isp.com

<HTML><HEAD><TITLE>Welcome to the Whatzit Emporium!</title></head><BODY>
<H1 ALIGN=CENTER>The Whatzit Emporium</h1>
<p>Greetings and welcome to the Whatzit Emporium. Here you will find
Whatzitses of all shapes, sizes and colors to suit your Whatziting needs.

<p>To add a Whatzit to your cart, simply click on the 'Pick Me!' button
next to it. When you are ready to leave, simply click on the 'Check Out!'
button on the bottom of the screen.

<p>
<FORM ACTION="cart.cgi" METHOD=POST>
<UL>
<LI><INPUT TYPE="SUBMIT" NAME="Blue" VALUE=" Pick Me! "> Blue Whatzit
\$$whatzit{'Blue'}
<LI><INPUT TYPE="SUBMIT" NAME="Red" VALUE=" Pick Me! "> Red Whatzit
\$$whatzit{'Red'}
<LI><INPUT TYPE="SUBMIT" NAME="Big" VALUE=" Pick Me! "> Big Whatzit
\$$whatzit{'Big'}
<LI><INPUT TYPE="SUBMIT" NAME="Tiny" VALUE=" Pick Me! "> Tiny Whatzit
\$$whatzit{'Tiny'}
<LI><INPUT TYPE="SUBMIT" NAME="Paisley" VALUE=" Pick Me! "> Paisley
Whatzit
\$$whatzit{'Paisley'}
</ul>
<hr>
<INPUT TYPE=SUBMIT NAME="Checkout" VALUE=" Check Out! ">
</form>
</body></html>
EOP
    die;
}
# Notice that in the form above we don't have lots of input fields and one
# submit button, but rather lots of submit buttons and no fields. This is
# a handy technique for when you have a set of several things for the user
# to choose from but no other information is needed.


# If there are cookies to look at, look at each one.
CLOOP:
foreach (%COOKIE) {

    # If the cookie begins with 'mycartid' we assume it is one of
    # ours.
    if ($_ =~ /^mycartid(.*)$/) {
        # Grab the $id number from the name.
        $id = $1;
        # Grab the cookie value.
        $cookie = $COOKIE{$_};
        # Since we only send one cookie to each browser, now that we
        # have it, we don't need to look at any other cookies.
        last CLOOP;
    }
}
# Dump the unneeded cookies.
undef %COOKIE;

# If the cookie value is '0' they just came from the intro page and have
# nothing in their cart.
if ($cookie = '0') { $cookie = "");

# When we set the cookie value, we separate each type of Whatzit with a
# '+'. Now we split them into an array.
@items = split(/+/,$cookie);

foreach (@items) {
    # When we set the cookie, we separate the type of Whatzit from the
    # amount requested with '-'. Now we split each one into a type and
    # an amount.
    ( $type, $amount ) = split(/-/,$_);
    # We set the variable ${$type} to the amount of the that type. So
    # the variable $Blue will hold the number of Blue Whatzitses.
    ${$type} = $amount;
}

foreach (keys %whatzit) {
    # For each type of Whatzit (defined at the top of the script) we
    # check to see if the user just added onto their cart. If so,
        # we increment the corresponding variable.
    if ($FORM{$_}) { ${$_}++; }
    # If, after adding the most recent entry, there are any of this
    # type, add it and the amount held to an array.
    if (${$_}) { push(@cstring, "$_-${$_}"); }
}
# Combine the types and amounts held into one big string.
# This string would look something like this:
# Blue-3+Red-1+Tiny-1
$cstring = join('+', @cstring);

if ($FORM{'Checkout'}) {

    # If the user wants to checkout, we first clear their cookie, then
    # print the start of the checkout message.
    print <<EOP;
Content-type: text/html
Set-cookie: $id=; expires=$date; path=/~me/; domain=my.isp.com

<HTML><HEAD><TITLE>The Whatzit Emporium!</title></head><BODY>
<H1 ALIGN=CENTER>The Whatzit Emporium</h1>
<p>Thank you for shopping the Whatzit Emporium! We hope you have enjoyed
your visit. Here are the totals of your shopping trip:

<p>
EOP

    # Now for each of the Whatzit types, we multiply the number of
    # Whatzit's in the cart by the price of each Whatzit.
    foreach (keys %whatzit) {
        if (${$_}) {
            my $subtotal = ${$_} * $whatzit{$_};
            print
"$_ Whatzit: ${$_} \@ \$$whatzit{$_} ea. = \$$subtotal<br>";

            # Add them all up for the total.
$total += $subtotal;
        }
    }
    # Say bye bye!
    print <<EOP;
<hr>
Your total is \$$total
<hr>
Have a nice day!
</body></html>
EOP
    die;
}


# If the user isn't checking out, we send them a new cookie and resend
# the form so they can choose more Whatzitses to buy.
print <<EOP;
Content-type: text/html
Set-cookie: $id=$cstring; expires=$date; path=/~me/; domain=my.isp.com

<HTML><HEAD><TITLE>The Whatzit Emporium!</title></head><BODY>
<H1 ALIGN=CENTER>The Whatzit Emporium</h1>
<p>We hope you are enjoying your visit. If you have any questions please
feel free to ask one of our helpful associates.

<p>To add a Whatzit to your cart, simply click on the 'Pick Me!' button
next to it. When you are ready to leave, simply click on the 'Check Out!'
button on the bottom of the screen.

<p>
<UL>
EOP

# As we print out the form, we tell the user how many Whatzitses they have
# in their cart.
foreach (keys %whatzit) {
    print <<EOP;
<LI><INPUT TYPE="SUBMIT" NAME="$_" VALUE=" Pick Me! "> $_ Whatzit
\$$whatzit{$_}
EOP
    if (${$_}) {
        print "<BR>( You currently have ${$_} $_ Whatzitses\n";
    }
}

# Give the user the option to check out.
print <<EOP;
<hr>
<INPUT TYPE=SUBMIT NAME="Checkout" VALUE=" Check Out! ">
</form></body></html>
EOP

The preceding example is about as simple as it gets for shopping carts. All the items are on one page. There is no mechanism for removing items from the cart or for buying multiple items at once. These topics are covered more in Chapter 23, "Shopping Carts." The point here is showing what can be done with cookies. The only data you passed through the HTML form is the most recent addition to the cart. Yet, through using cookies, you were able to remember every single past addition the user had ever made. If the user were to turn off his computer and come back an hour later, the data would still be there. You chose to make the cookie expire after three hours, but that is completely arbitrary. However, in the interest of good manners, you should set your cookies to expire as soon as they are not needed, so they won't clutter up the user's hard drive.
Cookies have many other uses as well. In lieu of image-based counters, some sites have begun keeping track of hits by sending each visitor a cookie. Keeping track of this data, however, requires a modification to the server, or each page must be CGI generated to receive the cookie information.

Other Server Extensions

Although cookies and server push get the most press, many other extensions are written for servers. Most are proprietary to one server or another, and some require the browser to have special capabilities as well, but all are useful, one way or another, in extending the capabilities of ordinary CGI.

WebServer/400

An extreme example of what can be done with a Web server is the WebServer/400 from I/Net (http://www.inetmi.com/products/webserv/webinfo.htm). This is a server that runs only IBM AS/400 mainframes and uses them to unique advantage. The AS/400 is usually run through TN/3270 terminals with no graphics capabilities. This means that the interface to any AS/400 program is easily reproducible on the Web. WebServer/400 allows this, in a sense making
every program on the mainframe a CGI program. Input fields in the program are translated to HTML forms and the data is passed through the program to create new forms with the results. The entire machine can be run from a Web browser.

Apache Modules

The Apache server is a freely available Web server for UNIX that has swiftly become the most widely used server on the Web. At last count, 34 percent of all Web sites were using Apache as a server. The most recent versions of Apache have introduced a feature called modules that should make server extensions much more common in the future. Modules are server extension that are loaded into the server while it's running and used as they are needed. This takes up less memory than keeping them in the server all the time, and it is faster than calling an external CGI program. Apache itself comes with several modules, and many more are available in the public domain. I'll discuss the ones that bear a direct relationship with CGI programming in the following sections.

XSSI

XSSI (Extended Server-Side Includes) was developed by Howard Fear as an enhanced version of the server-side includes provided by Apache HTTPD. A server-side include is part of the HTML document that is parsed before being sent to the client. XSSI provides several capabilities previously only accessible through CGIs, such as simple if-then-else flow control and access to all environment variables set by the client. Using XSSI, a page could detect which type of browser is calling it and display HTML accordingly. More information on XSSI is available at ftp://pageplus.com/pub/hsf/xssi/xssi-1.1.html.

mod_rewrite

mod_rewrite is a module that intercepts the URL sent from the client and rewrites it based on a set of regular expressions. This is similar to the concept of URL aliases provided by most servers but takes it one step further. The incoming URL can be mapped to any other URL on the host machine. mod_rewrite was written by Ralf S. Engelschall and is available at http://www.engelschall.com/sw/mod_rewrite/.

mod_perl

mod_perl, written by Doug MacEachern, is a fully functional Perl interpreter linked dynamically to Apache, which cuts downs on the startup cost of launching Perl for each CGI request. mod_perl allows you to write your own server extensions in Perl and dynamically link them to Apache. You can find information on mod_perl at http://www.osf.org/~dougm/apache/.

CGI_SUGid and suCGI

One of the biggest hurdles in programming CGI is that all CGI programs are run as the user of the Web server (usually user "nobody"). Without this, security would be hard to maintain, but it also means that CGI programs can only write to directories that are world writeable. This makes interfacing with flat-file databases rather difficult. If the database is world writeable, any user on the host machine can change or delete the database at any time. If it's not, the CGI program can't modify it.
CGI_SUGid and suCGI are attempts to remedy this. These modules are installed in Apache as a replacement for the default CGI handler (mod_cgi). When a CGI program is called, it checks the owner of the program and executes the CGI as that owner. This way, CGI programs owned by you are run as you, allowing them write access to your directories while preserving security for the machine as a whole. CGI_SUGid was written by Philippe Vanhaesendonck and is available at http://linux3.cc.kuleuven.ac.be/~seklos/mod_cgi_sugid.c. suCGI is by Jason A. Dour and is available at http://www.louisville.edu/~jadour01/mothersoft/apache/.

WebCounter

Web counters are a hot commodity right now; everybody wants one. This popularity is despite the fact that they are horribly unreliable and that the odometer motif was cute for about one second. The WebCounter module is an attempt to rectify the "horribly unreliable" part. Having the server itself keep a count of how many times each page is hit is much more efficient than CGI programs that count hits to graphics (which, of course, ignore hits from text browsers or browsers with images turned off). WebCounter was written by Brian Kolaci and is available from ftp://ftp.galaxy.net/pub/bk/webcounter.tar.gz.

NeoWebScript

NeoWebScript is similar in concept to XSSI in that it allows basic scripting to be done within the HTML file itself. It takes a different approach, however. Instead of embedding the scripting commands in SSI-style includes, NeoWebScript commands are separated from the HTML by comment tags but otherwise resemble full scripts (as in JavaScript). NeoWebScript's language is based on Safe TCL, which in turn is based on TCL. With the recent announcement that Sun Microsystems is going to push TCL (and its graphical extension, Tk) as a companion to Java and the planned support of TCL/Tk in most major browsers (including Netscape), a server-side TCL interpreter provides a very useful complement to client-side implementations. More information on NeoWebScript is available at ftp://ftp.neosoft.com/pub/tcl/neowebscript/.
These are just some of the modules that extend the capabilities of the Apache server. An extremely useful list of modules for Apache is maintained by Zyzzyva Enterprises at http://www.zyzzyva.com/server/module_registry.

Jigsaw Resources

Jigsaw is a new HTTP server created by the World Wide Web Consortium (W3C). W3C consists of the people who create the Web, as well as many other entities, and its purpose is to promote the Web by creating official standards for HTTP and HTML and by writing cutting-edge software to push the frontiers of the Web.
The main difference between Jigsaw and all other Web servers in existence is that Jigsaw is written in Java. The W3C saw Java as an advantage in terms of portability and extensibility. Java support is available for all major platforms. Jigsaw has an extension capability similar to Apache except that the extensions are called resources rather than modules. Because Java bytecode need only be compiled once to run on any Java interpreter, Java resources can easily be added and removed from the server just by telling Jigsaw the location of the resource class.
Jigsaw is still under development and has some lingering bugs, but it looks promising. Unfortunately, no third-party Java resources have been announced at the time of this writing, but due to Java's popularity, they should be appearing soon. You can find more information about Jigsaw at http://www.w3.org/pub/WWW/Jigsaw/.

Netscape and Microsoft

The two commercial giants of the Internet, Microsoft and Netscape, also have their own Web servers. These servers come with their own proprietary extension APIs, which are covered elsewhere in this book. (Netscape Server API is covered in Chapter 26, "NSAPI," and Microsoft's Internet Server API is covered in Chapter 25, "ISAPI.")

Summary

The concept of creating extensions to HTTP servers adds whole new worlds to the capabilities of CGI programming. Open standard extensions such as server push and cookies allow all servers and browsers to expand. Server push provides rudimentary animation abilities and slide-slow features to allow dynamic, near real-time HTML pages. Cookies allow the end users to store information sent to them by servers. This information allows servers to keep track of users' progress and keep state information alive during the users' visits to the site (and even long after they have left).
Some extensions, such as the everything-can-be-a-CGI feature of the WebServer/400, are narrowly specific. Others, such as the Apache and Jigsaw servers' capability to allow users to add their own extensions, have the most general appeal.
As browsers grow more powerful and flashy, the reports of the imminent death of CGI become more frequent. By adding new functionality and extending the power of the server side of the equation, CGI takes on new life and gains even more power to provide true interactivity on the Web.






Imagemaps:Chapter 12


Chapter 12

Imagemaps





CONTENTS


Imagemaps have been a commonly found CGI feature on the World Wide Web for years now-and with good reason. They allow Web designers to create a powerful and attractive hyperlinking user interface. Unfortunately, the very success of imagemaps has lead to stagnation in their development as CGIs. This chapter shows that this does not have to be the case.

Imagemaps-Myth, Metaphor, and Meaning

One of my earliest memories of the World Wide Web is of an imagemap a friend of mine put together. This was back in late 1993 when I really didn't see the point behind the Web, and aich-tee-tee-pee (HTTP) meant nothing to me. By virtue of adequate hardware and abundant time, graduate students at the University of Western Ontario Astronomy Department started experimenting with CGI programming-hey, this was recreation! So much nicer than coding Runge-Kutta solvers and Simplex Minimization routines.
My friend Marc grabbed a GIF image of a bunch of Matt Groening cartoon characters-Akbar and Jeff, Binky the Rabbit, and so on-and if you clicked on a character, the system would go to a shell, finger the "appropriate" person in the department, and return the finger status to the Web. Marc was kind enough to revive this many-year-old page for the sake of this book (see Figure 12.1). Thanks, Marc!
Figure 12.1: The "Web finger" imagemap.
In its day, this was state-of-the-art CGI programming. (Please forgive us, but forms weren't widely used back then.) I was mystified by it and mentioned this "Web finger" to a friend of mine at the University of Toronto. He was so impressed that he posted its URL to Usenet and as a result, the Astro department server stats shot through the roof for the next few weeks. (This UofT friend later went on to become a part of Damien Doligez's Netscape SSL-cracking team-I knew Netscape was cracked two months before Netscape did!)
This small story took place three years ago. Now, I want to ask you a question: When was the last time you saw an imagemap that did something remotely as complicated as finger someone? That's my point-it doesn't happen! Imagemap programming has come to the doldrums of click-on-a-shape-and-we'll-put-you-on-a-new-page period.
But why is that? My guess is because imagemaps have become so commonplace and so uniform and so standardized that Web developers just don't think of all the other possibilities that imagemaps can hold. In my own small way, I hope to change that view here.

Note
You can see many of the principles I discuss in this chapter in action by going to the following URL:
http://www.anadas.com/cgiunleashed/imagemaps/

Anatomy of an Image-Pixels and Coordinates

Imagemaps allow locations on images to be determined and dealt with through the CGI. So, before we talk about imagemaps, it seems wise to learn something about computer image measurement systems.
Just about any image you'll find on the World Wide Web will be described by pixels and coordinates. Pixels, a distorted contraction of "Picture Elements," are colored squares of light that appear on your monitor. If ever you hear that a monitor's video mode is 1024¥768¥256, then that means the monitor is displaying 1024 pixels in the horizontal, 768 in the vertical, and that each pixel can assume one of 256 colors. Occasionally, you might hear that last designation referred to as some number of bits. 8-bit color is the same as 256 colors, while 16-bit color is 65,536 colors, and 24-bit color is the same as 16,777,216 colors. The connection between "bit color" and "colors" is that 2 raised to the power of the bit number equals the number of colors displayable.

Note
Have you ever wondered why your video card might not be capable of certain video modes? Consider this: A full screen at 1024¥768¥24-bit resolution will require 18,874,368 bits of memory on your video card. This is equal to 2,359,296 bytes. If you have only 1 megabyte of video memory on your video card, you simply don't have enough memory for your video card to "remember" what it's supposed to display on your screen.

Each pixel can be referenced by an ordered pair coordinate (see Figure 12.2). The top-leftmost pixel on the screen is pixel (x=0,y=0). The bottom rightmost pixel is pixel (x=xmax-1,y=ymax-1), where xmax and ymax are the numbers stated when describing the video mode.
Figure 12.2: A friendly diagram showing how pixels are mapped onto a rectangular image..

Caution
In mathematics, x is the variable traditionally used to measure horizontal displacement from some reference point, while y is used to measure vertical displacement. With computer images, the same holds true. In mathematics, x increases towards the right, and y increases going up the page. In computer images, x increases in the same direction, but y increases going down. Watch out for this!

A completely analogous system is used to describe any image, only xmax and ymax don't have any predefined restrictions placed upon them. Also, the top-left (0,0) corner is defined relative to the image and not the screen.

Caution
As with so many other areas in computer science, the index of pixels within an image starts at zero, not one. So, if you have an image that is 100 pixels wide, its x coordinate will run between 0 and 99; likewise for its y coordinate. Be aware of this when planning out your map files and any programs that might process them.

HTML, ISMAP, and QUERY_STRING - Passing Imagemap Information to a CGI Program

Passing Imagemap Information to a

This far into your CGI programming experience, I'm sure you've encountered the GET and POST methods of passing information from the Web browser to the Web server and CGI program. These two techniques take name/value pairs created within a form and encode this information in a standardized fashion. The CGI program on the server is then responsible for decoding the provided string and reclaiming the name/value pairs.
The preceding is all true, but it's a very form-centric way of looking at things. The GET and POST methods aren't the most basic way to look at the problem of getting information from browser to server. Underlying these methods are STDIN and QUERY_STRING: The GET method places its encoded name/value string and places it in the QUERY_STRING, while POST places its encoded string within STDIN. However, GET doesn't have a monopoly on the use of QUERY_STRING. Because imagemaps, too, rely on QUERY_STRING, I'll restrict my discussion to it from now on.
Consider the following mock URL:
http://www.anyfirm.com/cgi-bin/getrichquick.cgi
It obviously invokes a CGI program. This fairly straightforward URL can be made more complex. If we were to append a ? to the end of the line, we could continue onward with more text:
http://www.anyfirm.com/cgi-bin/getrichquick.cgi?on_your_back
In a URL, ? is a reserved character. Anything following it becomes the QUERY_STRING. The QUERY_STRING can be read by a CGI program and used within that program to do whatever a program might do with string data. For instance, it could be parsed and used as data within mathematical calculations:
http://www.math.org/cgi-bin/whatis?6_times_9

Note
In a rare show of short-sightedness, HTML has imposed a limit of 1,024 characters on the length of a URL. This includes its QUERY_STRING and its PATH_INFO. Even then, there is no guarantee that any given Web browser can handle a URL of that length. For this reason, many forms that have to pass halfway decent amounts of information to a CGI program will use POST rather than GET, even though GET is the "preferred" method.

QUERY_STRING information is extracted by a CGI program in a number of different ways. In UNIX, the most common is through environment variables. Within a Borne shell script, you can reference it as in this example:
#!/bin/sh
echo Content-type: text/html
echo ""
echo Your query string was:
echo $QUERY_STRING
In Perl, this same mini-program would be written as the following:
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Your query string was: $ENV{QUERY_STRING}\n";
exit 0;
The QUERY_STRING is the heart and soul of imagemaps. A Web browser will generate a QUERY_STRING appropriate to the x,y value of a mouse-click upon an image when the following HTML is used:
<A HREF=URL><IMG SRC=image_URL ISMAP></A>
Note that it takes a combination of both the ISMAP attribute within the <IMG> tag and the presence of an <A HREF> statement for x,y QUERY_STRING information to be produced.
To use a slightly less generic example, let's say I had an image file called mercator.gif that had dimensions of 600¥300 pixels. I place my mouse pointer somewhere around Toronto, which might be located at 200,90. I want this information to be processed by a CGI program called imagemap.cgi. Some possible HTML that could be written to accomplish all this is
<A HREF=http://www.anyfirm.com/cgi-bin/imagemap.cgi><IMG SRC=http://Âwww.anyfirm.com/pics/mercator.gif ISMAP></A>
If I were to then click on Toronto, the URL the browser would try to invoke would be
http://www.anyfirm.com/cgi-bin/imagemap.cgi?200,90

Flatland Revisited-An Introduction to the Standard Imagemap System

To begin, I'll start by telling you that you won't be seeing any imagemap-handling source code any time soon. I hate to say this as it gives me great joy to both read and write the stuff. Conventional imagemap source code has become so standardized that there's no point in my going into it immediately. However, I will write my own nonconventional imagemap handler later on in this chapter to show you that you don't have to rely on the standard software if it doesn't fit your purposes. Instead, right now I'll go into the theory of how imagemaps work.
In the 19th century, a Shakespearean scholar named Edwin Abbott wrote a book called Flatland. It was a satirical description of the lives of two-dimensional creatures inhabiting a plane. The social status of a flatlander was almost entirely based on their geometry. I'm reminded of flatland when it comes to standard imagemaps, as they are a description of what to do when a particular two-dimensional geometry is encountered.

Imagemap.c-The Standard Imagemap Handler

The National Center for Supercomputing Applications (ncSA) released the first HTTP server that really caught on as well as the first Web browser, called Mosaic. It has also supported quite a lot of other Web-related projects, including the creation and distribution of imagemap.c, the standard imagemap handler. This C source code comes with the ncSA HTTPd distribution and will be automatically compiled with the HTTPd and installed in the /cgi-bin/ directory of the Web server. However, if this isn't the case with your installation or if you wish to get a newer version of the code, you may find it through the following URL:
http://hoohoo.ncsa.uiuc.edu/docs/tutorials/imagemapping.html
In fact, this link will tell you just about everything you need to know about the standard imagemap system.

Note
The ncSA and Apache World Wide Web servers have become the "modern standard" in the field. In the beginning, though, CERN was King. This is quite understandable-Tim Berners-Lee of CERN (Conseil Europeen pour la Recherche Nucleaire) was the driving force behind HTML and HTTP.
CERN proposed its own imagemap-handling system that was quite popular for a while. In case you ever run across it, you can find a discussion at
http://www.w3.org/pub/WWW/Daemon/User/CGI/HTImageDoc.html
The main difference between the CERN and ncSA imagemap-handling systems is that the .map files aren't quite the same. However, there is a 1:1 correspondence between the two map file types. Conceptually, the two schema are virtually identical.

I'll start here where I left off in the previous section. Through HTML, an x,y coordinate pair corresponding to a mouse-click on an image can be generated and sent to a CGI program. This program is then responsible for obtaining this information from the QUERY_STRING string environment variable. So, the question now becomes what to do with this coordinate pair. The ncSA standard imagemap system offers one possible solution.
Consider the following URL:
<A HREF=http://www.anyfirm.com/cgi-bin/imagemap.cgi/pathinfo/mapfile.map><IMG ÂSRC=imageURL ISMAP></A>
This HTML is almost identical to the HTML in the previous section, except that now imagemap.cgi has more information following it. This information tells imagemap.cgi to look into a .map file; .map files contain a geometrical description of the image in question and tell imagemap.cgi what to do when it finds a mouse-click lying within a given shape. The next section is an in-depth examination of .map files.

Caution
Note that this line will vary depending on whether or not your imagemap executable is named imagemap.cgi or simply imagemap, whether or not it is located within the /cgi-bin/ directory, and what sort of pathinfo you supply it with. If the imagemap executable file is not kept in the server's specified /cgi-bin/ directory, then it must be named imagemap.cgi. Within the /cgi-bin/ directory, it may or may not possess this extension.

pathinfo is a strange fixture of CGI programming at best, but it becomes even stranger in the context of the standard imagemap handler. A path string can be appended after any URL that terminates in a file. The CGI programmer can find this string in the PATH_INFO environment variable. In the case of the standard imagemap handler, one of two things can be done with this string:
It can be used to directly reference the .map file associated with the image that has been ISMAPed.
It can be used as an alias by the imagemap program to reference the imagemap.conf file.
The HTML code given earlier shows the usage when PATH_INFO is used to directly reference a .map file. In this context, imagemap.cgi will try to access the .map file that could be referenced by the URL:
http://www.anyfirm.com/pathinfo/mapfile.map
Note that pathinfo could have many different directory levels included within.

Note
Some servers are configured to recognize .map files as being specifically associated with imagemaps. If that is the case, the server will prevent you from accessing a .map file directly through its URL as I pointed out earlier.

If pathinfo is used as an alias, imagemap.cgi will look in its own directory for a file called imagemap.conf. If imagemap.conf is found, imagemap.cgi will parse it for references that match the pathinfo. Following these references, there will be map file names, which are then used. I have grabbed the following from http://www.anadas.com/ at my UNIX shell to show how it would look directly:
% ls -l *.map *.conf
-rw-r-----  1 anadas  www  590 Apr 16 02:55 button.map
-rw-r-----  1 anadas  www   38 Mar 23 17:42 imagemap.conf
-rw-r-----  1 anadas  www  507 Apr 17 08:53 swatch.map
% cat imagemap.conf
swatch: swatch.map
button: button.map

Tip
To do much in the way of CGI programming, a good working knowledge of UNIX is almost essential. ls will list the contents of a directory. cat will concatenate the contents of a file. These commands are roughly equivalent to dir and type in DOS.

Finally, an example of how pathinfo aliases and the imagemap.conf files are put into practice:
<A HREF="exe/imagemap.cgi/swatch"><IMG SRC="pics/swatch.jpg" ISMAP></A>

.map Files-Describing Shapes the Imagemap Way

Since the time of the Babylonians, people have been obsessed with geometry. Farmers, architects, and mathematicians-even lowly computer programmers-have all found it useful to be able to precisely describe shapes. We've come up with quite a few systems of doing so and a bunch of standardized shapes to work with. Of this vast storehouse of knowledge, the standard imagemap system recognizes only four methods: rectangles, circles, polygons, and points.
The basic idea behind the standard imagemap system is that whenever a mouse-click is registered corresponding with one of these shapes in an imagemap, a URL is invoked. This is accomplished by having the imagemap handler issue a Location directive via the Web server. A .map file is the means through which shapes are defined and the standard imagemap program knows how to tie which URL to what shape. The imagemap program gets the mouse-click coordinates and determines which shape contains the click.

Note
A Web server can issue three general sorts of header statements and be understood by a Web browser:
  • A Content Type description
  • A Server Error description
  • A URL Location
The first is issued when all is well. As you have probably guessed, the second occurs when something has gone wrong. The last redirects the Web browser to the URL the server has supplied. The Location directive, among other uses, is how imagemap software turns a mouse-click into a new Web page for the mouse-clicker.

A rectangle can be uniquely identified by specifying its top-left and bottom-right coordinates within an image as x1,y1 and x2,y2. A circle can be uniquely identified by specifying its center, xcen,ycen, and any point on its circumference, xcir,ycir. A polygon can be uniquely specified by a list of all its vertices: x1,y1, x2,y2, x3,y3, .. xn,yn. To make computation easier, imagemap handlers usually limit the number of vertices a polygon may possess to some large number, usually 100. For the purposes of .map files, rectangles are abbreviated to rect and polygons to poly.
Obviously, a point is fully described by itself: x,y. In the context of imagemap.cgi, if a mouse-click lies outside of any defined region (rect, circle, or poly), it is compared against all specified point coordinates. The imagemap handler will redirect the user to the URL specified on the point line whose coordinates are nearest to the mouse-click. As it is stated so eloquently in the ncSA Imagemap tutorial, "The nearest point wins."
The point method makes sense only if there is more than one point line in a file. Otherwise, the one point that is defined would always "win." If that's what you actually want to happen, you should use the default method. This is invoked when two conditions are met: The mouse-click isn't found to lie within any of the described shapes, and there are no point lines in the .map file.
Every line in a .map file has a very straightforward syntax. For all lines except default, it is
shape URL coordinates
This syntax differs for default to make it even simpler:
default URL
Putting all these concepts together, we get a logical .map file:
rect URL x1,y1 x2,y2
circle URL xcen,ycen xcir,ycir
poly URL x1,y1 x2,y2 x3,y3 .. xn,yn
point URL x,y
..
point URL x,y
default URL
You may have as many rect, circle, poly, and point lines in a .map file as you need. Also, blank lines are skipped, and lines beginning with the # character (also known as "number," "pound," or finally "hash" to the truly hackish) are ignored as comments.

Tip
You can find utility programs on the Internet that can help you efficiently create .map files. For Microsoft Windows users, try looking for them at Stroud's Consummate Winsock Application List:
http://www.cwsapps.com

A Tale of Two Web Browsers
While preparing for this chapter, I took a good, hard look at how imagemaps were handled by Web browsers, and I made some very interesting observations. In a variety of trial circumstances, my two trial browsers, Netscape 3.04b and Microsoft Internet Explorer 2.0, both had a very tough time properly determining the boundaries of the imagemaps I constructed. Both could handle finding the upper-left corner of my images pretty well, but the bottom-right corner (reflecting on both the bottom and right sides of the image) was quite elusive to them; though I knew my test image was 200¥100 pixels in size, I could get both browsers to tell me that I had clicked on pixel (202,103), for instance. Things got even less intuitive and more bizarre when I started doing things like setting BORDER=20, or HEIGHT=50 WIDTH=50, or playing around with HSPACE and VSPACE.
Even worse, there was no consistency between how the two browsers reacted. For instance, with BORDER=50, with Netscape I found that a click on the upper-left corner of the border would show 0,0 on the screen before clicking but would pass -20,-20 to the imagemap handler! MSIE didn't show anything on the screen before clicking but passed 0,0 when I tried clicking on the upper-left part of the oversized border.
And so, my advice is: As with everything else on the Web, take imagemaps with a grain of salt. It might be helpful for you to remember the limits of Web browsers when creating .map files and imagemap handlers.

Client-Side Imagemaps and Magic MIME Types

The best and worst labor-saving devices are those things that save you the trouble of thinking. Thinking is difficult and time consuming, and if you can get the job done without as much thought as you might normally have to invest, then great. However, by accepting someone else's prepackaged thought, you can fall into the trap of accepting the limits they decided to put on the problem. This undesirable condition runs rampant in the field of imagemap handling.
As I've vented before in this chapter, canned imagemap code is far too good. It's reasonably easy to install, easy to use, sufficiently powerful enough that it covers just about all imagemap cases, and allows people to forget that imagemaps can be treated as a creative CGI challenge. But it gets even worse; the canonical imagemap format has been considered a "standard" to the point where some Web browsers and servers now have special features that allow you to bypass imagemap.cgi altogether. These features are client-side imagemaps and the .map Magic MIME type.

Client-Side Imagemaps

Newer versions of Netscape, Microsoft Internet Explorer, and Spyglass Mosaic support a variant on the idea of an imagemap called a client-side imagemap. This technique places the burden of processing the x,y pair produced by the ISMAP attribute on the Web browser (the client) rather than on a remote CGI program. As far as a Web page developer is concerned, this is accomplished through completely nonstandard, nonsupported extensions to HTML.

Note
The implementation of client-side imagemaps discussed here does not have much to do with the proposed HTML 3.0 draft on client-side imagemaps, which is tied to the <FIG> tag. However, virtually no browser supports this style of client-side imagemaps while three of the biggest support the client-side imagemap system proposed by Spyglass-the system I talk about here. It practically reigns supreme. *sigh*

A client-side map is defined within the body of an HTML file. The logic of the map structure is very similar to that found in the .map files used by the ncSA imagemap.cgi program. I will include a portion of an HTML file as an example and discuss it here. You can see this example in action at
http://www.anadas.com/
As you can see in Figure 12.3, the mouse-pointer shows its "pointing hand" disposition as it is positioned to click on a client-side imagemap. Note that the URL this click will invoke is displayed at the bottom of the screen. This URL display appears only with client-side imagemaps and not server-side imagemaps.
Figure 12.3: A client-side imagemap about to be invoked.
<MAP NAME=anyname>
<AREA SHAPE=rect COORDS="0,0, 117,133" HREF=http://www.anadas.com/?minisearch>
<AREA SHAPE=rect COORDS="118,0, 212,133" HREF=http://www.anadas.com/products/>
<AREA SHAPE=rect COORDS="213,0, 306,133" HREF=http://www.anadas.com/?company>
<AREA SHAPE=rect COORDS="307,0, 419,133" HREF=http://www.anadas.com/?quotes>
<AREA SHAPE=default HREF=http://www.anadas.com>
</MAP>
Here's a quick review of this syntax:
  • To define a Spyglass style client-side imagemap, start it with a <MAP NAME=anyname> tag and end it with a </MAP> tag.
  • For each geometrical shape, have an <AREA SHAPE=anyshape> tag. Valid shapes are rect, circle, poly, and point-the same as are valid with the ncSA imagemap program.
  • The coordinates of the shapes are described within the COORDS=area.
  • A SHAPE=default may be added. If this is not included, a click in an area not defined by any SHAPE will not invoke any action.
  • An HREF must be specified as the action resulting from a click in the SHAPE.
  • To attach this client-side map to an image, follow the format <IMG SRC="images/mapped_image.gif" USEMAP="#anyname">. Note that the #anyname associated with the USEMAP attribute must match the name assigned in the <MAP> tag.
A complete review of Spyglass-style client-side imagemaps can be found online at
http://www.spyglass.com/techspec/tutorial/img_maps.html

But Master, How Can I Depend on Client-Side Imagemaps When So Many Browsers Don't Support Them?

An excellent question, grasshopper! The answer is that you don't have to. You can set up a redundant scheme whereby an image is tied to both a client-side and a server-side imagemap.
To do this, you must define a map in your HTML file as I talked about earlier. You must also create a .map file and store it on the server. Then, reference the image with the following:
<A HREF="/cgi-bin/imagemap.cgi/pathinfo/file.map"><IMG SRC="images/Âmapped_image.gif" USEMAP="#anyname" ISMAP></A>
As always, insert an appropriate URL for the imagemap.cgi executable, the pathinfo, and map file names.
When this format is used, the client-side imagemap is executed if the browser being used supports client-side imagemaps. If it doesn't, then all HTML having to do with client-side imagemaps is ignored and then the markups relating to server-side imagemaps kick in.

Caution
While this scheme certainly works, it can become an annoyance to maintain current map information in both the .map file and in the HTML file. There's no automatic way to deal with this; you'll just have to discipline yourself to do it.

The .map Magic MIME Type

Some CGI programmers are lucky enough to be able to control their httpd configuration, either directly or by being "tight" with the sysadmin. If you happen to be among these lucky few, then this section could be of use to you. Even if you aren't, you might be fortunate enough to have a sysadmin who pays special attention to their httpd.
By way of a small refresher, the World Wide Web is a client/server environment. Your Web browser (Netscape, Mosaic, MSIE, or Lynx) is the client. Each time you try to load a Web page, it makes a request of the Web server.

Note
A Web server is also known as an httpd, sometimes capitalized to HTTPd. This is an acronym standing for HyperText Transfer Protocol daemon. In the world of UNIX, a daemon is a program that lurks in the background and performs system-related tasks that don't require any human intervention. Daemons are generally started at boot-time. An httpd is a daemon responsible for dealing with, or serving, hypertext transfer protocol requests.
I will discuss only the ncSA and Apache UNIX Web servers because they are
  • Highly available
  • Widely installed and used
  • Well documented
  • Reasonably powerful
  • Completely free
  • Very similar to each other
Everything you ever wanted to know about these two Web servers can be found at
http://hoohoo.ncsa.uiuc.edu/
http://www.apache.org/

The ncSA and Apache Web servers are quite configurable. One of the more interesting sections when configuring one of these Web server is the "Magic MIME Type" area. This can be found in the ~/conf/srm.conf file, where ~ represents the home-level directory of the Web server installation, also known as ServerRoot. I'll quote a portion of this file here:

Caution
ServerRoot and DocumentRoot are two different configuration variables. For instance, a system might have ServerRoot set at /var/www and DocumentRoot at /var/www/docs. ServerRoot is defined in the httpd.conf file while DocumentRoot is defined in the srm.conf file.

# The following are known to the server as "Magic Mime Types"  They allow
# you to change how the server perceives a document by the extension.
# The server currently recognizes the following mime types for server side
# includes, internal imagemap, and CGI anywhere.  Uncomment them to use them.
# Note: If you disallow (in access.conf) Options Includes ExecCGI, and you
# uncomment the following, the files will be passed with the magic mime type
# as the content type, which causes most browsers to attempt to save the
# file to disk.

#AddType text/x-server-parsed-html .shtml
#AddType text/x-imagemap .map
#AddType application/x-httpd-cgi .cgi
The .map Magic MIME type is supported by the new ncSA/1.5 httpd and in the beta release of Apache httpd 1.1. It is unlikely that your sysadmin will upgrade to either of these newer httpds without it being brought explicitly to their attention, but beware! Sysadmins are quick to anger and their vengeance is mighty.
This about says it all except for the conclusion: Removing the # character from the front of any of these lines and then rebooting the httpd will give you access to that Magic MIME type. If you enable .shtml as a Magic MIME type, then server-parsed HTML files can be accessed outside of directories that are specifically assigned to this role. Enabling the .cgi line will allow the Web server to run CGI files in directories outside of /cgi-bin/. Enabling the .map line will give you awesome cosmic powers!

Tip
Enabling the .map line won't give you awesome cosmic powers. However, enabling the .cgi Magic MIME type can be very useful. You might find that this doesn't work the first time around, though. The .cgi Magic MIME type depends somewhat on the setting of a line in the accces.conf file. If you have any problems with enabling the .cgi line in srm.conf, check to see if your access.conf file has an
Options Includes ExecCGI
within.

Seriously, enabling the .map Magic MIME type allows you to write your imagemap HTML statements in a slightly altered format:
<A HREF="map_URL"><IMG SRC="image.gif" ISMAP></A>
What ncSA has essentially done is incorporate their imagemap.c source code into the httpd.c source code, making the standard imagemap handler part of the standard Web server! Rather than unloading imagemap handling onto an external imagemap handling program that references an auxiliary map file, the server references that map file directly and deals with it internally. I have composed a working (if simple) example of this in action:
http://www.anadas.com/cgiunleashed/imagemaps/magic.html<HTML>
<HEAD><TITLE>Example using the .map Magic MIME type</TITLE></HEAD>
<BODY>
<P>Click somewhere in the image below:<BR><BR>
<A HREF="exe/magic.map"><IMG SRC="images/magic.gif" ISMAP></A>
</BODY></HTML>
The ncSA/1.5 server is instructed by this HTML to reference the following map file:
default /cgiunleashed/imagemaps/elsewhere.html

# inside the black circle
circle /cgiunleashed/imagemaps/circle.html 70,72 70,122

Take a Walk on the Server-Side-Developing Imagemap Code

So far in this chapter, I've talked the talk. Now, I think it's time to walk the walk. For your viewing pleasure, I've written an imagemap handler that is functionally similar to the standard ncSA code but includes a fair number of differences, as well. Being the creative guy that I am, I have decided to call my masterpiece im.cgi.
im.cgi is written in Perl and developed for a UNIX platform. By now I'm sure you've all heard that Perl is the best language for CGI development. I would call this statement a justified simplification. Perl is an excellent language for writing programs that handle strings and files and that are reasonably small and uncomplicated. This description would fit the vast majority of CGI programs. I've written CGI programs in C and C++ when it was appropriate to do so, but for CGI programs, I always go to Perl first.
I'm not going to spend any time in this chapter discussing Perl as a language. Information about it is very available on the Internet and in bookstores-that's how I learned it, after all. im.cgi is fairly well documented, and you should be able to get the meaning of most Perl commands and structures from the context in which they are used.
im.cgi gets the ISMAP-produced x,y pair through the QUERY_STRING environment variable. This coordinate pair is compared against geometries found in an .imap file; .imap files are not structured in exactly the same way as an .map file, though. Valid methods in an .imap file are rect, ellipse, point, and default. The area that a rect or ellipse delimits is described in a new format I have outlined in a logical .imap example later in the chapter. For the sake of simplicity, I have eliminated the poly method.

Note
Dealing with polygons can be horrendously complicated both in computer programming and in mathematics (geometry and topology). A general polygon can be convex or concave. This makes it difficult for a program to determine whether a point is inside or outside of the polygon region. Even worse, a polygon may be connected multiply (see Figure 12.4). Then, it's tough to even know which side of the polygon is on the inside or outside! In any case, a reference point needs to be chosen to allow for a determination of on which side of the polygon an arbitrary point may lie.
Complex geometry is the square root of all evil.
Sorry.

Figure 12.4: Problem polygons.
Here are examples of a concave polygon and a multiply connected polygon. Shapes that fall under these categories are difficult to work with in computer programs. To help make the job easier, assumptions are usually made that simplify (though possibly misrepresent) the situation. In addition to (slightly) changing the allowable methods, I've changed the structure of each line. URLs follow rather than precede the geometry-specifying information. Also, im.cgi doesn't handle relative URLs-they have to be specified in full, including the http:// at the front. Blank lines are skipped, and lines starting with # are treated as comments and are ignored. The first line of an .imap file contains the alias meant to be compared against the PATH_INFO environment variable. Only uppercase and lowercase alphabetic characters are valid for this alias name; im.cgi doesn't support direct references to .imap files as imagemap.cgi does for .map files-pathinfo aliases must be used. As a result, .imap files must be in the same directory as im.cgi. If a mouse-click is found to be contained within a rect or ellipse, the program will "short-circuit" and immediately bring the user to the URL specified by that shape. The consequence of this is that if rects or ellipses overlap, the shape described first in the .imap file takes precedence. It is possible to specify a shape that lies outside the boundaries of the actual image file the .imap file relates to-this could be useful if you want to have a clickable semicircle at the edge of an image. The point method works the same as it did in imagemap.cgi. If a file has any point methods specified, any default methods are ignored. There are no restrictions on where methods are placed within a file apart from that the first line is reserved for the alias name.
This is all very similar to the structure of a .map file. I have added one "twist" to it all, though. rect, ellipse, and default methods may be specified by more than one URL. If more than one URL is specified for any one of these methods, im.cgi will choose one randomly. Also, there can be more than one default line-a convenience to allow for the input of many alternative default URLs to be randomly chosen from, without putting many URLs on one line. To be honest, I can't think of a really good application for this. However, I wanted to demonstrate that things that are completely outside the capabilities of imagemap.cgi are possible when you write your own imagemap handler.
All these principles are demonstrated by the following logical .imap file:
# mapfilealias
# The above is the path info alias for this .imap file.

rect upperleftx,upperlefty xsize ysize URL1 ... URLn
ellipse centerx,centery xsemiaxis ysemiaxis URL1 ... URLn

point x1,y1 URL
...
point xn,yn URL

default URL ... URL
...
default URL ... URL

Note
An ellipse is a useful and fascinating shape and fairly easy to work with, too. I'm not sure why the standard imagemap code uses circles instead. After all, a circle is just an ellipse with equal x and y radii. Figure 12.5 is a diagram showing you the geometry of an ellipse and the algebra associated with it.

Figure 12.5: Anatomy of an ellipse.
You can see an example of my im.cgi program (shown in Listing 12.1) at work at the following URL:
http://www.anadas.com/cgiunleashed/imagemaps/immap.html

Listing 12.1. im.cgi program-an alternative imagemap handler.
#!/usr/bin/perl
# the above line may need to be altered depending on where Perl is
# stored on your system

#
# This program written by Richard Dice of Anadas Software Development
# as part of the Sams Net "CGI Programming Unleashed" book.  The author
# intends this code to be used for instructional purposes and not for
# resale or commercial gain.
#
# Any questions or comments regarding this program are welcome.  You
# may contact the author by Internet email: rdice@anadas.com
#

#
# initialize some variables, partly because it's an old habit, partly
# so that they aren't treated as variables localized to loops
#
$imap = '';
$c = 0;
$found = 0;
$this_rect = 0;
$this_ellipse = 0;

# seed the random number generator for the 'rand' command
srand;

# Get GET co-ordinate information and place in x and y variables
($x,$y) = split(/,/,$ENV{QUERY_STRING});

#
# Get PATH_INFO information -- this will be used to determine which .imap file
# to use.  Any character which is not in the range a-z or A-Z will be removed.
# This is like the imagemap.conf file, but forces each .imap file to be
# responsible for its own alias.
#
($filematch = $ENV{PATH_INFO}) =~ tr/a-zA-Z//cd;

#
# determines the names of all .imap files in the directory which houses im.cgi
# and uses the UNIX head command to look at the first lines of each of them
# until a match with $filematch (derived from PATH_INFO) is made.
#
@imapfiles = <*.imap>;
foreach ( @imapfiles ) {
   $firstline = 'head -1 $_';
   if ( $firstline =~ /$filematch/ ) {
      $found = 1;
      $imap = $_;
      last;
   }
}
@imapfiles = ();        # this array is unneeded, so free up memory
&not_there unless $found; # if no matching .imap file, abort with error msg.

open(IMAP,$imap);        # now, I start parsing the desired .imap file
while ( <IMAP> ) {
   next if ($_ eq "\n"); # blank lines are ignored
   next if ($_ =~ /^\#/ ); # lines starting with # are also skipped

#
# the following line creates an array called @line based on the contents of
# the current line being read in from the .imap file.  Whitespace is used as
# array element delimiters.  Isn't Perl grand? :-)
#
   @line = split;

#
# break the .imap file-reading loop if the current line being read in is
# a rect or an ellipse descriptor and $x,$y lies within that shape
#
   if ($line[0] eq 'rect') {
      if (&check_rect($line[1],$line[2],$line[3],$x,$y)) {
         $this_rect = 1;
         last;
      }
   }
   if ($line[0] eq 'ellipse') {
      if (&check_ellipse($line[1],$line[2],$line[3],$x,$y)) {
         $this_ellipse = 1;
         last;
      }
   }

#
# build an array of points and their corresponding URLs
#
   if ($line[0] eq 'point') {
      $points[$c++] = join($;,$line[1],$line[2]);
   }

#
# makes a running list of 'default' URLs to be randomly chosen from should
# the $x,$y click not fall within a defined shape and no 'point' has
# been defined
#
   if ( $line[0] eq 'default' ) {
      $default .= join(' ',@line[1..$#line]);
   }
}
close(IMAP);

#
# output a Location randomly chosen from the list of URLs supplied
# following the co-ord info, should the $x,$y match with a rect or ellipse
#
if ( $this_rect || $this_ellipse ) {
   splice(@line,0,4);        # remove the first 4 elements from @line
   $i = int(rand($#line+1));
   print "Location: $line[$i]\n\n";
} elsif ( defined(@points) ) {
   $matchingkey = &nearest_point($x,$y,@points);
   ($discard,$keep) = split(/$;/,$points[$matchingkey]);
   print "Location: $keep\n\n";
}
#
# If the $x,$y doesn't fall inside any shape and no 'point's are defined,
# then randomly chose a default URL from the list.  If no default URLs
# are provided via the .imap file, return to the page which contains
# the imagemap
#
elsif ( $default ne '' ) {
   $urls = split(/ /,$default);
   $i = int(rand($#urls+1));
   print "Location: $urls[$i]\n\n";
} else {
   print "Location: $ENV{HTTP_REFERER}\n\n";
}

exit 0;

sub not_there {
   print "Content-type: text/html\n\n";
   print "IM, Richard's imagemap handler, couldn't find the .imap file ",
   "specified by the path info supplied in the URL.  Sorry!";
   exit 1;
}

sub check_rect {

   local($upleftx);
   local($uplefty);
   local($xmax);
   local($ymax);
   local($getx);
   local($gety);
   local($returnval);

   $returnval = 0;
   ($upleftx,$xmax,$ymax,$getx,$gety) = @_;
   ($upleftx,$uplefty) = split(/,/,$upleftx);

   if ( (($getx-$upleftx) >= 0) &&  (($getx-$upleftx) <= $xmax) ) {
      if ( (($gety-$uplefty) >= 0) &&  (($gety-$uplefty) <= $ymax) ) {
         $returnval = 1;
      }
   }

   return $returnval;
}

sub check_ellipse {

   local($centx);
   local($centy);
   local($a);
   local($b);
   local($getx);
   local($gety);
   local($returnval);

   $returnval = 0;
   ($centx,$a,$b,$getx,$gety) = @_;
   ($centx,$centy) = split(/,/,$centx);

   if ( ( (($getx-$centx)/$a)**2 + (($gety-$centy)/$b)**2 ) <= 1.0 ) {
      $returnval = 1;
   }

   return $returnval;
}

sub nearest_point {

   local ($min_key, $mindist, $i);
   local ($getx, $gety);

   local(@pt);
   ($getx,$gety,@pt) = @_;

   $min_key = 0;
   $mindist = 1000000000; # no image will be this large! (I hope)

   for $i (0..$#pt) {
      ($a,$b) = split(/$;/,$pt[$i]);
      ($a,$b) = split(/,/,$a);
      if ( ( ($a-$getx)**2 + ($b-$gety)**2 ) < $mindist ) {
         $min_key = $i;
         $mindist = ($a-$getx)**2 + ($b-$gety)**2;
      }
   }

   return $min_key;
}


Caution
So, smarty, you think you can crash my code? Of course you can! im.cgi assumes that you give it a correct .imap file. Want to see what happens when you give it a rect with no following URL or a negative xsize? That's up to you. However, if you feed it bizarre input, you can expect it to choke or barf. Such is the way of all computer programs.

Creative Imagemap Programming-Breaking the Paradigm with Glorglox

Throughout this chapter, I've been an advocate of thinking for oneself when creating imagemap code. The im.cgi code I wrote departs from the standard imagemap code, but not very much. It still follows the same basic principle of click-on-a-shape-and-we'll-take-you-there. That doesn't have to be the only way to deal with imagemaps, though.
Shape-based imagemap handlers are very good at describing shapes that can be described through simple analytic geometry but very bad at describing arbitrary curvy shapes, such as what one might find on an isometric (contour) map. And what useful maps these are! For the next few days, try keeping you eyes peeled for examples of contour maps in your everyday life. I think you'll be surprised with how many you see.
In addition to isometric maps, text isn't processed very well by shape-based imagemap handlers. It's possible to plot out the various cusps of a nice Geoslab font, I suppose, but it wouldn't be desirable. And brush script... forget it! You'll need every one of those 100 vertices you've got to play with in your poly method and more.
How could an imagemap handler be constructed to deal with these sorts of situations? Glorglox presents us with one possible answer.
Glorglox is an advanced imagemapping program written by Tom Rathborne, a University of Waterloo mathematics and computer science student. He came up with an elegant solution to the problems I discussed earlier. The key is to essentially forget about geometry. Imagemaps are images, period. Forget about the shapes we artificially impose on them and consider their basic property: color. Figure 12.6 shows the Glorglox home page and, in my opinion, immediately gives away its methodology.
Figure 12.6: The Gloralox home page.
The Glorglox Home Page contains two images that show what Glorglox is all about: http://www.uunet.ca/~tomr/glorglox/demo/.
The HTML used to invoke Glorglox is structurally identical to the HTML used to invoke imagemap.cgi. An example is
<A HREF="/cgi-bin/glorglox/glorglox-demo"><IMG SRC="demo.gif" ALT="[glorglox demo
image]" ISMAP WIDTH="512" HEIGHT="128" BORDER="0"></A>
When an x,y coordinate pair is provided to the Glorglox through QUERY_STRING, a .gmap file is referenced. This referencing is done through PATH_INFO and can be done either directly or through an alias found in the glorglox.conf file. The preceding HTML shows the alias method in use, and the glorglox.conf file is identical in form to the standard imagemap.conf file:
glorglox-demo : /u/tomr/www/glorglox/demo/demo.gmap
A Glorglox .gmap file is used to provide glorglox.cgi with information regarding what to do with its x,y pairs. Glorglox operates through pairs of image files: the one that is output to the Web and an auxiliary .gif file. When a click is registered on the viewable image, Glorglox determines the color of the pixel at that location in the auxiliary .gif file. The .gmap file is a look-up table of colors and URLs. Here is an example of a .gmap file:
/www/staff/tomr/glorglox/demo/demo-map.gif
#
# The auxiliary image _must_ be on the first line.
#
# everything else is: [value|"default"]<whitespace>[URL]
#
default http://www.uunet.ca/~tomr/glorglox/demo/error.html
0 NOWHERE
1 http://www.uunet.ca/~tomr/glorglox/demo/gg-bord.html
2 http://www.uunet.ca/~tomr/glorglox/demo/imap.html
3 http://www.uunet.ca/~tomr/glorglox/demo/gg-in.html
4 http://www.uunet.ca/~tomr/glorglox/demo/gg-out.html
5 http://www.uunet.ca/~tomr/glorglox/demo/box.html
6 http://www.uunet.ca/~tomr/glorglox/demo/adv.html
#
# For example, if you were to click on the words "image mapper",
# glorglox would send you to imap.html, because the pixel value there
# is 2 (red) in the auxiliary image.
#
# The new nifty NOWHERE URL sends the user back to the page they came
# from!
#
Notice that colors are specified as numbers between 0 and 255 in a Glorglox .gmap file. This is due to the fact that GIF images use indexed color mode. Consequently, a Glorglox-based imagemap has a maximum of 256 links it can invoke. This should be enough for most purposes.

Note
In RGB color mode, each of the red, green, and blue channels are represented by 8 bits, making for a 24-bit "true color" image. However, many images are very well represented by only 256 unique colors, as long as you get to choose which 256 colors are used. Indexed color mode is a compromise between having 224 = 16,777,216 colors available in an image and having each pixel represented by 1 byte rather than 3 bytes. In the header of a GIF file, a format that uses indexed color mode, there is a look-up table stating what RGB color is associated with which index. Then, each subsequent use of that 1 byte index represents 3 bytes of information.

The usefulness of Glorglox hinges on that it is often easier to create an auxiliary image than to define geometrical shapes in terms of the vertices of their pixels. Quite often, this is indeed the case. As a final note on Glorglox, take a look at the following URL and ponder for a moment how you'd make the imagemap you'll find there using a standard imagemap system:
http://www.erin.gov.au/land/regions/ibra_spatial/ibra.html

Imagebuttons-The End of Imagemaps Is Nigh

HTML was originally conceived as a content-description language. Its major conceptual step forward was the inclusion of native ways to deal with hypertextual links. Within these limits, HTML worked well.
Eventually, the demands of real life started to pull HTML in all sorts of funny directions. The pervasiveness of HTML documents and Web browsers and the flexibility of HTTP gave people the idea to start using the Web as a vehicle for information gathering as well as information distributing. ISINDEX tags and the CGI appeared back in the days of text-only browsing. The CGI layer and GUI-based Web browsers with inline images made room for the concept of an imagemap. HTML was no longer as simple as it once was, but it wasn't made so complicated that there was any real reason to complain.
This same CGI revolution also brought forms onto the scene. Forms blew ISINDEX out of the water. The basic set of form input elements very closely mimics the various dialog box options you'll find in a GUI-based operating system such as radio buttons, checkboxes, and selection lists. What forms didn't contain was any sort of graphical interaction capability...until now.
It has been noticed that throughout history, great conceptual advances tend to be independently developed at almost the same time. In the 1600s Leibnitz and Newton developed calculus within years of each other. In the 1800s a gaggle of mathematicians almost simultaneously challenged Euclid's 9th and 10th axioms and independently derived non-Euclidian geometry. In late 1995, I remarked to my programming partners that I wished there was a way to combine forms and imagemaps. About two months later, I noticed imagebuttons online. Funny how these things happen.

The HTML Side of Imagebuttons

I will assume that the reader has a knowledge of the working of HTML forms and will concentrate only on the imagebutton-related tags.
<HTML><BODY>
<FORM ACTION="exe/imagebtn.cgi" METHOD=GET>
Zen words of wisdom : <INPUT TYPE=TEXT NAME="zen" VALUE="It is difficult to kill a Âhorse with a flute." SIZE=40> <BR><BR>
<INPUT TYPE=IMAGE NAME="foo" SRC="images/imagebtn.gif" ALT="Optional">
</FORM>
</BODY></HTML>
There are some things here that should be familiar by now-the structure of the form statement, a CGI program as its action, and a specified method: in this case, GET.
Beyond the familiar, this form has two slightly unusual aspects to it. First, the explicit strangeness: the <INPUT TYPE=IMAGE> tag. It is this tag that creates an imagebutton within an HTML document. The second and implicit unusual point is that this form has no <INPUT TYPE=SUBMIT> tag! As people who are experienced with forms know, without such a tag, the form will not connect to its action, making the form absolutely useless apart from didactic purposes. Contrary to what you may think, that is not my goal-at least not right here. This is an honest-to-goodness working form. I'll go into how the imagebutton tag makes it so shortly.
An <INPUT TYPE=IMAGE> tag can be inserted into any form just as one would insert any other INPUT tag. The NAME attribute is optional but highly recommended for a reason I'll talk about soon. The VALUE attribute isn't forbidden, but it does nothing in the context of an imagebutton. Beyond these attributes, the ones that are normally found in INPUT tags, any attributes that are legal in IMG tags are legal here: SRC is necessary while ALT, HEIGHT, WIDTH, and BORDER are optional.
Move your mouse pointer on top of the image produced by the <INPUT TYPE=IMAGE> tag. Notice that the pointer doesn't transform itself into a "pointing hand" icon. However, when you click on the image, the form action is invoked.
I have created an example of an imagebutton that you can view at the following URL:
http://www.anadas.com/cgiunleashed/imagemaps/imagebtn.html
The action of the form that is found at the preceding URL is a program that "spills its guts," as it were. Listing 12.2 is that program.

Listing 12.2. A Perl program that prints GET/POST query information and environment variables.
#!/usr/bin/perl
# the above line may need to be altered depending on where Perl is
# stored on your system

$env_flag = 1; # set to 1 to print environment variables

if ( $ENV{REQUEST_METHOD} eq 'POST' ) {
   read(stdin,$input,$ENV{CONTENT_LENGTH});
} elsif ( $ENV{REQUEST_METHOD} eq 'GET' ) {
   $input = $ENV{QUERY_STRING};
} else {
   print "Content-type: text/plain\n\n";
   die "This program doesn't support the <b>$ENV{REQUEST_METHOD}</b> httpd",
   "request method.\n";
}
$input =~ tr/+/ /;
@fields = split(/\&/,$input);
$input = '';
foreach $i (@fields) {
   ($field,$data) = split(/=/,$i);
   $field =~ s/%(..)/pack("c",hex($1))/ge;
   $data =~ s/%(..)/pack("c",hex($1))/ge;
   $tokens{$field} = $data;
}
@fields = (); # delete the @fields array

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

print <<END;
<HTML><HEAD><TITLE>Imagebutton Form Processing Results</TITLE></HEAD>
<BODY>
<P><TABLE BORDER>
END

print "<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP COLSPAN=2>Form-based Variables</TH></ÂTR>",
"\n<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP>Name</TH><TH ALIGN=CENTER VALIGN=TOP",
" NOWRAP>Value</TH></TR>\n";
while ( ($a,$b) = each %tokens ) {
   print "<TR><TD ALIGN=LEFT VALIGN=TOP>$a</TD><TD ALIGN=LEFT VALIGN=TOP>$b</TD>",
   "</TR>\n";
}
if ( $env_flag ) {
   print "<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP COLSPAN=2>Environment Variables",
   "</TH></TR>\n<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP>Name</TH><TH ALIGN=CENTER",
   " VALIGN=TOP NOWRAP>Value</TH></TR>\n";
   while ( ($a,$b) = each %ENV ) {
      print "<TR><TD ALIGN=LEFT VALIGN=TOP>$a</TD><TD ALIGN=LEFT",
      " VALIGN=TOP>$b</TD></TR>\n";
   }
}
print "</TABLE></BODY></HTML>\n";

exit 0;

I'll include the report this form/CGI pair generates here, narrowed down to the relevant items:


Form-Based Variables
NameValue
foo.x 13
foo.y 5
zen It is difficult to kill a horse with a flute.


Environment Variables
NameValue
SERVER_SOFTWARE ncSA/1.5
GATEWAY_INTERFACE CGI/1.1
SERVER_PROTOCOL HTTP/1.0
REQUEST_METHOD GET
QUERY_STRING zen=It+is+difficult+to+kill+a+horse+with+a +flute.&foo.x=13&foo.y=5
HTTP_USER_AGENT Mozilla/3.0b4 (Win95; I)


Comparing Imagemaps and Imagebuttons
ImagemapImagebutton
Invoked by <A HREF="url"><IMG SRC=imagefile ISMAP></A> Invoked by <FORM ACTION="url"><INPUT ></A> TYPE=IMAGE NAME="name"></FORM> with options for specifying a method in the FORM tag and a name in the INPUT tag
Coordinates transferred toCoordinates transferred as specified by the
server via the QUERY_STRING associated form: by either GET (QUERY_STRING) or POST (STDIN)
Coordinate click information is in the form X,Y where X and Y are numeric values Coordinate click information is in the form name.x=X&name.y=Y as per the GET/POST data passing schemes
Can pass other information only via the PATH_INFO environment variable Other information can be passed via PATH_INFO; other form fields in either the QUERY_STRING or STDIN. If the POST (STDIN) method is used, additional information can be passed through QUERY_STRING.

Note
If a NAME attribute isn't included with the <INPUT TYPE=IMAGE> tag, then coordinate information is passed simply as x=X&y=Y.

But What Does It All Mean?

To begin, imagebuttons are a logical superset of imagemaps for all CGI applications. Put simply, this means that, with only a little fiddling around, you can use imagebuttons anywhere you use imagemaps. For instance, when using the im.cgi code I developed, the following HTML
<A HREF="imagemap.cgi/pathinfo"><IMG SRC="image.gif" ISMAP></A>
can be replaced with
<FORM ACTION="imagemap.cgi/pathinfo"><INPUT TYPE=IMAGE SRC="image.gif"></FORM>
as long as the im.cgi Perl code is modified so that
($x,$y) = split(/,/,$ENV{QUERY_STRING});
is replaced with
($qs = $ENV{QUERY_STRING}) =~ tr/=xy//d; ($x,$y) = split(/&/,$qs);

Caution
While you should be able to perform this direct code substitution, I don't recommend it as a practice. First, this is unnecessarily complicated for what could be done simply with imagemaps. Second, if any more input tags find their way into the form, this Perl modification is no longer applicable. Changes to the standard imagemap.c file are also possible, but I can't think of a good reason to do so, and you might end up shooting yourself in the foot.

Through this direct correspondence between imagemaps and imagebuttons, you can do at least everything with an imagebutton that you can with an imagemap. But there are added benefits to imagebuttons, as well, should you choose to exploit them.
Imagebuttons are by their nature form related. This means that you can tie more information to an imagebutton submission than just the x and y coordinates of the mouse-click. This has been all but impossible with imagemaps.
The unique way in which imagemap coordinate information is passed to the CGI program requires special (though minimal) treatment. Because information created by imagebuttons is passed to the CGI program through the same method as any other GET or POST created data, a standard CGI-handling package can be used to extract the information. This is demonstrated by the code I used earlier to show the environment variables in the imagebutton example.
As has been the case in the past, a multistate CGI based on form input has required the HTML code to possess multiple <INPUT TYPE=SUBMIT> tags, each with a different NAME attribute. This same multistate effect can now be accomplished by having the CGI program react differently depending on the x,y coordinate information supplied by the imagebutton-based form submission.
Imagemaps are still the right choice in many situations; they are both intrinsically simple to deal with and have a wealth of preexisting support. There is more documentation for imagemaps, and there are standard code libraries and "built-in" features supported by some browsers and servers (client-side imagemaps and the server-side .map Magic MIME type). However, there is a limit to the capabilities of imagemaps. Imagebuttons can transcend this limit.

Summary

For those of you who have survived reading these past 32 pages, I salute you. Here is a quick summary of what I have covered in this chapter.
Imagemaps are special, but they aren't mystical. What makes imagemaps special are the HTML markups that support their use and the unique way that information is passed from client to server.
There is a wealth of pre-existing imagemap-handling programs. In most cases, it will be sufficient to tackle your CGI problem at hand. But still try to understand how these programs work, as well. At their core, they output a specific Location HTTP header based on a comparison of the coordinate information supplied and various geometries gleaned from a map file.
Imagemap handling has become so common that the "labor-saving devices" of client-side imagemaps and server-parsed imagemaps (the .map Magic MIME type) have been introduced. Know how they work and use them as appropriate.
If necessary, or even if just plain desired, you can build your own imagemap handler. If you take this approach, you inherit a lot of responsibility but at the same time you acquire the power to create a CGI solution that fits the needs of your project. Don't be afraid to jump completely beyond the click-in-a-shape paradigm that has come to dominate the imagemap field when you develop your own code.
Finally, if imagemaps can almost (but not quite) do what you want them to, maybe imagebuttons are better suited for your project. Conversely, if you have a form you'd like to "spruce up" and make more attractive or user-friendly, imagebuttons might provide the kick you want.
Happy hacking!