Tadashi Okoshi
slash****@users*****
2005年 6月 21日 (火) 22:11:14 JST
Index: affelio/extlib/CGI/Session/BluePrint.pm
diff -u /dev/null affelio/extlib/CGI/Session/BluePrint.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/BluePrint.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,124 @@
+package CGI::Session::BluePrint;
+
+# $Id: BluePrint.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+use strict;
+use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+);
+
+
+# Load neccessary libraries below
+
+use vars qw($VERSION);
+
+$VERSION = '0.1';
+
+sub store {
+ my ($self, $sid, $options, $data) = @_;
+
+ my $storable_data = $self->freeze($data);
+
+ #now you need to store the $storable_data into the disk
+
+}
+
+
+sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ # you will need to retrieve the stored data, and
+ # deserialize it using $self->thaw() method
+}
+
+
+
+sub remove {
+ my ($self, $sid, $options) = @_;
+
+ # you simply need to remove the data associated
+ # with the id
+
+
+}
+
+
+
+sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ # this is called just before session object is destroyed
+}
+
+
+
+
+# $Id: BluePrint.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+1;
+
+=pod
+
+=head1 NAME
+
+CGI::Session::BluePrint - Default CGI::Session driver BluePrint
+
+=head1 SYNOPSIS
+
+ use CGI::Session::BluePrint
+ $session = new CGI::Session("driver:BluePrint", undef, {...});
+
+For more examples, consult L<CGI::Session> manual
+
+=head1 DESCRIPTION
+
+CGI::Session::BluePrint is a CGI::Session driver.
+To write your own drivers for B<CGI::Session> refere L<CGI::Session> manual.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2002 Your Name. All rights reserved.
+
+This library is free software and can be modified and distributed under the same
+terms as Perl itself.
+
+=head1 AUTHOR
+
+Your name
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
+
+
+# $Id: BluePrint.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
Index: affelio/extlib/CGI/Session/CookBook.pm
diff -u /dev/null affelio/extlib/CGI/Session/CookBook.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/CookBook.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,650 @@
+# $Id: CookBook.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+package CGI::Session::CookBook;
+
+use vars ('$VERSION');
+
+($VERSION) = '$Revision: 1.1 $' =~ m/Revision:\s*(\S+)/;
+
+1;
+
+__END__;
+
+=pod
+
+=head1 NAME
+
+CookBook - tutorial on session management in cgi applications
+
+=head1 NOTE
+
+This document is under construction.
+
+=head1 DESCRIPTION
+
+C<CGI::Session::CookBook> is a tutorial that accompanies B<CGI::Session>
+distribution. It shows the usage of the library in web applications and
+demonstrates practical solutions for certain problems. We do not recommend you
+to read this tutorial unless you're familiar with L<CGI::Session|CGI::Session>
+and it's syntax.
+
+=head1 CONVENTIONS
+
+To avoid unnecessary redundancy, in all the examples that follow we assume
+the following session and cgi objects:
+
+ use CGI::Session;
+ use CGI;
+
+ my $cgi = new CGI;
+ my $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
+
+Although we are using default B<DSN> in our examples, you feel free to
+use any configuration you please.
+
+After initializing the session, we should "mark" the user with that ID.
+We use HTTP Cookies to do it:
+
+ $cookie = $cgi->cookie(CGISESSID => $session->id );
+ print $cgi->header(-cookie=>$cookie);
+
+The first line is creating a cookie using B<CGI.pm>'s C<cookie()>
+method. The second line is sending the cookie to the user's browser
+using B<CGI.pm>'s C<header()> method.
+
+After the above confessions, we can move to some examples with a less
+guilty conscious.
+
+=head1 STORING THE USER'S NAME
+
+=head2 PROBLEM
+
+We have a form in our site that asks for user's name and email address.
+We want to store the data so that we can greet the user when he/she
+visits the site next time ( possibly after several days or even weeks ).
+
+=head2 SOLUTION
+
+Although quite simple and straight forward it seems, variations of this
+example are used in more robust session managing tricks.
+
+Assuming the name of the form input fields are called "first_name" and
+"email" respectively, we can first retrieve this information from the
+cgi parameter. Using B<CGI.pm> this can be achieved in the following
+way:
+
+ $first_name = $cgi->param("first_name");
+ $email = $cgi->param("email");
+
+After having the above two values from the form handy, we can now save
+them in the session like:
+
+ $session->param(first_name, $first_name);
+ $session->param(email, $email);
+
+If the above 4-line solution seems long for you (it does to me), you can
+achieve it with a single line of code:
+
+ $session->save_param($cgi, ["first_name", "email"]);
+
+The above syntax will get "first_name" and "email" parameters from the
+B<CGI.pm> and saves them to the B<CGI::Session> object.Now some other
+time or even in some other place we can simply say
+
+ $name = $session->param("first_name");
+ print "$name, I know it's you. Confess!";
+
+and it does surprise him ( if not scare :) )
+
+=head1 REMEMBER THE REFERER
+
+=head2 PROBLEM
+
+You run an outrourcing service, and people get refered to your program
+from other sites. After finishing the process, which might take several
+click-throughs, you need to provide them with a link which takes them to
+a site where they came from. In other words, after 10 clicks through
+your pages you need to recall the referered link, which takes the user
+to your site.
+
+=head2 SOLUTION
+
+This solution is similar to the previous one, but instead of getting the
+data from the submitted form, you get it from HTTP_REFERER environmental
+variable, which holds the link to the refered page. But you should be
+cautious, because the click on your own page to the same application
+generates a referal as well, in this case with your own link. So you
+need to watchout for that by saving the link only if it doesn't already
+exist. This approach is suitable for the application which ALWAYS get
+accessed by clicking links and posting forms, but NOT by typing in the
+url. Good examples would be voting polls, shopping carts among many
+others.
+
+ $ENV{HTTP_REFERER} or die "Illegal use";
+
+ unless ( $session->param("referer") ) {
+ $session->param("referer", $ENV{HTTP_REFERER});
+ }
+
+In the above code, we simply save the referer in the session under the
+"referer" parameter. Note, that we first check if it was previously
+saved, in which case there would be no need to override it. It also
+means, if the referer was not saved previously, it's most likely the
+first visit to the page, and the HTTP_REFERER holds the link to the link
+we're interested in, not our own.
+
+When we need to present the link back to the refered site, we just do:
+
+ $href = $session->param("referer");
+ print qq~<a href="$href">go back</a>~;
+
+=head1 BROWSING HISTORY
+
+=head2 PROBLEM
+
+You have an online store with about a dozen categories and thousands of
+items in each category. When a visitor is surfing the site, you want to
+display the last 10-20 visited pages/items on the left menu of the site
+( for examples of this refer to Amazon.com ). This will make the site
+more usable and a lot friendlier
+
+=head2 SOLUTION
+
+The solution might vary on the way you implement the application. Here
+we'll show an example of the user's browsing history, where it shows
+just visited links and the pages' titles. For obvious reasons we build
+the array of the link=>title relationship. If you have a dynamicly
+generated content, you might have a slicker way of doing it. Despite the
+fact your implementation might be different, this example shows how to
+store a complex data structure in the session parameter. It's a blast!
+
+ %pages = (
+ "Home" => "http://www.ultracgis.com",
+ "About us" => "http://www.ultracgis.com/about",
+ "Contact" => "http://www.ultracgis.com/contact",
+ "Products" => "http://www.ultracgis.com/products",
+ "Services" => "http://www.ultracgis.com/services",
+ "Portfolio" => "http://www.ultracgis.com/pfolio",
+ # ...
+ );
+
+ # Get a url of the page loaded
+ $link = $ENV{REQUEST_URI} or die "Errr. What the hack?!";
+
+ # get the previously saved arrayref from the session parameter
+ # named "HISTORY"
+ $history = $session->param("HISTORY") || [];
+
+ # push()ing a hashref to the arrayref
+ push (@{$history}, {title => $pages{ $link },
+ link => $link });
+
+ # storing the modified history back in the session
+ $session->param( "HISTORY", $history );
+
+
+What we want you to notice is the $history, which is a reference to an
+array, elements of which consist of references to anonymous hashes. This
+example illustrates that one can safely store complex data structures,
+including objects, in the session and they can be re-created for you the
+way they were once stored.
+
+Displaying the browsing history should be even more straight-forward:
+
+ # we first get the history information from the session
+ $history = $session->param("HISTORY") || [];
+
+ print qq~<div>Your recently viewed pages</div>~;
+
+ for $page ( @{ $history } ) {
+ print qq~<a href="$page->{link}">$page->{title}</a><br>~;
+ }
+
+If you use B<HTML::Template>, to access the above history in your
+templates simply C<associate> the $session object with that of
+B<HTML::Template>:
+
+ $template = new HTML::Template(filename=>"some.tmpl",
+associate=>$session );
+
+Now in your "some.tmpl" template you can access the above history like
+so:
+
+ <!-- left menu starts -->
+ <table width="170">
+ <tr>
+ <th> last visited pages </th>
+ </tr>
+ <TMPL_LOOP NAME=HISTORY>
+ <tr>
+ <td>
+ <a href="<TMPL_VAR NAME=LINK>"> <TMPL_VAR NAME=TITLE> </a>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <!-- left menu ends -->
+
+and this will print the list in nicely formated table. For more
+information on associating an object with the B<HTML::Template> refer to
+L<HTML::Template manual|HTML::Template>
+
+=head1 SHOPPING CART
+
+=head2 PROBLEM
+
+You have a site that lists the available products off the database. You
+need an application that would enable users' to "collect" items for
+checkout, in other words, to put into a virtual shopping cart. When they
+are done, they can proceed to checkout.
+
+=head2 SOLUTION
+
+Again, the exact implementation of the site will depend on the
+implementation of this solution. This example is pretty much similar to
+the way we implemented the browing history in the previous example. But
+instead of saving the links of the pages, we simply save the ProductID
+as the arrayref in the session parameter called, say, "CART". In the
+folloiwng example we tried to represent the imaginary database in the
+form of a hash.
+
+Each item in the listing will have a url to the shopping cart. The url
+will be in the following format:
+
+ http://ultracgis.com/cart.cgi?cmd=add;itemID=1001
+
+C<cmd> CGI parameter is a run mode for the application, in this
+particular example it's "add", which tells the application that an item
+is about to be added. C<itemID> tells the application which item should
+be added. You might as well go with the item title, instead of numbers,
+but most of the time in dynamicly generated sites you prefer itemIDs
+over their titles, since titles tend to be not consistent (it's from
+experience):
+
+ # Imaginary database in the form of a hash
+ %products = (
+ 1001 => [ "usr/bin/perl t-shirt", 14.99],
+ 1002 => [ "just perl t-shirt", 14.99],
+ 1003 => [ "shebang hat", 15.99],
+ 1004 => [ "linux mug", 19.99],
+ # on and on it goes....
+ );
+
+ # getting the run mode for the state. If doesn't exist,
+ # defaults to "display", which shows the cart's content
+ $cmd = $cgi->param("cmd") || "display";
+
+ if ( $cmd eq "display" ) {
+ print display_cart($cgi, $session);
+
+ } elsif ( $cmd eq "add" ) {
+ print add_item($cgi, $session, \%products,);
+
+ } elsif ( $cmd eq "remove") {
+ print remove_item($cgi, $session);
+
+ } elsif ( $cmd eq "clear" ) {
+ print clear_cart($cgi, $session);
+
+ } else {
+ print display_cart($cgi, $session);
+
+ }
+
+
+The above is the skeleton of the application. Now we start writing the
+functions (subroutines) associated with each run-mode. We'll start with
+C<add_item()>:
+
+ sub add_item {
+ my ($cgi, $session, $products) = @_;
+
+ # getting the itemID to be put into the cart
+ my $itemID = $cgi->param("itemID") or die "No item specified";
+
+ # getting the current cart's contents:
+ my $cart = $session->param("CART") || [];
+
+ # adding the selected item
+ push @{ $cart }, {
+ itemID => $itemID,
+ name => $products->{$itemID}->[0],
+ price => $products->{$itemID}->[1],
+ };
+
+ # now store the updated cart back into the session
+ $session->param( "CART", $cart );
+
+ # show the contents of the cart
+ return display_cart($cgi, $session);
+ }
+
+
+As you see, things are quite straight-forward this time as well. We're
+accepting three arguments, getting the itemID from the C<itemID> CGI
+parameter, retrieving contents of the current cart from the "CART"
+session parameter, updating the contents with the information we know
+about the item with the C<itemID>, and storing the modifed $cart back to
+"CART" session parameter. When done, we simply display the cart. If
+anything doesn't make sence to you, STOP! Read it over!
+
+Here are the contents for C<display_cart()>, which simply gets the
+shoping cart's contents from the session parameter and generates a list:
+
+ sub display_cart {
+ my ($cgi, $session) = @_;
+
+ # getting the cart's contents
+ my $cart = $session->param("CART") || [];
+ my $total_price = 0;
+ my $RV = q~<table><tr><th>Title</th><th>Price</th></tr>~;
+
+ if ( $cart ) {
+ for my $product ( @{$cart} ) {
+ $total_price += $product->{price};
+ $RV = qq~
+ <tr>
+ <td>$product->{name}</td>
+ <td>$product->{price}</td>
+ </tr>~;
+ }
+
+ } else {
+ $RV = qq~
+ <tr>
+ <td colspan="2">There are no items in your cart
+yet</td>
+ </tr>~;
+ }
+
+ $RV = qq~
+ <tr>
+ <td><b>Total Price:</b></td>
+ <td><b>$total_price></b></td>
+ </tr></table>~;
+
+ return $RV;
+ }
+
+
+A more professional approach would be to take the HTML outside the
+program code by using B<HTML::Template>, in which case the above
+C<display_cart()> will look like:
+
+ sub display_cart {
+ my ($cgi, $session) = @_;
+
+ my $template = new HTML::Template(filename=>"cart.tmpl",
+ associate=>$session,
+ die_on_bad_params=>0);
+ return $template->output();
+
+ }
+
+And respective portion of the html template would be something like:
+
+ <!-- shopping cart starts -->
+ <table>
+ <tr>
+ <th>Title</th><th>Price</th>
+ </tr>
+ <TMPL_LOOP NAME=CART>
+ <tr>
+ <td> <TMPL_VAR NAME=NAME> </td>
+ <td> <TMPL_VAR NAME=PRICE> </td>
+ </tr>
+ </TMPL_LOOP>
+ <tr>
+ <td><b>Total Price:</b></td>
+ <td><b> <TMPL_VAR NAME=TOTAL_PRICE> </td></td>
+ </tr>
+ </table>
+ <!-- shopping cart ends -->
+
+A slight problem in the above template: TOTAL_PRICE doesn't exist. To
+fix this problem we need to introduce a slight modification to our
+C<add_item()>, where we also save the precalculated total price in the
+"total_price" session parameter. Try it yourself.
+
+If you've been following the examples, you shouldn't discover anything
+in the above code either. Let's move to C<remove_item()>. That's what
+the link for removing an item from the shopping cart will look like:
+
+ http://ultracgis.com/cart.cgi?cmd=remove;itemID=1001
+
+ sub remove_item {
+ my ($cgi, $session) = @_;
+
+ # getting the itemID from the CGI parameter
+ my $itemID = $cgi->param("itemID") or return undef;
+
+ # getting the cart data from the session
+ my $cart = $session->param("CART") or return undef;
+
+ my $idx = 0;
+ for my $product ( @{$cart} ) {
+ $product->{itemID} == $itemID or next;
+ splice( @{$cart}, $idx++, 1);
+ }
+
+ $session->param("CART", $cart);
+
+ return display_cart($cgi, $session);
+ }
+
+C<clear_cart()> will get even shorter
+
+ sub clear_cart {
+ my ($cgi, $session) = @_;
+ $session->clear(["CART"]);
+ }
+
+=head1 MEMBERS AREA
+
+=head2 PROBLEM
+
+You want to create an area in the part of your site/application where
+only restricted users should have access to.
+
+=head2 SOLUTION
+
+I have encountered literally dozens of different implementations of this
+by other programmers, none of them perfect. Key properties of such an
+application are reliability, security and no doubt, user-friendliness.
+Consider this receipt not just as a CGI::Session implementation, but
+also a receipt on handling login/authentication routines transparently.
+Your users will love you for it.
+
+So first, let's build the logic, only then we'll start coding. Before
+going any further, we need to agree upon a username/password fields that
+we'll be using for our login form. Let's choose "lg_name" and
+"lg_password" respectively. Now, in our application, we'll always be
+watching out for those two fields at the very start of the program to
+detect if the user submitted a login form or not. Some people tend to
+setup a dedicated run-mode like "_cmd=login" which will be handled
+seperately, but later you'll see why this is not a good idea.
+
+If those two parameters are present in our CGI object, we will go ahead
+and try to load the user's profile from the database and set a special
+session flag "~logged-in" to a true value. If those parameters are
+present, but if the login/password pairs do not match with the ones in
+the database, we leave "~logged-in" untouched, but increment another
+flag "~login-trials" to one. So here is an init() function (for
+initializer) which should be called at the top of the program:
+
+ sub init {
+ my ($session, $cgi) = @_; # receive two args
+
+ if ( $session->param("~logged-in") ) {
+ return 1; # if logged in, don't bother going further
+ }
+
+ my $lg_name = $cgi->param("lg_name") or return;
+ my $lg_psswd=$cgi->param("lg_password") or return;
+
+ # if we came this far, user did submit the login form
+ # so let's try to load his/her profile if name/psswds match
+ if ( my $profile = _load_profile($lg_name, $lg_psswd) ) {
+ $session->param("~profile", $profile);
+ $session->param("~logged-in", 1);
+ $session->clear(["~login-trials"]);
+ return 1;
+
+ }
+
+ # if we came this far, the login/psswds do not match
+ # the entries in the database
+ my $trials = $session->param("~login-trials") || 0;
+ return $session->param("~login-trials", ++$trials);
+ }
+
+
+Syntax for _load_profile() totally depends on where your user profiles
+are stored. I normally store them in MySQL tables, but suppose you're
+storing them in flat files in the following format:
+
+ username password email
+
+Your _load_profile() would look like:
+
+ sub _load_profile {
+ my ($lg_name, $lg_psswd) = @_;
+
+ local $/ = "\n";
+ unless (sysopen(PROFILE, "profiles.txt", O_RDONLY) ) {
+ die "Couldn't open profiles.txt: $!");
+ }
+ while ( <PROFILES> ) {
+ /^(\n|#)/ and next;
+ chomp;
+ my ($n, $p, $e) = split "\s+";
+ if ( ($n eq $lg_name) && ($p eq $lg_psswd) ) {
+ my $p_mask = "x" . length($p);
+ return {username=>$n, password=>$p_mask, email=>$e};
+
+ }
+ }
+ close(PROFILE);
+
+ return undef;
+ }
+
+
+Now regardless of what run mode user is in, you just call the above
+C<init()> method somewhere in the beginning of your program, and if the
+user is logged in properly, you're guaranteed that "~logged-in" session
+flag would be set to true and the user's profile information will be
+available to you all the time from the "~profile" session parameter:
+
+ init($cgi, $session);
+
+ if ( $session->param("~login-trials") >= 3 ) {
+ print error("You failed 3 times in a row.\n" .
+ "Your session is blocked. Please contact us with ".
+ "the details of your action");
+ exit(0);
+
+ }
+
+ unless ( $session->param("~logged-in") ) {
+ print login_page($cgi, $session);
+ exit(0);
+
+ }
+
+In the above example we're using exit() to stop the further processing.
+If you require mod_perl compatibility, you will want some other, more
+graceful way.
+
+To access the user's profile data without accessing the database again,
+you simply do:
+
+ my $profile = $session->param("~profile");
+ print "Hello $profile->{username}, I know it's you. Confess!";
+
+and the user will be terrified :-).
+
+But here is a trick. Suppose, a user clicked on the link with the
+following query_string: "profile.cgi?_cmd=edit", but he/she is not
+logged in. If you're performing the above init() function, the user will
+see a login_page(). What happens after they submit the form with proper
+username/password? Ideally you would want the user to be taken directly
+to "?_cmd=edit" page, since that's the link they clicked before being
+prompted to login, rather than some other say "?_cmd=view" page. To
+deal with this very important usabilit feature, you need to include a
+hiidden field in your login form similar to:
+
+ <INPUT TYPE="hidden" NAME="_cmd" VALUE="$cmd" />
+
+Since I prefer using HTML::Template, that's what I can find in my login
+form most of the time:
+
+ <input type="hidden" name="_cmd" value="<tmpl_var _cmd>">
+
+The above _cmd slot will be filled in properly by just associating $cgi
+object with HTML::Template.
+
+Implementing a "sign out" functionality is even more straight forward.
+Since the application is only checking for "~logged-in" session flag, we
+simply clear the flag when a user click on say "?_cmd=logout" link:
+
+ if ( $cmd eq "logout" ) {
+ $session->clear(["~logged-in"]);
+
+ }
+
+You can choose to clear() "~profile" as well, but wouldn't you want to
+have an ability to greet the user with his/her username or fill out his
+username in the login form next time? This might be a question of
+beliefs. But we believe it's the question of usability. You may also
+choose to delete() the session... agh, let's not argue what is better
+and what is not. As long as you're happy, that's what counts :-). Enjoy!
+
+=head1 SUGGESTIONS AND CORRECTIONS
+
+We tried to put together some simple examples of CGI::Session usage.
+There're litterally hundreds of different exciting tricks one can
+perform with proper session management. If you have a problem, and
+believe CGI::Session is a right tool but don't know how to implement it,
+or, if you want to see some other examples of your choice in this Cook
+Book, just drop us an email, and we'll be happy to work on them as soon
+as this evil time permits us.
+
+Send your questions, requests and corrections to CGI::Session mailing
+list, Cgi-s****@ultra*****.
+
+=head1 AUTHOR
+
+ Sherzod Ruzmetov <sherz****@cpan*****>
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
Index: affelio/extlib/CGI/Session/DB_File.pm
diff -u /dev/null affelio/extlib/CGI/Session/DB_File.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/DB_File.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,168 @@
+package CGI::Session::DB_File;
+
+# $Id: DB_File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+use strict;
+use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+);
+
+use DB_File;
+use File::Spec;
+use Fcntl (':DEFAULT', ':flock');
+
+# Load neccessary libraries below
+
+use vars qw($VERSION $FILE_NAME);
+$FILE_NAME = 'cgisess.db';
+
+$VERSION = '0.1';
+
+sub store {
+ my ($self, $sid, $options, $data) = @_;
+
+ my $storable_data = $self->freeze($data);
+
+ my $args = $options->[1];
+ my $file = File::Spec->catfile($args->{Directory}, $args->{FileName} || $FILE_NAME);
+
+ tie my %db, "DB_File", $file, O_RDWR|O_CREAT, 0644 or die $!;
+ $db{$sid} = $storable_data;
+ untie(%db) or die $!;
+
+ return 1;
+
+}
+
+
+sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ # you will need to retrieve the stored data, and
+ # deserialize it using $self->thaw() method
+
+ my $args = $options->[1];
+ my $file = File::Spec->catfile($args->{Directory}, $args->{FileName} || $FILE_NAME);
+
+ tie my %db, "DB_File", $file, O_RDWR|O_CREAT, 0644 or die $!;
+ my $data = $self->thaw($db{$sid});
+ untie(%db);
+
+ return $data;
+}
+
+
+
+sub remove {
+ my ($self, $sid, $options) = @_;
+
+ # you simply need to remove the data associated
+ # with the id
+
+ my $args = $options->[1];
+ my $file = File::Spec->catfile($args->{Directory}, $args->{FileName} || $FILE_NAME);
+ tie my %db, "DB_File", $file, O_RDWR|O_CREAT, 0644 or die $!;
+ delete $db{$sid};
+ untie(%db) or die $!;
+
+ return 1;
+
+
+}
+
+
+
+sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ # this is called just before session object is destroyed
+}
+
+
+
+
+# $Id: DB_File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+1;
+
+=pod
+
+=head1 NAME
+
+CGI::Session::DB_File - DB_File driver for CGI::Session
+
+=head1 SYNOPSIS
+
+ use CGI::Session;
+ $session = new CGI::Session("driver:DB_File", undef, {Directory=>'/tmp'});
+
+For more details, refer to L<CGI::Session> manual
+
+=head1 DESCRIPTION
+
+CGI::Session::DB_File is a CGI::Session driver to store session data in BerkeleyDB.
+Filename to store the session data is by default 'cgisess.db'. If you want different
+name, you can either specify it with the "FileName" option as below:
+
+ $s = new CGI::Session::DB_File(undef, {Directory=>'/tmp', FileName=>'sessions.db'});
+
+or by setting the value of the $CGI::Session::DB_File::NAME variable before creating
+the session object:
+
+ $CGI::Session::DB_File::NAME = 'sessions.db';
+ $s = new CGI::Session("driver:DB_File", undef, {Directory=>'/tmp'});
+
+The only driver option required, as in the above examples, is "Directory", which tells the
+driver where the session file and lock files should be created.
+
+"FileName" option is also available, but not required.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2001-2002 Sherzod Ruzmetov. All rights reserved.
+
+This library is free software and can be modified and distributed under the same
+terms as Perl itself.
+
+Bug reports should be directed to sherz****@cpan*****, or posted to Cgi-s****@ultra*****
+mailing list.
+
+=head1 AUTHOR
+
+CGI::Session::DB_File is written and maintained by Sherzod Ruzmetov <sherz****@cpan*****>
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
+
+# $Id: DB_File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
Index: affelio/extlib/CGI/Session/Example.pm
diff -u /dev/null affelio/extlib/CGI/Session/Example.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/Example.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,203 @@
+package CGI::Session::Example;
+
+# $Id: Example.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+use strict;
+#use diagnostics;
+use File::Spec;
+use base 'CGI::Application';
+
+
+# look into CGI::Application for the details of setup() method
+sub setup {
+ my $self = shift;
+
+ $self->mode_param(\&parsePathInfo);
+ $self->run_modes(
+ start => 'default',
+ default => 'default',
+ 'dump-session' => \&dump_session,
+ 'params' => \&display_params,
+ );
+
+ # setting up default HTTP header. See the details of query() and
+ # header_props() methods in CGI::Application manpage
+ my $cgi = $self->query();
+ my $session = $self->session();
+ my $sid_cookie = $cgi->cookie($session->name(), $session->id());
+ $self->header_props(-type=>'text/html', -cookie=>$sid_cookie);
+}
+
+
+
+
+
+# this method simply returns CGI::Session object.
+sub session {
+ my $self = shift;
+
+ if ( defined $self->param("_SESSION") ) {
+ return $self->param("_SESSION");
+ }
+ require CGI::Session;
+ my $dsn = $self->param("_SESSION_DSN") || undef;
+ my $options = $self->param("_SESSION_OPTIONS") || {Directory=>File::Spec->tmpdir };
+ my $session = CGI::Session->new($dsn, $self->query, $options);
+ unless ( defined $session ) {
+ die CGI::Session->error();
+ }
+ $self->param(_SESSION => $session);
+ return $self->session();
+}
+
+# parses PATH_INFO and retrieves a portion which defines a run-mode
+# to be executed to display the current page. Refer to CGI::Application
+# manpage for details of run-modes and mode_param() method
+sub parsePathInfo {
+ my $self = shift;
+
+ unless ( defined $ENV{PATH_INFO} ) {
+ return;
+ }
+ my ($cmd) = $ENV{PATH_INFO} =~ m!/cmd/-/([^?]+)!;
+ return $cmd;
+}
+
+
+# see CGI::Application manpage
+sub teardown {
+ my $self = shift;
+
+ my $session = $self->param("_SESSION");
+ if ( defined $session ) {
+ $session->close();
+ }
+}
+
+
+
+
+
+# overriding CGI::Application's load_tmpl() method. It doesn't
+# return an HTML object, but the contents of the HTML template
+sub load_tmpl {
+ my ($self, $filename, $args) = @_;
+
+ # defining a default param set for the templates
+ $args ||= {};
+ my $cgi = $self->query();
+ my $session = $self->session();
+ # making all the %ENV variables available for all the templates
+ map { $args->{$_} = $ENV{$_} } keys %ENV;
+ # making session id available for all the templates
+ $args->{ $session->name() } = $session->id;
+ # making library's version available for all the templates
+ $args->{ VERSION } = $session->version();
+
+ # loading the template
+ require HTML::Template;
+ my $t = new HTML::Template(filename => $filename,
+ associate => [$session, $cgi],
+ vanguard_compatibility_mode => 1);
+ $t->param(%$args);
+ return $t->output();
+}
+
+
+
+sub urlf {
+ my ($self, $cmd) = @_;
+
+ my $sid = $self->session()->id;
+ my $name = $self->session()->name;
+
+ return sprintf("$ENV{SCRIPT_NAME}/cmd/-/%s?%s=%s", $cmd, $name, $sid);
+}
+
+
+
+sub page {
+ my ($self, $body) = @_;
+
+ my %params = (
+ body => $body,
+ url_default => $self->urlf('default'),
+ url_dump => $self->urlf('dump-session'),
+ url_params => $self->urlf('params'),
+ );
+ return $self->load_tmpl('page.html', \%params);
+}
+
+
+
+
+# Application methods
+sub default {
+ my $self = shift;
+
+ my $session = $self->session();
+
+ my $body = $self->load_tmpl('welcome.html');
+
+ return $self->page($body);
+}
+
+
+sub dump_session {
+ my $self = shift;
+
+ my $dmp = $self->session()->dump(undef, 1);
+ return $self->page(sprintf "<pre>%s</pre>", $dmp );
+}
+
+
+sub delete_session {
+ my $self = shift;
+
+ $self->session()->delete();
+ $self->header_type('redirect');
+ $self->header_props(-uri=>$ENV{HTTP_REFERER});
+}
+
+
+sub display_params {
+ my $self = shift;
+
+ my $session = $self->session();
+ my @list = ();
+ for my $name ( $session->param() ) {
+ $name =~ /^_SESSION_/ and next;
+ my $value = $session->param($_);
+ push @list, {name => $name, value=>$value};
+ }
+ my %params = (
+ list => \@list,
+ );
+ my $body = $self->load_tmpl('display-params.html', \%params);
+ return $self->page($body);
+}
+
+
+
+
+
+
+
+
+
+
+1;
+
+__END__
+# Below is stub documentation for your module. You'd better edit it!
+
+=head1 NAME
+
+CGI::Session::Example - Example on using CGI::Session
+
+=head1 DESCRIPTION
+
+STILL NOT COMPLETED. CHECK BACK FOR THE NEXT RELEASE OF CGI::Session.
+
+
+
Index: affelio/extlib/CGI/Session/File.pm
diff -u /dev/null affelio/extlib/CGI/Session/File.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/File.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,190 @@
+package CGI::Session::File;
+
+# $Id: File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+use strict;
+use File::Spec;
+use Fcntl (':DEFAULT', ':flock');
+use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+);
+
+use vars qw($FileName $VERSION);
+
+($VERSION) = '$Revision: 1.1 $' =~ m/Revision:\s*(\S+)/;
+$FileName = 'cgisess_%s';
+
+sub store {
+ my ($self, $sid, $options, $data) = @_;
+
+ $self->File_init($sid, $options);
+ unless ( sysopen (FH, $self->{_file_path}, O_RDWR|O_CREAT|O_TRUNC, 0644) ) {
+ $self->error("Couldn't store $sid into $self->{_file_path}: $!");
+ return undef;
+ }
+ unless (flock(FH, LOCK_EX) ) {
+ $self->error("Couldn't get LOCK_EX: $!");
+ return undef;
+ }
+ print FH $self->freeze($data);
+ unless ( close(FH) ) {
+ $self->error("Couldn't close $self->{_file_path}: $!");
+ return undef;
+ }
+ return 1;
+}
+
+
+sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ $self->File_init($sid, $options);
+
+ # If the session data does not exist, return.
+ unless ( -e $self->{_file_path} ) {
+ return undef;
+ }
+
+ unless ( sysopen(FH, $self->{_file_path}, O_RDONLY) ) {
+ $self->error("Couldn't open $self->{_file_path}: $!");
+ return undef;
+ }
+ unless (flock(FH, LOCK_SH) ) {
+ $self->error("Couldn't lock the file: $!");
+ return undef;
+ }
+ my $data = undef;
+ $data .= $_ while <FH>;
+
+ close(FH);
+ return $self->thaw($data);
+}
+
+
+
+sub remove {
+ my ($self, $sid, $options) = @_;
+
+ $self->File_init($sid, $options);
+ unless ( unlink ( $self->{_file_path} ) ) {
+ $self->error("Couldn't unlink $self->{_file_path}: $!");
+ return undef;
+ }
+ return 1;
+}
+
+
+
+sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ return 1;
+}
+
+
+
+
+sub File_init {
+ my ($self, $sid, $options) = @_;
+
+ my $dir = $options->[1]->{Directory};
+ my $path = File::Spec->catfile($dir, sprintf("$FileName", $sid));
+ $self->{_file_path} = $path;
+}
+
+
+
+
+
+
+# $Id: File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+1;
+
+=pod
+
+=head1 NAME
+
+CGI::Session::File - Default CGI::Session driver
+
+=head1 REVISION
+
+This manual refers to $Revision: 1.1 $
+
+=head1 SYNOPSIS
+
+ use CGI::Session;
+ $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
+
+For more examples, consult L<CGI::Session> manual
+
+=head1 DESCRIPTION
+
+CGI::Session::File is a default CGI::Session driver. Stores the session data
+in plain files. For the list of available methods, consult L<CGI::Session> manual.
+
+Each session is stored in a seperate file. File name is by default formatted as "cgisess_%s",
+where '%s' is replaced with the effective session id. To change file name formatting,
+update $CGI::Session::File::NAME variable. Examples:
+
+ $CGI::Session::File::FileName = 'cgisess_%s.dat'; # with .dat extention
+ $CGI::Session::File::FileName = '%s.session';
+ $CGI::Session::File::FileName = '%CGI-Session-%s.dat'; # old style
+
+The only driver option required is 'Directory', which denotes the location
+session files are stored in.
+
+Example:
+
+ $session = new CGI::Session("driver:File", undef, {Directory=>'some/directory'});
+
+=head1 COPYRIGHT
+
+Copyright (C) 2001-2002 Sherzod Ruzmetov. All rights reserved.
+
+This library is free software and can be modified and distributed under the same
+terms as Perl itself.
+
+Bug reports should be directed to sherz****@cpan*****, or posted to Cgi-s****@ultra*****
+mailing list.
+
+=head1 AUTHOR
+
+CGI::Session::File is written and maintained by Sherzod Ruzmetov <sherz****@cpan*****>
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
+
+
+# $Id: File.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
Index: affelio/extlib/CGI/Session/MySQL.pm
diff -u /dev/null affelio/extlib/CGI/Session/MySQL.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/MySQL.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,239 @@
+package CGI::Session::MySQL;
+
+# $Id: MySQL.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+use strict;
+# Inheriting necessary functionalities from the
+# following libraries. Do not change it unless you know
+# what you are doing
+use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+);
+
+
+# driver specific libraries should go below
+
+use vars qw($VERSION $TABLE_NAME);
+
+($VERSION) = '$Revision: 1.1 $' =~ m/Revision:\s*(\S+)/;
+
+$TABLE_NAME = 'sessions';
+
+########################
+# Driver methods follow
+########################
+
+
+# stores the serialized data. Returns 1 for sucess, undef otherwise
+sub store {
+ my ($self, $sid, $options, $data) = @_;
+
+ my $dbh = $self->MySQL_dbh($options);
+ my $lck_status = $dbh->selectrow_array(qq|SELECT GET_LOCK("$sid", 10)|);
+ unless ( $lck_status == 1 ) {
+ $self->error("Couldn't acquire lock on id '$sid'. Lock status: $lck_status");
+ return undef;
+ }
+
+ $dbh->do(qq|REPLACE INTO $TABLE_NAME (id, a_session) VALUES(?,?)|,
+ undef, $sid, $self->freeze($data));
+
+ return $dbh->selectrow_array(qq|SELECT RELEASE_LOCK("$sid")|);
+}
+
+
+
+# retrieves the serialized data and deserializes it
+sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ # after you get the data, deserialize it using
+ # $self->thaw(), and return it
+ my $dbh = $self->MySQL_dbh($options);
+ my $lck_status = $dbh->selectrow_array(qq|SELECT GET_LOCK("$sid", 10)|);
+ unless ( $lck_status == 1 ) {
+ $self->error("Couldn't acquire lock on is '$sid'. Lock status: $lck_status");
+ return undef;
+ }
+
+ my $data = $dbh->selectrow_array(qq|SELECT a_session FROM $TABLE_NAME WHERE id=?|, undef, $sid);
+ $lck_status = $dbh->selectrow_array(qq|SELECT RELEASE_LOCK("$sid")|);
+ unless ( $lck_status == 1 ) {
+ $self->error("Couldn't release lock of '$sid'. Lock status: $lck_status");
+ return undef;
+ }
+
+ return $self->thaw($data);
+}
+
+
+# removes the given data and all the disk space associated with it
+sub remove {
+ my ($self, $sid, $options) = @_;
+
+ my $dbh = $self->MySQL_dbh($options);
+ my $lck_status = $dbh->selectrow_array(qq|SELECT GET_LOCK("$sid", 10)|);
+ unless ( $lck_status == 1 ) {
+ $self->error("Couldn't acquire lock on id '$sid'. Lock status; $lck_status");
+ return undef;
+ }
+
+ $dbh->do(qq|DELETE FROM $TABLE_NAME WHERE id=?|, undef, $sid);
+ $lck_status = $dbh->selectrow_array(qq|SELECT RELEASE_LOCK("$sid")|);
+ unless ( $lck_status == 1 ) {
+ $self->error("Couldn't release lock of '$sid'. Lock status: $lck_status");
+ return undef;
+ }
+
+ return 1;
+}
+
+
+
+
+# called right before the object is destroyed to do cleanup
+sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ my $dbh = $self->MySQL_dbh($options);
+
+ # Call commit if it isn't meant to be autocommited!
+ unless ( $dbh->{AutoCommit} ) {
+ $dbh->commit();
+ }
+
+ if ( $self->{MySQL_disconnect} ) {
+ $dbh->disconnect();
+ }
+
+ return 1;
+}
+
+
+
+
+
+
+sub MySQL_dbh {
+ my ($self, $options) = @_;
+
+ my $args = $options->[1] || {};
+
+ if ( defined $self->{MySQL_dbh} ) {
+ return $self->{MySQL_dbh};
+
+ }
+
+ require DBI;
+
+ $self->{MySQL_dbh} = $args->{Handle} || DBI->connect(
+ $args->{DataSource},
+ $args->{User} || undef,
+ $args->{Password} || undef,
+ { RaiseError=>1, PrintError=>1, AutoCommit=>1 } );
+
+ # If we're the one established the connection,
+ # we should be the one who closes it
+ $args->{Handle} or $self->{MySQL_disconnect} = 1;
+ return $self->{MySQL_dbh};
+
+}
+
+
+
+
+# $Id: MySQL.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+1;
+=pod
+
+=head1 NAME
+
+CGI::Session::MySQL - MySQL driver for CGI::Session
+
+=head1 SYNOPSIS
+
+ use CGI::Session;
+ $session = new CGI::Session("driver:MySQL", undef, {Handle=>$dbh});
+
+For more examples, consult L<CGI::Session> manual
+
+=head1 DESCRIPTION
+
+CGI::Session::MySQL is a CGI::Session driver to store session data in MySQL table.
+To write your own drivers for B<CGI::Session> refere L<CGI::Session> manual.
+
+
+=head1 STORAGE
+
+To store session data in MySQL database, you first need to create a suitable table for it
+with the following command:
+
+ CREATE TABLE sessions (
+ id CHAR(32) NOT NULL UNIQUE,
+ a_session TEXT NOT NULL
+ );
+
+
+You can also add any number of additional columns to the table, but the above "id"
+and "a_session" are required.
+
+If you want to store the session data in other table than "sessions", before creating
+the session object you need to set the special variable B<$CGI::Session::MySQL::TABLE_NAME>
+to the name of the table:
+
+ use CGI::Session;
+
+ $CGI::Session::MySQL::TABLE_NAME = 'my_sessions';
+ $session = new CGI::Session("driver:MySQL", undef, {Handle=>$dbh});
+
+=head1 COPYRIGHT
+
+Copyright (C) 2001, 2002 Sherzod Ruzmetov. All rights reserved.
+
+This library is free software and can be modified and distributed under the same
+terms as Perl itself.
+
+
+=head1 AUTHOR
+
+Sherzod Ruzmetov <sherz****@cpan*****>. All the bug reports should be sent to the author
+to sherz****@cpan*****>
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
+
+
+
+# $Id: MySQL.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
Index: affelio/extlib/CGI/Session/PostgreSQL.pm
diff -u /dev/null affelio/extlib/CGI/Session/PostgreSQL.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/PostgreSQL.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,296 @@
+# CGI::Session::PostgreSQL - PostgreSQL driver for CGI::Session
+#
+# Copyright (C) 2001-2002 Sherzod Ruzmetov, sherz****@cpan*****
+#
+# Copyright (C) 2002 Cosimo Streppone, cosim****@cpan*****
+# This module is based on CGI::Session::MySql module
+# by Sherzod Ruzmetov, original author of CGI::Session modules
+# and CGI::Session::MySQL driver.
+#
+# 2002/12/08 cosim****@cpan*****
+# Initial release
+# 2003/03/01 cosim****@cpan*****
+# Added `FOR UPDATE' sql clauses to enable database row lock management
+#
+# $Id: PostgreSQL.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+package CGI::Session::PostgreSQL;
+
+use strict;
+# Inheriting necessary functionalities from the
+# following libraries. Do not change it unless you know
+# what you are doing
+use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+);
+
+
+# driver specific libraries should go below
+
+use vars qw($VERSION $TABLE_NAME);
+
+($VERSION) = '$Revision: 1.1 $' =~ m/Revision:\s*(\S+)/;
+$TABLE_NAME = 'sessions';
+
+########################
+# Driver methods follow
+########################
+
+
+# stores the serialized data. Returns 1 for sucess, undef otherwise
+sub store {
+
+ my ($self, $sid, $options, $data) = @_;
+ my $dbh = $self->PostgreSQL_dbh($options);
+ my $db_data;
+
+ eval {
+
+ ($db_data) = $dbh->selectrow_array(
+ ' SELECT a_session FROM '.$TABLE_NAME.
+ ' WHERE id = '.$dbh->quote($sid).' FOR UPDATE'
+ );
+
+ };
+
+ if( $@ ) {
+ $self->error("Couldn't acquire data on id '$sid'");
+ return undef;
+ }
+
+ eval {
+
+ if( $db_data ) {
+
+#warn('do update sid='.$sid.' data='.$self->freeze($data));
+
+ $dbh->do(
+ ' UPDATE '.$TABLE_NAME.
+ ' SET a_session='.$dbh->quote($self->freeze($data)).
+ ' WHERE id='.$dbh->quote($sid)
+ );
+
+ } else {
+
+#warn('do insert sid='.$sid.' data='.$self->freeze($data));
+
+ $dbh->do(
+ 'INSERT INTO '.$TABLE_NAME.' (id,a_session) '.
+ 'VALUES ('.$dbh->quote($sid).', '.$dbh->quote($self->freeze($data)).')'
+ );
+
+ }
+
+ };
+
+ if( $@ ) {
+ $self->error("Error in session update on id '$sid'. $@");
+ warn("Error in session update on id '$sid'. $@");
+ return undef;
+ }
+
+ return 1;
+}
+
+
+
+# retrieves the serialized data and deserializes it
+sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ # after you get the data, deserialize it using
+ # $self->thaw(), and return it
+ my $dbh = $self->PostgreSQL_dbh($options);
+ my $data;
+ eval {
+ $data = $dbh->selectrow_array(
+ ' SELECT a_session FROM '.$TABLE_NAME.
+ ' WHERE id = '.$dbh->quote($sid)
+ );
+ };
+ if( $@ ) {
+ $self->error("Couldn't acquire data on id '$sid'");
+ return undef;
+ }
+ return $self->thaw($data);
+}
+
+
+# removes the given data and all the disk space associated with it
+sub remove {
+ my ($self, $sid, $options) = @_;
+
+ my $dbh = $self->PostgreSQL_dbh($options);
+ my $data;
+ eval {
+ $data = $dbh->selectrow_array(
+ ' SELECT a_session FROM '.$TABLE_NAME.
+ ' WHERE id = '.$dbh->quote($sid).' FOR UPDATE'
+ );
+ };
+ if( $@ ) {
+ $self->error("Couldn't acquire data on id '$sid'");
+ return undef;
+ }
+
+ eval {
+ $dbh->do(
+ 'DELETE FROM '.$TABLE_NAME.' WHERE id = '.$dbh->quote($sid)
+ );
+ };
+ if( $@ ) {
+ $self->error("Couldn't release lock of '$sid'");
+ return undef;
+ }
+
+ return 1;
+
+}
+
+
+
+
+# Called right before the object is destroyed to do cleanup
+sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ my $dbh = $self->PostgreSQL_dbh($options);
+
+ # Call commit if it isn't meant to be autocommited!
+ unless ( $dbh->{AutoCommit} ) {
+ $dbh->commit();
+ }
+
+ if ( $self->{PostgreSQL_disconnect} ) {
+ $dbh->disconnect();
+ }
+
+ return 1;
+}
+
+
+sub PostgreSQL_dbh {
+ my ($self, $options) = @_;
+
+ my $args = $options->[1] || {};
+
+ if ( defined $self->{PostgreSQL_dbh} ) {
+ return $self->{PostgreSQL_dbh};
+
+ }
+
+ if ( defined $args->{TableName} ) {
+ $TABLE_NAME = $args->{TableName};
+ }
+
+ require DBI;
+
+ $self->{PostgreSQL_dbh} = $args->{Handle} || DBI->connect(
+ $args->{DataSource},
+ $args->{User} || undef,
+ $args->{Password} || undef,
+ { RaiseError=>1, PrintError=>1, AutoCommit=>1 } );
+
+ # If we're the one established the connection,
+ # we should be the one who closes it
+ $args->{Handle} or $self->{PostgreSQL_disconnect} = 1;
+
+ return $self->{PostgreSQL_dbh};
+
+}
+
+
+
+
+# $Id: PostgreSQL.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+1;
+
+=pod
+
+=head1 NAME
+
+CGI::Session::PostgreSQL - PostgreSQL driver for CGI::Session
+
+=head1 SYNOPSIS
+
+ use CGI::Session;
+ $session = new CGI::Session("driver:PostgreSQL", undef, {Handle=>$dbh});
+
+For more examples, consult L<CGI::Session> manual
+
+=head1 DESCRIPTION
+
+CGI::Session::PostgreSQL is a CGI::Session driver to store session data in a PostgreSQL table.
+To write your own drivers for B<CGI::Session> refere L<CGI::Session> manual.
+
+=head1 STORAGE
+
+To store session data in PostgreSQL database, you first need
+to create a suitable table for it with the following command:
+
+ CREATE TABLE sessions (
+ id CHAR(32) NOT NULL,
+ a_session TEXT NOT NULL
+ );
+
+
+You can also add any number of additional columns to the table,
+but the above "id" and "a_session" are required.
+If you want to store the session data in other table than "sessions",
+you will also need to specify B<TableName> attribute as the
+first argument to new():
+
+ use CGI::Session;
+
+ $session = new CGI::Session("driver:PostgreSQL", undef,
+ {Handle=>$dbh, TableName=>'my_sessions'});
+
+Every write access to session records is done through PostgreSQL own row locking mechanism,
+enabled by `FOR UPDATE' clauses in SELECTs or implicitly enabled in UPDATEs and DELETEs.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2002 Cosimo Streppone. All rights reserved.
+
+This library is free software and can be modified and distributed
+under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+Cosimo Streppone <cosim****@cpan*****>, heavily based on the CGI::Session::MySQL
+driver by Sherzod Ruzmetov, original author of CGI::Session.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut
Index: affelio/extlib/CGI/Session/Tutorial.pm
diff -u /dev/null affelio/extlib/CGI/Session/Tutorial.pm:1.1
--- /dev/null Tue Jun 21 22:11:14 2005
+++ affelio/extlib/CGI/Session/Tutorial.pm Tue Jun 21 22:11:14 2005
@@ -0,0 +1,506 @@
+# $Id: Tutorial.pm,v 1.1 2005/06/21 13:11:14 slash5234 Exp $
+
+package CGI::Session::Tutorial;
+
+use vars ('$VERSION');
+
+($VERSION) = '$Revision: 1.1 $' =~ m/Revision:\s*(\S+)/
+
+1;
+
+__END__;
+
+=pod
+
+=head1 NAME
+
+Tutorial - extended CGI::Session manual
+
+=head1 STATE MAINTANANCE OVERVIEW
+
+Since HTTP is a stateless protocol, each subsequent click to a web site is treated as new by the web server. The server does not relate the visits with previous one, thus all the state information from the previous requests are lost. This makes creating such applications as shopping carts, login/authentication routines, secure restricted services in the web impossible. So people had to do something against this despair situation HTTP was putting us in.
+
+For our rescue come such technologies as HTTP Cookies and QUERY_STRINGs that help us save the users' session for a certain period. Since cookies and query_strings alone cannot take us too far B<RFC 2965, Section 5, "Implementation Limitations">, several other libraries/technologies have been developed to extend their capabilities and promise a more reliable and a more persistent system. CGI::Session is one of them.
+
+Before we discuss this library, let's look at some alternative solutions.
+
+=head2 COOOKIE
+
+Cookie is a piece of text-information that a web server is entitled to place in the user's hard disk, assuming a user agent (i.e.. Web Browser) is compatible with the specification. After the cookie being placed, user agents are required to send these cookies back to the server as part of the HTTP request. This way the server application ( CGI ) will have a way of relating previous requests by the same user agent, thus overcoming statelessness of HTTP.
+
+Although cookies seem to be promising solutions for the statelessness of HTTP, they do carry certain limitations, such as limited number of cookies per domain and per user agent and limited size on each cookie. User Agents are required to store at least 300 cookies at a time, 20 cookies per domain and allow 4096 bytes of storage for each cookie. They also rise several Privacy and Security concerns, the lists of which can be found on the sections 6-B<"Privacy" and 7-"Security Considerations"> of B<RFC 2965> respectively.
+
+=head2 QUERY STRING
+
+Query string is a string appended to URL following a question mark (?) such as:
+
+ http://my.dot.com/login.cgi?user=sherzodr&password=topSecret
+
+As you probably guessed already, it can also help you to pass state information from a click to another, but how secure is it do you think? Considering these URLs tend to get cached by most of the user agents and also logged in the servers access log, to which everyone can have access to, it is not secure.
+
+=head2 HIDDEN FIELDS
+
+Hidden field is another alternative to using query strings and they come in two flavors: hidden fields used in POST methods and the ones in GET. The ones used in GET methods will turn into a true query string once submitted, so all the disadvantages of QUERY_STRINGs do apply. Although POST requests do not have limitations of its sister-GET, the pages that hold them do the cached by web browser, and are available within the source code of the page (obviously). They also become unwieldily to manage when one has oodles of state information to keep track of ( for instance, a shopping cart or an advanced search engine).
+
+Query strings and hidden fields are also lost easily by closing the browser, or by clicking the browser's "Back" button.
+
+=head2 SERVER SIDE SESSION MANAGEMENT
+
+This technique is built upon the aforementioned technologies plus a server-side storage device, which saves the state data for a particular session. Each session has a unique id associated with the data in the server. This id is also associated with the user agent in either the form of a cookie, a query_string parameter, a hidden field or all at the same time.
+
+Advantages:
+
+=over 4
+
+=item *
+
+We no longer need to depend on the User Agent constraints in cookie amounts and sizes
+
+=item *
+
+Sensitive data like user's username, email address, preferences and such no longer need to be traveling across the network at each request (which is the case with query strings, cookies and hidden_fields). Only thing that travels across the network is the unique id generated for the session ("ID-1234", for instance), which should make no sense to bad guys whatsoever.
+
+=item *
+
+User will not have sensitive data stored in his computer in an unsecured plain text format (which is a cookie file).
+
+=item *
+
+It's possible to handle very big and even complex (in-memory) data structures transparently.
+
+=back
+
+That's what CGI::Session is all about - implementing server side session management. Now is a very good time to get the feet wet.
+
+=head1 PROGRAMMING STYLE
+
+Server side session management system might be seeming awfully convoluted if you have never dealt with it. Fortunately, with CGI::Session this cumbersome task can be achieved in much elegent way, all the complexity being handled by the library transparently. This section of the manual can be treated as an introductory tutorial to both logic behind session management, and to CGI::Session programming style.
+
+=head1 WHAT YOU NEED TO KNOW FIRST
+
+Before you start using the library, you will need to decide where and how you want the session data to be stored in disk. In other words, you will need to tell what driver to use. You can choose either of "File", "DB_File" and "MySQL" drivers, which are shipped with the distribution by default. Examples in this document will be using "File" driver exclusively to make sure the examples are accessible in all machines with the least requirements. To do this, we create the session object like so:
+
+ use CGI::Session;
+ $session = new CGI::Session("driver:File", $cgi, {Directory=>'/tmp'});
+
+The first argument is called Data Source Name (DSN in short). If it's undef, the library will use the default driver, which is "File". So instead of being explicit about the driver as in the above example, we could simply say:
+
+ $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
+
+and we're guaranteed it will fall back to default settings.
+
+The second argument is session id to be initialized. If it's undef, it will force CGI::Session to create a new session. Instead of passing a session id, you can also pass a CGI.pm object, or any other object that can implement either of cookie() or param() methods. In this case, the library will try to retrieve the session id from either B<CGISESSID> cookie or B<CGISESSID> CGI parameter (query string)
+
+The third argument should be in the form of hashref. This will be used by specific CGI::Session driver only. For the list of all the available attributes, consult respective CGI::Session driver. If you want to write a code
+which is expected to run in various operating systems, and want to reference that particular system's
+temporary folder, use tmpdir() method documented in File::Spec:
+
+ $session = new CGI::Session(undef, $cgi, {Directory=>File::Spec->tmpdir});
+
+Following drivers are available:
+
+=over 4
+
+=item *
+
+L<File|CGI::Session::File> - default driver for storing session data in plain files. Full name: B<CGI::Session::File>
+
+=item *
+
+L<DB_File|CGI::Session::DB_File> - for storing session data in BerkelyDB. Requires: L<DB_File>. Full name: B<CGI::Session::DB_File>
+
+=item *
+
+L<MySQL|CGI::Session::MySQL> - for storing session data in MySQL tables. Requires L<DBI|DBI> and L<DBD::mysql|DBD::mysql>. Full name: B<CGI::Session::MySQL>
+
+=back
+
+Note: You can also write your own driver for the library. Consult respective
+section of this manual for details.
+
+=head1 CREATING NEW SESSION
+
+To generate a brand new session for a user, just pass an undefined value as the second argument to the constructor - new():
+
+ $session = new CGI::Session("driver:File", undef, {Directory=>"/tmp"});
+
+Directory refers to a place where the session files and their locks will be stored in the form of separate files. When you generate the session object, as we did above, you will have:
+
+=over 4
+
+=item 1
+
+Session ID generated for you and
+
+=item 2
+
+Storage file associated with the id in the directory you specified.
+
+=back
+
+From now on, in case you want to access the newly generated session id just do:
+
+ $sid = $session->id();
+
+It returns a string something similar to B<a983c8302e7a678a2e53c65e8bd3316> which you can now send as a cookie or use as a query string or in your forms' hidden fields. Using standard L<CGI> library we can send the session id as a cookie to the user's browser like so:
+
+ $cookie = $cgi->cookie(CGISESSID => $session->id);
+ print $cgi->header( -cookie=>$cookie );
+
+If anything in the above example doesn't make sense, please consult L<CGI> for the details.
+
+=head2 INITIALIZING EXISTING SESSIONS
+
+When a user clicks another link or re-visits the site after a short while should we be creating a new session again? Absolutely not. This would defeat the whole purpose of state maintenance. Since we already send the id as a cookie, all we need is to pass that id as the seconds argument while creating a session object:
+
+ $sid = $cgi->cookie("CGISESSID") || undef;
+ $session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
+
+The above syntax will first try to initialize an existing session data, if it fails ( if the session doesn't exist ) creates a new session: just what we want. But what if the user doesn't support cookies? In that case we would need to append the session id to all the urls as a query string, and look for them in addition to cookie:
+
+ $sid = $cgi->cookie('CGISESSID') || $cgi->param('CGISESSID') || undef;
+ $session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
+
+Assuming you have CGI object handy, you can minimize the above two lines into one:
+
+ $session = new CGI::Session(undef, $cgi, {Directory=>"/tmp"});
+
+If you pass an object, instead of a string as the second argument, as we did above, CGI::Session will try to retrieve the session id from either the cookie or query string and initialize the session accordingly. Name of the cookie and query string parameters are assumed to be B<CGISESSID> by default. To change this setting, you will need to invoke C<name()> class method on either CGI::Session or its object:
+
+ CGI::Session->name("MY_SID");
+ # or
+ $session->name("MY_SID");
+
+ $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
+
+=head2 STORING DATA IN THE SESSION
+
+To store a single variable in the object use C<param()> method:
+
+ $session->param("my_name", $name);
+
+You can use C<param()> method to store complex data such as arrays, hashes, objects and so forth. While storing arrays and hashes, make sure to pass them as a reference:
+
+ @my_array = ("apple", "grapes", "melon", "casaba");
+ $session->param("fruits", \@my_array);
+
+You can store objects as well:
+
+ $session->param("cgi", $cgi); # stores CGI.pm object
+
+Sometimes you wish there was a way of storing all the CGI parameters in the session object. You would start dreaming of this feature after having to save dozens of query parameters from each form element to your session object. Consider the following syntax:
+
+ $session->save_param($cgi, ["keyword", "category", "author", "orderby"]);
+
+save_param() makes sure that all the above CGI parameters get saved in the session object. It's the same as saying:
+
+ $session->param("keyword", $cgi->param("keyword"));
+ $session->param("category", $cgi->param("category"));
+ # etc... for all the form elements
+
+In case you want to save all the CGI parameters. Just omit the second argument to C<save_param()>:
+
+ $session->save_param($cgi);
+
+The above syntax saves all the available/accessible CGI parameters
+
+=head2 ACCESSING STORED DATA
+
+There's no point of storing data if you cannot access it. You can access stored session data by using the same C<param()> method you once used to store them:
+
+ $name = $session->param("my_name");
+
+Above form of param() retrieves session parameter previously stored as "my_name". To retrieve previously stored @my_array:
+
+ $my_array = $session->param("fruits");
+
+It will return a reference to the array, and can be dereferenced as @{$my_array}.
+
+Very frequently, you may find yourself having to create a pre-filled and pre-selected forms, like radio buttons, checkboxes and drop down menus according to the user's preferences or previous action. With text and textareas it's not a big deal: you can simply retrieve a single parameter from the session and hardcode the value into the text field. But how would you do it when you have a group of radio buttons, checkboxes and scrolling lists? For this purpose, CGI::Session provides load_param() method, which loads given session parameters to a CGI object (assuming they have been previously saved with save_param() method or alternative):
+
+ $session->load_param($cgi, ["fruits"]);
+
+Now you can use CGI.pm to generate those preselected checkboxes:
+
+ print $cgi->checkbox_group(fruits=>['apple', 'banana', 'appricot']);
+
+If you're making use of HTML::Template to separate the code from the skins, you can as well associate CGI::Session object with HTML::Template and access all the parameters from within HTML files. We love this trick!
+
+ $template = new HTML::Template(filename=>"some.tmpl", associate=>$session);
+ print $template->output();
+
+Assuming the session object stored "first_name" and "email" parameters while being associated with HTML::Template, you can access those values from within your "some.tmpl" file now:
+
+ Hello <a href="mailto:<TMPL_VAR email>"> <TMPL_VAR first_name> </a>!
+
+For more tricks with HTML::Template, please refer to the library's manual (L<HTML::Template>) and L<CGI Session CookBook|CGI::Session::CookBook>.
+
+=head2 CLOSING THE SESSION
+
+Normally you don't have to close the session explicitly. It gets closed when your program terminates or session object goes out of scope. However in some few instances you might want to close the session explicitly by calling CGI::Session's C<close()> method or undefining the object. What is closing all about - you'd ask. While session is active, updates to session object doesn't get stored in the disk right away. It stores them in the memory until you either choose to flush the buffer by calling C<flush()> method or destroy the session object by either terminating the program or calling close() method explicitly.
+
+In some circumstances you might want to close the session but at the same time don't want to terminate the process for a while. Might be the case with GUI and in daemon applications. In this case close() is what you want. Note: we prefer simpl undefing the session rather than calling close() method. close() is less efficient):
+
+ undef($session);
+
+If you want to keep the session object but for any reason want to synchronize the data in the buffer with the one in the disk, C<flush()> method is what you need.
+
+Note: close() calls flush() as well. So there's no need to call flush() before calling close()
+
+=head2 CLEARING SESSION DATA
+
+You store session data, you access session data and at some point you will want to clear certain session data, if not all. For this purpose CGI::Session provides C<clear()> method which optionally takes one argument as an arrayref indicating which session parameters should be deleted from the session object:
+
+ $session->clear(["~logged-in", "email"]);
+
+Above line deletes "~logged-in" and "email" session parameters from the session. And next time you say:
+
+ $email = $session->param("email");
+
+it returns undef. If you omit the argument to C<clear()>, be warned that all the session parameters you ever stored in the session object will get deleted. Note that it does not delete the session itself. Session stays open and accessible. It's just the parameters you stored in it gets deleted
+
+=head2 DELETING A SESSION
+
+If there's a start there's an end. If session could be created, it should be possible to delete it from the disk for good:
+
+ $session->delete();
+
+The above call to C<delete()> deletes the session from the disk for good. Do not confuse it with C<clear()>, which only clears certain session parameters but keeps the session open.
+
+=head2 EXPIRATION
+
+CGI::Session also provides limited means to expire session data. Expiring session is the same as deleting it via delete(), but deletion takes place automaticly. To expire a session, you need to tell the library how long the session would be valid after the last access time. When that time is met, CGI::Session refuses to retrieve the session. It deletes the session and returns a brand new one. To assign expiration ticker for a session, use the expire() method:
+
+ $session->expire(3600); # expire after 3600 seconds
+ $session->expire('+1h'); # expire after 1 hour
+ $session->expire('+15m'); # expire after 15 minutes
+ $session->expire('+1M'); # expire after a month and so on.
+
+But sometimes, it makes perfect sense to expire a certain session parameter, instead of the whole session. The author usually does this in his login/authentication enabled sites, where after the user logs in successfully, sets a "_logged_in" flag to true, and assigns an expiration ticker on that flag to something like 30 minutes. It means, after 30 idle minutes CGI::Session will clear() "_logged_in" flag, indicating the user should log in over again. I aggree, the same effect can be achieved by simply expiring() the session itself, but in thise we would loose other session parameters, such as user's shopping cart, session-preferences and the like.
+
+This feature can also be used to simulate layered security/authentication, such as, you can keep the user's access to his/her personal profile information for as long as 10 idle hours after successful login, but expire his/her access to his credit card information after 10 idle minutes. To achieve this effect, we will use expire() method again, but with a slightly different syntax:
+
+ $session->expire(_profile_access, '+10h');
+ $session->expire(_cc_access, '+10m');
+
+With the above syntax, the person will still have access to his personal information even after 5 idle hours. But when he tries to access or update his/her credit card information, he may be displayed a "login again, please" screen.
+
+This concludes our discussion of CGI::Session programming style for now (at least till the new releases of the library ). The rest of the manual covers some L<"SECUIRITY"> issues and L<"DRIVER SPECIFICATIONS"> for those want to implement their own drivers or understand the library architecture.
+
+=head1 SECURITY
+
+"How secure is using CGI::Session?", "Can others hack down people's sessions using another browser if they can get the session id of the user?", "Are the session ids guessable?" are the questions I find myself answering over and over again.
+
+=head2 STORAGE
+
+Security of the library does in many aspects depend on the implementation. After making use of this library, you no longer have to send all the information to the user's cookie except for the session id. But, you still have to store the data in the server side. So another set of questions arise, can an evil person have access to session data in your server, even if they do, can they make sense out of the data in the session file, and even if they can, can they reuse the information against a person who created that session. As you see, the answer depends on yourself who is implementing it.
+
+First rule of thumb, do not save the users' passwords or other sensitive data in the session. If you can persuade yourself that this is necessary, make sure that evil eyes don't have access to session files in your server. If you're using RDBMS driver such as MySQL, the database will be protected with a username/password pair. But if it will be storing in the file system in the form of plain files, make sure no one except you can have access to those files.
+
+Default configuration of the driver makes use of Data::Dumper class to serialize data to make it possible to save it in the disk. Data::Dumper's result is a human readable data structure, which if opened, can be interpreted against you. If you configure your session object to use either Storable or FreezeThaw as a serializer, this would make more difficult for bad guys to make sense out of session data. But don't use this as the only precaution for security. Since evil fingers can type a quick program using Storable or FreezeThaw which deciphers that session file very easily.
+
+Also, do not allow sick minds to update the contents of session files. Of course CGI::Session makes sure it doesn't happen, but your cautiousness does no harm either.
+
+Do not keep sessions open with sensitive information for very long period. This will increase the possibility that some bad guy may have someone's valid session id at a given time (acquired somehow).
+
+ALWAYS USE "-ip-match" SWITCH!!!
+
+Read on for the details of "-ip-match".
+
+=head2 SESSION IDs
+
+Session ids are not easily guessable (unless you're using Incr Id generator)! Default configuration of CGI::Session uses Digest::MD5 which takes process id, time in seconds since epoch and a random number, generates a 32 character long digest out of it. Although this string cannot be guessable by others, if they find it out somehow, can they use this identifier against the other person?
+
+Consider the scenario, where you just give someone either via email or an instant messaging a link to your online-account profile, where you're currently logged in. The URL you give to that person contains a session id as part of a query string. If the site was initializing the session solely using query string parameter, after clicking on that link that person now appears to that site as you, and might have access to all of your private data instantly. How scary and how unwise implementation. And what a poor kid who didn't know that pasting URLs with session ids could be an accident waiting to happen.
+
+Even if you're solely using cookies as the session id transporters, it's not that difficult to plant a cookie in the cookie file with the same id and trick the web browser to send that particular session id to the server. So key for security is to check if the person who's asking us to retrieve a session data is indeed the person who initially created the session data. CGI::Session helps you to watch out for such cases by enabling "-ip_match" switch while "use"ing the library:
+
+ use CGI::Session qw/-ip-match/;
+
+or alternatively, setting $CGI::Session::IP_MATCH to a true value, say to 1. This makes sure that before initializing a previously stored session, it checks if the ip address stored in the session matches the ip address of the user asking for that session. In which case the library returns the session, otherwise it dies with a proper error message.
+
+=head1 DRIVER SPECIFICATIONS
+
+This section is for driver authors who want to implement their own storing mechanism for the library. Those who enjoy sub-classing stuff should find this section useful as well. Here we discuss the architecture of CGI::Session and its drivers.
+
+=head2 LIBRARY OVERVIEW
+
+Library provides all the base methods listed in the L<METHODS> section. The only methods CGI::Session doesn't bother providing are the ones that need to deal with writing the session data in the disk, retrieving the data from the disk, and deleting the data. These are the methods specific to the driver, so that's where they should belong.
+
+In other words, driver is just another Perl library which uses CGI::Session as a base class, and provides several additional methods that deal with disk access.
+
+=head2 SERIALIZATION
+
+Before getting to driver specs, let's talk about how the data should be stored. When flush() is called, or the program terminates, CGI::Session asks a driver to store the data somewhere in the disk, and passes the data in the form of a hash reference. Then it's the driver's obligation to serialize the data so that it can be stored in the disk.
+
+Although you are free to implement your own serializing engine for your driver, CGI::Session distribution comes with several libraries you can inherit from and call freeze() method on the object to serialize the data and store it. Those libraries are:
+
+=over 4
+
+=item L<CGI::Session::Serialize::Default|CGI::Session::Serialize::Default>
+
+=item L<CGI::Session::Serialize::Storable|CGI::Session::Serialize::Storable>
+
+=item L<CGI::Session::Serialize::FreezeThaw|CGI::Session::Serialize::FreezeThaw>
+
+=back
+
+Example:
+
+ # $data is a hashref that needs to be stored
+ my $storable_data = $self->freeze($data)
+
+$storable_data can now be saved in the disk safely.
+
+When the driver is asked to retrieve the data from the disk, that serialized data should be accordingly de-serialized. The aforementioned serializers also provides thaw() method, which takes serialized data as the first argument and returns Perl data structure, as it was before saved. Example:
+
+ my $hashref = $self->thaw($stored_data);
+
+=head2 DRIVER METHODS
+
+Driver is just another Perl library, which uses CGI::Session as a base class and is required to provide the following methods:
+
+=over 4
+
+=item C<retrieve($self, $sid, $options)>
+
+retrieve() is called by CGI::Session with the above 3 arguments when it's asked to retrieve the session data from the disk. $self is the session object, $sid is the session id, and $options is the list of the arguments passed to new() in the form of a hashref. Method should return un-serialized session data, or undef indicating the failure. If an error occurs, instead of calling die() or croak(), we suggest setting the error message to error() and returning undef:
+
+ unless ( sysopen(FH, $options->{FileName}, O_RDONLY) ) {
+ $self->error("Couldn't read from $options->{FileName}: $!");
+ return undef;
+ }
+
+If the driver detects that it's been asked for a non-existing session, it should not generate any error message, but simply return undef. This will signal CGI::Session to create a new session id.
+
+=item C<store($self, $sid, $options, $data)>
+
+store() is called by CGI::Session when session data needs to be stored. Data to be stored is passed as the third argument to the method, and is a reference to a hash. Should return any true value indicating success, undef otherwise. Error message should be passed to error().
+
+=item C<remove($self, $sid, $options)>
+
+remove() called when CGI::Session is asked to remove the session data from the disk via delete() method. Should return true indicating success, undef otherwise, setting the error message to error()
+
+=item C<teardown($self, $sid, $options)>
+
+called when session object is about to get destroyed, either explicitly via close() or implicitly when the program terminates
+
+=back
+
+=head2 GENERATING ID
+
+CGI::Session also requires the driver to provide a generate_id() method, which returns an id for a new session. Again, you are welcome to re-invent your own wheel, but note, that CGI::Session distribution comes with couple of id generating libraries that provide you with generate_id(). You should simply inherit from them. Following ID generators are available:
+
+=over 4
+
+=item L<CGI::Session::ID::MD5|CGI::Session::ID::MD5>
+
+=item L<CGI::Session::ID::Incr|CGI::Session::ID::Incr>
+
+=back
+
+Refer to their respective manuals for more details.
+
+In case you want to have your own style of ids, you can define a generate_id() method explicitly without inheriting from the above libraries. Or write your own B<CGI::Session::ID::YourID> library, that simply defines "generate_id()" method, which returns a session id, then give the name to the constructor as part of the DSN:
+
+ $session = new CGI::Session("id:YourID", undef, {Neccessary=>Attributes});
+
+=head2 BLUEPRINT
+
+Your CGI::Session distribution comes with a Session/Blueprint.pm file
+which can be used as a starting point for your driver:
+
+ package CGI::Session::BluePrint;
+
+ use strict;
+ use base qw(
+ CGI::Session
+ CGI::Session::ID::MD5
+ CGI::Session::Serialize::Default
+ );
+
+ # Load neccessary libraries below
+
+ use vars qw($VERSION);
+
+ $VERSION = '0.1';
+
+ sub store {
+ my ($self, $sid, $options, $data) = @_;
+
+ my $storable_data = $self->freeze($data);
+
+ #now you need to store the $storable_data into the disk
+ }
+
+ sub retrieve {
+ my ($self, $sid, $options) = @_;
+
+ # you will need to retrieve the stored data, and
+ # deserialize it using $self->thaw() method
+ }
+
+ sub remove {
+ my ($self, $sid, $options) = @_;
+
+ # you simply need to remove the data associated
+ # with the id
+ }
+
+
+
+ sub teardown {
+ my ($self, $sid, $options) = @_;
+
+ # this is called just before session object is destroyed
+ }
+
+ 1;
+
+ __END__;
+
+
+After filling in the above blanks, you can do:
+
+ $session = new CGI::Session("driver:MyDriver", $sid, {Option=>"Value"});
+
+and follow CGI::Session manual.
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2002 Sherzod Ruzmetov. All rights reserved.
+
+This library is free software. You can modify and distribute it under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+Sherzod Ruzmetov <sherz****@cpan*****>. Suggestions, feedbacks and patches are welcome.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CGI::Session|CGI::Session> - CGI::Session manual
+
+=item *
+
+L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
+
+=item *
+
+B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
+
+=item *
+
+L<CGI|CGI> - standard CGI library
+
+=item *
+
+L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
+
+=back
+
+=cut