##############################################################################
# HTTP Cookie Library           Version 2.1                                  #
# Copyright 1996 Matt Wright    mattw@worldwidemart.com                      #
# Created 07/13/96              Last Modified 12/23/96                       #
# Scripts Archive at:           http://www.worldwidemart.com/scripts/        #
# Parts of Code Contributed by Jeff Carnahan (http://terminalp.com/scripts/) #
##############################################################################
# If you run into any problems while trying to configure this script, help   #
# is available.  The steps you should take to get the fastest results, are:  #
#       1) Read this file thoroughly                                         #
#       2) Consult the Matt's Script Archive Frequently Asked Questions:     #
#               http://www.worldwidemart.com/scripts/faq/                    #
#       3) If you are still having difficulty installing this script, send   #
#          e-mail to: scripts-help@tahoenet.com                              #
#          Include any error messages you are receiving and as much detail   # 
#          as you can so we can spot your problem.  Also include the variable#
#          configuration block that is located at the top of the script.     #
#                                                                            #
# Hopefully we will be able to help you solve your problems.  Thank you.     #
##############################################################################
# COPYRIGHT NOTICE                                                           #
# Copyright 1996 Matthew M. Wright  All Rights Reserved.                     #
#                                                                            #
# HTTP Cookie Library may be used and modified free of charge by anyone so   #
# long as this copyright notice and the comments above remain intact.  By    #
# using this code you agree to indemnify Matthew M. Wright from any          #
# liability that might arise from it's use.                                  #  
#                                                                            #
# Selling the code for this program without prior written consent is         #
# expressly forbidden.  In other words, please ask first before you try and  #
# make money off of my program.                                              #
#                                                                            #
# Obtain permission before redistributing this software over the Internet or #
# in any other medium.  In all cases copyright and header must remain intact #
##############################################################################

TABLE OF CONTENTS:
==================

1) Overview
2) cookie.lib Configuration
    a) $Cookie_Exp_Date
    b) $Cookie_Path
    c) $Cookie_Domain
    d) $Secure_Cookie
    e) @Cookie_Encode_Chars
    f) %Cookie_Encode_Chars
    g) @Cookie_Decode_Chars
    h) %Cookie_Decode_Chars
3) Using this Library in your CGI script.
    a) Requiring the library.
    b) Subroutine calls.
        + &GetCookies
        + &SetCookieExpDate
        + &SetCookiePath
        + &SetCookieDomain
        + &SetSecureCookie
        + &SetCookies
        + &SetCompressedCookies
        + &GetCompressedCookies
4) Character Translation Information
5) Any Examples Available?
    a) ccounter.pl
    b) Scripts Around the World
6) Other Cookie Resources
7) History

##############################################################################

                                   OVERVIEW
                                   ========

HTTP Cookie Library is a Perl 4 and 5 compatible library which allows you to 
easily use Persistent Client State HTTP Cookies by allowing you to get the 
cookies from the environment, prepare cookies, set cookies, change the 
expiration date, domain and path all with easy subroutine calls.

Version 2.0 and 2.1 contain many changes, including the ability to compress
multiple cookies into one, the elimination of the &PrepareCookies subroutine,
and fixes allowing you to set more than one cookie at a time.  The addition of
character URL-encoding and the fixing of a scoping bug brought on by 
localizing the global variables used in this script are new to version 2.1.

NOTE: Anyone who used 2.0 and &UnCompressCookies should now change all
      references from &UnCompressCookies to &GetCompressedCookies.  The calls
      to these routines are the same, however the name has been changed.

For more information on Cookies, and to see the preliminary specifications
of Persistent Client State HTTP Cookies to which this library conforms, visit
http://www.netscape.com/newsref/std/cookie_spec.html

There are three files included with this script:
        1) README       - This file; includes detailed installation 
                          instructions.
        2) cookie.lib   - The Perl script which generates the random phrase
                          and displays it on your web page.
        3) ccounter.pl  - An example of how to use this library.

