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