#############################################################################
## Name:        File.pm
## Purpose:     AutoSession::Driver::File
## Author:      Graciliano M. P.
## Modified by:
## Created:     20/5/2003
## RCS-ID:      
## Copyright:   (c) 2003 Graciliano M. P.
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################

package AutoSession::Driver::File ;
our $VERSION = '1.0' ;

use strict qw(vars) ;

no warnings ;

use vars qw(@ISA) ;
@ISA = qw(AutoSession::Driver) ;

#################
# LOAD_STORABLE #
#################

sub load_storable {
  $INC{'Log/Agent.pm'} = '#ignore#' ;
  eval(q`use Storable qw(thaw freeze) ;`) ;
}

#######
# NEW #
#######

sub new {
  &load_storable() ;
  
  my $class = shift ;
  my ( %args ) = @_ ;

  my $this = {} ;
  
  bless($this,$class) ;
  
  $this->{id} = $args{id} ;
  $this->{idsize} = $args{idsize} || $AutoSession::DEF_IDSIZE ;

  if (defined $args{directory} && !defined $args{dir}) { $args{dir} = $args{directory} ;}
  if (defined $args{dir} && $args{dir} eq '') { $args{dir} = '.' ;}
  
  $this->{dir} = $args{dir} ;
  if ($this->{dir} eq '' || !-d $this->{dir}) { $this->{dir} = '/tmp' ;}
  
  $this->{dir} =~ s/[\\\/]+$//gs ;
  
  if ($this->{id} eq '') {
    $this->{id} = $this->new_id ;
    $this->{file} = $this->filename( $this->{id} ) ;
  }
  else {
    $this->{file} = $this->exist_id( $this->{id} ) || $this->filename( $this->{id} ) ;
  }
  
  $this->{expire} = $args{expire} || $AutoSession::DEF_EXPIRE ;
  
  if (!defined $args{expire}) { $this->{defexpire} = 1 ;}
  
  $this->{expire} = $this->parse_expire($this->{expire}) ;

  $this->{base64} = $args{base64} ;
  
  $this->{nocreate} = $args{nocreate} ;
  
  ## Create file if needed:
  $this->create if !$this->{nocreate} ;
  
  if (!-e $this->{file}) { return( undef ) ;}

  return( $this ) ;
}

##########
# CREATE #
##########

sub create {
  my $this = shift ;
  
  if ($this->{nocreate}) { return ;}
  
  if (!-e $this->{file}) {
    my $fh ;
    open ($fh,">$this->{file}") ; binmode($fh) ;
    close($fh) ;
    return( 1 ) ;
  }

  return( undef ) ;
}

##########
# DELETE #
##########

sub delete {
  my $this = shift ;
  
  if (-e $this->{file}) {
    my $v = unlink($this->{file}) ;
    $this->{closed} = 1 ;
    return( 1 ) if (!-e $this->{file}) ;
  }
  return( undef ) ;
}

########
# TIME #
########

sub time {
  my $this = shift ;
  my @stats = stat($this->{file}) ;
  if (! $stats[7] ) { return( 0 ) ;}
  return( $stats[9] ) ;
}

########
# LOAD #
########

sub load {
  my $this = shift ;
  
  if ($this->{closed}) { return( undef ) ;}
  
  if (!-s $this->{file}) {
    $this->{tree} = {} ;
    $this->{time} = 0 ;
  }
  else {
    my ($data,$header,$hsz,$fh) ;

    open ($fh,$this->{file}) ; binmode($fh) ;
    
    while($hsz !~ />/s) { my $n = read($fh , $hsz , 1 , length($hsz) ) ; last if !$n ;}
    $hsz =~ s/\D//gs ;
    
    read($fh , $header , $hsz) ;
    
    1 while( read($fh , $data , 1024*8 , length($data) ) )  ;
    
    close($fh) ;

    my %headers = $this->parse_header($header) ;    

    if ( $this->{defexpire} ) {
      $this->{expire} = $headers{expire} if $headers{expire} ;
    }
    
    if ( $headers{base64} ) {
      require AutoSession::Base64 ;
      $data = &AutoSession::Base64::decode_base64($data) ;
    }
  
    $this->{tree} = Storable::thaw($data) ;

    $this->{time} = $this->time ;
  }

  return( $this->{tree} ) ;
}