The cookie.lib perl library is not a stand-alone CGI script.  It is meant to
be used in conjunction with a script that you build.  It's only purpose is to
facilitate the usage of client side persistent HTTP cookies.  This means, in 
order to use this library, you should always have the following line in your
script somewhere:

	require '/path/to/cookie.lib';

This will then tell Perl to retrieve this file and include it's code and 
subroutines in your script.  Nonetheless, there are some variables which you
can modify in the cookie.lib file before you place it on your system.  All
of these variables can be modified through special subroutines in cookie.lib
so it is not necessary to edit any of them.

##############################################################################

                           COOKIE.LIB CONFIGURATION
                           ========================

This program contains three variables which can be modified in the library, to
make it easier to use on your system.  They can also be set through calling
subroutines, which is explained in: 'Using this Library in Your CGI Script,'
or by setting them in your main script, as they are local variables which can
be modified.  The subroutines provide checks to make sure the value is valid.

      $Cookie_Exp_Date = '';
            This variable defines the expiration date of your cookie.
            When this date arrives the browser will ask the user if
            they want to delete the cookie, and that it is ok to do so.
            This date must always be represented as:
            Wdy, DD-Mon-YYYY HH:MM:SS GMT
            and always has to be in GMT time.  An example would be:
            Wed, 09-Nov-1999 00:00:00 GMT.  By default, if this is
            not filled in, the browser will delete the cookie when the
            session ends.  Also see &SetCookieExpDate.

      $Cookie_Path = '';
            This path defines where under your domain the cookies 
            should be sent.  For instance, if your URL is: 
            http://domain.xxx/your_dir/, there is no reason for the 
            browser to send the Cookie header to the base domain name.
            Therefore, you would set this to '/your_dir';  However,
            because this can be set through the script, set it to 
            whatever directory will use this most often or whichever 
            directory you want as default.  If nothing is placed here,
            the default will be assumed to be the script or page which
            created the cookie.  Also see &SetCookiePath.

      $Cookie_Domain = '';
            This defines the base domain to which the browse passes the
            cookie.  If you have a domain: www.host.xxx, your.host.xxx
            and host.xxx, which all will use this, you would set this 
            variable to: '.host.xxx';  The following is an excerpt from
            the Netscape Preliminary Specifications of Persistent Client
            State HTTP Cookies:

            "Only hosts within the specified domain can set a cookie for 
            a domain and domains must have at least two (2) or three (3)
            periods in them to prevent domains of the form: ".com", 
            ".edu", and "va.us". Any domain that fails within one of the
            seven special top level domains listed below only require 
            two periods. Any other domain requires at least three. The 
            seven special top level domains are: "COM", "EDU", "NET",
            "ORG", "GOV", "MIL", and "INT"."

            Also see &SetCookieDomain.

      $Secure_Cookie = '0';
            This defines whether or not you want to restrict the 
            transmission of the cookie to a secure server.  If this flag
            is set to '1', by default the cookies you create will only be
            sent to secure servers by the client.  This can also be 
            changed through &SetSecureCookie.

      @Cookie_Encode_Chars = ('\%', '\+', '\;', '\,', '\=', '\&', '\:\:', '\s');
            This defines the order in which the special characters should
            be encoded.  More info can be found in section entitled
            'Character Translation Information'.

      %Cookie_Encode_Chars = ('\%',   '%25',
                              '\+',   '%2B',
                              '\;',   '%3B',
                              '\,',   '%2C',
                              '\=',   '%3D',
                              '\&',   '%26',
                              '\:\:', '%3A%3A',
                              '\s',   '+');
            This associative array defines the characters to be
            translated as the keys, which can be referred to as:
            $Cookie_Encode_Chars{key} and the values which these
            characters should be translated into.  More info can
            be found in section entitled 'Character Translation
            Information'.

      @Cookie_Decode_Chars = ('\+', '\%3A\%3A', '\%26', '\%3D', '\%2C', '\%3B', '\%2B', '\%25');
            This defines the order in which the special characters should
            be decoded.  More info can be found in section entitled
            'Character Translation Information'.

      %Cookie_Decode_Chars = ('\+',       ' ',
                              '\%3A\%3A', '::',
                              '\%26',     '&',
                              '\%3D',     '=',
                              '\%2C',     ',',
                              '\%3B',     ';',
                              '\%2B',     '+',
                              '\%25',     '%');
            This associative array defines the characters to be
            translated as the keys, which can be referred to as:
            $Cookie_Decode_Chars{key} and the values which these
            characters should be decoded into.  More info can be
            found in section entitled 'Character Translation
            Information'.


