File Coverage

blib/lib/CGI/Session/ExpireSessions.pm
Criterion Covered Total %
statement 72 107 67.2
branch 19 64 29.6
condition 6 20 30.0
subroutine 13 15 86.6
pod 3 5 60.0
total 113 211 53.5


line stmt bran cond sub pod time code
1             package CGI::Session::ExpireSessions;
2              
3             # Name:
4             # CGI::Session::ExpireSessions.
5             #
6             # Documentation:
7             # POD-style documentation is at the end. Extract it with pod2html.*.
8             #
9             # Reference:
10             # Object Oriented Perl
11             # Damian Conway
12             # Manning
13             # 1-884777-79-1
14             # P 114
15             #
16             # Note:
17             # o Tab = 4 spaces || die.
18             #
19             # Author:
20             # Ron Savage
21             # Home page: http://savage.net.au/index.html
22             #
23             # Licence:
24             # Australian copyright (c) 2004 Ron Savage.
25             #
26             # All Programs of mine are 'OSI Certified Open Source Software';
27             # you can redistribute them and/or modify them under the terms of
28             # The Artistic License, a copy of which is available at:
29             # http://www.opensource.org/licenses/index.html
30              
31 4     4   610351 use strict;
  4         33  
  4         122  
32 4     4   22 use warnings;
  4         8  
  4         175  
33              
34             require 5.005_62;
35              
36             require Exporter;
37              
38 4     4   23 use Carp;
  4         7  
  4         251  
39 4     4   1310 use CGI::Session;
  4         10928  
  4         32  
40 4     4   129 use File::Spec;
  4         10  
  4         6499  
