Cookie Tracking

This web page has been accessed times since 9/28/2000. 
Cookies are a general mechanism which server side connections (such as CGI scripts) can use to both store and retrieve information on the client side of the connection. The addition of a simple, persistent, client-side state significantly extends the capabilities of Web-based client/server applications. Netscape browsers versions 1.1 and higher support a so-called "cookie" designed to help maintain state
within a browser session. CGI.pm has several methods that support cookies.

A cookie is a name=value pair much like the named parameters in a CGI query string. CGI scripts create
one or more cookies and send them to the browser in the HTTP header. The browser maintains a list of
cookies that belong to a particular Web server, and returns them to the CGI script during subsequent
interactions.

The interface to Netscape cookies is the cookie() method:

    $cookie = $query->cookie(-name=>'sessionID',
                                               -value=>'xyzzy',
                                               -expires=>'+1h',
                                               -path=>'/cgi-bin/database',
                                               -domain=>'.capricorn.org',
                                               -secure=>1);
    print $query->header(-cookie=>$cookie);

cookie() creates a new cookie. Its parameters include:

-name
    The name of the cookie (required). This can be any string at all. Although Netscape limits its
    cookie names to non-whitespace alphanumeric characters, CGI.pm removes this restriction by
    escaping and unescaping cookies behind the scenes.

-value
    The value of the cookie. This can be any scalar value, array reference, or even associative array
    reference. For example, you can store an entire associative array into a cookie this way:

            $cookie=$query->cookie(-name=>'family information',
                                                     -value=>\%childrens_ages);

-path
    The optional partial path for which this cookie will be valid, as described above.

-domain
    The optional partial domain for which this cookie will be valid, as described above.
-expires
    The optional expiration date for this cookie. The format is as described in the section on the
    header() method:

            "+1h"  one hour from now

-secure
    If set to true, this cookie will only be used within a secure SSL session.

The cookie created by cookie() must be incorporated into the HTTP header within the string returned by
the header() method:

        print $query->header(-cookie=>$my_cookie);

To create multiple cookies, give header() an array reference:

        $cookie1 = $query->cookie(-name=>'riddle_name',
                                  -value=>"The Sphynx's Question");
        $cookie2 = $query->cookie(-name=>'answers',
                                  -value=>\%answers);
        print $query->header(-cookie=>[$cookie1,$cookie2]);

To retrieve a cookie, request it by name by calling cookie() method without the -value parameter:

        use CGI;
        $query = new CGI;
        %answers = $query->cookie('answers');
        # $query->cookie(-name=>'answers') works too!

To retrieve the names of all cookies passed to your script, call cookie() without any parameters. This
allows you to iterate through all cookies:

        foreach $name ($query->cookie()) {
            print $query->cookie($name);
        }

The cookie and CGI namespaces are separate. If you have a parameter named 'answers' and a cookie
named 'answers', the values retrieved by param() and cookie() are independent of each other. However,
it's simple to turn a CGI parameter into a cookie, and vice-versa:

   # turn a CGI parameter into a cookie
   $c=$q->cookie(-name=>'answers',-value=>[$q->param('answers')]);
   # vice-versa
   $q->param(-name=>'answers',-value=>[$q->cookie('answers')]);

See the cookie.cgi example script for some ideas on how to use cookies effectively.

NOTE: There appear to be some (undocumented) restrictions on Netscape cookies. In Netscape 2.01, at
least, I haven't been able to set more than three cookies at a time. There may also be limits on the length
of cookies. If you need to store a lot of information, it's probably better to create a unique session ID,
store it in a cookie, and use the session ID to locate an external file/database saved on the server's side
of the connection.

oc.pl: Code for Generating Order Form with Cookie

#!/usr/bin/perl

use CGI qw(:standard);

@cookies=sort qw/peanut mint chocolate butter mm/;

# Recover the previous cookie order from the magic cookie.
# The cookie has been formatted as an associative array
# mapping cookie name to the number of cookies package ordered.
%orders = cookie('cookie_order');
$ID = cookie('ID');
#if there is no unique ID create one
if (length($ID) eq 0) {
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
        $ID = "CS401_$year\_$mon\_$mday:$hour:$min:$sec";
}

# Recover the new order(s) from the parameter 'new_animal'
@neworder = param;
foreach $na (@neworder) {
        $val = param($na);
        $orders{$na} = $val;
}

# Put them in a cookie
$the_cookie = cookie(-name=>'cookie_order',
                     -value=>\%orders,
                     -expires=>'+1h');