##############################################################################

                    USING THIS LIBRARY IN YOUR CGI SCRIPT.
                    ======================================

HTTP Cookie Library is not meant as a standalone CGI script.  It must be used
in conjunction with other scripts you have written, and provides a basic
outline and easy subroutines which makes implementing the cookies into your
perl program easy.

Also, to give cookies to the user, the browser must be pointed to the CGI
application or the CGI application must be imbedded in the HTML document
through Server Side Includes or other technology, such as JavaScript.  If
you choose the JavaScript path, some browsers which support cookies won't
be able to receive them and JavaScript users have no use for this library
as it is only compatible in Perl scripted CGI applications.

Requiring the Library:
----------------------

Near the top of your Perl CGI script (or before you make the first call to a
subroutine which is in cookie.lib), you will need to include the line:

require '/path/to/cookie.lib';

If you place the cookie.lib in the same directory as your CGI script, or in
a path which is defined in @INC, you can call it with:

require 'cookie.lib';

Now all of the subroutines which this script contains can be used in your CGI
Perl script.  Here's is a summary of them, what they do and how to call them:

Subroutine Calls:
-----------------

&GetCookies('cookie_name1',...,'cookie_namen');
      This function can be called as simply &GetCookies, or using arguments
      such as &GetCookies('cookie1','cookie2').  Calling it without
      arguments means that it will return a '1' if cookies are found, a 0
      if they are not.  This is useful, so that you can perform function 
      like:

            if (&GetCookies) {
                # Successful Code Here;
            }
            else {
                # Give them a Cookie Here;
            }

      In the above circumstance, if cookies are found, the successful code
      gets executed.  Otherwise, you can give them a cookie.  The 
      &GetCookies subroutine then takes the cookies from the environment
      and places them in an associative array.  This array is %Cookies.
      So if you have set a cookie named 'visit' with a value which contains
      whatever you set it for, then you would obtain this value by using
      the scalar variable $Cookies{'visit'}.

      Now, let's say that a lot of cookies are set for your base domain and
      path.  They would all get included in the return if you didn't
      specify arguments for &GetCookies.  For instance, in the above 
      example, since &GetCookies would return a value of '1' if any cookies
      are set, that could mess up this script, if all we want to know is if
      the visit cookie is set.  Therefore, we would change the above program
      to only need to 'visit' cookie:

		if (&GetCookies('visit')) {
                # Successful Code Here;
		}
            else {
                # Give them a Cookie Here;
            }

      Now, only the $Cookies{'visit'} scalar will be set and if it is not
      &GetCookies will return a 0 and the else { } statement will be 
      executed.

      You can check for multiple cookies to be set by &GetCookies('name1',
      'name2','etc...');  However this will return a true value (1) if any
      of those cookies are set.  You can get around this by calling them one
      at a time, although this is tedious.

      If you have compressed your cookies with &SetCompressedCookies, you
      will need to look into the &GetCompressedCookies routine to do what
      &GetCookies does.

&SetCookieExpDate('Wdy, DD-Mon-YYYY HH:MM:SS GMT');

	By default in the script, the cookie is set to expire when the browser
      session ends.  If	you wish for the cookie to expire at a later date,
      then all you have to do is change the date by calling &SetCookieExpDate
      with an argument containing the new date.  The new date must be in the
      format as shown above, or it will not set right. The following is an
      example:

	&SetCookieExpDate('Wed, 09-Nov-1999 00:00:00 GMT');

	If your date does not match a regular expression which checks to see
	if it is valid, it will return a 0.  So it you catch the value, you
	can do:

		if (&SetCookieExpDate('Wdy, DD-Mon-YYYY HH:MM:SS GMT')) {
		    # Continue here.
		}
		else {
		    # Do Error Stuff Here.  &SetCookieExpDate failed.
		}

	For any cookies which you wish to be able to detect if the user closes
      the browser's session and then re-opens it, you will need to set this
      to a specific date.

      The $Cookie_Exp_Date variable can also be changed at the top of
      cookie.lib, or at any point inside of the perl script which required
      cookie.lib, simply with the statement: 
      $Cookie_Exp_Date = 'Wdy, DD-Mon-YYYY HH:MM:SS GMT';
      The subroutine is provided to check the syntax of your date.

