package Sessions;
use strict;
#+------------------------------------------------------------------------------------------------------
#| Ikonboard [ v3.0 ]
#|
#| No parts of this script can be used outside Ikonboard without prior consent.
#| You must keep this header intact and all copyright links visable.
#| (c)2001 Ikonboard.com <http://www.ikonboard.com>
#|
#| Please Read the licence for more information.
#+------------------------------------------------------------------------------------------------------

BEGIN {
    require 'Lib/FUNC.pm';
}
my $mem    = FUNC::Member->new();
my $std    = FUNC::STD->new();


#+------------------------------------------------------------------------------------------------------

sub new {
  my $pkg = shift;
  my $obj = { 'total_guests'  => 0,
              'total_members' => 0,
              'active_forums' => {},
              'member_names'  => [],
              'anon_members'  => 0,
              'member_added'  => 0,
              'seen_name'     => {},
            }; 
  bless $obj, $pkg;
  return $obj;
}

#+------------------------------------------------------------------------------------------------------



sub active_users {
    my ($obj, $db) = @_;

    my $time = time - 900;
   
    my $total_sessions = $db->query(  TABLE     => 'active_sessions',
                                      MATCH     => 'ALL',
                                      SORT_KEY  => 'RUNNING_TIME',
                                      SORT_BY   => 'Z-A',
                                      WHERE     => "RUNNING_TIME > $time",
                                    );

    for my $session (@{$total_sessions}) {

        if ($session->{'MEMBER_NAME'}) {

            # XXX Remove Dupes (this occurs if a user is on a proxy, and their IP changes).

            next if exists $obj->{'seen_name'}->{ $session->{'MEMBER_NAME'} };
            $obj->{'seen_name'}->{ $session->{'MEMBER_NAME'} } = 1;

            $session->{'LOG_IN_TYPE'} == 1 ? $obj->{'anon_members'}++
                                           : push @{$obj->{'member_names'}}, { NAME => $session->{'MEMBER_NAME'}, ID => $session->{'MEMBER_ID'}, GROUP => $session->{MEMBER_GROUP} };
        }
        else {
            $obj->{'total_guests'}++;
        }
        
        if ($session->{'LOCATION'}) {
            my ($act, $q_string) = split (/\|&\|/, $session->{'LOCATION'});
            if ( $q_string =~ m{[&;\?]f=(\d+)(?:[&;|]|$)} ) {
                $obj->{'active_forums'}->{$1}++;
            }
        }

    }

    $obj->{'total_members'} = scalar @{$obj->{'member_names'}} || 0;
    
    return { FORUMS   => $obj->{'active_forums'},
             MEMBERS  => $obj->{'total_members'},
             GUESTS   => $obj->{'total_guests'},
             NAMES    => $obj->{'member_names'},
             ANON     => $obj->{'anon_members'}
           };

}



