1package CGI::Session;
2
3# $Id: Session.pm,v 1.3 2005/11/08 14:31:58 takezoe Exp $
4
5
6use strict;
7#use diagnostics;
8use Carp;
9use CGI::Session::File;
10#use AutoLoader 'AUTOLOAD';
11
12use vars qw($VERSION $REVISION $errstr $IP_MATCH $NAME $API_3 $FROZEN);
13
14($REVISION) = '$Revision: 1.3 $' =~ m/Revision:\s*(\S+)/;
15$VERSION    = '3.94';
16$NAME       = 'CGISESSID';
17
18# import() - we do not import anything into the callers namespace, however,
19# we enable the user to specify hooks at compile time
20sub import {
21    my $class = shift;
22    @_ or return;
23    for ( my $i=0; $i < @_; $i++ ) {
24        $IP_MATCH   = ( $_[$i] eq '-ip_match'   ) and next;
25        $API_3      = ( $_[$i] eq '-api3'       ) and next;
26        $FROZEN     = ( $_[$i] eq '-frozen'     ) and next;
27    }
28}
29
30
31# Session _STATUS flags
32sub SYNCED   () { 0 }
33sub MODIFIED () { 1 }
34sub DELETED  () { 2 }
35
36
37# new() - constructor.
38# Returns respective driver object
39sub new {
40    my $class = shift;
41    $class = ref($class) || $class;
42
43    my $self = {
44        _OPTIONS    => [ @_ ],
45        _DATA       => undef,
46        _STATUS     => MODIFIED,
47        _API3       => { },
48		_IS_NEW		=> 0, # to Chris Dolan's request
49    };
50
51    if ( $API_3 || (@_ == 3 ) ) {
52        return $class->api_3(@_);
53    }
54
55    bless ($self, $class);
56    $self->_validate_driver() && $self->_init() or return;
57    return $self;
58}
59
60
61
62
63
64
65
66
67
68
69sub api_3 {
70    my $class = shift;
71    $class = ref($class) || $class;
72
73    my $self = {
74        _OPTIONS    => [ $_[1], $_[2] ],
75        _DATA       => undef,
76        _STATUS     => MODIFIED,
77        _API_3      => {
78            DRIVER      => 'File',
79            SERIALIZER  => 'Default',
80            ID          => 'MD5',
81        },
82	    _IS_NEW	    => 0, # to Chris Dolan's request
83    };
84
85    # supporting DSN namme abbreviations:
86    require Text::Abbrev;
87    my $dsn_abbrev = Text::Abbrev::abbrev('driver', 'serializer', 'id');
88
89    if ( defined $_[0] ) {
90        my @arg_pairs = split (/;/, $_[0]);
91        for my $arg ( @arg_pairs ) {
92            my ($key, $value) = split (/:/, $arg) or next;
93            $key = $dsn_abbrev->{$key};
94            $self->{_API_3}->{ uc($key) } = $value || $self->{_API_3}->{uc($key)};
95        }
96    }
97
98    my $driver = "CGI::Session::$self->{_API_3}->{DRIVER}";
99    eval "require $driver" or die $@;
100
101    my $serializer = "CGI::Session::Serialize::$self->{_API_3}->{SERIALIZER}";
102    eval "require $serializer" or die $@;
103
104    my $id = "CGI::Session::ID::$self->{_API_3}->{ID}";
105    eval "require $id" or die $@;
106
107
108    # Now re-defining ISA according to what we have above
109    {
110        no strict 'refs';
111        @{$driver . "::ISA"} = ( $class, $serializer, $id );
112    }
113
114    bless ($self, $driver);
115    $self->_validate_driver() && $self->_init() or return;
116    return $self;
117}
118
119
120
121# DESTROY() - destructor.
122# Flushes the memory, and calls driver's teardown()
123sub DESTROY {
124    my $self = shift;
125
126    $self->flush(); # or croak "could not flush: " . $self->error();
127    $self->can('teardown') && $self->teardown();
128}
129
130
131# options() - used by drivers only. Returns the driver
132# specific options. To be used in the future releases of the
133# library, may be
134sub driver_options {
135    my $self = shift;
136
137    return $self->{_OPTIONS}->[1];
138}
139
140# _validate_driver() - checks driver's validity.
141# Return value doesn't matter. If the driver doesn't seem
142# to be valid, it croaks
143sub _validate_driver {
144    my $self = shift;
145
146    my @required = qw(store retrieve remove generate_id);
147
148    for my $method ( @required ) {
149        unless ( $self->can($method) ) {
150            my $class = ref($self);
151            confess "$class doesn't seem to be a valid CGI::Session driver. " .
152                "At least one method ('$method') is missing";
153        }
154    }
155    return 1;
156}
157
158
159
160
161# _init() - object initialializer.
162# Decides between _init_old_session() and _init_new_session()
163sub _init {
164    my $self = shift;
165
166    my $claimed_id = undef;
167    my $arg = $self->{_OPTIONS}->[0];
168    if ( defined ($arg) && ref($arg) ) {
169        if ( $arg->isa('CGI') ) {
170            $claimed_id = $arg->cookie($NAME) || $arg->param($NAME) || undef;
171            $self->{_SESSION_OBJ} = $arg;
172        } elsif ( ref($arg) eq 'CODE' ) {
173            $claimed_id = $arg->() || undef;
174
175        }
176    } else {
177        $claimed_id = $arg;
178    }
179
180    if ( defined $claimed_id ) {
181        my $rv = $self->_init_old_session($claimed_id);
182
183        unless ( $rv ) {
184            return $self->_init_new_session();
185        }
186        return 1;
187    }
188    return $self->_init_new_session();
189}
190
191
192
193
194# _init_old_session() - tries to retieve the old session.
195# If suceeds, checks if the session is expirable. If so, deletes it
196# and returns undef so that _init() creates a new session.
197# Otherwise, checks if there're any parameters to be expired, and
198# calls clear() if any. Aftewards, updates atime of the session, and
199# returns true
200sub _init_old_session {
201    my ($self, $claimed_id) = @_;
202
203    my $options = $self->{_OPTIONS} || [];
204    my $data = $self->retrieve($claimed_id, $options);
205
206    # Session was initialized successfully
207    if ( defined $data ) {
208
209        $self->{_DATA} = $data;
210
211        # Check if the IP of the initial session owner should
212        # match with the current user's IP
213        if ( $IP_MATCH ) {
214            unless ( $self->_ip_matches() ) {
215                $self->delete();
216                $self->flush();
217                return undef;
218            }
219        }
220
221        # Check if the session's expiration ticker is up
222        if ( $self->_is_expired() ) {
223            $self->delete();
224            $self->flush();
225            return undef;
226        }
227
228        # Expring single parameters, if any
229        $self->_expire_params();
230
231        # Updating last access time for the session
232        $self->{_DATA}->{_SESSION_ATIME} = time();
233
234        # Marking the session as modified
235        $self->{_STATUS} = MODIFIED;
236
237        return 1;
238    }
239    return undef;
240}
241
242
243
244
245
246sub _ip_matches {
247    return ( $_[0]->{_DATA}->{_SESSION_REMOTE_ADDR} eq $ENV{REMOTE_ADDR} );
248}
249
250
251
252
253
254# _is_expired() - returns true if the session is to be expired.
255# Called from _init_old_session() method.
256sub _is_expired {
257    my $self = shift;
258
259    unless ( $self->expire() ) {
260        return undef;
261    }
262
263    return ( time() >= ($self->expire() + $self->atime() ) );
264}
265
266
267
268
269
270# _expire_params() - expires individual params. Called from within
271# _init_old_session() method on a sucessfully retrieved session
272sub _expire_params {
273    my $self = shift;
274
275    # Expiring
276    my $exp_list = $self->{_DATA}->{_SESSION_EXPIRE_LIST} || {};
277    my @trash_can = ();
278    while ( my ($param, $etime) = each %{$exp_list} ) {
279        if ( time() >= ($self->atime() + $etime) ) {
280            push @trash_can, $param;
281        }
282    }
283
284    if ( @trash_can ) {
285        $self->clear(\@trash_can);
286    }
287}
288
289
290
291
292
293# _init_new_session() - initializes a new session
294sub _init_new_session {
295    my $self = shift;
296
297	my $currtime = time();
298    $self->{_DATA} = {
299        _SESSION_ID => $self->generate_id($self->{_OPTIONS}),
300        _SESSION_CTIME => $currtime,
301        _SESSION_ATIME => $currtime,
302        _SESSION_ETIME => undef,
303        _SESSION_REMOTE_ADDR => $ENV{REMOTE_ADDR} || undef,
304        _SESSION_EXPIRE_LIST => { },
305    };
306
307    # to Chris Dolan's request:
308	# I'm not sure if this information should be serialized (placed under _DATA),
309	# but I don't see any desperate need for it. So let it be part of the object
310	$self->{_IS_NEW} = 1;
311
312    $self->{_STATUS} = MODIFIED;
313
314    return 1;
315}
316
317
318
319
320# id() - accessor method. Returns effective id
321# for the current session. CGI::Session deals with
322# two kinds of ids; effective and claimed. Claimed id
323# is the one passed to the constructor - new() as the first
324# argument. It doesn't mean that id() method returns that
325# particular id, since that ID might be either expired,
326# or even invalid, or just data associated with that id
327# might not be available for some reason. In this case,
328# claimed id and effective id are not the same.
329sub id {
330    my $self = shift;
331
332    return $self->{_DATA}->{_SESSION_ID};
333}
334
335
336
337# param() - accessor method. Reads and writes
338# session parameters ( $self->{_DATA} ). Decides
339# between _get_param() and _set_param() accordingly.
340sub param {
341    my $self = shift;
342
343
344    unless ( defined $_[0] ) {
345        return keys %{ $self->{_DATA} };
346    }
347
348    if ( @_ == 1 ) {
349        return $self->_get_param(@_);
350    }
351
352    # If it has more than one arguments, let's try to figure out
353    # what the caller is trying to do, since our tricks are endless ;-)
354    my $arg = {
355        -name   => undef,
356        -value  => undef,
357        @_,
358    };
359
360    if ( defined($arg->{'-name'}) && defined($arg->{'-value'}) ) {
361        return $self->_set_param($arg->{'-name'}, $arg->{'-value'});
362
363    }
364
365    if ( defined $arg->{'-name'} ) {
366        return $self->_get_param( $arg->{'-name'} );
367    }
368
369    if ( @_ == 2 ) {
370        return $self->_set_param(@_);
371    }
372
373    unless ( @_ % 2 ) {
374        my $n = 0;
375        my %args = @_;
376        while ( my ($key, $value) = each %args ) {
377            $self->_set_param($key, $value) && ++$n;
378        }
379        return $n;
380    }
381
382    confess "param(): something smells fishy here. RTFM!";
383}
384
385
386
387# _set_param() - sets session parameter to the '_DATA' table
388sub _set_param {
389    my ($self, $key, $value) = @_;
390
391    if ( $self->{_STATUS} == DELETED ) {
392        return;
393    }
394
395    # session parameters starting with '_session_' are
396    # private to the class
397    if ( $key =~ m/^_SESSION_/ ) {
398        return undef;
399    }
400
401    $self->{_DATA}->{$key} = $value;
402    $self->{_STATUS} = MODIFIED;
403
404    return $value;
405}
406
407
408
409
410# _get_param() - gets a single parameter from the
411# '_DATA' table
412sub _get_param {
413    my ($self, $key) = @_;
414
415    if ( $self->{_STATUS} == DELETED ) {
416        return;
417    }
418
419    return $self->{_DATA}->{$key};
420}
421
422
423# flush() - flushes the memory into the disk if necessary.
424# Usually called from within DESTROY() or close()
425sub flush {
426  my $self = shift;
427
428  my $status = $self->{_STATUS};
429
430  if ( $status == MODIFIED ) {
431      $self->store($self->id, $self->{_OPTIONS}, $self->{_DATA}) or return;
432      $self->{_STATUS} = SYNCED;
433  } elsif ( $status == DELETED ) {
434      $self->remove($self->id, $self->{_OPTIONS}) or return;
435  }
436  return 1;
437}
438
439# Autoload methods go after =cut, and are processed by the autosplit program.
440
441#1;
442
443#__END__;
444
445
446# $Id: Session.pm,v 1.3 2005/11/08 14:31:58 takezoe Exp $
447
448=pod
449
450=head1 NAME
451
452CGI::Session - persistent session data in CGI applications
453
454=head1 SYNOPSIS
455
456    # Object initialization:
457    use CGI::Session;
458
459    my $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
460
461    # getting the effective session id:
462    my $CGISESSID = $session->id();
463
464    # storing data in the session
465    $session->param('f_name', 'Sherzod');
466    # or
467    $session->param(-name=>'l_name', -value=>'Ruzmetov');
468
469    # retrieving data
470    my $f_name = $session->param('f_name');
471    # or
472    my $l_name = $session->param(-name=>'l_name');
473
474    # clearing a certain session parameter
475    $session->clear(["_IS_LOGGED_IN"]);
476
477    # expire '_IS_LOGGED_IN' flag after 10 idle minutes:
478    $session->expire(_IS_LOGGED_IN => '+10m')
479
480    # expire the session itself after 1 idle hour
481    $session->expire('+1h');
482
483    # delete the session for good
484    $session->delete();
485
486=head1 DESCRIPTION
487
488CGI-Session is a Perl5 library that provides an easy, reliable and modular
489session management system across HTTP requests. Persistency is a key feature for
490such applications as shopping carts, login/authentication routines, and
491application that need to carry data accross HTTP requests. CGI::Session
492does that and many more
493
494=head1 TO LEARN MORE
495
496Current manual is optimized to be used as a quick reference. To learn more both about the logic behind session management and CGI::Session programming style, consider the following:
497
498=over 4
499
500=item *
501
502L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual. Also includes library architecture and driver specifications.
503
504=item *
505
506L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
507
508=item *
509
510We also provide mailing lists for CGI::Session users. To subscribe to the list or browse the archives visit https://lists.sourceforge.net/lists/listinfo/cgi-session-user
511
512=item *
513
514B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
515
516=item *
517
518L<CGI|CGI> - standard CGI library
519
520=item *
521
522L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
523
524=back
525
526=head1 METHODS
527
528Following is the overview of all the available methods accessible via
529CGI::Session object.
530
531=over 4
532
533=item C<new( DSN, SID, HASHREF )>
534
535Requires three arguments. First is the Data Source Name, second should be
536the session id to be initialized or an object which provides either of 'param()'
537or 'cookie()' mehods. If Data Source Name is undef, it will fall back
538to default values, which are "driver:File;serializer:Default;id:MD5".
539
540If session id is missing, it will force the library to generate a new session
541id, which will be accessible through C<id()> method.
542
543Examples:
544
545    $session = new CGI::Session(undef, undef, {Directory=>'/tmp'});
546    $session = new CGI::Session("driver:File;serializer:Storable", undef,  {Directory=>'/tmp'})
547    $session = new CGI::Session("driver:MySQL;id:Incr", undef, {Handle=>$dbh});
548
549Following data source variables are supported:
550
551=over 4
552
553=item *
554
555C<driver> - CGI::Session driver. Available drivers are "File", "DB_File" and
556"MySQL". Default is "File".
557
558=item *
559
560C<serializer> - serializer to be used to encode the data structure before saving
561in the disk. Available serializers are "Storable", "FreezeThaw" and "Default".
562Default is "Default", which uses standard L<Data::Dumper|Data::Dumper>
563
564=item *
565
566C<id> - ID generator to use when new session is to be created. Available ID generators
567are "MD5" and "Incr". Default is "MD5".
568
569=back
570
571Note: you can also use unambiguous abbreviations of the DSN parameters. Examples:
572
573    new CGI::Session("dr:File;ser:Storable", undef, {Diretory=>'/tmp'});
574
575
576=item C<id()>
577
578Returns effective ID for a session. Since effective ID and claimed ID
579can differ, valid session id should always be retrieved using this
580method.
581
582=item C<param($name)>
583
584=item C<param(-name=E<gt>$name)>
585
586this method used in either of the above syntax returns a session
587parameter set to C<$name> or undef on failure.
588
589=item C<param( $name, $value)>
590
591=item C<param(-name=E<gt>$name, -value=E<gt>$value)>
592
593method used in either of the above syntax assigns a new value to $name
594parameter, which can later be retrieved with previously introduced
595param() syntax.
596
597=item C<param_hashref()>
598
599returns all the session parameters as a reference to a hash
600
601
602=item C<save_param($cgi)>
603
604=item C<save_param($cgi, $arrayref)>
605
606Saves CGI parameters to session object. In otherwords, it's calling
607C<param($name, $value)> for every single CGI parameter. The first
608argument should be either CGI object or any object which can provide
609param() method. If second argument is present and is a reference to an array, only those CGI parameters found in the array will
610be stored in the session
611
612=item C<load_param($cgi)>
613
614=item C<load_param($cgi, $arrayref)>
615
616loads session parameters to CGI object. The first argument is required
617to be either CGI.pm object, or any other object which can provide
618param() method. If second argument is present and is a reference to an
619array, only the parameters found in that array will be loaded to CGI
620object.
621
622=item C<sync_param($cgi)>
623
624=item C<sync_param($cgi, $arrayref)>
625
626experimental feature. Synchronizes CGI and session objects. In other words, it's the same as calling respective syntaxes of save_param() and load_param().
627
628=item C<clear()>
629
630=item C<clear([@list])>
631
632clears parameters from the session object. If passed an argument as an
633arrayref, clears only those parameters found in the list.
634
635=item C<flush()>
636
637synchronizes data in the buffer with its copy in disk. Normally it will
638be called for you just before the program terminates, session object
639goes out of scope or close() is called.
640
641=item C<close()>
642
643closes the session temporarily until new() is called on the same session
644next time. In other words, it's a call to flush() and DESTROY(), but
645a lot slower. Normally you never have to call close().
646
647=item C<atime()>
648
649returns the last access time of the session in the form of seconds from
650epoch. This time is used internally while auto-expiring sessions and/or session parameters.
651
652=item C<ctime()>
653
654returns the time when the session was first created.
655
656=item C<expire()>
657
658=item C<expire($time)>
659
660=item C<expire($param, $time)>
661
662Sets expiration date relative to atime(). If used with no arguments, returns the expiration date if it was ever set. If no expiration was ever set, returns undef.
663
664Second form sets an expiration time. This value is checked when previously stored session is asked to be retrieved, and if its expiration date has passed will be expunged from the disk immediately and new session is created accordingly. Passing 0 would cancel expiration date.
665
666By using the third syntax you can also set an expiration date for a
667particular session parameter, say "~logged-in". This would cause the
668library call clear() on the parameter when its time is up.
669
670All the time values should be given in the form of seconds. Following
671time aliases are also supported for your convenience:
672
673    +===========+===============+
674    |   alias   |   meaning     |
675    +===========+===============+
676    |     s     |   Second      |
677    |     m     |   Minute      |
678    |     h     |   Hour        |
679    |     w     |   Week        |
680    |     M     |   Month       |
681    |     y     |   Year        |
682    +-----------+---------------+
683
684Examples:
685
686    $session->expires("+1y");   # expires in one year
687    $session->expires(0);       # cancel expiration
688    $session->expires("~logged-in", "+10m");# expires ~logged-in flag in 10 mins
689
690Note: all the expiration times are relative to session's last access time, not to its creation time. To expire a session immediately, call C<delete()>. To expire a specific session parameter immediately, call C<clear()> on that parameter.
691
692=item C<remote_addr()>
693
694returns the remote address of the user who created the session for the
695first time. Returns undef if variable REMOTE_ADDR wasn't present in the
696environment when the session was created
697
698=item C<delete()>
699
700deletes the session from the disk. In other words, it calls for
701immediate expiration after which the session will not be accessible
702
703=item C<error()>
704
705returns the last error message from the library. It's the same as the
706value of $CGI::Session::errstr. Example:
707
708    $session->flush() or die $session->error();
709
710=item C<dump()>
711
712=item C<dump("logs/dump.txt")>
713
714creates a dump of the session object. Argument, if passed, will be
715interpreted as the name of the file object should be dumped in. Used
716mostly for debugging.
717
718=item C<header()>
719
720header() is simply a replacement for L<CGI.pm|CGI>'s header() method. Without this method, you usually need to create a CGI::Cookie object and send it as part of the HTTP header:
721
722    $cookie = new CGI::Cookie(-name=>'CGISESSID', -value=>$session->id);
723    print $cgi->header(-cookie=>$cookie);
724
725You can minimize the above into:
726
727    $session->header()
728
729It will retrieve the name of the session cookie from $CGI::Session::NAME variable, which can also be accessed via CGI::Session->name() method. If you want to use a different name for your session cookie, do something like following before creating session object:
730
731    CGI::Session->name("MY_SID");
732    $session = new CGI::Session(undef, $cgi, \%attrs);
733
734Now, $session->header() uses "MY_SID" as a name for the session cookie.
735
736=back
737
738=head1 DATA TABLE
739
740Session data is stored in the form of hash table, in key value pairs.
741All the parameter names you assign through param() method become keys
742in the table, and whatever value you assign become a value associated with
743that key. Every key/value pair is also called a record.
744
745All the data you save through param() method are called public records.
746There are several read-only private records as well. Normally, you don't have to know anything about them to make the best use of the library. But knowing wouldn't hurt either. Here are the list of the private records and some description  of what they hold:
747
748=over 4
749
750=item _SESSION_ID
751
752Session id of that data. Accessible through id() method.
753
754=item _SESSION_CTIME
755
756Session creation time. Accessible through ctime() method.
757
758=item _SESSION_ATIME
759
760Session last access time. Accessible through atime() method.
761
762=item _SESSION_ETIME
763
764Session's expiration time, if any. Accessible through expire() method.
765
766=item _SESSION_REMOTE_ADDR
767
768IP address of the user who create that session. Accessible through remote_addr()
769method
770
771=item _SESSION_EXPIRE_LIST
772
773Another internal hash table that holds the expiration information for each
774expirable public record, if any. This table is updated with the two-argument-syntax of expires() method.
775
776=back
777
778These private methods are essential for the proper operation of the library
779while working with session data. For this purpose, CGI::Session doesn't allow
780overriding any of these methods through the use of param() method. In addition,
781it doesn't allow any parameter names that start with string B<_SESSION_> either
782to prevent future collisions.
783
784So the following attempt will have no effect on the session data whatsoever
785
786    $session->param(_SESSION_XYZ => 'xyz');
787
788Although private methods are not writable, the library allows reading them
789using param() method:
790
791    my $sid = $session->param(_SESSION_ID);
792
793The above is the same as:
794
795    my $sid = $session->id();
796
797But we discourage people from accessing private records using param() method.
798In the future we are planning to store private records in their own namespace
799to avoid name collisions and remove restrictions on session parameter names.
800
801=head1 DISTRIBUTION
802
803CGI::Session consists of several modular components such as L<drivers|"DRIVERS">, L<serializers|"SERIALIZERS"> and L<id generators|"ID Generators">. This section lists what is available.
804
805=head2 DRIVERS
806
807Following drivers are included in the standard distribution:
808
809=over 4
810
811=item *
812
813L<File|CGI::Session::File> - default driver for storing session data in plain files. Full name: B<CGI::Session::File>
814
815=item *
816
817L<DB_File|CGI::Session::DB_File> - for storing session data in BerkelyDB. Requires: L<DB_File>. Full name: B<CGI::Session::DB_File>
818
819=item *
820
821L<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>
822
823=back
824
825=head2 SERIALIZERS
826
827=over 4
828
829=item *
830
831L<Default|CGI::Session::Serialize::Default> - default data serializer. Uses standard L<Data::Dumper|Data::Dumper>. Full name: B<CGI::Session::Serialize::Default>.
832
833=item *
834
835L<Storable|CGI::Session::Serialize::Storable> - serializes data using L<Storable>. Requires L<Storable>. Full name: B<CGI::Session::Serialize::Storable>.
836
837=item *
838
839L<FreezeThaw|CGI::Session::Serialize::FreezeThaw> - serializes data using L<FreezeThaw>. Requires L<FreezeThaw>. Full name: B<CGI::Session::Serialize::FreezeThaw>
840
841=back
842
843=head2 ID GENERATORS
844
845Following ID generators are available:
846
847=over 4
848
849=item *
850
851L<MD5|CGI::Session::ID::MD5> - generates 32 character long hexidecimal string.
852Requires L<Digest::MD5|Digest::MD5>. Full name: B<CGI::Session::ID::MD5>.
853
854=item *
855
856L<Incr|CGI::Session::ID::Incr> - generates auto-incrementing ids. Full name: B<CGI::Session::ID::Incr>
857
858=back
859
860
861=head1 COPYRIGHT
862
863Copyright (C) 2001-2002 Sherzod Ruzmetov <sherzodr@cpan.org>. All rights reserved.
864
865This library is free software. You can modify and or distribute it under the same terms as Perl itself.
866
867=head1 AUTHOR
868
869Sherzod Ruzmetov <sherzodr@cpan.org>. Feedbacks, suggestions are welcome.
870
871=head1 SEE ALSO
872
873=over 4
874
875=item *
876
877L<CGI::Session::Tutorial|CGI::Session::Tutorial> - extended CGI::Session manual
878
879=item *
880
881L<CGI::Session::CookBook|CGI::Session::CookBook> - practical solutions for real life problems
882
883=item *
884
885B<RFC 2965> - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
886
887=item *
888
889L<CGI|CGI> - standard CGI library
890
891=item *
892
893L<Apache::Session|Apache::Session> - another fine alternative to CGI::Session
894
895=back
896
897=cut
898
899# dump() - dumps the session object using Data::Dumper.
900# during development it defines global dump().
901sub dump {
902    my ($self, $file, $indent) = @_;
903
904    require Data::Dumper;
905    local $Data::Dumper::Indent = $indent || 2;
906
907    my $d = new Data::Dumper([$self], [ref $self]);
908
909    if ( defined $file ) {
910        unless ( open(FH, '<' . $file) ) {
911            unless(open(FH, '>' . $file)) {
912                $self->error("Couldn't open $file: $!");
913                return undef;
914            }
915            print FH $d->Dump();
916            unless ( CORE::close(FH) ) {
917                $self->error("Couldn't dump into $file: $!");
918                return undef;
919            }
920        }
921    }
922    return $d->Dump();
923}
924
925
926
927sub version {   return $VERSION   }
928
929
930# delete() - sets the '_STATUS' session flag to DELETED,
931# which flush() uses to decide to call remove() method on driver.
932sub delete {
933    my $self = shift;
934
935    # If it was already deleted, make a confession!
936    if ( $self->{_STATUS} == DELETED ) {
937        confess "delete attempt on deleted session";
938    }
939
940    $self->{_STATUS} = DELETED;
941}
942
943
944
945
946
947# clear() - clears a list of parameters off the session's '_DATA' table
948sub clear {
949    my $self = shift;
950    my $class = ref($self);
951
952    my @params = $self->param();
953    if ( defined $_[0] ) {
954        unless ( ref($_[0]) eq 'ARRAY' ) {
955            confess "Usage: $class->clear([\@array])";
956        }
957        @params = @{ $_[0] };
958    }
959
960    my $n = 0;
961    for ( @params ) {
962        /^_SESSION_/ and next;
963        # If this particular parameter has an expiration ticker,
964        # remove it.
965        if ( $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{$_} ) {
966            delete ( $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{$_} );
967        }
968        delete ($self->{_DATA}->{$_}) && ++$n;
969    }
970
971    # Set the session '_STATUS' flag to MODIFIED
972    $self->{_STATUS} = MODIFIED;
973
974    return $n;
975}
976
977
978# save_param() - copies a list of third party object parameters
979# into CGI::Session object's '_DATA' table
980sub save_param {
981    my ($self, $cgi, $list) = @_;
982
983    unless ( ref($cgi) ) {
984        confess "save_param(): first argument should be an object";
985
986    }
987    unless ( $cgi->can('param') ) {
988        confess "save_param(): Cannot call method param() on the object";
989    }
990
991    my @params = ();
992    if ( defined $list ) {
993        unless ( ref($list) eq 'ARRAY' ) {
994            confess "save_param(): second argument must be an arrayref";
995        }
996
997        @params = @{ $list };
998
999    } else {
1000        @params = $cgi->param();
1001
1002    }
1003
1004    my $n = 0;
1005    for ( @params ) {
1006        # It's imporatnt to note that CGI.pm's param() returns array
1007        # if a parameter has more values associated with it (checkboxes
1008        # and crolling lists). So we should access its parameters in
1009        # array context not to miss anything
1010        my @values = $cgi->param($_);
1011
1012        if ( defined $values[1] ) {
1013            $self->_set_param($_ => \@values);
1014
1015        } else {
1016            $self->_set_param($_ => $values[0] );
1017
1018        }
1019
1020        ++$n;
1021    }
1022
1023    return $n;
1024}
1025
1026
1027# load_param() - loads a list of third party object parameters
1028# such as CGI, into CGI::Session's '_DATA' table
1029sub load_param {
1030    my ($self, $cgi, $list) = @_;
1031
1032    unless ( ref($cgi) ) {
1033        confess "save_param(): first argument must be an object";
1034
1035    }
1036    unless ( $cgi->can('param') ) {
1037        my $class = ref($cgi);
1038        confess "save_param(): Cannot call method param() on the object $class";
1039    }
1040
1041    my @params = ();
1042    if ( defined $list ) {
1043        unless ( ref($list) eq 'ARRAY' ) {
1044            confess "save_param(): second argument must be an arrayref";
1045        }
1046        @params = @{ $list };
1047
1048    } else {
1049        @params = $self->param();
1050
1051    }
1052
1053    my $n = 0;
1054    for ( @params ) {
1055        $cgi->param(-name=>$_, -value=>$self->_get_param($_));
1056    }
1057    return $n;
1058}
1059
1060
1061
1062
1063# another, but a less efficient alternative to undefining
1064# the object
1065sub close {
1066    my $self = shift;
1067
1068    $self->DESTROY();
1069}
1070
1071
1072
1073# error() returns/sets error message
1074sub error {
1075    my ($self, $msg) = @_;
1076
1077    if ( defined $msg ) {
1078        $errstr = $msg;
1079    }
1080
1081    return $errstr;
1082}
1083
1084
1085# errstr() - alias to error()
1086sub errstr {
1087    my $self = shift;
1088
1089    return $self->error(@_);
1090}
1091
1092
1093
1094# atime() - rerturns session last access time
1095sub atime {
1096    my $self = shift;
1097
1098    if ( @_ ) {
1099        confess "_SESSION_ATIME - read-only value";
1100    }
1101
1102    return $self->{_DATA}->{_SESSION_ATIME};
1103}
1104
1105
1106# ctime() - returns session creation time
1107sub ctime {
1108    my $self = shift;
1109
1110    if ( @_ ) {
1111        confess "_SESSION_ATIME - read-only value";
1112    }
1113
1114    return $self->{_DATA}->{_SESSION_CTIME};
1115}
1116
1117
1118# expire() - sets/returns session/parameter expiration ticker
1119sub expire {
1120    my $self = shift;
1121
1122    unless ( @_ ) {
1123        return $self->{_DATA}->{_SESSION_ETIME};
1124    }
1125
1126    if ( @_ == 1 ) {
1127        return $self->{_DATA}->{_SESSION_ETIME} = _time_alias( $_[0] );
1128    }
1129
1130    # If we came this far, we'll simply assume user is trying
1131    # to set an expiration date for a single session parameter.
1132    my ($param, $etime) = @_;
1133
1134    # Let's check if that particular session parameter exists
1135    # in the '_DATA' table. Otherwise, return now!
1136    defined ($self->{_DATA}->{$param} ) || return;
1137
1138    if ( $etime eq '-1' ) {
1139        delete $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{$param};
1140        return;
1141    }
1142
1143    $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{$param} = _time_alias( $etime );
1144}
1145
1146
1147# expires() - alias to expire(). For backward compatibility
1148sub expires {
1149	return expire(@_);
1150}
1151
1152
1153# parses such strings as '+1M', '+3w', accepted by expire()
1154sub _time_alias {
1155    my ($str) = @_;
1156
1157    # If $str consists of just digits, return them as they are
1158    if ( $str =~ m/^\d+$/ ) {
1159        return $str;
1160    }
1161
1162    my %time_map = (
1163        s           => 1,
1164        m           => 60,
1165        h           => 3600,
1166        d           => 86400,
1167        w           => 604800,
1168        M           => 2592000,
1169        y           => 31536000
1170    );
1171
1172    my ($koef, $d) = $str =~ m/^([+-]?\d+)(\w)$/;
1173
1174    if ( defined($koef) && defined($d) ) {
1175        return $koef * $time_map{$d};
1176    }
1177}
1178
1179
1180# remote_addr() - returns ip address of the session
1181sub remote_addr {
1182    my $self = shift;
1183
1184    return $self->{_DATA}->{_SESSION_REMOTE_ADDR};
1185}
1186
1187
1188# param_hashref() - returns parameters as a reference to a hash
1189sub param_hashref {
1190    my $self = shift;
1191
1192    return $self->{_DATA};
1193}
1194
1195
1196# name() - returns the cookie name associated with the session id
1197sub name {
1198    my ($class, $name)  = @_;
1199
1200    if ( defined $name ) {
1201        $CGI::Session::NAME = $name;
1202    }
1203
1204    return $CGI::Session::NAME;
1205}
1206
1207
1208# header() - replacement for CGI::header() method
1209sub header {
1210    my $self = shift;
1211
1212    my $cgi = $self->{_SESSION_OBJ};
1213    unless ( defined $cgi ) {
1214        require CGI;
1215        $self->{_SESSION_OBJ} = CGI->new();
1216        return $self->header();
1217    }
1218
1219    my $cookie = $cgi->cookie($self->name(), $self->id() );
1220
1221    return $cgi->header(
1222        -type   => 'text/html',
1223        -cookie => $cookie,
1224        @_
1225    );
1226}
1227
1228
1229# sync_param() - synchronizes CGI and Session parameters.
1230sub sync_param {
1231    my ($self, $cgi, $list) = @_;
1232
1233    unless ( ref($cgi) ) {
1234        confess("$cgi doesn't look like an object");
1235    }
1236
1237    unless ( $cgi->UNIVERSAL::can('param') ) {
1238        confess(ref($cgi) . " doesn't support param() method");
1239    }
1240
1241    # we first need to save all the available CGI parameters to the
1242    # object
1243    $self->save_param($cgi, $list);
1244
1245    # we now need to load all the parameters back to the CGI object
1246    return $self->load_param($cgi, $list);
1247}
1248
1249
1250# to Chris Dolan's request
1251sub is_new {
1252	my $self = shift;
1253
1254	return $self->{_IS_NEW};
1255}
1256
12571;
1258
1259# $Id: Session.pm,v 1.3 2005/11/08 14:31:58 takezoe Exp $
1260