File Coverage

inc/CGI/Session.pm
Criterion Covered Total %
statement 9 261 3.4
branch 0 142 0.0
condition 0 50 0.0
subroutine 3 40 7.5
pod n/a
total 12 493 2.4


line stmt bran cond sub pod time code
1             #line 1
2             package CGI::Session;
3              
4             # $Id: Session.pm 353 2006-12-05 02:10:19Z markstos $
5 4     4   20  
  4         6  
  4         114  
6 4     4   17 use strict;
  4         7  
  4         204  
7 4     4   3345 use Carp;
  4         13  
  4         16951  
8             use CGI::Session::ErrorHandler;
9              
10             @CGI::Session::ISA = qw( CGI::Session::ErrorHandler );
11             $CGI::Session::VERSION = '4.20';
12             $CGI::Session::NAME = 'CGISESSID';
13             $CGI::Session::IP_MATCH = 0;
14              
15             sub STATUS_NEW () { 1 } # denotes session that's just created
16             sub STATUS_MODIFIED () { 2 } # denotes session that needs synchronization
17             sub STATUS_DELETED () { 4 } # denotes session that needs deletion
18             sub STATUS_EXPIRED () { 8 } # denotes session that was expired.
19              
20 0     0     sub import {
21             my ($class, @args) = @_;
22 0 0          
23             return unless @args;
24              
25 0           ARG:
26 0 0         foreach my $arg (@args) {
27 0           if ($arg eq '-ip_match') {
28 0           $CGI::Session::IP_MATCH = 1;
29             last ARG;
30             }
31             }
32             }
33              
34 0     0     sub new {
35             my ($class, @args) = @_;
36 0            
37 0 0         my $self;
38             if (ref $class) {
39             #
40             # Called as an object method as in $session->new()...
41 0           #
42 0           $self = bless { %$class }, ref( $class );
43 0           $class = ref $class;
44             $self->_reset_status();
45             #
46             # Object may still have public data associated with it, but we
47             # don't care about that, since we want to leave that to the
48             # client's disposal. However, if new() was requested on an
49             # expired session, we already know that '_DATA' table is
50             # empty, since it was the job of flush() to empty '_DATA'
51             # after deleting. How do we know flush() was already called on
52             # an expired session? Because load() - constructor always
53             # calls flush() on all to-be expired sessions
54             #
55             }
56             else {
57             #
58             # Called as a class method as in CGI::Session->new()
59 0           #
60 0 0         $self = $class->load( @args );
61 0           if (not defined $self) {
62             return $class->set_error( "new(): failed: " . $class->errstr );
63             }
64 0           }
65 0 0         my $dataref = $self->{_DATA};
66             unless ($dataref->{_SESSION_ID}) {
67             #
68             # Absence of '_SESSION_ID' can only signal:
69             # * Expired session: Because load() - constructor is required to
70             # empty contents of _DATA - table
71             # * Unavailable session: Such sessions are the ones that don't
72             # exist on datastore, but are requested by client
73             # * New session: When no specific session is requested to be loaded
74 0           #
75             my $id = $self->_id_generator()->generate_id(
76             $self->{_DRIVER_ARGS},
77             $self->{_CLAIMED_ID}
78 0 0         );
79 0           unless (defined $id) {
80             return $self->set_error( "Couldn't generate new SESSION-ID" );
81 0           }
82 0           $dataref->{_SESSION_ID} = $id;
83 0           $dataref->{_SESSION_CTIME} = $dataref->{_SESSION_ATIME} = time();
84             $self->_set_status( STATUS_NEW );
85 0           }
86             return $self;
87             }
88 0     0      
89 0     0     sub DESTROY { $_[0]->flush() }
90             sub close { $_[0]->flush() }
91              
92             *param_hashref = \&dataref;
93 0     0     my $avoid_single_use_warning = *param_hashref;
94             sub dataref { $_[0]->{_DATA} }
95 0     0      
96             sub is_empty { !defined($_[0]->id) }
97 0     0      
98             sub is_expired { $_[0]->_test_status( STATUS_EXPIRED ) }
99 0     0      
100             sub is_new { $_[0]->_test_status( STATUS_NEW ) }
101 0 0   0      
102             sub id { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_ID} : undef }
103              
104 0 0   0     # Last Access Time
105             sub atime { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_ATIME} : undef }
106              
107 0 0   0     # Creation Time
108             sub ctime { return defined($_[0]->dataref) ? $_[0]->dataref->{_SESSION_CTIME} : undef }
109              
110 0     0     sub _driver {
111 0 0         my $self = shift;
112 0           defined($self->{_OBJECTS}->{driver}) and return $self->{_OBJECTS}->{driver};
113 0 0         my $pm = "CGI::Session::Driver::" . $self->{_DSN}->{driver};
114             defined($self->{_OBJECTS}->{driver} = $pm->new( $self->{_DRIVER_ARGS} ))
115 0           or die $pm->errstr();
116             return $self->{_OBJECTS}->{driver};
117             }
118              
119 0     0     sub _serializer {
120 0 0         my $self = shift;
121 0           defined($self->{_OBJECTS}->{serializer}) and return $self->{_OBJECTS}->{serializer};
122             return $self->{_OBJECTS}->{serializer} = "CGI::Session::Serialize::" . $self->{_DSN}->{serializer};
123             }
124              
125              
126 0     0     sub _id_generator {
127 0 0         my $self = shift;
128 0           defined($self->{_OBJECTS}->{id}) and return $self->{_OBJECTS}->{id};
129             return $self->{_OBJECTS}->{id} = "CGI::Session::ID::" . $self->{_DSN}->{id};
130             }
131              
132 0     0     sub _ip_matches {
133             return ( $_[0]->{_DATA}->{_SESSION_REMOTE_ADDR} eq $ENV{REMOTE_ADDR} );
134             }
135              
136              
137             # parses the DSN string and returns it as a hash.
138             # Notably: Allows unique abbreviations of the keys: driver, serializer and 'id'.
139             # Also, keys and values of the returned hash are lower-cased.
140 0     0     sub parse_dsn {
141 0           my $self = shift;
142 0 0         my $dsn_str = shift;
143             croak "parse_dsn(): usage error" unless $dsn_str;
144 0            
145 0           require Text::Abbrev;
146 0           my $abbrev = Text::Abbrev::abbrev( "driver", "serializer", "id" );
  0            
147 0           my %dsn_map = map { split /:/ } (split /;/, $dsn_str);
  0            
148 0           my %dsn = map { $abbrev->{lc $_}, lc $dsn_map{$_} } keys %dsn_map;
149             return \%dsn;
150             }
151              
152 0     0     sub query {
153             my $self = shift;
154 0 0          
155 0           if ( $self->{_QUERY} ) {
156             return $self->{_QUERY};
157             }
158             # require CGI::Session::Query;
159 0           # return $self->{_QUERY} = CGI::Session::Query->new();
160 0           require CGI;
161             return $self->{_QUERY} = CGI->new();
162             }
163              
164              
165 0     0     sub name {
166             my $self = shift;
167 0 0        
168 0 0         if (ref $self) {
169 0   0       unless ( @_ ) {
170             return $self->{_NAME} || $CGI::Session::NAME;
171 0           }
172             return $self->{_NAME} = $_[0];
173             }
174 0 0        
175 0           $CGI::Session::NAME = $_[0] if @_;
176             return $CGI::Session::NAME;
177             }
178              
179              
180 0     0     sub dump {
181             my $self = shift;
182 0            
183 0           require Data::Dumper;
184 0           my $d = Data::Dumper->new([$self], [ref $self]);
185 0           $d->Deepcopy(1);
186             return $d->Dump();
187             }
188              
189              
190 0     0     sub _set_status {
191 0 0         my $self = shift;
192 0           croak "_set_status(): usage error" unless @_;
193             $self->{_STATUS} |= $_ for @_;
194             }
195              
196              
197 0     0     sub _unset_status {
198 0 0         my $self = shift;
199 0           croak "_unset_status(): usage error" unless @_;
200             $self->{_STATUS} &= ~$_ for @_;
201             }
202              
203              
204 0     0     sub _reset_status {
205             $_[0]->{_STATUS} = 0;
206             }
207              
208 0     0     sub _test_status {
209             return $_[0]->{_STATUS} & $_[1];
210             }
211              
212              
213 0     0     sub flush {
214             my $self = shift;
215              
216             # Would it be better to die or err if something very basic is wrong here?
217             # I'm trying to address the DESTORY related warning
218             # from: http://rt.cpan.org/Ticket/Display.html?id=17541
219             # return unless defined $self;
220 0 0          
221 0 0 0       return unless $self->id; # <-- empty session
222             return if !defined($self->{_STATUS}) or $self->{_STATUS} == 0; # <-- neither new, nor deleted nor modified
223 0 0 0        
224 0           if ( $self->_test_status(STATUS_NEW) && $self->_test_status(STATUS_DELETED) ) {
225 0           $self->{_DATA} = {};
226             return $self->_unset_status(STATUS_NEW, STATUS_DELETED);
227             }
228 0            
229 0           my $driver = $self->_driver();
230             my $serializer = $self->_serializer();
231 0 0          
232 0 0         if ( $self->_test_status(STATUS_DELETED) ) {
233             defined($driver->remove($self->id)) or
234 0           return $self->set_error( "flush(): couldn't remove session data: " . $driver->errstr );
235             $self->{_DATA} = {}; # <-- removing all the data, making sure
236 0           # it won't be accessible after flush()
237             return $self->_unset_status(STATUS_DELETED);
238             }
239 0 0 0        
240 0           if ( $self->_test_status(STATUS_NEW) || $self->_test_status(STATUS_MODIFIED) ) {
241 0 0         my $datastr = $serializer->freeze( $self->dataref );
242 0           unless ( defined $datastr ) {
243             return $self->set_error( "flush(): couldn't freeze data: " . $serializer->errstr );
244 0 0         }
245             defined( $driver->store($self->id, $datastr) ) or
246 0           return $self->set_error( "flush(): couldn't store datastr: " . $driver->errstr);
247             $self->_unset_status(STATUS_NEW, STATUS_MODIFIED);
248 0           }
249             return 1;
250             }
251 0     0      
252 0     0     sub trace {}
253             sub tracemsg {}
254              
255 0     0     sub param {
256             my ($self, @args) = @_;
257 0 0          
258 0           if ($self->_test_status( STATUS_DELETED )) {
259             carp "param(): attempt to read/write deleted session";
260             }
261              
262             # USAGE: $s->param();
263 0 0         # DESC: Returns all the /public/ parameters
    0          
264 0           if (@args == 0) {
  0            
  0            
265             return grep { !/^_SESSION_/ } keys %{ $self->{_DATA} };
266             }
267             # USAGE: $s->param( $p );
268             # DESC: returns a specific session parameter
269 0           elsif (@args == 1) {
270             return $self->{_DATA}->{ $args[0] }
271             }
272              
273              
274             # USAGE: $s->param( -name => $n, -value => $v );
275             # DESC: Updates session data using CGI.pm's 'named param' syntax.
276 0           # Only public records can be set!
277 0           my %args = @args;
278 0 0 0       my ($name, $value) = @args{ qw(-name -value) };
279 0 0         if (defined $name && defined $value) {
280             if ($name =~ m/^_SESSION_/) {
281 0            
282 0           carp "param(): attempt to write to private parameter";
283             return undef;
284 0           }
285 0           $self->_set_status( STATUS_MODIFIED );
286             return $self->{_DATA}->{ $name } = $value;
287             }
288              
289             # USAGE: $s->param(-name=>$n);
290 0 0         # DESC: access to session data (public & private) using CGI.pm's 'named parameter' syntax.
291             return $self->{_DATA}->{ $args{'-name'} } if defined $args{'-name'};
292              
293             # USAGE: $s->param($name, $value);
294             # USAGE: $s->param($name1 => $value1, $name2 => $value2 [,...]);
295 0 0         # DESC: updates one or more **public** records using simple syntax
296 0           if ((@args % 2) == 0) {
297             my $modified_cnt = 0;
298 0           ARG_PAIR:
299 0 0         while (my ($name, $val) = each %args) {
300 0           if ( $name =~ m/^_SESSION_/) {
301 0           carp "param(): attempt to write to private parameter";
302             next ARG_PAIR;
303 0           }
304 0           $self->{_DATA}->{ $name } = $val;
305             ++$modified_cnt;
306 0           }
307 0           $self->_set_status(STATUS_MODIFIED);
308             return $modified_cnt;
309             }
310              
311             # If we reached this far none of the expected syntax were
312 0           # detected. Syntax error
313             croak "param(): usage error. Invalid syntax";
314             }
315              
316              
317 0     0      
318             sub delete { $_[0]->_set_status( STATUS_DELETED ) }
319              
320              
321             *header = \&http_header;
322             my $avoid_single_use_warning_again = *header;
323 0     0     sub http_header {
324 0           my $self = shift;
325             return $self->query->header(-cookie=>$self->cookie, -type=>'text/html', @_);
326             }
327              
328 0     0     sub cookie {
329             my $self = shift;
330 0            
331 0           my $query = $self->query();
332             my $cookie= undef;
333 0 0          
    0          
334 0           if ( $self->is_expired ) {
335             $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, -expires=> '-1d', @_ );
336             }
337 0           elsif ( my $t = $self->expire ) {
338             $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, -expires=> '+' . $t . 's', @_ );
339             }
340 0           else {
341             $cookie = $query->cookie( -name=>$self->name, -value=>$self->id, @_ );
342 0           }
343             return $cookie;
344             }
345              
346              
347              
348              
349              
350 0     0     sub save_param {
351 0           my $self = shift;
352             my ($query, $params) = @_;
353 0   0        
354 0   0       $query ||= $self->query();
355             $params ||= [ $query->param ];
356 0            
357 0 0         for my $p ( @$params ) {
358 0 0         my @values = $query->param($p) or next;
359 0           if ( @values > 1 ) {
360             $self->param($p, \@values);
361 0           } else {
362             $self->param($p, $values[0]);
363             }
364 0           }
365             $self->_set_status( STATUS_MODIFIED );
366             }
367              
368              
369              
370 0     0     sub load_param {
371 0           my $self = shift;
372             my ($query, $params) = @_;
373 0   0        
374 0   0       $query ||= $self->query();
375             $params ||= [ $self->param ];
376 0            
377 0           for ( @$params ) {
378             $query->param(-name=>$_, -value=>$self->param($_));
379             }
380             }
381              
382              
383 0     0     sub clear {
384 0           my $self = shift;
385             my $params = shift;
386 0 0         #warn ref($params);
387 0 0         if (defined $params) {
388             $params = [ $params ] unless ref $params;
389             }
390 0           else {
391             $params = [ $self->param ];
392             }
393 0            
  0            
394 0           for ( grep { ! /^_SESSION_/ } @$params ) {
395             delete $self->{_DATA}->{$_};
396 0           }
397             $self->_set_status( STATUS_MODIFIED );
398             }
399              
400              
401 0     0     sub find {
402 0           my $class = shift;
403             my ($dsn, $coderef, $dsn_args);
404              
405 0 0         # find( \%code )
406 0           if ( @_ == 1 ) {
407             $coderef = $_[0];
408             }
409             # find( $dsn, \&code, \%dsn_args )
410 0           else {
411             ($dsn, $coderef, $dsn_args) = @_;
412             }
413 0 0 0        
      0        
414 0           unless ( $coderef && ref($coderef) && (ref $coderef eq 'CODE') ) {
415             croak "find(): usage error.";
416             }
417 0            
418 0 0         my $driver;
419 0           if ( $dsn ) {
420 0           my $hashref = $class->parse_dsn( $dsn );
421             $driver = $hashref->{driver};
422 0   0       }
423 0           $driver ||= "file";
424 0           my $pm = "CGI::Session::Driver::" . ($driver =~ /(.*)/)[0];
425 0 0         eval "require $pm";
426 0           if (my $errmsg = $@ ) {
427             return $class->set_error( "find(): couldn't load driver." . $errmsg );
428             }
429 0            
430 0 0         my $driver_obj = $pm->new( $dsn_args );
431 0           unless ( $driver_obj ) {
432             return $class->set_error( "find(): couldn't create driver object. " . $pm->errstr );
433             }
434 0            
435             my $dont_update_atime = 0;
436 0     0     my $driver_coderef = sub {
437 0           my ($sid) = @_;
438 0 0         my $session = $class->load( $dsn, $sid, $dsn_args, $dont_update_atime );
439 0           unless ( $session ) {
440             return $class->set_error( "find(): couldn't load session '$sid'. " . $class->errstr );
441 0           }
442 0           $coderef->( $session );
443             };
444 0 0          
445             defined($driver_obj->traverse( $driver_coderef ))
446 0           or return $class->set_error( "find(): traverse seems to have failed. " . $driver_obj->errstr );
447             return 1;
448             }
449              
450             # $Id: Session.pm 353 2006-12-05 02:10:19Z markstos $
451              
452             #line 632
453              
454             # pass a true value as the fourth parameter if you want to skip the changing of
455             # access time This isn't documented more formally, because it only called by
456             # find().
457             sub load {
458             my $class = shift;
459             return $class->set_error( "called as instance method") if ref $class;
460             return $class->set_error( "Too many arguments") if @_ > 4;
461              
462             my $self = bless {
463             _DATA => {
464             _SESSION_ID => undef,
465             _SESSION_CTIME => undef,
466             _SESSION_ATIME => undef,
467             _SESSION_REMOTE_ADDR => $ENV{REMOTE_ADDR} || "",
468             #
469             # Following two attributes may not exist in every single session, and declaring
470             # them now will force these to get serialized into database, wasting space. But they
471             # are here to remind the coder of their purpose
472             #
473             # _SESSION_ETIME => undef,
474             # _SESSION_EXPIRE_LIST => {}
475             }, # session data
476             _DSN => {}, # parsed DSN params
477             _OBJECTS => {}, # keeps necessary objects
478             _DRIVER_ARGS=> {}, # arguments to be passed to driver
479             _CLAIMED_ID => undef, # id **claimed** by client
480             _STATUS => 0, # status of the session object
481             _QUERY => undef # query object
482             }, $class;
483              
484             my ($dsn,$query_or_sid,$dsn_args,$update_atime);
485             # load($query||$sid)
486             if ( @_ == 1 ) {
487             $self->_set_query_or_sid($_[0]);
488             }
489             # Two or more args passed:
490             # load($dsn, $query||$sid)
491             elsif ( @_ > 1 ) {
492             ($dsn, $query_or_sid, $dsn_args,$update_atime) = @_;
493              
494             # Since $update_atime is not part of the public API
495             # we ignore any value but the one we use internally: 0.
496             if (defined $update_atime and $update_atime ne '0') {
497             return $class->set_error( "Too many arguments");
498             }
499              
500             if ( defined $dsn ) { # <-- to avoid 'Uninitialized value...' warnings
501             $self->{_DSN} = $self->parse_dsn($dsn);
502             }
503             $self->_set_query_or_sid($query_or_sid);
504              
505             # load($dsn, $query, \%dsn_args);
506              
507             $self->{_DRIVER_ARGS} = $dsn_args if defined $dsn_args;
508              
509             }
510              
511             $self->_load_pluggables();
512              
513             if (not defined $self->{_CLAIMED_ID}) {
514             my $query = $self->query();
515             eval {
516             $self->{_CLAIMED_ID} = $query->cookie( $self->name ) || $query->param( $self->name );
517             };
518             if ( my $errmsg = $@ ) {
519             return $class->set_error( "query object $query does not support cookie() and param() methods: " . $errmsg );
520             }
521             }
522              
523             # No session is being requested. Just return an empty session
524             return $self unless $self->{_CLAIMED_ID};
525              
526             # Attempting to load the session
527             my $driver = $self->_driver();
528             my $raw_data = $driver->retrieve( $self->{_CLAIMED_ID} );
529             unless ( defined $raw_data ) {
530             return $self->set_error( "load(): couldn't retrieve data: " . $driver->errstr );
531             }
532            
533             # Requested session couldn't be retrieved
534             return $self unless $raw_data;
535              
536             my $serializer = $self->_serializer();
537             $self->{_DATA} = $serializer->thaw($raw_data);
538             unless ( defined $self->{_DATA} ) {
539             #die $raw_data . "\n";
540             return $self->set_error( "load(): couldn't thaw() data using $serializer:" .
541             $serializer->errstr );
542             }
543             unless (defined($self->{_DATA}) && ref ($self->{_DATA}) && (ref $self->{_DATA} eq 'HASH') &&
544             defined($self->{_DATA}->{_SESSION_ID}) ) {
545             return $self->set_error( "Invalid data structure returned from thaw()" );
546             }
547              
548             # checking if previous session ip matches current ip
549             if($CGI::Session::IP_MATCH) {
550             unless($self->_ip_matches) {
551             $self->_set_status( STATUS_DELETED );
552             $self->flush;
553             return $self;
554             }
555             }
556              
557             # checking for expiration ticker
558             if ( $self->{_DATA}->{_SESSION_ETIME} ) {
559             if ( ($self->{_DATA}->{_SESSION_ATIME} + $self->{_DATA}->{_SESSION_ETIME}) <= time() ) {
560             $self->_set_status( STATUS_EXPIRED ); # <-- so client can detect expired sessions
561             $self->_set_status( STATUS_DELETED ); # <-- session should be removed from database
562             $self->flush(); # <-- flush() will do the actual removal!
563             return $self;
564             }
565             }
566              
567             # checking expiration tickers of individuals parameters, if any:
568             my @expired_params = ();
569             while (my ($param, $max_exp_interval) = each %{ $self->{_DATA}->{_SESSION_EXPIRE_LIST} } ) {
570             if ( ($self->{_DATA}->{_SESSION_ATIME} + $max_exp_interval) <= time() ) {
571             push @expired_params, $param;
572             }
573             }
574             $self->clear(\@expired_params) if @expired_params;
575              
576             # We update the atime by default, but if this (otherwise undocoumented)
577             # parameter is explicitly set to false, we'll turn the behavior off
578             if ( ! defined $update_atime ) {
579             $self->{_DATA}->{_SESSION_ATIME} = time(); # <-- updating access time
580             $self->_set_status( STATUS_MODIFIED ); # <-- access time modified above
581             }
582            
583             return $self;
584             }
585              
586              
587             # set the input as a query object or session ID, depending on what it looks like.
588             sub _set_query_or_sid {
589             my $self = shift;
590             my $query_or_sid = shift;
591             if ( ref $query_or_sid){ $self->{_QUERY} = $query_or_sid }
592             else { $self->{_CLAIMED_ID} = $query_or_sid }
593             }
594              
595              
596             sub _load_pluggables {
597             my ($self) = @_;
598              
599             my %DEFAULT_FOR = (
600             driver => "file",
601             serializer => "default",
602             id => "md5",
603             );
604             my %SUBDIR_FOR = (
605             driver => "Driver",
606             serializer => "Serialize",
607             id => "ID",
608             );
609             my $dsn = $self->{_DSN};
610             foreach my $plug qw(driver serializer id) {
611             my $mod_name = $dsn->{ $plug };
612             if (not defined $mod_name) {
613             $mod_name = $DEFAULT_FOR{ $plug };
614             }
615             if ($mod_name =~ /^(\w+)$/) {
616              
617             # Looks good. Put it into the dsn hash
618             $dsn->{ $plug } = $mod_name = $1;
619              
620             # Put together the actual module name to load
621             my $prefix = join '::', (__PACKAGE__, $SUBDIR_FOR{ $plug }, q{});
622             $mod_name = $prefix . $mod_name;
623              
624             ## See if we can load load it
625             eval "require $mod_name";
626             if ($@) {
627             my $msg = $@;
628             return $self->set_error("couldn't load $mod_name: " . $msg);
629             }
630             }
631             else {
632             # do something here about bad name for a pluggable
633             }
634             }
635             return;
636             }
637 0     0      
638 0 0         #line 947
639 0 0          
640             *expires = \&expire;
641 0   0       my $prevent_warning = \&expires;
642             sub etime { $_[0]->expire() }
643             sub expire {
644             my $self = shift;
645              
646             # no params, just return the expiration time.
647             if (not @_) {
648             return $self->{_DATA}->{_SESSION_ETIME};
649             }
650             # We have just a time
651             elsif ( @_ == 1 ) {
652             my $time = $_[0];
653             # If 0 is passed, cancel expiration
654             if ( defined $time && ($time =~ m/^\d$/) && ($time == 0) ) {
655             $self->{_DATA}->{_SESSION_ETIME} = undef;
656             $self->_set_status( STATUS_MODIFIED );
657             }
658             # set the expiration to this time
659             else {
660             $self->{_DATA}->{_SESSION_ETIME} = $self->_str2seconds( $time );
661             $self->_set_status( STATUS_MODIFIED );
662             }
663 0           }
664             # If we get this far, we expect expire($param,$time)
665 0 0         # ( This would be a great use of a Perl6 multi sub! )
    0          
666 0           else {
667             my ($param, $time) = @_;
668             if ( ($time =~ m/^\d$/) && ($time == 0) ) {
669             delete $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{ $param };
670             $self->_set_status( STATUS_MODIFIED );
671 0           } else {
672             $self->{_DATA}->{_SESSION_EXPIRE_LIST}->{ $param } = $self->_str2seconds( $time );
673             $self->_set_status( STATUS_MODIFIED );
674             }
675 0 0 0       }
676 0           return 1;
677             }
678              
679 0 0         # =head2 _str2seconds()
680 0           #
681             # my $secs = $self->_str2seconds('1d')
682 0           #
683             # Takes a CGI.pm-style time representation and returns an equivalent number
684             # of seconds.
685             #
686 0 0         # See the docs of expire() for more detail.
687             #
688             # =cut
689              
690 0           sub _str2seconds {
691             my $self = shift;
692 0 0         my ($str) = @_;
693 0            
694 0           return unless defined $str;
695 0   0       return $str if $str =~ m/^[-+]?\d+$/;
696              
697 0 0         my %_map = (
698 0           s => 1,
699             m => 60,
700             h => 3600,
701             d => 86400,
702             w => 604800,
703 0 0         M => 2592000,
704             y => 31536000
705             );
706 0            
707 0           my ($koef, $d) = $str =~ m/^([+-]?\d+)([smhdwMy])$/;
708 0 0         unless ( defined($koef) && defined($d) ) {
709 0           die "_str2seconds(): couldn't parse '$str' into \$koef and \$d parts. Possible invalid syntax";
710             }
711             return $koef * $_map{ $d };
712             }
713 0 0          
714              
715 0           #line 1160
716 0            
717 0 0         sub remote_addr { return $_[0]->{_DATA}->{_SESSION_REMOTE_ADDR} }
718              
719 0           #line 1366
720              
721             1;
722 0 0 0        
      0        
      0