use
Fcntl
qw( :DEFAULT :flock :mode )
;
use
vars
qw( $FileName $NoFlock $UMask $NO_FOLLOW )
;
BEGIN {
no
strict
'refs'
;
*FileName
= \
$CGI::Session::File::FileName
;
}
@CGI::Session::Driver::file::ISA
= (
"CGI::Session::Driver"
);
$CGI::Session::Driver::file::VERSION
=
'4.43'
;
$FileName
=
"cgisess_%s"
;
$NoFlock
= 0;
$UMask
= 0660;
$NO_FOLLOW
=
eval
{ O_NOFOLLOW } || 0;
sub
init {
my
$self
=
shift
;
$self
->{Directory} ||= File::Spec->tmpdir();
unless
( -d
$self
->{Directory} ) {
unless
( File::Path::mkpath(
$self
->{Directory}) ) {
return
$self
->set_error(
"init(): couldn't create directory path: $!"
);
}
}
$self
->{NoFlock} =
$NoFlock
unless
exists
$self
->{NoFlock};
$self
->{UMask} =
$UMask
unless
exists
$self
->{UMask};
return
1;
}
sub
_file {
my
(
$self
,
$sid
) =
@_
;
my
$id
=
$sid
;
$id
=~ s|\\|/|g;
if
(
$id
=~ m|/|)
{
return
$self
->set_error(
"_file(): Session ids cannot contain \\ or / chars: $sid"
);
}
return
File::Spec->catfile(
$self
->{Directory},
sprintf
(
$FileName
,
$sid
));
}
sub
retrieve {
my
$self
=
shift
;
my
(
$sid
) =
@_
;
my
$path
=
$self
->_file(
$sid
);
return
0
unless
-e
$path
;
local
*FH
;
if
(-l
$path
) {
unlink
(
$path
) or
return
$self
->set_error(
"retrieve(): '$path' appears to be a symlink and I couldn't remove it: $!"
);
return
0;
}
sysopen
(FH,
$path
, O_RDONLY |
$NO_FOLLOW
) ||
return
$self
->set_error(
"retrieve(): couldn't open '$path': $!"
);
$self
->{NoFlock} ||
flock
(FH, LOCK_SH) or
return
$self
->set_error(
"retrieve(): couldn't lock '$path': $!"
);
my
$rv
=
""
;
while
( <FH> ) {
$rv
.=
$_
;
}
close
(FH);
return
$rv
;
}
sub
store {
my
$self
=
shift
;
my
(
$sid
,
$datastr
) =
@_
;
my
$path
=
$self
->_file(
$sid
);
local
*FH
;
my
$mode
= O_WRONLY|
$NO_FOLLOW
;
if
(-l
$path
) {
unlink
(
$path
) or
return
$self
->set_error(
"store(): '$path' appears to be a symlink and I couldn't remove it: $!"
);
}
$mode
= O_RDWR|O_CREAT|O_EXCL
unless
-e
$path
;
sysopen
(FH,
$path
,
$mode
,
$self
->{UMask}) or
return
$self
->set_error(
"store(): couldn't open '$path': $!"
);
if
(-l
$path
) {
return
$self
->set_error(
"store(): '$path' is a symlink, check for malicious processes"
);
}
$self
->{NoFlock} ||
flock
(FH, LOCK_EX) or
return
$self
->set_error(
"store(): couldn't lock '$path': $!"
);
truncate
(FH, 0) or
return
$self
->set_error(
"store(): couldn't truncate '$path': $!"
);
print
FH
$datastr
;
close
(FH) or
return
$self
->set_error(
"store(): couldn't close '$path': $!"
);
return
1;
}
sub
remove {
my
$self
=
shift
;
my
(
$sid
) =
@_
;
my
$path
=
$self
-> _file(
$sid
);
unlink
(
$path
) or
return
$self
->set_error(
"remove(): couldn't unlink '$path': $!"
);
return
1;
}
sub
traverse {
my
$self
=
shift
;
my
(
$coderef
) =
@_
;
unless
(
$coderef
&&
ref
(
$coderef
) && (
ref
$coderef
eq
'CODE'
) ) {
croak
"traverse(): usage error"
;
}
opendir
( DIRHANDLE,
$self
->{Directory} )
or
return
$self
->set_error(
"traverse(): couldn't open $self->{Directory}, "
. $! );
my
$filename_pattern
=
$FileName
;
$filename_pattern
=~ s/\./\\./g;
$filename_pattern
=~ s/\
%s
/(\.\+)/g;
while
(
my
$filename
=
readdir
(DIRHANDLE) ) {
next
if
$filename
=~ m/^\.\.?$/;
my
$full_path
= File::Spec->catfile(
$self
->{Directory},
$filename
);
my
$mode
= (
stat
(
$full_path
))[2]
or
return
$self
->set_error(
"traverse(): stat failed for $full_path: "
. $! );
next
if
S_ISDIR(
$mode
);
if
(
$filename
=~ /^
$filename_pattern
$/ ) {
$coderef
->($1);
}
}
closedir
( DIRHANDLE );
return
1;
}
sub
DESTROY {
my
$self
=
shift
;
}
1;