File Coverage

blib/lib/CGI/Session/ExpireSessions.pm
Criterion Covered Total %
statement 64 100 64.0
branch 17 58 29.3
condition 3 20 15.0
subroutine 11 15 73.3
pod 3 5 60.0
total 98 198 49.4


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 3     3   474848 use strict;
  3         7  
  3         110  
32 3     3   18 use warnings;
  3         7  
  3         128  
33              
34             require 5.005_62;
35              
36             require Exporter;
37              
38 3     3   17 use Carp;
  3         10  
  3         228  
39 3     3   1177 use CGI::Session;
  3         6224  
  3         17  
40 3     3   78 use File::Spec;
  3         7  
  3         4978  
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.13';
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 2     2   4 my($self, $D) = @_;
86 2         4 my($expired) = 0;
87 2         4 my($time) = time();
88              
89 2 50       9 if ( ($time - $$D{'_SESSION_ATIME'}) >= $$self{'_delta'})
90             {
91 2         3 $expired = 1;
92              
93 2 50       401 print STDOUT "Delta time: $$self{'_delta'}. Time elapsed: ", $time - $$D{'_SESSION_ATIME'}, ". Expired?: $expired. \n" if ($$self{'_verbose'});
94             }
95              
96 2 50 33     17 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 2         10 $expired;
104             }
105              
106             sub _default_for
107             {
108 10     10   16 my($self, $attr_name) = @_;
109              
110 10         37 $_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 0     0   0 my($session, $args) = @_;
122              
123 0 0       0 return if ($session -> is_empty() );
124              
125 0 0 0     0 if ($session -> is_expired() || ($$args{'_time'} - $session -> atime() >= $$args{'_delta'}) )
126             {
127 0 0       0 print STDOUT "Expiring id @{[$session -> id()]}. \n" if ($$args{'_verbose'});
  0         0  
128              
129 0         0 $session -> delete();
130 0         0 $session -> flush();
131             }
132             }
133              
134             sub _standard_keys
135             {
136 2     2   18 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($data, $D, @id, $untainted_data);
156              
157 0         0 while ($data = $sth -> fetchrow_hashref() )
158             {
159             # Untaint the data the brute force way.
160              
161 0         0 ($untainted_data) = $$data{'a_session'} =~ /(.*)/;
162              
163 0         0 eval $untainted_data;
164              
165 0 0       0 push @id, $$data{'id'} if ($self -> _check_expiry($D) );
166             }
167              
168 0         0 for (@id)
169             {
170 0 0       0 print STDOUT "Expiring db id: $_. \n" if ($$self{'_verbose'});
171              
172 0         0 $sth = $$self{'_dbh'} -> prepare("delete from $$self{'_table_name'} where id = ?");
173              
174 0         0 $sth -> execute($_);
175              
176 0         0 $sth -> finish();
177             }
178              
179 0 0 0     0 if ( ($#id < 0) && $$self{'_verbose'})
180             {
181 0         0 print STDOUT "No db ids are due to expire. \n";
182             }
183              
184             } # End of expire_db_sessions.
185              
186             # -----------------------------------------------
187              
188             sub expire_file_sessions
189             {
190 1     1 1 2 my($self, %arg) = @_;
191              
192 1 50       6 $self -> set(%arg) if (%arg);
193              
194 1 50       6 Carp::croak(__PACKAGE__ . ". You must specify a value for the parameter 'temp_dir'") if (! $$self{'_temp_dir'});
195              
196 1 50       30 opendir(INX, $$self{'_temp_dir'}) || Carp::croak("Can't opendir($$self{'_temp_dir'}): $!");
197 1         41 my(@file) = map{File::Spec -> catfile($$self{'_temp_dir'}, $_)} grep{/cgisess_[0-9a-f]{32}/} readdir(INX);
  2         27  
  8         21  
198 1         16 closedir INX;
199              
200 1         3 my($count) = 0;
201 1         3 my($time) = time();
202              
203 1         2 my($file, @stat, $D);
204              
205 1         3 for my $file (@file)
206             {
207 2         34 @stat = stat($file);
208              
209             # Delete old, tiny files.
210              
211 2 50 33     19 if ( ( ($time - $stat[8]) >= $$self{'_delta'}) && ($stat[7] <= 5) )
212             {
213 0         0 $count++;
214              
215 0 0       0 print STDOUT "Delta time: $$self{'_delta'}. Size: $stat[7] bytes. Time elapsed: ", $time - $stat[8], ". Expired?: 1. \n" if ($$self{'_verbose'});
216              
217 0         0 unlink $file;
218              
219 0         0 next;
220             }
221              
222             # Ignore new, tiny files.
223              
224 2 50       5 next if ($stat[7] <= 5);
225              
226 2 50       65 open(INX, $file) || Carp::croak("Can't open($file): $!");
227 2         7 binmode INX;
228 2         34 my(@session) = ;
229 2         19 close INX;
230              
231             # Pod/perlfunc.html#item_eval
232             # This does not work:
233             # eval{no warnings 'all'; $session[0]};
234             # This was when I used to say 'eval $session[0];', but that fails
235             # when the session data contains \n characters. Hence the join.
236              
237 2         172 eval join('', @session);
238              
239 2 50       9 if ($@)
240             {
241 0 0       0 print STDOUT "Unable to parse contents of file: $file. \n" if ($$self{'_verbose'});
242              
243 0         0 next;
244             }
245              
246 2 50       9 if ($self -> _check_expiry($D) )
247             {
248 2         3 $count++;
249              
250 2 50       236 print STDOUT "Expiring file id: $$D{'_SESSION_ID'}. \n" if ($$self{'_verbose'});
251              
252 2         175 unlink $file;
253             }
254             }
255              
256 1 50 33     121 print STDOUT "No file ids are due to expire. \n" if ( ($count == 0) && $$self{'_verbose'});
257              
258             } # End of expire_file_sessions.
259              
260             # -----------------------------------------------
261              
262             sub expire_sessions
263             {
264 1     1 1 3 my($self, %arg) = @_;
265              
266 1 50       20 return if (! CGI::Session -> can('find') );
267              
268             # Return the result of find, which is:
269             # o Undef for failure
270             # o 1 for success
271              
272 1 50       5 $self -> set(%arg) if (%arg);
273              
274 0   0 0   0 return CGI::Session -> find
      0        
275             (
276             $$self{'_cgi_session_dsn'},
277             sub{_purge(@_,
278             { # This hashref is a parameter for _purge().
279             _delta => $$self{'_delta'} || 0, # These 2 defaults are in case the user sets them to undef!
280             _time => $$self{'_time'} || time(), # The defaults then stop Perl issuing warning messages about
281             _verbose => $$self{'_verbose'}, # uninitialized variables during the call to sub _purge().
282             })},
283 1         11 $$self{'_dsn_args'}
284             );
285              
286             } # End of expire_sessions.
287              
288             # -----------------------------------------------
289              
290             sub new
291             {
292 2     2 0 194506 my($class, %arg) = @_;
293 2         11 my($self) = bless({}, $class);
294              
295 2         14 for my $attr_name ($self -> _standard_keys() )
296             {
297 16         53 my($arg_name) = $attr_name =~ /^_(.*)/;
298              
299 16 100       35 if (exists($arg{$arg_name}) )
300             {
301 6         24 $$self{$attr_name} = $arg{$arg_name};
302             }
303             else
304             {
305 10         43 $$self{$attr_name} = $self -> _default_for($attr_name);
306             }
307             }
308              
309 2         25 return $self;
310              
311             } # End of new.
312              
313             # -----------------------------------------------
314              
315             sub set
316             {
317 0     0 0   my($self, %arg) = @_;
318              
319 0           for my $arg (keys %arg)
320             {
321 0 0         $$self{"_$arg"} = $arg{$arg} if (exists($$self{"_$arg"}) );
322             }
323              
324             } # End of set.
325              
326             # -----------------------------------------------
327              
328             1;
329              
330             __END__