sub authenticate {

    my ($obj, $db) = @_;

    #Get the 'true' input from 's'

    $iB::IN{'s'} = $iB::CGI->param('s');

    #Remove non-alphanumerics

    $iB::IN{'s'} =~ s!\W!!g;

    #XXX Are they banned?
    if ($iB::INFO->{'IP_FILTER'}) {
        for my $ip (split /\|&\|/,$iB::INFO->{'IP_FILTER'}) {
            $ip =~ s/\\\*/.*/g;
            $std->Error( DB       => $db,
                         LEVEL    => 5,
                         MESSAGE  => 'you_are_banned'
                       ) if $iB::IN{'IP_ADDRESS'} =~ m#^$ip$#;
        }
    }
    
    my ($this_member, $in_member, $in_password, $method, $this_session, $member_name, $sess_id);

    delete $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBSessionID'} if $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBSessionID'} eq '-';
    delete $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBMemberID'}  if $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBMemberID'}  eq '-';
    delete $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBPassWord'}  if $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBPassWord'}  eq '-';
    delete $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'anonlogin'}   if $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'anonlogin'}   eq '-';
    
    my $session_cookie  = $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBSessionID'};
    $iB::last_activity  = $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'lastactivity'} || time;
    $iB::last_visit     = $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'lastvisit'}    || time;

    #If there hasn't been a cookie update for 2 hours, we assume that they've gone and come back...
    #So we use the last_activity time as the last time they were on the board.
    if ( (time - $iB::last_activity) > 7200) {
            $iB::last_visit = $iB::last_activity;
            push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'lastvisit', -value => $iB::last_activity, -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'});
    }
    
    # Add last activity cookie to the cookie jar
    push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'lastactivity',  -value => time,  -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
 
    # If we don't have a last visit cookie...
    unless ( $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'lastvisit'} ) {
        push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'lastvisit', -value => $iB::last_activity, -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'});
    }

    $std->Error( DB       => $db,
                 LEVEL    => 5,
                 MESSAGE  => 'poss_hack'
               ) if $session_cookie && $session_cookie !~ /\A[\d\w\S]+\Z/;
  

	$std->Error( DB      => $db,
                 LEVEL   => 5,
                 MESSAGE => 'poss_hack'
               ) if $ENV{'HTTP_USER_AGENT'} =~ /\Ateleport/i;

    if (exists($iB::IN{'UserName'})  and exists($iB::IN{'PassWord'}) ) {
        $iB::SESSION = $session_cookie || $iB::IN{'s'} || 0;
        $std->Error( DB      => $db, 
                     LEVEL   => 5,
                     MESSAGE => 'pass_too_long'
                   ) if length($iB::IN{'PassWord'}) > 32;

        $std->Error( DB      => $db,
                     LEVEL   => 5,
                     MESSAGE => 'username_long'
                   ) if length($iB::IN{'UserName'}) > 32;

        $in_member   = $iB::IN{'UserName'};
        $in_password = $mem->Crypt($iB::IN{'UserName'}, $iB::IN{'PassWord'});
        $method      = 'by name';
        goto AUTHENTICATE;
    } elsif
        ($session_cookie) {
            $this_session = $obj->get_session( DB=> $db, ID => $session_cookie);
        goto CHECK;
    } elsif
        ($iB::IN{'s'}) {
            $this_session = $obj->get_session( DB=> $db, ID => $iB::IN{'s'});
        goto CHECK;
    }

CHECK:

    $iB::IN{Privacy} = $this_session->{LOG_IN_TYPE}          || $iB::IN{Privacy} || $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'anonlogin'} || 0;
    $iB::SESSION     = $this_session->{'ID'}                 || undef;
    $in_member       = $this_session->{'MEMBER_ID'}          || $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBMemberID'} || undef;
    $in_password     = $this_session->{'MEMBER_PASSWORD'}    || $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'iBPassWord'} || undef;
    $member_name     = $this_session->{'MEMBER_NAME'}        || undef;
    $method          = ($in_member and $in_password) ? 'by id' : undef;