41              
42             our @ISA = qw(Exporter);
43              
44             # Items to export into callers namespace by default. Note: do not export
45             # names by default without a very good reason. Use EXPORT_OK instead.
46             # Do not simply export all your public functions/methods/constants.
47              
48             # This allows declaration use CGI::Session::ExpireSessions ':all';
49             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
50             # will save memory.
51             our %EXPORT_TAGS = ( 'all' => [ qw(
52              
53             ) ] );
54              
55             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
56              
57             our @EXPORT = qw(
58              
59             );
60             our $VERSION = '1.14';
61              
62             # -----------------------------------------------
63              
64             # Preloaded methods go here.
65              
66             # -----------------------------------------------
67              
68             # Encapsulated class data.
69              
70             {
71             my(%_attr_data) =
72             (
73             _cgi_session_dsn => undef,
74             _dbh => '',
75             _delta => 2 * 24 * 60 * 60, # Seconds.
76             _dsn_args => undef,
77             _table_name => 'sessions',
78             _temp_dir => '/tmp',
79             _time => time(),
80             _verbose => 0,
81             );
82              
83             sub _check_expiry
84             {
85 1     1   3 my($self, $D) = @_;
86 1         3 my($expired) = 0;
87 1         2 my($time) = time();
88              
89 1 50       5 if ( ($time - $$D{'_SESSION_ATIME'}) >= $$self{'_delta'})
90             {
91 1         2 $expired = 1;
92              
93 1 50       51 print STDOUT "Delta time: $$self{'_delta'}. Time elapsed: ", $time - $$D{'_SESSION_ATIME'}, ". Expired?: $expired. \n" if ($$self{'_verbose'});
94             }
95              
96 1 50 33     12 if ($$D{'_SESSION_ETIME'} && ! $expired)
97             {
98 0 0       0 $expired = 1 if ($time >= ($$D{'_SESSION_ATIME'} + $$D{'_SESSION_ETIME'}) );
99              
100 0 0       0 print STDOUT "Last access time: $$D{'_SESSION_ATIME'}. Expiration time: $$D{'_SESSION_ETIME'}. Time elapsed: ", $time - $$D{'_SESSION_ATIME'}, ". Expired?: $expired. \n" if ($$self{'_verbose'});
101             }
102              
103 1         5 $expired;
104             }
105              
106             sub _default_for
107             {
108 10     10   21 my($self, $attr_name) = @_;
109              
110 10         33 $_attr_data{$attr_name};
111             }
112              
113             # Warning: The args hashref passed in to sub _purge() has /no/ connexion
114             # with the $self hashref with belongs to the object instantiated by our client.
115             # The client code did something like this to create an object:
116             # my($expirer) = CGI::Session::ExpireSessions -> new(delta => 1);
117             # and we, the object, i.e. $expirer, are in fact the server.
118              
119             sub _purge
120             {
121 1     1   5 my($session, $args) = @_;
122              
123 1 50       6 return if ($session -> is_empty() );
124              
125 1 50 33     18 if ($session -> is_expired() || ($$args{'_time'} - $session -> atime() >= $$args{'_delta'}) )
126             {
127 1 50       29 print STDOUT "Expiring id @{[$session -> id()]}. \n" if ($$args{'_verbose'});
  1         4  
128              
129 1         76 $session -> delete();
130 1         14 $session -> flush();
131             }
132             }
133              
134             sub _standard_keys
135             {
136 2     2   50 keys %_attr_data;
137             }
138              
139             } # End of encapsulated class data.
140              
141             # -----------------------------------------------
142              
143             sub expire_db_sessions
144             {
145 0     0 1 0 my($self, %arg) = @_;
146              
147 0 0       0 $self -> set(%arg) if (%arg);
148              
149 0 0       0 Carp::croak(__PACKAGE__ . ". You must specify a value for the parameter 'dbh'") if (! $$self{'_dbh'});
150              
151 0         0 my($sth) = $$self{'_dbh'} -> prepare("select * from $$self{'_table_name'}");
152              
153 0         0 $sth -> execute();
154              
155 0         0 my($serializer) = 'eval';
156              
157 0 0       0 if (defined $$self{_serializer})
158             {
159 0         0 $serializer = $$self{_serializer};
160             }
161              
162 0         0 my($data, $D, @id, $untainted_data);
163              
164 0         0 while ($data = $sth -> fetchrow_hashref() )
165             {
166             # Untaint the data the brute force way.
167              
168 0         0 ($untainted_data) = $$data{'a_session'} =~ /(.*)/;
169              
170 0 0       0 if ($serializer eq 'eval')
171             {
172 0         0 eval $untainted_data;
173              
174 0 0       0 push @id, $$data{id} if ($self -> _check_expiry($D) );
175             }
176             else
177             {
178 0         0 my($serializer) = "CGI::Session::Serialize::$serializer";
179 0         0 my($thawed) = $serializer -> thaw($untainted_data);
180              
181 0 0       0 push @id, $$data{id} if ($self -> _check_expiry($thawed) );
182             }
183             }
184              
185 0         0 for (@id)
186             {
187 0 0       0 print STDOUT "Expiring db id: $_. \n" if ($$self{'_verbose'});
188              
189 0         0 $sth = $$self{'_dbh'} -> prepare("delete from $$self{'_table_name'} where id = ?");
190              
191 0         0 $sth -> execute($_);
192              
193 0         0 $sth -> finish();
194             }
195              
196 0 0 0     0 if ( ($#id < 0) && $$self{'_verbose'})
197             {
198 0         0 print STDOUT "No db ids are due to expire. \n";
199             }
200              
201             } # End of expire_db_sessions.
202              
203             # -----------------------------------------------
204              
205             sub expire_file_sessions
206             {
207 1     1 1 10 my($self, %arg) = @_;
208              
209 1 50       5 $self -> set(%arg) if (%arg);
210              
211 1 50       4 Carp::croak(__PACKAGE__ . ". You must specify a value for the parameter 'temp_dir'") if (! $$self{'_temp_dir'});
212              
213 1 50       52 opendir(INX, $$self{'_temp_dir'}) || Carp::croak("Can't opendir($$self{'_temp_dir'}): $!");
214 1         36 my(@file) = map{File::Spec -> catfile($$self{'_temp_dir'}, $_)} grep{/cgisess_[0-9a-f]{32}/} readdir(INX);
  1         18  
  8         26  
215 1         22 closedir INX;
216              
217 1         5 my($count) = 0;
218 1         3 my($time) = time();
219              
220 1         3 my($file, @stat, $D);
221              
222 1         3 for my $file (@file)
223             {
224 1         18 @stat = stat($file);
225              
226             # Delete old, tiny files.
227              
228 1 50 33     13 if ( ( ($time - $stat[8]) >= $$self{'_delta'}) && ($stat[7] <= 5) )
229             {
230 0         0 $count++;
231              
232 0 0       0 print STDOUT "Delta time: $$self{'_delta'}. Size: $stat[7] bytes. Time elapsed: ", $time - $stat[8], ". Expired?: 1. \n" if ($$self{'_verbose'});
233              
234 0         0 unlink $file;
235              
236 0         0 next;
237             }
238              
239             # Ignore new, tiny files.
240              
241 1 50       3 next if ($stat[7] <= 5);
242              
243 1 50       65 open(INX, $file) || Carp::croak("Can't open($file): $!");
244 1         6 binmode INX;
245 1         31 my(@session) = ;
246 1         12 close INX;
247              
248             # Pod/perlfunc.html#item_eval
249             # This does not work:
250             # eval{no warnings 'all'; $session[0]};
251             # This was when I used to say 'eval $session[0];', but that fails
252             # when the session data contains \n characters. Hence the join.
253              
254 1         86 eval join('', @session);
255              
256 1 50       6 if ($@)
257             {
258 0 0       0 print STDOUT "Unable to parse contents of file: $file. \n" if ($$self{'_verbose'});
259              
260 0         0 next;
261             }
262              
263 1 50       6 if ($self -> _check_expiry($D) )
264             {
265 1         2 $count++;
266              
267 1 50       15 print STDOUT "Expiring file id: $$D{'_SESSION_ID'}. \n" if ($$self{'_verbose'});
268              
269 1         712 unlink $file;
270             }
271             }
272              
273 1 0 33     161 print STDOUT "No file ids are due to expire. \n" if ( ($count == 0) && $$self{'_verbose'});
274              
275             } # End of expire_file_sessions.
276              
277             # -----------------------------------------------
278              
279             sub expire_sessions
280             {
281 1     1 1 12 my($self, %arg) = @_;
282              
283 1 50       14 return if (! CGI::Session -> can('find') );
284              
285             # Return the result of find, which is:
286             # o Undef for failure
287             # o 1 for success
288              
289 1 50       5 $self -> set(%arg) if (%arg);
290              
291             return CGI::Session -> find
292             (
293             $$self{'_cgi_session_dsn'},
294             sub{_purge(@_,
295             { # This hashref is a parameter for _purge().
296             _delta => $$self{'_delta'} || 0, # These 2 defaults are in case the user sets them to undef!
297             _time => $$self{'_time'} || time(), # The defaults then stop Perl issuing warning messages about
298 1   50 1   3245 _verbose => $$self{'_verbose'}, # uninitialized variables during the call to sub _purge().
      33        
299             })},
300 1         11 $$self{'_dsn_args'}
301             );
302              
303             } # End of expire_sessions.
304              
305             # -----------------------------------------------
306              
307             sub new
308             {
309 2     2 0 180660 my($class, %arg) = @_;
310 2         9 my($self) = bless({}, $class);
311              
312 2         11 for my $attr_name ($self -> _standard_keys() )
313             {
314 16         61 my($arg_name) = $attr_name =~ /^_(.*)/;
315              
316 16 100       41 if (exists($arg{$arg_name}) )
317             {
318 6         22 $$self{$attr_name} = $arg{$arg_name};
319             }
320             else
321             {
322 10         25 $$self{$attr_name} = $self -> _default_for($attr_name);
323             }
324             }
325              
326 2         17 return $self;
327              
328             } # End of new.
329              
330             # -----------------------------------------------
331              
332             sub set
333             {
334 0     0 0   my($self, %arg) = @_;
335              
336 0           for my $arg (keys %arg)
337             {
338 0 0         $$self{"_$arg"} = $arg{$arg} if (exists($$self{"_$arg"}) );
339             }
340              
341             } # End of set.
342              
343             # -----------------------------------------------
344              
345             1;
346              
347             __END__