$cookie2 = cookie(-name=>'ID',
                  -value=>$ID,
                  -expires=>'+3333h');
# Print the header, incorporating the cookie and the expiration date...
print header(-cookie=>[$the_cookie,$cookie2]);

# Now we're ready to create our HTML page.
print start_html('cookie order'),
      h1('Cookie Order'),
      "Your ID = $ID<BR>",
      "Enter the number of boxs for each cookie you like to order",
      hr,
      start_form("POST", "/cgi-bin/op.pl");
print "previous order<br>";
foreach $key (keys %orders) {
        print "$key=$orders{$key}<br>";
}
print "<table BORDER=2>";
print "<tr><td><b>Cookie</b></td><td><b># of Boxes</b></td></tr>";
foreach $cookie (@cookies) {
        print "<tr><td>$cookie</td><td align=right>",
              textfield(-name=>"$cookie",
                        -default=>"$orders{$cookie}",
                        -size=>10),
              "</td></tr>";
}
print "</table>";
    print submit(-name=>"action", -value=>"Order");
    print reset;
    end_form;
print end_html;
 

op.pl: Code for Processing Cookie Order

#!/usr/bin/perl

use CGI qw(:standard);

@cookies=sort qw/peanut mint chocolate butter mm/;

# Recover the previous cookie order from the magic cookie.
# The cookie has been formatted as an associative array
# mapping cookie name to the number of cookies package ordered.
%orders = cookie('cookie_order');
$ID = cookie('ID');
#if there is no unique ID create one
if (length($ID) eq 0) {
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
        $ID = "CS401_$year\_$mon\_$mday:$hour:$min:$sec";
}

# Recover the new order(s) from the parameter
@neworder = param;
foreach $na (@neworder) {
        $val = param($na);
        $orders{$na} = $val;
}

# Put them in a cookie
# Print the header, incorporating the cookie and the expiration date...
$the_cookie = cookie(-name=>'cookie_order',
                     -value=>\%orders,
                     -expires=>'+1h');
$cookie2 = cookie(-name=>'ID',
                  -value=>$ID,
                  -expires=>'+3333h');
# Print the header, incorporating the cookie and the expiration date...
print header(-cookie=>[$the_cookie,$cookie2]);
# Now we're ready to create our HTML page.
print header('text/html');
print start_html('cookie order'),
      h1('Cookie Order'),
      "Order# = $ID<BR>",
      "Please confirm the order",
      hr;
print "previous order<br>";
foreach $key (keys %orders) {
        print "$key=$orders{$key}<br>";
}
print start_form("POST", "/cgi-bin/cp.pl");
print "Order#=$ID<br>";
print "<table BORDER=2>";
print "<tr><td><b>Cookie</b></td><td><b># of boxes</b></td></tr>";
foreach $cookie (@cookies) {
        print "<tr><td>$cookie</td><td align=right>$orders{$cookie}</td></tr>";
}
print "</table>";
    print submit(-name=>"action", -value=>"confirm");
    print submit(-name=>"action", -value=>"cancel");
    end_form;
print end_html;

cp.pl: Code for Confirming that the Order Has Been Processed

#!/usr/bin/perl

use CGI qw(:standard);

@cookies=sort qw/peanut mint chocolate butter mm/;

# Recover the previous cookie order from the magic cookie.
# The cookie has been formatted as an associative array
# mapping cookie name to the number of cookies package ordered.
%orders = cookie('cookie_order');
$ID = cookie('ID');
#if there is no unique ID create one
if (length($ID) eq 0) {
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
        $ID = "CS401_$year\_$mon\_$mday:$hour:$min:$sec";
}

print header('text/html');
print start_html('cookie order');
print "The following order has been ";
if (param('action') eq 'cancel') {
        print "canceled. Please press the button for start another order.<BR>";
} else {
       print "confirmed and processed.  You should receive the cookie at the end
 of March. Thanks.<BR>";
}
      print hr,
      start_form("POST", "oc.pl");
print "Order#=$ID<br>";
print "<table BORDER=2>";
print "<tr><td><b>Cookie</b></td><td><b># of boxes</b></td></tr>";
foreach $cookie (@cookies) {
        print "<tr><td>$cookie</td><td align=right>$orders{$cookie}</td></tr>";
}
print "</table>", hr;
    print submit(-name=>"action", -value=>"Start Another Order");
    end_form;
print end_html;