########
# SAVE #
########

sub save {
  my $this = shift ;
  
  if (!$this->{tree} || $this->{closed}) { return( undef ) ;}
  
  if ($this->{nocreate} && !-e $this->{file}) { return ;}
  
  if ( !ref($this->{tree}) ) { $this->{tree} = {} ;}

  my $data = Storable::freeze($this->{tree}) ;
  
  if ( $this->{base64} ) {
    require AutoSession::Base64 ;
    $data = &AutoSession::Base64::encode_base64($data) ;
  }
  
  my $fh ;
  open ($fh,">$this->{file}") ; binmode($fh) ;
  
  print $fh $this->header ;
  print $fh $data ;
  
  close($fh) ;
  
  return( 1 ) ;
}

#########
# LOCAL #
#########

sub local {
  my $this = shift ;
  return( $this->{file} ) ;
}

############
# EXIST_ID #
############

sub exist_id {
  my $this = shift ;
  my ( $id ) = @_ ;
  
  if ($id eq '') { $id = $this->{id} ;}
  
  my @file = $this->filename($id) ;
  
  foreach my $file ( @file ) {
    if (-e $file) { return( $file ) ;}
  }
  
  return( undef ) ;
}

#################
# CHECK_EXPIRED #
#################

sub check_expired {
  my $this = shift ;

  my $dh ; opendir($dh, $this->{dir}) ;

  while (my $filename = readdir $dh) {
    if ($filename =~ /^SESSION-(\w+)\.(?:tmp|hpl)$/s) {
      my $id = $1 ;
      my $file = "$this->{dir}/$filename" ;
      
      my @stats = stat($file) ;
      my $size = @stats[7] ;
      my $mdtime = @stats[9] ;      
      
      if ($id ne $this->{id} && ($size || ($size == 0 && (time-$mdtime) > 60*60*24) ) ) {
        my %headers = $this->get_file_header($file) ;
        my $idle = time - $headers{time} ;
        if ($idle >= $headers{expire}) { unlink($file) ;}
      }
    }
  }

  closedir($dh) ;
}

###################
# GET_FILE_HEADER #
###################

sub get_file_header {
  my $this = shift ;
  my $file = $_[0] || $this->{file} ;
  
  if (-s $file) {
    my $fh ; open ($fh,$file) ; binmode($fh) ;
    
    my $sz ;
    while($sz !~ />/s) {
      my $n = read($fh , $sz , 1 , length($sz) ) ;
      last if !$n ;
    }
    $sz =~ s/\D//gs ;
    
    my $data ;
    read($fh , $data , $sz) ;
    
    close($fh);
    
    return $this->parse_header($data) ;
  }

  return() ;
}

############
# FILENAME #
############

sub filename {
  my $this = shift ;
  my ( $id ) = @_ ;
  
  my $file = $this->{dir} . "/SESSION-$id" ;
  
  if ( wantarray ) {
    return( "$file.tmp" , "$file.hpl" ) ;
  }
  
  if ( $AutoSession::WITH_HPL ) { $file .= '.hpl' ;}
  else { $file .= '.tmp' ;}

  return( $file ) ;
}

##########
# HEADER #
##########

sub header {
  my $this = shift ;
  
  my $time = time() ;
  my $id = $this->{id} ;
  my $expire = $this->{expire} ;
  my $version = $AutoSession::VERSION ;
  my $base64 = $this->{base64} ;
  
  my $header = "AutoSession:$version:$time:$expire:$base64:$id" ;
  $header = length($header) . ">$header" ;
  
  if ( $AutoSession::WITH_HPL ) { $header = "#!hidden\n" . $header ;}
  
  return($header) ;
}

################
# PARSE_HEADER #
################

sub parse_header {
  my $this = shift ;
  my ( $header ) = @_ ;
  
  my ($module , $ver , $time , $expire , $base64 , $id) = split(":" , $header , 6) ;
  
  if ($module eq 'AutoSession' && $ver == $AutoSession::VERSION) {
    my %header = (
    time => $time ,
    expire => $expire ,
    id => $id ,
    base64 => $base64 ,
    ) ;

    return( %header ) ;
  }

  return() ;
}

#######
# END #
#######

1;