#! /usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; # not running under some shell
BEGIN { $main::DEFA = "/usr/share/dbman"; }

require 5.003;
use strict qw/subs vars/;
use vars qw/$VERSION $VERNAME $ORIG_0 $DEFAULT_DIR $mylib $DEBUG $port 
		$interface %Config @CHILDS $LANG %Message $DEFA/;
use POSIX "sys_wait_h";
use FileHandle;

BEGIN {
	sub help_cl {
                print <<EOF;
Usage: dbman-sql-server [OPTIONS...]
	-d	Debug information
        -h      This little help
	-i	interface (host), e.g. rosomak.kocourkov.cz
	-p	port, e.g. 2400
        -v      Print version
EOF
	}       

	$DEBUG = 0;
	$LANG = 'en';
	$LANG = $ENV{LANG} if defined $ENV{LANG};
	$LANG = $ENV{DBMAN_LANG} if defined $ENV{DBMAN_LANG};
	$VERSION = '0.0.9';
        $VERNAME = "dbMan SQL Server $VERSION";  
	use FindBin;
	$ORIG_0 = $FindBin::RealScript; 
	$0 = "dbman-sql-server";
        $interface = '';  $port = ''; 
        while (@ARGV and $ARGV[0] =~ /^-(.*)/) {
                if (uc $1 eq 'V' || uc $1 eq 'VERSION'
	                        || uc $1 eq '-VERSION') {
			print "$VERNAME\n\n";  exit;
	        } elsif (uc $1 eq 'H' || uc $1 eq 'HELP' || uc $1 eq '-HELP') {
                        print "$VERNAME\n\n";  help_cl();  exit;
	        } elsif (uc $1 eq 'I' || uc $1 eq 'INTERFACE' 
				|| uc $1 eq '-INTERFACE') {
                       	shift @ARGV;  $interface = shift @ARGV; 
	        } elsif (uc $1 eq 'P' || uc $1 eq 'PORT' 
				|| uc $1 eq '-PORT') {
                       	shift @ARGV;  $port = shift @ARGV; 
	        } elsif (uc $1 eq 'D' || uc $1 eq 'DEBUG' 
				|| uc $1 eq '-DEBUG') {
                       	shift @ARGV;  ++$DEBUG;
		} else { last; }
	}
	$DEFAULT_DIR = $DEFA;
        if ($ORIG_0 !~ /\//) { $ORIG_0 = './' . $ORIG_0; }
        $mylib = $ORIG_0;  $mylib =~ s/\/[^\/]*$//;
        unshift @INC,".";
        unshift @INC,$DEFAULT_DIR;
        unshift @INC,$mylib;
        unshift @INC,$ENV{DBMAN_LIB} if $ENV{DBMAN_LIB};
}

use Socket;
use IO::Socket;
require dbManLang;  dbManLang->import($LANG);

sub sexit { exit; }

$SIG{QUIT} = \&sexit;  $SIG{ABRT} = \&sexit;  $SIG{KILL} = \&sexit;
$SIG{INT} = \&sexit;   $SIG{TERM} = \&sexit;  $SIG{HUP} = \&sexit;
$SIG{CHLD} = \&dispatcher;

############################## SUBS ######################################

sub read_config  {
        my $config = $ENV{HOME}."/.dbman-sql-serverrc";
        unless (-e $config) {
                $config = $ORIG_0;  $config =~ s/\/[^\/]*//;
                $config .= 'dbman-sql-serverrc';  return unless -e $config;
        }
        if (open CONFIG,$config) {
                while (<CONFIG>) {
                        chomp;  s/#.*//;  s/^\s+//;  s/\s+$//;  next unless $_;
                        my ($tag,$value) = ('','');
                        if (/^(.*?)\s+(.*)/) {
                                ($tag,$value) = ($1,$2);
                        }
                        $Config{lc $tag} .= "\n" if exists $Config{lc $tag};
                        $Config{lc $tag} .= $value;
                }
                close CONFIG;
	}
}

sub do_log {
	print STDERR scalar localtime;	
	print STDERR " ", join ',',@_;
	print STDERR "\n";
}

sub dispatcher {
	my @CH = ();
	for (@CHILDS) {
		waitpid($_,WNOHANG);
		unless (kill 0 => $_) {
			waitpid $_,0;
		} else {
			push @CH,$_;
		}
	}
	@CHILDS = @CH;
	alarm(10);
}

sub process_cmd {
	my ($cmd,$rights) = @_;
	my $result = $Message{dbsunknown}."\n";

	return $result;
}

sub client_process {
	my $sock = shift;
	print $sock "$VERNAME\n\n";
	print $sock $Message{dbsuser}.": ";
	my $username = <$sock>;
	chomp $username;
	$username =~ s/^.//;
	print $sock $Message{dbspass}.": ";
	my $password = <$sock>;
	chomp $password;
	my $cpasswd = 'Xy';
	my $rights = '';
	if (open F,$Config{passwd}||"/etc/dbman-passwd") {
		while (<F>) {
			chomp;
			my ($login,$passwd);
			($login,$passwd,$rights) = split /:/;
			if ($login =~ $username) {
				$cpasswd = $passwd;
			}
		}
		close F;
	} else {
		if ($username eq 'root') {
			$password = 'root_ok';
			$cpasswd = 'XymealqLLFHLs';
			$rights = '@';
		}
	}
	if ($password eq '' && $cpasswd ne 'Xy') {
		my $sol = "Xy";
		if ($cpasswd) {
			$sol = $1 if $cpasswd =~ /^(..)/;
		}
		print $sock $Message{dbsinicrypt}.": $sol\n";
		print $sock $Message{dbscrypt}.": ";
		my $crypted = <$sock>;
		chomp $crypted;
		if ($crypted ne $cpasswd) {
			print $sock $Message{dbspermd}."\n";
			return 1;
		}
	} else {
		if (crypt ($password,$cpasswd) ne $cpasswd) {
			print $sock $Message{dbspermd}."\n";
			return 1;
		}
	}
	for (;;) {
		print $sock "> ";
		my $cmd = <$sock>;
		last if $cmd =~ /^(quit|exit|logout|stop|break)$/i;
		print $sock process_cmd($cmd,$rights);
	}
	print $sock $Message{dbsbye}."\n";
}

############################## MAIN ######################################

read_config;

my $sock = new IO::Socket::INET(LocalHost => $interface || $Config{host} 
		|| 'localhost',
		LocalPort => $port || $Config{port} || 2400, Proto => 'tcp',
		Listen => 5, Reuse => 1) or die $Message{socknot}.": $!";

$SIG{ALRM} = \&dispatcher;

@CHILDS = ();

alarm(10);

while (my $new_sock = $sock->accept()) {
	my $host = $new_sock->peerhost();
	$host = gethostbyaddr(inet_aton($host),AF_INET);
	if (defined $Config{maxchilds} and 
			scalar @CHILDS >= $Config{maxchilds}) {
		do_log sprintf($Message{toomany},$host);
		close $new_sock;
		next;
	}
	my $pid = fork();
	if (not defined $pid) {
		do_log $Message{proxyerror};
	} else {
		unless ($pid) { # child
			do_log sprintf($Message{dbsconnect},$host); 
			close $sock; $| = 1;  $/="\012";
			$0 = "dbman-sql-server-session $host";
			select ((select($new_sock), $|=1)[0]);
			client_process($new_sock);
			close $new_sock;
			do_log sprintf($Message{dbsdisconnect},$host);
			exit(0);
		} 
		push @CHILDS,$pid;
	}
	dispatcher();
	close $new_sock;
}

close $sock if defined $sock;

