# -*- perl -*-
#
# Copyright (c) 2003-2007 by Jeremy Kister
# Author: Jeremy Kister <argus-devel at jeremykister.com>
# Date: 2007/07/09 16:06 (EDT)
# Function:  authenticate via sql server
#
# take a look at http://jeremy.kister.net/
# for hosting/colo/Internet stuff, check out http://www.nntx.net

# Table structure for table `users`
#
# CREATE TABLE `users` (
#   `id` int(10) unsigned NOT NULL auto_increment,
#   `username` varchar(16) NOT NULL default '',
#   `crypt` char(13) NOT NULL default '',
#   `home` varchar(255) NOT NULL default '',
#   `pref` varchar(255) NOT NULL default '-',
#   `groups` varchar(255) NOT NULL default '',
#   PRIMARY KEY (`id`),
#   KEY `username` (`username`),
# ) TYPE=MyISAM COMMENT='argus.example.net user data' AUTO_INCREMENT=1 ;
#
# Table structure for table `sessions`
#
# CREATE TABLE `sessions` (
#   `id` int(10) unsigned NOT NULL,
#   `sess_id` char(64) NOT NULL default '',
#   `start` int(10) unsigned NOT NULL default '0',
#   `hush` int(10) unsigned NOT NULL default '0',
#   PRIMARY KEY (`sess_id`),
# ) TYPE=MyISAM COMMENT='argus.example.net session data' ;
#
#  INSERT INTO argus VALUES ('anonymous', 'any', 'Top', 'user');


use DBI;

my $dbun = 'db_username';
my $dbpw = 'db_password';
my $dsn = 'DBI:mysql:host=mysql.example.net;database=argus';
my $dbh;

sub db_connect {
	my $tries = 1;
	until(dbping($dbh)){
		eval {
			local $SIG{ALRM} = sub { die "DBI connect timeout!\n"; };
			alarm(5);
			$dbh = DBI->connect($dsn, $dbun, $dbpw);

			alarm(0);
		};
		alarm(0);
		sleep 1;
		$tries++;

		if($tries == 3){ # arbitrary
			die "cannot connect to database after 3 tries.\n";
		}
	}
	$dbh;
}

sub auth_session {
	my $sess_id = shift;
	
	# returns (username,start,last,home,groups) on success
	#		undef on failure

	eval {
		local $SIG{ALRM} = sub { die "auth_session timeout!\n"; };
		alarm(5);
		
		db_connect() unless(dbping($dbh));

		my $sql = 'SELECT users.username,sessions.start,sessions.hush,users.home,sessions.pref,users.groups';
		$sql .= ' FROM users,sessions';
		$sql .= ' WHERE users.id = sessions.id AND sessions.sess_id = ' . $dbh->quote($sess_id);
		my $sth = $dbh->prepare($sql);
		$sth->execute;
		my $row = $sth->fetchrow_arrayref;
		
		alarm(0);
		return (@{$row} == 6) ? @{$row} : undef;
	};
	alarm(0);
	if($@){
		warn "timed out connecting to database ($@): allowing root login from [$ENV{'REMOTE_ADDR'}].\n";
		return('root',time(),0,'Top','-','root'); # dont want to be blind if db is off-line
	}
	return;
}

sub auth_user {
	my $user = shift;
	my $pass = shift;
	my $sess_id = shift;

	# returns (home_obj, groups) on success
	#         undef on failure
   
	eval {
		local $SIG{ALRM} = sub { die "auth_user timeout!\n"; };
		alarm(5);
	
		db_connect() unless(dbping($dbh));

		my $sql = 'SELECT id,crypt,home,groups FROM users WHERE username = ' . $dbh->quote($user);
		my $sth = $dbh->prepare($sql);
		$sth->execute;
  
		my $row=$sth->fetchrow_arrayref;
		my ($id,$crypt,$home) = ($row->[0],$row->[1],$row->[2]);
		my @cgroups = split /\s+/, $row->[3];

		if( (($crypt eq crypt($pass, $crypt)) && defined($crypt)) || ($crypt eq 'any') ){
			my $sql = 'INSERT INTO sessions (id,sess_id,start) VALUES (';
			$sql .= $dbh->quote($id) . ',' . $dbh->quote($sess_id) . ',';
			$sql .= $dbh->quote(time()) . ')'; # start
			my $sth = $dbh->prepare($sql);
			$sth->execute;

			alarm(0);
			return ($home, @cgroups);
		}
		alarm(0);
	};
	alarm(0);
	if($@){
		warn "timed out connecting to database ($@): allowing root for user [${user}] from [$ENV{'REMOTE_ADDR'}].\n";
		return('Top','root'); # dont want to be blind if db is off-line
	}
	return;
}

sub dbping {
	my $test_dbh = shift;
	my $xcode = 0;
	eval {
		$SIG{ALRM} = sub { die "dbping timeout!"; };
		alarm(2);

		if($test_dbh){
			if(my $sth = $test_dbh->prepare('SELECT 1')){
				if(defined(my $rc = $sth->execute)){
					$sth->finish;
					$xcode=1; # good
				}
			}
		}
		alarm(0);
	};
	alarm(0);
	return $xcode;
}

1;