AUTHENTICATE:

    if ($in_member and $in_password) {
        $this_member = $mem->LoadMember( DB => $db, KEY => $in_member, METHOD => $method );
        unless ( $this_member->{'MEMBER_ID'} ) {
            if ($method eq 'by id') {
                $iB::SESSION = undef;
                goto CREATE_SESSION;
            } else {
                $obj->my_bash_cookies();
                $iB::IN{'AD'} ? $obj->invalid_admin($db) : $obj->do_log_in(MSG => "wrong_name", DB => $db);
            }
        }
        unless ($in_password eq $this_member->{'MEMBER_PASSWORD'}) {
            $obj->my_bash_cookies();
            $iB::IN{'AD'} ? $obj->invalid_admin($db) : $obj->do_log_in(MSG => "wrong_pass", DB => $db);
        }

        if ($iB::IN{'CookieDate'}) {
            push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBPassWord', -value => $this_member->{'MEMBER_PASSWORD'}, -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
            push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBMemberID', -value => $this_member->{'MEMBER_ID'},       -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
        }
        
        $iB::SESSION ?  $db->update( TABLE  => 'active_sessions',
                                     KEY    => $iB::SESSION,
                                     VALUES => { 
                                                 MEMBER_NAME      =>  $this_member->{'MEMBER_NAME'},
                                                 MEMBER_PASSWORD  =>  $this_member->{'MEMBER_PASSWORD'},
                                                 MEMBER_ID        =>  $this_member->{'MEMBER_ID'},
                                                 THIS_IP          =>  $iB::IN{'IP_ADDRESS'},
                                                 USER_AGENT       =>  $ENV{'HTTP_USER_AGENT'},
                                                 RUNNING_TIME     =>  time,
                                                 MEMBER_LOGSTATE  =>  1,
                                                 LOCATION         =>  $iB::IN{'act'}."|&|".$ENV{'QUERY_STRING'},
                                                 LOG_IN_TYPE      =>  $iB::IN{'Privacy'},
                                                 MEMBER_GROUP     =>  $this_member->{MEMBER_GROUP},
                                                }
                                    )
            
                     :   $obj->create_session( DB => $db, MEMBER => $this_member, DELETE => 1);
            
       
    } else {

CREATE_SESSION:

        $iB::SESSION ?  $db->update( TABLE  => 'active_sessions',
                                     KEY    => $iB::SESSION,
                                     VALUES => { 
                                                 MEMBER_NAME      =>  "",
                                                 MEMBER_PASSWORD  =>  "",
                                                 MEMBER_ID        =>  "",
                                                 THIS_IP          =>  $iB::IN{'IP_ADDRESS'},
                                                 USER_AGENT       =>  $ENV{'HTTP_USER_AGENT'},
                                                 RUNNING_TIME     =>  time,
                                                 MEMBER_LOGSTATE  =>  1,
                                                 LOCATION         =>  $iB::IN{'act'}."|&|".$ENV{'QUERY_STRING'},
                                                 LOG_IN_TYPE      =>  $iB::IN{'Privacy'},
                                                 MEMBER_GROUP     =>  $iB::INFO->{GUEST_GROUP},
                                               }
                                   )
         
                    :   $obj->create_session( DB => $db, MEMBER => undef, DELETE => 1);
         
    }

    push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBSessionID', -value => $iB::SESSION, -expires => '+1d', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'});

    $this_member = $mem->SetUpGuest() unless $this_member->{'MEMBER_ID'};

    $iB::MEMBER_GROUP = $db->select( TABLE  => 'mem_groups',
                                     KEY    => $this_member->{'MEMBER_GROUP'},
                                   );

    unless ($iB::MEMBER_GROUP->{'VIEW_BOARD'} or $iB::IN{'act'} eq 'Login' or $iB::IN{'act'} eq 'LostPass') {
        $std->Error( DB => $db, LEVEL => 1, MESSAGE => 'no_view_board');
    }

    #X Is their email addy banned?
    if ($iB::INFO->{'EMAIL_FILTER'}) {
        for my $em (split /\|&\|/,$iB::INFO->{'EMAIL_FILTER'}) {
            $em =~ s/\\\*/.*/g;
            $std->Error( DB       => $db,
                         LEVEL    => 5,
                         MESSAGE  => 'you_are_banned'
                       ) if $this_member->{'MEMBER_EMAIL'} =~ m#^$em$#;
        }
    }
    # Do we have skin?
    if ($this_member->{'MEMBER_SKIN'} and ($iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'skin'} eq '-' or $iB::COOKIES->{$iB::INFO->{'COOKIE_ID'}.'skin'} eq '') ) {
        push @{$iB::COOKIES_OUT}, $iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'skin', -value => $this_member->{'MEMBER_SKIN'}, -expires => '+1y', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
    }

    return $this_member;

}


sub invalid_admin {
    my ($obj, $db) = @_;

    my @log;

    if (-e $iB::INFO->{'DB_DIR'}."Temp/log-$iB::MEMBER->{'MEMBER_ID'}.cgi") {
        open LOG, "<".$iB::INFO->{'DB_DIR'}."Temp/log-$iB::MEMBER->{'MEMBER_ID'}.cgi" or die $!;
        @log = <LOG>;
        close LOG or die $!;
    
        if (scalar (@log) > 5) {
            $std->Error( DB        => $db,
                         LEVEL     => 1,
                         MESSAGE   => 'too_many_logins'
                         );
        }
    }

    open LOG, ">>".$iB::INFO->{'DB_DIR'}."Temp/log-$iB::MEMBER->{'MEMBER_ID'}.cgi" or die $!;
    print LOG time."\n";
    close LOG or die $!;

    my $num = 5 - (scalar @log + 1);
    
    $std->Error( DB      => $db,
                 STD     => $std,
                 MESSAGE => 'invalid_login'
               );
    
}



