The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package CGI::Portal::Sessions;
# Copyright (c) 2008 Alexander David P. All rights reserved.
#
# Authentication and Session class

use strict;

use Digest::MD5 qw(md5_hex);

use vars qw($VERSION);

$VERSION = "0.12";

1;

sub new {
  my ($class, $i) = @_;
  bless $i, $class;
  return $i;
}

            # Verify password or session
sub authenticate_user {
  my $self = shift;

            # User is logging in
  if ($self->{'in'}{'user'} && $self->{'in'}{'password'}){

            # Get users stored password hash
    my $users = $self->{'rdb'}->exec("select $self->{'conf'}{'user_user_field'},$self->{'conf'}{'user_passw_field'} from $self->{'conf'}{'user_table'} where $self->{'conf'}{'user_user_field'} like " . $self->{'rdb'}->escape($self->{'in'}{'user'}))->fetch;

            # Compare password hashes
    if (md5_hex($self->{'in'}{'password'}) eq $users->[1]){

            # Assign user to object
      $self->{'user'} = $users->[0];

            # Start session
      $self->start_session($users->[0]);

            # Clean sessions
      $self->clean_sessions();
      return;
    }
  }elsif (my $sid = getcookie('sid')){

            # Session expiration
    my $exps = time() - $self->{'conf'}{'session_length'};

            # Get session start
    my $sessions = $self->{'rdb'}->exec("select $self->{'conf'}{'session_user_field'},$self->{'conf'}{'session_start_field'} from $self->{'conf'}{'session_table'} where $self->{'conf'}{'session_sid_field'}=" . $self->{'rdb'}->escape($sid))->fetch;

            # Session not expired
    if ($sessions->[0] && $sessions->[1] >= $exps){

            # Assign user to object
      $self->{'user'} = $sessions->[0];

            # Renew session
      $self->renew_session($self->{'user'});
      return;
    }
  }

            # Assign tmpl
  $self->assign_tmpl("Sessions.html");
}

            # Create a session
sub start_session {
  my ($self, $user) = @_;
  my $current_time = time();

            # Generate a session id
  my $sid;
  my $cids;
  while ($cids->[0] || ! $sid) {
    $sid = md5_hex($$ , time() , rand(8888) );
    $cids = $self->{'rdb'}->exec("select $self->{'conf'}{'session_index_field'} from $self->{'conf'}{'session_table'} where $self->{'conf'}{'session_sid_field'} = $sid limit 1")->fetch;
  }

            # Get current session index
  my $cc = $self->{'rdb'}->exec("select $self->{'conf'}{'session_index_field'} from $self->{'conf'}{'session_table'} order by $self->{'conf'}{'session_index_field'} desc limit 1")->fetch;
  my $c = $cc->[0]+1;

            # Insert session and prepare cookie
  $self->{'rdb'}->exec("insert into $self->{'conf'}{'session_table'} ($self->{'conf'}{'session_index_field'},$self->{'conf'}{'session_sid_field'},$self->{'conf'}{'session_user_field'},$self->{'conf'}{'session_start_field'}) values (" . $self->{'rdb'}->escape($c, $sid, $user, $current_time) . ")");
  $self->{'cookies'} .= "Set-Cookie: sid=$sid; path=/\n";
}

            # Update session start
sub renew_session {
  my $self = shift;
  my $sid = getcookie('sid');
  my $current_time = time();
  $self->{'rdb'}->exec("update $self->{'conf'}{'session_table'} set $self->{'conf'}{'session_start_field'}=$current_time where $self->{'conf'}{'session_sid_field'}=" . $self->{'rdb'}->escape($sid));
}

            # Remove session
sub logoff {
  my $self = shift;
  my $sid = getcookie('sid');
  $self->{'rdb'}->exec("delete from $self->{'conf'}{'session_table'} where $self->{'conf'}{'session_sid_field'}=" . $self->{'rdb'}->escape($sid));
  $self->{'user'} = "";
}

            # Remove expired sessions
sub clean_sessions {
  my $self = shift;
  my $exps = time() - $self->{'conf'}{'session_length'};
  $self->{'rdb'}->exec("delete from $self->{'conf'}{'session_table'} where $self->{'conf'}{'session_start_field'} < $exps");
}

            # Assign template output to object out
sub assign_tmpl {
  my ($self, $i) = @_;

            # Read template
  my $template = HTML::Template->new(die_on_bad_params => 0, filename => "$self->{'conf'}{'template_dir'}$i");
  $self->{'tmpl_vars'}{'SCRIPT_NAME'} = $ENV{'SCRIPT_NAME'};

            # Assign vars to template
  $template->param(%{$self->{'tmpl_vars'}});

            # Assign template output to object out
  $self->{'out'} = $template->output;
}

sub getcookie {
  my $cookiename = shift;
  my $cookie;
  my $value;
  if ($ENV{'HTTP_COOKIE'}) {
    foreach (split(/; /,$ENV{'HTTP_COOKIE'})) {
      ($cookie,$value) = split(/=/);
      if ($cookiename eq $cookie) {
        return $value;
      }
    }
  }
}