File Coverage

blib/lib/CGI/Session.pm
Criterion Covered Total %
statement 196 278 70.5
branch 95 148 64.1
condition 28 59 47.4
subroutine 34 43 79.0
pod 21 28 75.0
total 374 556 67.2


line stmt bran cond sub pod time code
1             package CGI::Session;
2              
3             # $Id: Session.pm 225 2005-09-16 08:17:15Z sherzodr $
4              
5 18     18   2627884 use strict;
  18         37  
  18         742  
6 18     18   97 use Carp;
  18         29  
  18         1253  
7 18     18   7505 use CGI::Session::ErrorHandler;
  18         38  
  18         62561  
8              
9             @CGI::Session::ISA = qw( CGI::Session::ErrorHandler );
10             $CGI::Session::VERSION = '4.02_01';
11             $CGI::Session::NAME = 'CGISESSID';
12             $CGI::Session::IP_MATCH = 0;
13              
14             sub STATUS_NEW () { 1 } # denotes session that's just created
15             sub STATUS_MODIFIED () { 2 } # denotes session that needs synchronization
16             sub STATUS_DELETED () { 4 } # denotes session that needs deletion
17             sub STATUS_EXPIRED () { 8 } # denotes session that was expired.
18              
19             sub import {
20 18     18   156 my $class = shift;
21 18 100       12375 @_ or return;
22              
23 6         18 for(@_) {
24 6         3177 $CGI::Session::IP_MATCH = ( $_ eq '-ip_match' );
25             }
26             }
27              
28             sub new {
29 26     26 1 10191 my $class = shift;
30              
31             # If called as object method as in $session->new()...
32 26         46 my $self;
33 26 100       99 if ( ref $class ) {
34 1         20 $self = bless { %$class }, ref($class);
35 1         4 $class = ref($class);
36 1         6 $self->_reset_status();
37              
38             # Object may still have public data associated with it, but we don't care about that,
39             # since we want to leave that to the client's disposal. However, if new() was requested on
40             # an expired session, we already know that '_DATA' table is empty, since it was the
41             # job of flush() to empty '_DATA' after deleting. How do we know flush() was already
42             # called on an expired session? Because load() - constructor always calls flush()
43             # on all to-be expired sessions
44             } else {
45 25 100       119 defined($self = $class->load( @_ ))
46             or return $class->set_error( "new(): failed: " . $class->errstr );
47             }
48              
49             # Absence of '_SESSION_ID' can only signal:
50             # * expired session
51             # Because load() - constructor is required to empty contents of _DATA - table
52             # * unavailable session
53             # Such sessions are the ones that don't exist on datastore, but requested by client
54             # * new sessions
55             # When no specific session is requested to be loaded
56 23 100       139 unless ( $self->{_DATA}->{_SESSION_ID} ) {
57 18         108 $self->{_DATA}->{_SESSION_ID} = $self->_id_generator()->generate_id($self->{_DRIVER_ARGS}, $self->{_CLAIMED_ID});
58 18 50       88 unless ( defined $self->{_DATA}->{_SESSION_ID} ) {
59 0         0 return $self->set_error( "Couldn't generate new SID" );
60             }
61 18         76 $self->{_DATA}->{_SESSION_CTIME} = $self->{_DATA}->{_SESSION_ATIME} = time();
62 18         81 $self->_set_status(STATUS_NEW);
63             }
64 23         107 return $self;
65             }
66              
67 33     33   5313 sub DESTROY { $_[0]->flush() }
68 1     1 1 5 sub close { $_[0]->flush() }
69              
70             *param_hashref = \&dataref;
71             my $avoid_single_use_warning = *param_hashref;
72 246     246 1 1130 sub dataref { $_[0]->{_DATA} }
73              
74 4     4 1 17 sub is_empty { !defined($_[0]->id) }
75              
76 7     7 1 40 sub is_expired { $_[0]->_test_status( STATUS_EXPIRED ) }
77              
78 3     3 1 730 sub is_new { $_[0]->_test_status( STATUS_NEW ) }
79              
80 107 100   107 1 4220 sub id { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_ID} : undef }
81              
82 6 50   6 1 22 sub atime { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_ATIME} : undef }
83              
84 6 50   6 1 24 sub ctime { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_CTIME} : undef }
85              
86             sub _driver {
87 40     40   65 my $self = shift;
88 40 100       164 defined($self->{_OBJECTS}->{driver}) and return $self->{_OBJECTS}->{driver};
89 24         74 my $pm = "CGI::Session::Driver::" . $self->{_DSN}->{driver};
90 24         371 return $self->{_OBJECTS}->{driver} = $pm->new( $self->{_DRIVER_ARGS} );
91             }
92              
93             sub _serializer {
94 32     32   47 my $self = shift;
95 32 100       130 defined($self->{_OBJECTS}->{serializer}) and return $self->{_OBJECTS}->{serializer};
96 23         95 return $self->{_OBJECTS}->{serializer} = "CGI::Session::Serialize::" . $self->{_DSN}->{serializer};
97             }
98              
99              
100             sub _id_generator {
101 18     18   44 my $self = shift;
102 18 50       88 defined($self->{_OBJECTS}->{id}) and return $self->{_OBJECTS}->{id};
103 18         259 return $self->{_OBJECTS}->{id} = "CGI::Session::ID::" . $self->{_DSN}->{id};
104             }
105              
106             sub _ip_matches {
107 0     0   0 return ( $_[0]->{_DATA}->{_SESSION_REMOTE_ADDR} eq $ENV{REMOTE_ADDR} );
108             }
109              
110              
111             # parses the DSN string and returns it as a hash.
112             # Notably: Allows unique abbreviations of the keys: driver, serializer and 'id'.
113             # Also, keys and values of the returned hash are lower-cased.
114             sub parse_dsn {
115 25     25 0 51 my $self = shift;
116 25         46 my $dsn_str = shift;
117 25 50       77 croak "parse_dsn(): usage error" unless $dsn_str;
118              
119 25         7078 require Text::Abbrev;
120 25         458 my $abbrev = Text::Abbrev::abbrev( "driver", "serializer", "id" );
121 25         2095 my %dsn_map = map { split /:/ } (split /;/, $dsn_str);
  42         156  
122 25         83 my %dsn = map { $abbrev->{lc $_}, lc $dsn_map{$_} } keys %dsn_map;
  40         151  
123 25         200 return \%dsn;
124             }
125              
126             sub query {
127 19     19 1 35 my $self = shift;
128              
129 19 100       74 if ( $self->{_QUERY} ) {
130 4         13 return $self->{_QUERY};
131             }
132 15         147944 require CGI;
133 15         167236 $self->{_QUERY} = CGI->new();
134 15         53867 return $self->{_QUERY};
135             }
136              
137              
138             sub name {
139 32 50   32 0 41845 unless ( defined $_[1] ) {
140 32         222 return $CGI::Session::NAME;
141             }
142 0         0 $CGI::Session::NAME = $_[1];
143             }
144              
145              
146             sub dump {
147 0     0 1 0 my $self = shift;
148              
149 0         0 require Data::Dumper;
150 0         0 my $d = Data::Dumper->new([$self], [ref $self]);
151 0         0 $d->Deepcopy(1);
152 0         0 return $d->Dump();
153             }
154              
155              
156             sub _set_status {
157 72     72   100 my $self = shift;
158 72 50       174 croak "_set_status(): usage error" unless @_;
159 72         311 $self->{_STATUS} |= $_ for @_;
160             }
161              
162              
163             sub _unset_status {
164 25     25   52 my $self = shift;
165 25 50       76 croak "_unset_status(): usage error" unless @_;
166 25         209 $self->{_STATUS} &= ~$_ for @_;
167             }
168              
169              
170             sub _reset_status {
171 1     1   4 $_[0]->{_STATUS} = 0;
172             }
173              
174             sub _test_status {
175 181     181   655 return $_[0]->{_STATUS} & $_[1];
176             }
177              
178              
179             sub flush {
180 43     43 1 90 my $self = shift;
181              
182 43 100       152 return unless $self->id; # <-- empty session
183 35 100       214 return if $self->{_STATUS} == 0; # <-- neither new, nor deleted nor modified
184              
185 26 100 100     91 if ( $self->_test_status(STATUS_NEW) && $self->_test_status(STATUS_DELETED) ) {
186 6         15 $self->{_DATA} = {};
187 6         35 return $self->_unset_status(STATUS_NEW, STATUS_DELETED);
188             }
189              
190 20         69 my $driver = $self->_driver();
191 20         67 my $serializer = $self->_serializer();
192              
193 20 100       52 if ( $self->_test_status(STATUS_DELETED) ) {
194 6 50       16 defined($driver->remove($self->id)) or
195             return $self->set_error( "flush(): couldn't remove session data: " . $driver->errstr );
196 6         20 $self->{_DATA} = {}; # <-- removing all the data, making sure
197             # it won't be accessible after flush()
198 6         42 return $self->_unset_status(STATUS_DELETED);
199             }
200              
201 14 100 100     42 if ( $self->_test_status(STATUS_NEW) || $self->_test_status(STATUS_MODIFIED) ) {
202 13         74 my $datastr = $serializer->freeze( $self->dataref );
203 13 50       1104 unless ( defined $datastr ) {
204 0         0 return $self->set_error( "flush(): couldn't freeze data: " . $serializer->errstr );
205             }
206 13 50       46 defined( $driver->store($self->id, $datastr) ) or
207             return $self->set_error( "flush(): couldn't store datastr: " . $driver->errstr);
208 13         78 $self->_unset_status(STATUS_NEW, STATUS_MODIFIED);
209             }
210 14         79 return 1;
211             }
212              
213 0     0 0 0 sub trace {}
214 0     0 0 0 sub tracemsg {}
215              
216             sub param {
217 91     91 1 213 my $self = shift;
218              
219 91 50       188 carp "param(): attempt to read/write deleted session" if $self->_test_status(STATUS_DELETED);
220              
221             #
222             # USAGE: $s->param();
223             # DESC: returns all the **public** parameters
224 91 100       227 unless ( @_ ) {
225 5         7 return grep { !/^_SESSION_/ } keys %{ $self->{_DATA} };
  36         78  
  5         21  
226             }
227              
228             #
229             # USAGE: $s->param($p);
230             # DESC: returns a specific session parameter
231 86 100       555 return $self->{_DATA}->{$_[0]} if @_ == 1;
232              
233 23         109 my %args = (
234             -name => undef,
235             -value => undef,
236             @_
237             );
238              
239             #
240             # USAGE: $s->param(-name=>$n, -value=>$v);
241             # DESC: updates session data using CGI.pm's 'named parameter' syntax. Only
242             # public records can be set!
243 23 100 100     99 if ( defined( $args{'-name'} ) && defined( $args{'-value'} ) ) {
244 6 50       25 if ( $args{'-name'} =~ m/^_SESSION_/ ) {
245 0         0 carp "param(): attempt to write to private parameter";
246 0         0 return undef;
247             }
248 6         22 $self->_set_status(STATUS_MODIFIED);
249 6         34 return $self->{_DATA}->{ $args{'-name'} } = $args{'-value'};
250             }
251              
252             #
253             # USAGE: $s->param(-name=>$n);
254             # DESC: access to session data (public & private) using CGI.pm's 'named parameter' syntax.
255 17 100       69 return $self->{_DATA}->{ $args{'-name'} } if defined $args{'-name'};
256              
257             # USAGE: $s->param($name, $value);
258             # USAGE: $s->param($name1 => $value1, $name2 => $value2 [,...]);
259             # DESC: updates one or more **public** records using simple syntax
260 13 50       47 unless ( @_ % 2 ) {
261 13         51 for ( my $i=0; $i < @_; $i += 2 ) {
262 21 50       79 if ( $_[$i] =~ m/^_SESSION_/) {
263 0         0 carp "param(): attempt to write to private parameter";
264 0         0 next;
265             }
266 21         76 $self->{_DATA}->{ $_[$i] } = $_[$i+1];
267             }
268 13         40 $self->_set_status(STATUS_MODIFIED);
269 13         74 return 1;
270             }
271              
272             #
273             # If we reached this far none of the expected syntax were detected. Syntax error
274 0         0 croak "param(): usage error. Invalid number";
275             }
276              
277              
278              
279 12     12 1 818 sub delete { $_[0]->_set_status( STATUS_DELETED ) }
280              
281              
282             *header = \&http_header;
283             my $avoid_single_use_warning_again = *header;
284             sub http_header {
285 2     2 0 1971 my $self = shift;
286 2         7 return $self->query->header(-cookie=>$self->cookie, type=>'text/html', @_);
287             }
288              
289             sub cookie {
290 2     2 0 3 my $self = shift;
291              
292 2         4 my $query = $self->query();
293 2         3 my $cookie= undef;
294              
295 2 50       7 if ( $self->is_expired ) {
    50          
296 0         0 $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, -expires=> '-1d', @_ );
297             } elsif ( my $t = $self->expire ) {
298 0         0 $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, -expires=> $t . 's', @_ );
299             } else {
300 2         7 $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, @_ );
301             }
302              
303 2         404 return $cookie;
304             }
305              
306              
307              
308              
309              
310             sub save_param {
311 0     0 1 0 my $self = shift;
312 0         0 my ($query, $params) = @_;
313              
314 0   0     0 $query ||= $self->query();
315 0   0     0 $params ||= [ $query->param ];
316              
317 0         0 for my $p ( @$params ) {
318 0 0       0 my @values = $query->param($p) or next;
319 0 0       0 if ( @values > 1 ) {
320 0         0 $self->param($p, \@values);
321             } else {
322 0         0 $self->param($p, $values[0]);
323             }
324             }
325 0         0 $self->_set_status( STATUS_MODIFIED );
326             }
327              
328              
329              
330             sub load_param {
331 0     0 1 0 my $self = shift;
332 0         0 my ($query, $params) = @_;
333              
334 0   0     0 $query ||= $self->query();
335 0   0     0 $params ||= [ $self->param ];
336              
337 0         0 for ( @$params ) {
338 0         0 $query->param(-name=>$_, -value=>$self->param($_));
339             }
340             }
341              
342              
343             sub clear {
344 0     0 1 0 my $self = shift;
345 0         0 my $params = shift;
346             #warn ref($params);
347 0 0       0 if (defined $params) {
348 0 0       0 $params = [ $params ] unless ref $params;
349             }
350             else {
351 0         0 $params = [ $self->param ];
352             }
353              
354 0         0 for ( @$params ) {
355 0         0 delete $self->{_DATA}->{$_};
356             }
357 0         0 $self->_set_status( STATUS_MODIFIED );
358             }
359              
360              
361             sub find {
362 0     0 1 0 my $class = shift;
363 0         0 my ($dsnstr, $coderef, $dsn_args);
364              
365 0 0       0 if ( @_ == 1 ) {
366 0         0 $coderef = $_[0];
367             } else {
368 0         0 ($dsnstr, $coderef, $dsn_args) = @_;
369             }
370              
371 0 0 0     0 unless ( $coderef && ref($coderef) && (ref $coderef eq 'CODE') ) {
      0        
372 0         0 croak "find(): usage error.";
373             }
374              
375 0         0 my $driver;
376 0 0       0 if ( $dsnstr ) {
377 0         0 my $hashref = $class->parse_dsn( $dsnstr );
378 0         0 $driver = $hashref->{driver};
379             }
380 0   0     0 $driver ||= "file";
381 0         0 my $pm = "CGI::Session::Driver::" . $driver;
382 0         0 eval "require $pm";
383 0 0       0 if (my $errmsg = $@ ) {
384 0         0 return $class->set_error( "find(): couldn't load driver." . $errmsg );
385             }
386              
387 0         0 my $driver_obj = $pm->new( $dsn_args );
388 0 0       0 unless ( $driver_obj ) {
389 0         0 return $class->set_error( "find(): couldn't create driver object. " . $pm->errstr );
390             }
391              
392             my $driver_coderef = sub {
393 0     0   0 my ($sid) = @_;
394 0         0 my $session = $class->load( $dsnstr, $sid, $dsn_args );
395 0 0       0 unless ( $session ) {
396 0         0 return $class->set_error( "find(): couldn't load session '$sid'. " . $class->errstr );
397             }
398 0         0 $coderef->( $session );
399 0         0 };
400              
401 0 0       0 defined($driver_obj->traverse( $driver_coderef ))
402             or return $class->set_error( "find(): traverse seems to have failed. " . $driver_obj->errstr );
403 0         0 return 1;
404             }
405              
406             # $Id: Session.pm 225 2005-09-16 08:17:15Z sherzodr $
407              
408             =pod
409              
410             =head1 NAME
411              
412             CGI::Session - persistent session data in CGI applications
413              
414             =head1 SYNOPSIS
415              
416             # Object initialization:
417             use CGI::Session;
418             $session = new CGI::Session();
419              
420             $CGISESSID = $session->id();
421              
422             # send proper HTTP header with cookies:
423             print $session->header();
424              
425             # storing data in the session
426             $session->param('f_name', 'Sherzod');
427             # or
428             $session->param(-name=>'l_name', -value=>'Ruzmetov');
429              
430             # retrieving data
431             my $f_name = $session->param('f_name');
432             # or
433             my $l_name = $session->param(-name=>'l_name');
434              
435             # clearing a certain session parameter
436             $session->clear(["l_name", "f_name"]);
437              
438             # expire '_is_logged_in' flag after 10 idle minutes:
439             $session->expire('is_logged_in', '+10m')
440              
441             # expire the session itself after 1 idle hour
442             $session->expire('+1h');
443              
444             # delete the session for good
445             $session->delete();
446              
447             =head1 DESCRIPTION
448              
449             CGI-Session is a Perl5 library that provides an easy, reliable and modular session management system across HTTP requests.
450             Persistency is a key feature for such applications as shopping carts, login/authentication routines, and application that
451             need to carry data across HTTP requests. CGI::Session does that and many more.
452              
453             =head1 TO LEARN MORE
454              
455             Current manual is optimized to be used as a quick reference. To learn more both about the philosophy and CGI::Session
456             programming style, consider the following:
457              
458             =over 4
459              
460             =item *
461              
462             L - extended CGI::Session manual. Also includes library architecture and driver specifications.
463              
464             =item *
465              
466             We 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
467              
468             =item *
469              
470             B - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
471              
472             =item *
473              
474             L - standard CGI library
475              
476             =item *
477              
478             L - another fine alternative to CGI::Session.
479              
480             =back
481              
482             =head1 METHODS
483              
484             Following is the overview of all the available methods accessible via CGI::Session object.
485              
486             =over 4
487              
488             =item new()
489              
490             =item new( $sid )
491              
492             =item new( $query )
493              
494             =item new( $dsn, $query||$sid )
495              
496             =item new( $dsn, $query||$sid, \%dsn_args )
497              
498             Constructor. Returns new session object, or undef on failure. Error message is accessible through L. If called on an already initialized session will re-initialize the session based on already configured object. This is only useful after a call to L.
499              
500             Can accept up to three arguments, $dsn - Data Source Name, $query||$sid - query object OR a string representing session id, and finally, \%dsn_args, arguments used by $dsn components.
501              
502             If called without any arguments, $dsn defaults to I, $query||$sid defaults to C<< CGI->new() >>, and C<\%dsn_args> defaults to I.
503              
504             If called with a single argument, it will be treated either as C<$query> object, or C<$sid>, depending on its type. If argument is a string , C will treat it as session id and will attempt to retrieve the session from data store. If it fails, will create a new session id, which will be accessible through L. If argument is an object, L and L methods will be called on that object to recover a potential C<$sid> and retrieve it from data store. If it fails, C will create a new session id, which will be accessible through L. C<$CGI::Session::NAME> will define the name of the query parameter and/or cookie name to be requested, defaults to I.
505              
506             If called with two arguments first will be treated as $dsn, and second will be treated as $query or $sid or undef, depending on its type. Some examples of this syntax are:
507              
508             $s = CGI::Session->new("driver:mysql", undef);
509             $s = CGI::Session->new("driver:sqlite", $sid);
510             $s = CGI::Session->new("driver:db_file", $query);
511             $s = CGI::Session->new("serializer:storable;id:incr", $sid);
512             # etc...
513              
514              
515             Following data source components are supported:
516              
517             =over 4
518              
519             =item *
520              
521             B - CGI::Session driver. Available drivers are L, L, L and L. Third party drivers are welcome. For driver specs consider L
522              
523             =item *
524              
525             B - serializer to be used to encode the data structure before saving
526             in the disk. Available serializers are L, L and L. Default serializer will use L.
527              
528             =item *
529              
530             B - ID generator to use when new session is to be created. Available ID generator is L
531              
532             =back
533              
534             For example, to get CGI::Session store its data using DB_File and serialize data using FreezeThaw:
535              
536             $s = new CGI::Session("driver:DB_File;serializer:FreezeThaw", undef);
537              
538             If called with three arguments, first two will be treated as in the previous example, and third argument will be C<\%dsn_args>, which will be passed to C<$dsn> components (namely, driver, serializer and id generators) for initialization purposes. Since all the $dsn components must initialize to some default value, this third argument should not be required for most drivers to operate properly.
539              
540             undef is acceptable as a valid placeholder to any of the above arguments, which will force default behavior.
541              
542             =item load()
543              
544             =item load($query||$sid)
545              
546             =item load($dsn, $query||$sid)
547              
548             =item load($dsn, $query, \%dsn_args);
549              
550             Constructor. Usage is identical to L, so is the return value. Major difference is, L can create new session if it detects expired and non-existing sessions, but C does not.
551              
552             C is useful to detect expired or non-existing sessions without forcing the library to create new sessions. So now you can do something like this:
553              
554             $s = CGI::Session->load() or die CGI::Session->errstr();
555             if ( $s->is_expired ) {
556             print $s->header(),
557             $cgi->start_html(),
558             $cgi->p("Your session timed out! Refresh the screen to start new session!")
559             $cgi->end_html();
560             exit(0);
561             }
562              
563             if ( $s->is_empty ) {
564             $s = $s->new() or die $s->errstr;
565             }
566              
567             Notice, all I sessions are empty, but not all I sessions are expired!
568              
569             =cut
570              
571             sub load {
572 34     34 1 95 my $class = shift;
573              
574 34 100       161 return $class->set_error( "called as instance method") if ref $class;
575 33 100       158 return $class->set_error( "invalid number of arguments") if @_ > 3;
576              
577 32   100     577 my $self = bless {
578             _DATA => {
579             _SESSION_ID => undef,
580             _SESSION_CTIME => undef,
581             _SESSION_ATIME => undef,
582             _SESSION_REMOTE_ADDR => $ENV{REMOTE_ADDR} || "",
583             #
584             # Following two attributes may not exist in every single session, and declaring
585             # them now will force these to get serialized into database, wasting space. But they
586             # are here to remind the coder of their purpose
587             #
588             # _SESSION_ETIME => undef,
589             # _SESSION_EXPIRE_LIST => {}
590             }, # session data
591             _DSN => {}, # parsed DSN params
592             _OBJECTS => {}, # keeps necessary objects
593             _DRIVER_ARGS=> {}, # arguments to be passed to driver
594             _CLAIMED_ID => undef, # id **claimed** by client
595             _STATUS => 0, # status of the session object
596             _QUERY => undef # query object
597             }, $class;
598              
599             #$self->{_DATA}->{_SESSION_CTIME} = $self->{_DATA}->{_SESSION_ATIME} = time();
600              
601 32 100       114 if ( @_ == 1 ) {
602 1 50       3 if ( ref $_[0] ){ $self->{_QUERY} = $_[0] }
  0         0  
603 1         3 else { $self->{_CLAIMED_ID} = $_[0] }
604             }
605              
606             # Two or more args passed:
607 32 100       108 if ( @_ > 1 ) {
608 23 100       78 if ( defined $_[0] ) { # <-- to avoid 'Uninitialized value...' warnings
609 21         88 $self->{_DSN} = $self->parse_dsn( $_[0] );
610             }
611             #
612             # second argument can either be $sid, or $query
613 23 50       151 if ( ref $_[1] ){ $self->{_QUERY} = $_[1] }
  0         0  
614 23         66 else { $self->{_CLAIMED_ID} = $_[1] }
615             }
616              
617             #
618             # grabbing the 3rd argument, if any
619 32 100       161 if ( @_ == 3 ){ $self->{_DRIVER_ARGS} = $_[2] }
  23         48  
620              
621             #
622             # setting defaults, since above arguments might be 'undef'
623 32   100     228 $self->{_DSN}->{driver} ||= "file";
624 32   100     182 $self->{_DSN}->{serializer} ||= "default";
625 32   100     155 $self->{_DSN}->{id} ||= "md5";
626              
627             # Beyond this point used to be '_init()' method. But I had to merge them together
628             # since '_init()' did not serve specific purpose
629              
630              
631             #
632             # Checking and loading driver, serializer and id-generators
633             #
634 32         87 my @pms = ();
635 32         126 $pms[0] = "CGI::Session::Driver::" . $self->{_DSN}->{driver};
636 32         93 $pms[1] = "CGI::Session::Serialize::" . $self->{_DSN}->{serializer};
637 32         84 $pms[2] = "CGI::Session::ID::" . $self->{_DSN}->{id};
638 32         80 for ( @pms ) {
639 96         5550 eval "require $_";
640 96 50       530 if ( my $errmsg = $@ ) {
641 0         0 return $self->set_error("couldn't load $_: " . $errmsg);
642             }
643             }
644              
645 32 100       148 unless ( $self->{_CLAIMED_ID} ) {
646 15         74 my $query = $self->query();
647 15         37 eval {
648 15   33     79 $self->{_CLAIMED_ID} = $query->cookie( $self->name ) || $query->param( $self->name );
649             };
650 15 50       360 if ( my $errmsg = $@ ) {
651 0         0 return $class->set_error( "query object $query does not support cookie() and param() methods: " . $errmsg );
652             }
653             }
654              
655             #
656             # No session is being requested. Just return an empty session
657 32 100       260 return $self unless $self->{_CLAIMED_ID};
658              
659             #
660             # Attempting to load the session
661 17         66 my $driver = $self->_driver();
662 17         84 my $raw_data = $driver->retrieve( $self->{_CLAIMED_ID} );
663 17 50       59 unless ( defined $raw_data ) {
664 0         0 return $self->set_error( "load(): couldn't retrieve data: " . $driver->errstr );
665             }
666             #
667             # Requested session couldn't be retrieved
668 17 100       58 return $self unless $raw_data;
669              
670 12         49 my $serializer = $self->_serializer();
671 12         67 $self->{_DATA} = $serializer->thaw($raw_data);
672 12 100       288 unless ( defined $self->{_DATA} ) {
673             #die $raw_data . "\n";
674 5         69 return $self->set_error( "load(): couldn't thaw() data using $serializer:" .
675             $serializer->errstr );
676             }
677 7 50 33     97 unless (defined($self->{_DATA}) && ref ($self->{_DATA}) && (ref $self->{_DATA} eq 'HASH') &&
      33        
      33        
678             defined($self->{_DATA}->{_SESSION_ID}) ) {
679 0         0 return $self->set_error( "Invalid data structure returned from thaw()" );
680             }
681              
682             #
683             # checking if previous session ip matches current ip
684 7 50       24 if($CGI::Session::IP_MATCH) {
685 0 0       0 unless($self->_ip_matches) {
686 0         0 $self->_set_status( STATUS_DELETED );
687 0         0 $self->flush;
688 0         0 return $self;
689             }
690             }
691              
692             #
693             # checking for expiration ticker
694 7 100       24 if ( $self->{_DATA}->{_SESSION_ETIME} ) {
695 3 100       16 if ( ($self->{_DATA}->{_SESSION_ATIME} + $self->{_DATA}->{_SESSION_ETIME}) <= time() ) {
696 1         7 $self->_set_status( STATUS_EXPIRED ); # <-- so client can detect expired sessions
697 1         6 $self->_set_status( STATUS_DELETED ); # <-- session should be removed from database
698 1         5 $self->flush(); # <-- flush() will do the actual removal!
699 1         95 return $self;
700             }
701             }
702              
703             # checking expiration tickers of individuals parameters, if any:
704 6         12 my @expired_params = ();
705 6         8 while (my ($param, $max_exp_interval) = each %{ $self->{_DATA}->{_SESSION_EXPIRE_LIST} } ) {
  6         68  
706 0 0       0 if ( ($self->{_DATA}->{_SESSION_ATIME} + $max_exp_interval) <= time() ) {
707 0         0 push @expired_params, $param;
708             }
709             }
710 6 50       14 $self->clear(\@expired_params) if @expired_params;
711 6         30 $self->{_DATA}->{_SESSION_ATIME} = time(); # <-- updating access time
712 6         19 $self->_set_status( STATUS_MODIFIED ); # <-- access time modified above
713 6         21 return $self;
714             }
715              
716             =pod
717              
718             =item id()
719              
720             Returns effective ID for a session. Since effective ID and claimed ID can differ, valid session id should always
721             be retrieved using this method.
722              
723             =item param($name)
724              
725             =item param(-name=E$name)
726              
727             Used in either of the above syntax returns a session parameter set to $name or undef if it doesn't exist. If it's called on a deleted method param() will issue a warning but return value is not defined.
728              
729             =item param($name, $value)
730              
731             =item param(-name=E$name, -value=E$value)
732              
733             Used in either of the above syntax assigns a new value to $name parameter,
734             which can later be retrieved with previously introduced param() syntax. C<$value>
735             may be a scalar, arrayref or hashref.
736              
737             Attempts to set parameter names that start with I<_SESSION_> will trigger
738             a warning and undef will be returned.
739              
740             =item param_hashref()
741              
742             B. Use L instead.
743              
744             =item dataref()
745              
746             Returns reference to session's data table:
747              
748             $params = $s->dataref();
749             $sid = $params->{_SESSION_ID};
750             $name= $params->{name};
751             # etc...
752              
753             Useful for having all session data in a hashref, but too risky to update.
754              
755             =item save_param()
756              
757             =item save_param($query)
758              
759             =item save_param($query, \@list)
760              
761             Saves query parameters to session object. In other words, it's the same as calling L for every single query parameter returned by C<< $query->param() >>. The first argument, if present, should be either CGI object or any object which can provide param() method. If it's undef, defaults to the return value of L, which returns C<< CGI->new >>. If second argument is present and is a reference to an array, only those query parameters found in the array will be stored in the session. undef is a valid placeholder for any argument to force default behavior.
762              
763             =item load_param()
764              
765             =item load_param($query)
766              
767             =item load_param($query, \@list)
768              
769             Loads session parameters into a query object. The first argument, if present, should be query object, or any other object which can provide param() method. If second argument is present and is a reference to an array, only parameters found in that array will be loaded to the query object.
770              
771             =item clear()
772              
773             =item clear('field')
774              
775             =item clear(\@list)
776              
777             Clears parameters from the session object.
778              
779             With no parameters, all fields are cleared. If passed a single parameter or a
780             reference to an array, only the named parameters are cleared.
781              
782             =item flush()
783              
784             Synchronizes data in the buffer with its copy in disk. Normally it will be called for you just before the program terminates, or session object goes out of scope, so you should never have to flush() on your own.
785              
786             =item atime()
787              
788             Read-only method. Returns the last access time of the session in seconds from epoch. This time is used internally while
789             auto-expiring sessions and/or session parameters.
790              
791             =item ctime()
792              
793             Read-only method. Returns the time when the session was first created in seconds from epoch.
794              
795             =item expire()
796              
797             =item expire($time)
798              
799             =item expire($param, $time)
800              
801             Sets expiration interval relative to L.
802              
803             If used with no arguments, returns the expiration interval if it was ever set. If no expiration was ever set, returns undef. For backwards compatibility, a method named C does the same thing.
804              
805             Second form sets an expiration time. This value is checked when previously stored session is asked to be retrieved, and if its expiration interval has passed, it will be expunged from the disk immediately. Passing 0 cancels expiration.
806              
807             By using the third syntax you can set the expiration interval for a particular session parameter, say I<~logged-in>. This would cause the library call clear() on the parameter when its time is up. Passing 0 cancels expiration.
808              
809             All the time values should be given in the form of seconds. Following keywords are also supported for your convenience:
810              
811             +-----------+---------------+
812             | alias | meaning |
813             +-----------+---------------+
814             | s | Second |
815             | m | Minute |
816             | h | Hour |
817             | d | Day |
818             | w | Week |
819             | M | Month |
820             | y | Year |
821             +-----------+---------------+
822              
823             Examples:
824              
825             $session->expire("2h"); # expires in two hours
826             $session->expire(0); # cancel expiration
827             $session->expire("~logged-in", "10m"); # expires '~logged-in' parameter after 10 idle minutes
828              
829             Note: all the expiration times are relative to session's last access time, not to its creation time. To expire a session immediately, call L. To expire a specific session parameter immediately, call L.
830              
831             =cut
832              
833             *expires = \&expire;
834             my $prevent_warning = \&expires;
835 9     9 0 30 sub etime { $_[0]->expire() }
836             sub expire {
837 40     40 1 64 my $self = shift;
838              
839             # no params, just return the expiration time.
840 40 100       117 if (not @_) {
    100          
841 25         148 return $self->{_DATA}->{_SESSION_ETIME};
842             }
843             # We have just a time
844             elsif ( @_ == 1 ) {
845 13         21 my $time = $_[0];
846             # If 0 is passed, cancel expiration
847 13 100 66     110 if ( defined $time && ($time =~ m/^\d$/) && ($time == 0) ) {
      66        
848 1         3 $self->{_DATA}->{_SESSION_ETIME} = undef;
849 1         3 $self->_set_status( STATUS_MODIFIED );
850             }
851             # set the expiration to this time
852             else {
853 12         36 $self->{_DATA}->{_SESSION_ETIME} = $self->_str2seconds( $time );
854 12         33 $self->_set_status( STATUS_MODIFIED );
855             }
856             }
857             # If we get this far, we expect expire($param,$time)
858             # ( This would be a great use of a Perl6 multi sub! )
859             else {
860 2         3 my ($param, $time) = @_;
861 2 100 66     13 if ( ($time =~ m/^\d$/) && ($time == 0) ) {
862 1         4 delete $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{ $param };
863 1         4 $self->_set_status( STATUS_MODIFIED );
864             } else {
865 1         3 $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{ $param } = $self->_str2seconds( $time );
866 1         4 $self->_set_status( STATUS_MODIFIED );
867             }
868             }
869 15         32 return 1;
870             }
871              
872             # =head2 _str2seconds()
873             #
874             # my $secs = $self->_str2seconds('1d')
875             #
876             # Takes a CGI.pm-style time representation and returns an equivalent number
877             # of seconds.
878             #
879             # See the docs of expire() for more detail.
880             #
881             # =cut
882              
883             sub _str2seconds {
884 22     22   3924 my $self = shift;
885 22         36 my ($str) = @_;
886              
887 22 50       73 return unless defined $str;
888 22 100       106 return $str if $str =~ m/^[-+]?\d+$/;
889              
890 19         102 my %_map = (
891             s => 1,
892             m => 60,
893             h => 3600,
894             d => 86400,
895             w => 604800,
896             M => 2592000,
897             y => 31536000
898             );
899              
900 19         71 my ($koef, $d) = $str =~ m/^([+-]?\d+)([smhdwMy])$/;
901 19 50 33     95 unless ( defined($koef) && defined($d) ) {
902 0         0 die "_str2seconds(): couldn't parse '$str' into \$koef and \$d parts. Possible invalid syntax";
903             }
904 19         110 return $koef * $_map{ $d };
905             }
906              
907              
908             =pod
909              
910             =item is_new()
911              
912             Returns true only for a brand new session.
913              
914             =item is_expired()
915              
916             Tests whether session initialized using L is to be expired. This method works only on sessions initialized with load():
917              
918             $s = CGI::Session->load() or die CGI::Session->errstr;
919             if ( $s->is_expired ) {
920             die "Your session expired. Please refresh";
921             }
922             if ( $s->is_empty ) {
923             $s = $s->new() or die $s->errstr;
924             }
925              
926              
927             =item is_empty()
928              
929             Returns true for sessions that are empty. It's preferred way of testing whether requested session was loaded successfully or not:
930              
931             $s = CGI::Session->load($sid);
932             if ( $s->is_empty ) {
933             $s = $s->new();
934             }
935              
936             Actually, the above code is nothing but waste. The same effect could've been achieved by saying:
937              
938             $s = CGI::Session->new( $sid );
939              
940             L is useful only if you wanted to catch requests for expired sessions, and create new session afterwards. See L for an example.
941              
942             =item delete()
943              
944             Deletes a session from the data store and empties session data from memory, completely, so subsequent read/write requests on the same object will fail. Technically speaking, it will only set object's status to I and will trigger L, and flush() will do the actual removal.
945              
946             =item find( \&code )
947              
948             =item find( $dsn, \&code )
949              
950             =item find( $dsn, \&code, \%dsn_args )
951              
952             Experimental feature. Executes \&code for every session object stored in disk, passing initialized CGI::Session object as the first argument of \&code. Useful for housekeeping purposes, such as for removing expired sessions. Following line, for instance, will remove sessions already expired, but are still in disk:
953              
954             CGI::Session->find( sub {} );
955              
956             Notice, above \&code didn't have to do anything, because load(), which is called to initialize sessions inside find(), will automatically remove expired sessions. Following example will remove all the objects that are 10+ days old:
957              
958             CGI::Session->find( \&purge );
959             sub purge {
960             my ($session) = @_;
961             next if $session->empty; # <-- already expired?!
962             if ( ($session->ctime + 3600*240) <= time() ) {
963             $session->delete() or warn "couldn't remove " . $session->id . ": " . $session->errstr;
964             }
965             }
966              
967             B find() is meant to be convenient, not necessarily efficient. It's best suited in cron scripts.
968              
969             =back
970              
971             =head1 MISCELLANEOUS METHODS
972              
973             =over 4
974              
975             =item remote_addr()
976              
977             Returns the remote address of the user who created the session for the first time. Returns undef if variable REMOTE_ADDR wasn't present in the environment when the session was created.
978              
979             =cut
980              
981 3     3 1 13 sub remote_addr { return $_[0]->{_DATA}->{_SESSION_REMOTE_ADDR} }
982              
983             =pod
984              
985             =item errstr()
986              
987             Class method. Returns last error message from the library.
988              
989             =item dump()
990              
991             Returns a dump of the session object. Useful for debugging purposes only.
992              
993             =item header()
994              
995             Replacement for L's header() method. Without this method, you usually need to create a CGI::Cookie object and send it as part of the HTTP header:
996              
997             $cookie = CGI::Cookie->new(-name=>$session->name, -value=>$session->id);
998             print $cgi->header(-cookie=>$cookie);
999              
1000             You can minimize the above into:
1001              
1002             print $session->header();
1003              
1004             It 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:
1005              
1006             CGI::Session->name("MY_SID");
1007             $session = new CGI::Session(undef, $cgi, \%attrs);
1008              
1009             Now, $session->header() uses "MY_SID" as a name for the session cookie.
1010              
1011             =item query()
1012              
1013             Returns query object associated with current session object. Default query object class is L.
1014              
1015             =back
1016              
1017             =head2 DEPRECATED METHODS
1018              
1019             These methods exist solely for for compatibility with CGI::Session 3.x.
1020              
1021             =over 4
1022              
1023             =item close()
1024              
1025             Closes the session. Using flush() is recommended instead, since that's exactly what a call
1026             to close() does now.
1027              
1028             =back
1029              
1030             =head1 DISTRIBUTION
1031              
1032             CGI::Session consists of several components such as L, L and L. This section lists what is available.
1033              
1034             =head2 DRIVERS
1035              
1036             Following drivers are included in the standard distribution:
1037              
1038             =over 4
1039              
1040             =item *
1041              
1042             L - default driver for storing session data in plain files. Full name: B
1043              
1044             =item *
1045              
1046             L - for storing session data in BerkelyDB. Requires: L.
1047             Full name: B
1048              
1049             =item *
1050              
1051             L - for storing session data in MySQL tables. Requires L and L.
1052             Full name: B
1053              
1054             =item *
1055              
1056             L - for storing session data in SQLite. Requires L and L.
1057             Full name: B
1058              
1059             =back
1060              
1061             =head2 SERIALIZERS
1062              
1063             =over 4
1064              
1065             =item *
1066              
1067             L - default data serializer. Uses standard L.
1068             Full name: B.
1069              
1070             =item *
1071              
1072             L - serializes data using L. Requires L.
1073             Full name: B.
1074              
1075             =item *
1076              
1077             L - serializes data using L. Requires L.
1078             Full name: B
1079              
1080             =back
1081              
1082             =head2 ID GENERATORS
1083              
1084             Following ID generators are available:
1085              
1086             =over 4
1087              
1088             =item *
1089              
1090             L - generates 32 character long hexadecimal string. Requires L.
1091             Full name: B.
1092              
1093             =item *
1094              
1095             L - generates incremental session ids.
1096              
1097             =item *
1098              
1099             L - generates static session ids. B
1100              
1101             =back
1102              
1103              
1104             =head1 CREDITS
1105              
1106             CGI::Session evolved to what it is today with the help of following developers. The list doesn't follow any strict order, but somewhat chronological. Specifics can be found in F file
1107              
1108             =over 4
1109              
1110             =item Andy Lester Ealester@flr.follett.comE
1111              
1112             =item Brian King Emrbbking@mac.comE
1113              
1114             =item Olivier Dragon Edragon@shadnet.shad.caE
1115              
1116             =item Adam Jacob Eadam@sysadminsith.orgE
1117              
1118             =item Igor Plisco Eigor@plisco.ruE
1119              
1120             =item Mark Stosberg Emarkstos@cpan.orgE
1121              
1122             =item Matt LeBlanc
1123              
1124             =item Shawn Sorichetti
1125              
1126             =back
1127              
1128             =head1 COPYRIGHT
1129              
1130             Copyright (C) 2001-2005 Sherzod Ruzmetov Esherzodr@cpan.orgE. All rights reserved.
1131             This library is free software. You can modify and or distribute it under the same terms as Perl itself.
1132              
1133             =head1 PUBLIC CODE REPOSITORY
1134              
1135             You can see what the developers have been up to since the last release by
1136             checking out the code repository. You can browse the Subversion repository from here:
1137              
1138             http://svn.cromedome.net/
1139              
1140             Or check it directly with C from here:
1141              
1142             svn://svn.cromedome.net/CGI-Session
1143              
1144             =head1 SUPPORT
1145              
1146             If you need help using CGI::Session consider the mailing list. You can ask the list by sending your questions to
1147             cgi-session-user@lists.sourceforge.net .
1148              
1149             You can subscribe to the mailing list at https://lists.sourceforge.net/lists/listinfo/cgi-session-user .
1150              
1151             Bug reports can be submitted at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Session
1152              
1153             =head1 AUTHOR
1154              
1155             Sherzod Ruzmetov Esherzodr@cpan.orgE, http://author.handalak.com/
1156              
1157             Mark Stosberg became a co-maintainer during the development of 4.0. C.
1158              
1159             =head1 SEE ALSO
1160              
1161             =over 4
1162              
1163             =item *
1164              
1165             L - extended CGI::Session manual
1166              
1167             =item *
1168              
1169             B - "HTTP State Management Mechanism" found at ftp://ftp.isi.edu/in-notes/rfc2965.txt
1170              
1171             =item *
1172              
1173             L - standard CGI library
1174              
1175             =item *
1176              
1177             L - another fine alternative to CGI::Session
1178              
1179             =back
1180              
1181             =cut
1182              
1183             1;
1184