File Coverage

blib/lib/CGI/Session/Driver/file.pm
Criterion Covered Total %
statement 83 91 91.2
branch 29 50 58.0
condition 6 15 40.0
subroutine 15 15 100.0
pod 5 5 100.0
total 138 176 78.4


line stmt bran cond sub pod time code
1             package CGI::Session::Driver::file;
2              
3             # $Id$
4              
5 24     24   1304 use strict;
  24         42  
  24         773  
6              
7 24     24   87 use Carp;
  24         25  
  24         1507  
8 24     24   162 use File::Spec;
  24         32  
  24         685  
9 24     24   128 use Fcntl qw( :DEFAULT :flock :mode );
  24         37  
  24         11347  
10 24     24   8801 use CGI::Session::Driver;
  24         54  
  24         735  
11 24     24   105 use vars qw( $FileName $NoFlock $UMask $NO_FOLLOW );
  24         34  
  24         1506  
12              
13             BEGIN {
14             # keep historical behavior
15              
16 24     24   91 no strict 'refs';
  24         32  
  24         877  
17            
18 24     24   24782 *FileName = \$CGI::Session::File::FileName;
19             }
20              
21             @CGI::Session::Driver::file::ISA = ( "CGI::Session::Driver" );
22             $CGI::Session::Driver::file::VERSION = '4.43';
23             $FileName = "cgisess_%s";
24             $NoFlock = 0;
25             $UMask = 0660;
26             $NO_FOLLOW = eval { O_NOFOLLOW } || 0;
27              
28             sub init {
29 48     48 1 65 my $self = shift;
30 48   66     631 $self->{Directory} ||= File::Spec->tmpdir();
31              
32 48 50       1049 unless ( -d $self->{Directory} ) {
33 0         0 require File::Path;
34 0 0       0 unless ( File::Path::mkpath($self->{Directory}) ) {
35 0         0 return $self->set_error( "init(): couldn't create directory path: $!" );
36             }
37             }
38            
39 48 50       203 $self->{NoFlock} = $NoFlock unless exists $self->{NoFlock};
40 48 50       146 $self->{UMask} = $UMask unless exists $self->{UMask};
41            
42 48         292 return 1;
43             }
44              
45             sub _file {
46 87     87   147 my ($self,$sid) = @_;
47 87         149 my $id = $sid;
48 87         181 $id =~ s|\\|/|g;
49              
50 87 50       209 if ($id =~ m|/|)
51             {
52 0         0 return $self->set_error( "_file(): Session ids cannot contain \\ or / chars: $sid" );
53             }
54              
55 87         1403 return File::Spec->catfile($self->{Directory}, sprintf( $FileName, $sid ));
56             }
57              
58             sub retrieve {
59 34     34 1 50 my $self = shift;
60 34         73 my ($sid) = @_;
61              
62 34         80 my $path = $self->_file($sid);
63            
64 34 100       1092 return 0 unless -e $path;
65              
66             # make certain our filehandle goes away when we fall out of scope
67 20         75 local *FH;
68              
69 20 50       153 if (-l $path) {
70 0 0       0 unlink($path) or
71             return $self->set_error("retrieve(): '$path' appears to be a symlink and I couldn't remove it: $!");
72 0         0 return 0; # we deleted this so we have no hope of getting back anything
73             }
74 20 50       785 sysopen(FH, $path, O_RDONLY | $NO_FOLLOW ) || return $self->set_error( "retrieve(): couldn't open '$path': $!" );
75            
76 20 50 33     246 $self->{NoFlock} || flock(FH, LOCK_SH) or return $self->set_error( "retrieve(): couldn't lock '$path': $!" );
77              
78 20         39 my $rv = "";
79 20         475 while ( ) {
80 69         161 $rv .= $_;
81             }
82 20         215 close(FH);
83 20         98 return $rv;
84             }
85              
86              
87              
88             sub store {
89 35     35 1 54 my $self = shift;
90 35         68 my ($sid, $datastr) = @_;
91            
92 35         131 my $path = $self->_file($sid);
93            
94             # make certain our filehandle goes away when we fall out of scope
95 35         117 local *FH;
96            
97 35         70 my $mode = O_WRONLY|$NO_FOLLOW;
98            
99             # kill symlinks when we spot them
100 35 100       2175 if (-l $path) {
101 1 50       69 unlink($path) or
102             return $self->set_error("store(): '$path' appears to be a symlink and I couldn't remove it: $!");
103             }
104            
105 35 100       290 $mode = O_RDWR|O_CREAT|O_EXCL unless -e $path;
106            
107 35 50       4693 sysopen(FH, $path, $mode, $self->{UMask}) or return $self->set_error( "store(): couldn't open '$path': $!" );
108            
109             # sanity check to make certain we're still ok
110 35 50       376 if (-l $path) {
111 0         0 return $self->set_error("store(): '$path' is a symlink, check for malicious processes");
112             }
113            
114             # prevent race condition (RT#17949)
115 35 50 33     477 $self->{NoFlock} || flock(FH, LOCK_EX) or return $self->set_error( "store(): couldn't lock '$path': $!" );
116 35 50       1648 truncate(FH, 0) or return $self->set_error( "store(): couldn't truncate '$path': $!" );
117            
118 35         434 print FH $datastr;
119 35 50       3071 close(FH) or return $self->set_error( "store(): couldn't close '$path': $!" );
120 35         346 return 1;
121             }
122              
123              
124             sub remove {
125 16     16 1 28 my $self = shift;
126 16         33 my ($sid) = @_;
127 16         39 my $path = $self -> _file($sid);
128 16 50       2103 unlink($path) or return $self->set_error( "remove(): couldn't unlink '$path': $!" );
129 16         110 return 1;
130             }
131              
132              
133             sub traverse {
134 1     1 1 2 my $self = shift;
135 1         2 my ($coderef) = @_;
136              
137 1 50 33     10 unless ( $coderef && ref($coderef) && (ref $coderef eq 'CODE') ) {
      33        
138 0         0 croak "traverse(): usage error";
139             }
140              
141             opendir( DIRHANDLE, $self->{Directory} )
142 1 50       31 or return $self->set_error( "traverse(): couldn't open $self->{Directory}, " . $! );
143              
144 1         3 my $filename_pattern = $FileName;
145 1         2 $filename_pattern =~ s/\./\\./g;
146 1         3 $filename_pattern =~ s/\%s/(\.\+)/g;
147 1         34 while ( my $filename = readdir(DIRHANDLE) ) {
148 53 100       77 next if $filename =~ m/^\.\.?$/;
149 51         214 my $full_path = File::Spec->catfile($self->{Directory}, $filename);
150 51 50       611 my $mode = (stat($full_path))[2]
151             or return $self->set_error( "traverse(): stat failed for $full_path: " . $! );
152 51 100       102 next if S_ISDIR($mode);
153 50 100       180 if ( $filename =~ /^$filename_pattern$/ ) {
154 1         4 $coderef->($1);
155             }
156             }
157 1         10 closedir( DIRHANDLE );
158 1         5 return 1;
159             }
160              
161              
162             sub DESTROY {
163 48     48   1143 my $self = shift;
164             }
165              
166             1;
167              
168             __END__;