&SetCookiePath('/path');

	By default, this is set to the path of the script or web page which sets
      the cookie, so unless this has been set differently in cookie.lib to
      contain a value, you need not call this function.  However, if you wish
      for those values to be passed to other scripts under the same domain,
	you will need to have this set to that path of the URL.  For 
	instance, if I only want my cookies passed to URLs which are at least:
	http://www.worldwidemart.com/scripts, I would call this function as:

		&SetCookiePath('/scripts');

      You can also set the cookie path with the statement:
      $Cookie_Path = '/path/you/want/to/set';
      You can place this statement anywhere in your Perl programs after you
      have required the cookie.lib library.

&SetCookieDomain('.host.xxx');

      If the value of your desired domain suffix (.host.xxx) was assigned	
	a different value as default, you can change this by placing a domain
      name in the call to &SetCookieDomain.  Most of the time you shouldn't
	have to worry about this.  Below are the rules which this script
      follows, taken directly from Netscape documentation:

            "Only hosts within the specified domain can set a cookie for 
            a domain and domains must have at least two (2) or three (3)
            periods in them to prevent domains of the form: ".com", 
            ".edu", and "va.us". Any domain that fails within one of the
            seven special top level domains listed below only require 
            two periods. Any other domain requires at least three. The 
            seven special top level domains are: "COM", "EDU", "NET",
            "ORG", "GOV", "MIL", and "INT"."

      The $Cookie_Domain can also be set using the statemement:
      $Cookie_Domain = 'host.xxx';
      You can place this statement anywhere in your Perl programs after you
      have required the cookie.lib library.

&SetSecureCookie('0' || '1');

      This defines whether or not this cookie should be relayed only to 
      secure servers.  Calling &SetSecureCookie('1'); means that clients 
      will only pass those cookies you have set to secure servers.  
      Calling &SetSecureCookie('0'); means the cookies will be transferred
      whenever the path and domain are correct, regardless of security issues.
      Here is an excerpt from Netscape specs:

            "If a cookie is marked secure, it will only be transmitted if the
            communications channel with the host is a secure one. Currently
            this means that secure cookies will only be sent to HTTPS (HTTP
            over SSL) servers.  If secure is not specified, a cookie is 
            considered safe to be sent in the clear over unsecured channels."

      You can also change the value of $Secure_Cookie through a statement:
      $Secure_Cookie = 1;        or        $Secure_Cookie = 0;
      You can place this statement anywhere in your Perl programs after you
      have required the cookie.lib library.

&SetCookies('cookie_name1','cookie_value1',...,'cookie_namen','cookie_valuen');
      This function has been completely changed since Version 1.1.1, except
      for the fact that it actually does set the cookies.  There is no longer
      a call to &PrepareCookies, but rather &SetCookies does it all.

      If you wish to set the cookies name and email, and have the values of
      these cookies in the variables $name and $email, you would set them with
      the following code:

            print "Content-type: text/html\n";
            &SetCookies('name',"$name",'email',"$email");
            print "\n";

      The first line prints out the text/html header, with one new line
      following it, rather than two, since the header sent to the browser is
      not yet complete.  Then, our call to &SetCookies is made.  This call
      will send two Set-Cookie: headers to the browser and set both of the
      cookies for us.

      You can send as many cookies to this subroutine as you wish to set.  
      Browsers do have a limit though, so if you are setting multiple
      cookies, it is suggested that you look into the &SetCompressedCookies
      subroutine.