sub get_session {
    my $obj = shift;

    $ENV{'HTTP_USER_AGENT'} =~ s!\n!!;

    my $IN  = { DB     => "",
                ID     => "",
                @_,
              };

    my $session = $IN->{'DB'}->select( TABLE => 'active_sessions', KEY => $IN->{'ID'} );

    return {} unless $session->{'ID'};
    
    if ($session->{'MEMBER_ID'}) {

        if ($iB::IN{'IP_ADDRESS'} ne $session->{'THIS_IP'}) {
            return {};
        }
       
        if ($iB::INFO->{'CHECK_USER_AGENT'}) {
            return {} unless $ENV{'HTTP_USER_AGENT'} eq $session->{'USER_AGENT'};
        }
    }
    return $session;
}




sub create_session {
    my $obj = shift;

    my $IN  = { DB     => "",
                MEMBER => "",
                DELETE => "",
                @_,
              };

    my $db         = $IN->{'DB'};
    my $member     = $IN->{'MEMBER'};
    $iB::SESSION   = $obj->my_gen_id();

    my $logstate   = $member->{'MEMBER_ID'} ? 1 : 0;
    $member->{MEMBER_GROUP} ||= $iB::INFO->{GUEST_GROUP};

    $obj->clean_sessions( DB => $db, DELETE => $IN->{'DELETE'});

    $db->insert( TABLE   => 'active_sessions',
                 VALUES  =>  { ID               =>  $iB::SESSION,
                               MEMBER_NAME      =>  $member->{'MEMBER_NAME'},
                               MEMBER_PASSWORD  =>  $member->{'MEMBER_PASSWORD'},
                               MEMBER_ID        =>  $member->{'MEMBER_ID'},
                               THIS_IP          =>  $iB::IN{'IP_ADDRESS'},
                               LAST_LOG_IN      =>  time,
                               USER_AGENT       =>  $ENV{'HTTP_USER_AGENT'},
                               RUNNING_TIME     =>  time,
                               MEMBER_LOGSTATE  =>  $logstate,
                               LOCATION         =>  $iB::IN{'act'}."|&|".$ENV{'QUERY_STRING'},
                               LOG_IN_TYPE      =>  $iB::IN{'Privacy'},
                               MEMBER_GROUP     =>  $member->{MEMBER_GROUP},
                             }
                );    
}


sub clean_sessions {
    my $obj = shift;
    my $IN  = { DB => "", DELETE => "", @_, };

    my $db = $IN->{'DB'};

    $iB::INFO->{'SESSION_EXPIRATION'} = (time - $iB::INFO->{'SESSION_EXPIRATION'});

    $IN->{'DELETE'} = " or THIS_IP eq '$iB::IN{'IP_ADDRESS'}'" if $IN->{'DELETE'} == 1;

    $db->delete( TABLE    => 'active_sessions',
                 WHERE    => "RUNNING_TIME < $iB::INFO->{'SESSION_EXPIRATION'}$IN->{'DELETE'}",
                 SORT_KEY => 'RUNNING_TIME',
               );
    return 1;
}


sub my_gen_id {
    my $obj = shift;
    srand($$|time);
    my $session = int(rand(600000000000));
    return unpack("H*", pack("Nnn", time, $$, $session));

}

sub do_log_in {
    my $obj = shift;

    my %IN = ( DB  => "",
               MSG => "",
               OR  => "",
               @_,
             );
    unless ($IN{OR}) {
        $iB::SKIN   = $std->LoadSkin();
        $iB::INFO->{'IMAGES_URL'}       .= '/' . $iB::SKIN->{'FULL_DIR'};
        $obj->my_bash_cookies();
    }
    require LogInOut;
    my $idx = LogInOut->new()->ShowForm($IN{'DB'}, $IN{'MSG'});
    iB::exit();

}


sub my_bash_cookies {
    my $obj = shift;
    push @{$iB::COOKIES_OUT},$iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBSessionID', -value => '-', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
    push @{$iB::COOKIES_OUT},$iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBMemberID',  -value => '-', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
    push @{$iB::COOKIES_OUT},$iB::CGI->cookie( -name => $iB::INFO->{'COOKIE_ID'}.'iBPassWord',  -value => '-', -path => $iB::INFO->{'COOKIE_PATH'}, -domain => $iB::INFO->{'COOKIE_DOMAIN'} );
}




1;