&SetCompressedCookies('compressed_cookie_name','cookie_name1','cookie_value1',
                      ...,'cookie_namen','cookie_valuen');

      This subroutine does almost the same thing that &SetCookies does, but
      instead it takes one extra argument.  You call it with the name of the
      compressed cookie which you wish to set and then the name/value pairs of
      of the cookies which you wish to compress.  This subroutine doesn't
      actually compress cookies, but it takes multiple cookies and strings
      them together, so to the browser they appear as one.  The = signs are
      converted into :: and the name/value pairs are separated from each other
      by &, however there is no need to change these characters before hand,
      as they will be translated back when uncompressed.

      You may be saying, why should I bother compressing cookies when it takes
      extra work?  I can just set 50 cookies all for different things.  The
      main reason is, that according to Netscape's specification:

            "There are limitations on the number of cookies that a client can 
            store at any one time. This is a specification of the minimum 
            number of cookies that a client should be prepared to receive and
            store. 

               -- 300 total cookies 
               -- 4 kilobytes per cookie, where the name and the value combine
                  to form the 4 kilobyte limit. 
               -- 20 cookies per server or domain. (note that completely 
                  specified hosts and domains are treated as separate entities
                  and have a 20 cookie limitation for each, not combined)

            Servers should not expect clients to be able to exceed these 
            limits. When the 300 cookie limit or the 20 cookie per server 
            limit is exceeded, clients should delete the least recently used
            cookie. When a cookie larger than 4 kilobytes is encountered the
            cookie should be trimmed to fit, but the name should remain intact
            as long as it is less than 4 kilobytes."

      This also means that it is up to you to check and make sure that your
      compressed cookie will not take more than 4KB.  This is not going to be
      a common problem most likely, though, as 4KB is 4,000 characters, which
      if typed out on a common 80 column screen is about 50 lines, or almost
      a full page of text.  The 20 cookie limit on one server name is
      definitely a common problem, and this helps save some of this space.
      As cookies become more common, clients may start reaching the 300
      cookie maximum more rapidly as well.

      Let's take the example from &SetCookies above, with the name and email
      cookies we wish to set.  Now, rather than taking up two cookies, we
      can set one cookie named user, which will contain the information
      stored in the name and email cookies.  To do this, make you make a call
      like:

           print "Content-type: text/html\n";
           &SetCompressedCookies('user','name',$name,'email',$email);
           print "\n";

      This sets a cookie named user which contains the cookies name and email.
      In order to uncompress this cookie, you need to use the
      &GetCompressedCookies subroutine explained below.  It acts much like
      &GetCookies.

      WARNING:  It is a good idea not to set the compressed cookie name equal
                to the name of a cookie being stored in the compressed cookie.
                It can mess things up.

&GetCompressedCookies('compressed_cookie_name','cookie_name1',...,'cookie_namen');

      This subroutine works in almost the same way as &GetCookies, except that
      it needs the name of the compressed cookie first, followed by the names
      of the cookies you wish to retrieve out of the compressed cookie.  If
      this subroutine is called with only the name of the compressed cookie,
      it will retrieve all values and place them in the %Cookies array.

      For instance, if we were to take the example used in 
      &SetCompressedCookies and we needed to only get the name of the user out
      of the compressed cookie, we would do something like:

      &GetCompressedCookies('user','name');

      Where 'user' was the name of the cookie we specified to be set when we
      compressed the cookies and 'name' was one of the cookies we compressed
      into 'user'.  The value of name can not be retrieved from 
      $Cookies{'name'}.

      Or if you wished to get every cookie out of the compressed cookie, you
      can simply say:

      &GetCompressedCookies('user');

      And now $Cookies{'name'} and $Cookies{'email'} are set.

##############################################################################

                       CHARACTER TRANSLATION INFORMATION
                       =================================

Version 2.1 of the HTTP Cookie Library introduces some simple character
URL-encoding which eases the use and fixes some common problems with cookies.
Previously, cookies would often not be set if they included spaces, semi-
colons, and a few other special characters.  There was also the problem found
if information contained two :: or an & and you tried to compress the cookies,
because those two characters are used for the compression sequence that HTTP
Cookie Library uses.

According to Netscape's Persistent Client State HTTP Specification:

      "This string is a sequence of characters excluding semi-colon, comma
       and white space. If there is a need to place such data in the name
       or value, some encoding method such as URL style %XX encoding is 
       recommended, though no encoding is defined or required."

HTTP Cookie Library does use the URL style %XX (also known as hex) encoding to
ensure that these characters are not lost and that the cookie is set.

##############################################################################

                            ANY EXAMPLES AVAILABLE?                           
                            =======================

ccounter.pl
-----------

      ccounter.pl, an example of how to use some of the features in this
      library comes bundled with the script.  Hopefully the docs have been
      written extensively enough and the library commented enough (this is
      the most commented and well-documented script at MSA) that it should
      be fairly easy to figure out.

Scripts Around the World
------------------------

      You can find real-world implementations of this library listed at MSA
      in the Scripts Around the World section.  The HTTP Cookie Library page
      for this can be found at:

          http://www.worldwidemart.com/scripts/examples/cookielib.shtml

      If you have used the library, please let us know so we can add it!

##############################################################################

                            OTHER COOKIE RESOURCES
                            ======================

There are many other cookie resources available on the Internet.  Below are
just a few that are the most useful and should help you out.  Enjoy.

Netscape Persistent Client State HTTP Cookies Specification
      http://www.netscape.com/newsref/std/cookie_spec.html
      The specifications to which this library conforms.  Contains a lot of
      useful information on Cookies in general and how they can be used.

Andy's Netscape HTTP Cookie Info
      http://www.illuminatus.com/cookie.fcgi
      A lot of information on cookies, the security of them, Frequently Asked
      Questions and much more.  A LOT of cookie information. :-)

Malcolm's Guide to Persistent Cookies
      http://www.emf.net/~mal/cookiesinfo.html
      This guide to Persistant Cookies explains what cookies are, discusses
      security and privacy issues, gives URLS to more documentation and shows
      some examples of places using cookies.

Javascript Cookie Functions
      http://www.hidaho.com/cookies/cookie.txt
      A library much like this one with different subroutines, only
      implemented in JavaScript.

HTTP State Management Mechanism RFC
      http://portal.research.bell-labs.com/~dmk/cookie-2.36.txt
      Looks very cookie-related.  Maybe I should start conforming to this.

##############################################################################

HISTORY

   Version 2.1   - 12/19/96     - Changed the local() scope of global
                                  variables because they weren't setting at
                                  the top of the script right.
                 - 12/20/96     - Added URL-encoding to the cookies for the
                                  characters: '%', '+', ':', ',', '=', '&',
                                  '::', and space.  More on this explained
                                  in the section, 'Character Translation'.
                                - &UnCompressCookies changed to
                                  &GetCompressedCookies, making more sense.
                 - 12/23/96     - &SetCookieExpDate fixed to allow a blank
                                  date.
   Version 2.0   - 11/28/96     - &SetCookieExpDate still not working right,
                                  and finall fixed.
                                - &SetCompressedCookies and &UnCompressCookies
                                  subroutines added so multiple cookies can be
                                  set as one cookie and take up less room in
                                  the cookies.txt file.
                                - &PrepareCookie replaced by changing
                                  &SetCookies to do all of the work.  This
                                  subroutine now actually takes values and
                                  sets them.
                                - Multiple cookied can be set now.  A bug
                                  existed before which wouldn't set all of
                                  the cookies.
                                - Checking now done when setting cookie domain
                                  so you can know if it will work.  Based on
                                  information in Netscape's Specifications.
   Version 1.1.1 - 07/15/96     - Andy Kington <andyk@teleport.com> 
                                  pointed out that the ExpDate Year should be
                                  represented as YYYY instead of YY.
   Version 1.1   - 07/14/96     - Added secure option and routines.
   Version 1.0   - 07/14/96     - First Version Released

##############################################################################

Have fun with this script, but if you can, please, please let me know the URL
of where it is implemented so I can see my work in action (and add you to the
list of sites using this script.)

##############################################################################
Matt Wright - mattw@worldwidemart.com  http://www.worldwidemart.com/scripts/