#! /usr/bin/perl

#   msqlconfig - Script to configure the Mini SQL engine
#   Copyright (c) 1996  Martin Schulze <joey@debian.org>

#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.

#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


$cfg = "/etc/msql.acl";
$head = "#  /etc/msql.acl - access list file for mSQL database engine
#  Written by Martin Schulze <joey\@debian.org>
#
#  See msql.acl(5) for details.
#
#  This file is generated/modified by msqlconfig.
#
";

$def_port = 4333;
$deleted = 'DeLeTeD DatAbAsE';
%default = ('SuppressMail', 'no',
	    'MailTo', 'msql',
	    'Port', '',
	    'Debug', '',
	    'Configured', 'no',
	    );
@options = ('SuppressMail','MailTo','Port','Debug');
%database = ();

# Possible options per database
#
@dboptions = ("read","write","host","option","access");

# Possible values per option
#
%values = ('Debug', 'cache,query,error,key,malloc,trace,mmap,general',
	   'option', 'ident',
	   'access', 'local,remote'
	   );

sub read_config
{
    local ($db);

    open (CFG, "$cfg") || die "Can't open config $cfg";
    while ($line = <CFG>) {
	if ($line =~ m/^#\s*(.*)=(.*)$/) {
	    $option{$1} = $2;
	} elsif ($line =~ m/^database=(.*)$/) {
	    $db = $1;
	    push (@databases,$db);
	} elsif ($line =~ m/^(.*)=(.*)$/) {
	    $database{$db,$1} = $2;
	}
    }
    close (CFG);
}

sub write_config
{
    open (CFG, ">$cfg") || die "Can't open config $cfg";

    print CFG $head;
    foreach $opt (keys(%option)) {
	if (length($option{$opt})) {
	    printf CFG "# %s=%s\n", $opt, $option{$opt};
	}
    }
    foreach $db (@databases) {
	printf CFG "\ndatabase=%s\n", $db;
	foreach $opt (@dboptions) {
	    if (length($database{$db,$opt})) {
		printf CFG "%s=%s\n", $opt, $database{$db,$opt};
	    }
	}
    }
    system "chown msql.msql $cfg";
    system "chmod 770 $cfg";
}

# first argument is a list of possible options, separated by a komma
#
sub check_option
{
    local ($part);

    (@poss) = split (/,/,shift(@_));

    local ($okopt) = 1;
    foreach $part (@_) {
	if (!grep(/^$part$/,@poss)) {
	    $okopt = 0;
	    printf "\t'%s' is not a valid option.\n", $part;
	    return $okopt;
	}
    }
    return $okopt;
}

sub first_config
{
    local ($ans);
    local ($dflt);

    if ($option{SuppressMail} eq 'yes') { $dflt = 'Y/n';}
    else { $dflt = 'y/N';}
    printf "\tSuppress output of msqld (may be sent via mail) [$dflt]? "; 
    $ans = <STDIN>; chop $ans; $_ = $ans;
    /[Yy]/ && ($option{SuppressMail} = 'yes');
    /[Nn]/ && ($option{SuppressMail} = 'no');

    if ($option{SuppressMail} eq 'no') {
	do {
	    if (length($option{MailTo})) { $dflt = $option{MailTo}; }
	    else { $dflt = '';}
	    printf "\tSend msqld output to [$dflt]? "; 
	    $ans = <STDIN>; chop $ans;
	    $option{MailTo} = $ans if (length($ans));
	} until (length($option{MailTo}));
    }

    do {
	if (length($option{Port})) { $dflt = $option{Port}; }
	else { $dflt = $def_port;}
	printf "\tOn which port shall the mSQL engine listen [$dflt]? "; 
	$ans = <STDIN>; chop $ans;
	$option{Port} = $ans if (length($ans));
      } until (!length($option{Port}) || ($option{Port} > 1024));

    $option{Configured} = 'yes';
    do write_config();
}

sub general_config
{
    local ($ans);
    local ($dflt);
    local ($okopt);

    $ans = '';
    do {
	printf "\nGeneral Options\n\n";
	printf "Actual configuration:\n\n";
	foreach $opt (@options) {
	    printf "\t%s=%s\n", $opt, $option{$opt};
	}
	printf "\n";

	do {
	    printf "\tSelect first letter to change or 'q' to quit this menue: ";
	    $ans = <STDIN>; chop $ans;
	} until ($ans =~ /[SsMmPpDdQq]/);

	if ($ans =~ /[Ss]/) { # SuppressMail
	    if ($option{SuppressMail} eq 'yes') { $dflt = 'Y/n';}
	    else { $dflt = 'y/N';}
	    printf "\tSuppress output of msqld (may be sent via mail) [$dflt]? "; 
	    $ans = <STDIN>; chop $ans; $_ = $ans;
	    /[Yy]/ && ($option{SuppressMail} = 'yes');
	    /[Nn]/ && ($option{SuppressMail} = 'no');
	} elsif ($ans =~ /[Mm]/) { # MailTo
	    do {
		if (length($option{MailTo})) { $dflt = $option{MailTo}; }
		else { $dflt = '';}
		printf "\tSend msqld output to [$dflt]? "; 
		$ans = <STDIN>; chop $ans;
		$option{MailTo} = $ans if (length($ans));
	    } until (length($option{MailTo}));
	} elsif ($ans =~ /[Pp]/) { # Port
	    do {
		if (length($option{Port})) { $dflt = $option{Port}; }
		else { $dflt = $def_port;}
		printf "\tOn which port shall the mSQL engine listen [$dflt]? "; 
		$ans = <STDIN>; chop $ans;
		$option{Port} = $ans if (length($ans));
		$option{Port} = '' if ($ans = $def_port);
	    } until (!length($option{Port}) || ($option{Port} > 1024));
	} elsif ($ans =~ /[Dd]/) { # Debug
	    printf "\tChoose any of these values to enable debugging.  Use a colon `:' as\n";
	    printf "\tdelimiter.  Possible values: %s\n", $values{Debug};
	    do {
		printf "\tEnter new debugging options: ";
		$ans = <STDIN>; chop $ans;
		if (!length($ans)) { $okopt = 1; }
		else {
		    $okopt = &check_option($values{Debug},split(/:/,$ans));
		}
	    } until ($okopt);
	    $option{Debug} = $ans;
	}

    } until ($ans =~ /[Qq]/);
}

sub query_databases
{
    local ($db);
    local ($select);
    local ($count);
    local ($ans);

#    printf "Configured databases:\n\n";
    $count = 0;
    foreach $db (@databases) {
	printf "\t%2d  %s\n", $count, $db;
	$count++;
    }
    print "\n";
    print "\t'*' is a pseudo database which matches all unspecified databases, so\n";
    print "\tbetter don't change it.\n\n";
    do {
	printf "\tSelect database, `n' to create a new one, 'd' to delete: ";
	$ans = <STDIN>; chop $ans;
	if ($ans =~ /[Nn]/) {
	    printf "Enter the name of the new database: ";
	    $ans = <STDIN>; chop $ans;
	    push (@databases, $ans);
	    $select = $ans;
	} elsif ($ans =~ /[Dd]/) {
	    printf "\tSelect Database to delete: ";
	    $ans = <STDIN>; chop $ans;
	    if (($ans >= 0) && ($ans < $count)) {
		printf "\tDatabase '%s' removed.\n", $databases[$ans];
		splice(@databases,$ans,1);
	    }
	    $select = $deleted;
	} else {
	    $select = $databases[$ans] if (($ans >= 0) && ($ans < $count));
	}
    } until (length($select));
    return $select;
}

sub db_config
{
    local ($db);
    local ($opt);
    local ($ans);

    printf "\nDatabase configuration\n\n";
    $db = &query_databases();
    if ($db ne $deleted) {
	do {
	    printf "\nActual configuration for $db:\n\n";
	    foreach $opt (@dboptions) {
		printf "\t%s=%s\n", $opt, $database{$db,$opt};
	    }
	    printf "\n";

	    do {
		printf "Select first letter to change or 'q' to quit: ";
		$ans = <STDIN>; chop $ans;
	    } until ($ans =~ /[RrWwHhOoAaQq]/);

	    if ($ans =~ /[Rr]/) { # read
		printf "\n\tEnter login names of persons who are permitted to read the database,\n";
		printf "\t`*' for any `-*' for none.  Use the comma as delimiter.\n";
		printf "\tEnter login names: ";
		$inp = <STDIN>; chop $inp;
		$database{$db,'read'} = $inp;
	    } elsif ($ans =~ /[Ww]/) { # write
		printf "\n\tEnter login names of persons who shall have write-access to the database,\n";
		printf "\t`*' for any `-*' for none.  Use the comma as delimiter.\n";
		printf "\tEnter login names: ";
		$inp = <STDIN>; chop $inp;
		$database{$db,'write'} = $inp;
	    } elsif ($ans =~ /[Hh]/) { # host
		printf "\n\tEnter hosts (wildcards are ok) that shall have access to the database\n";
		printf "\taccording to read/write options from above.  Use the comma as delimiter\n";
		printf "\tEnter hostnames: ";
		$inp = <STDIN>; chop $inp;
		$database{$db,'host'} = $inp;
	    } elsif ($ans =~ /[Oo]/) { # option
		printf "\n\tEnter additional options for this database.  Actually only `ident' is allowed.\n";
#		printf "\taccording to read/write options from above.  Use the comma as delimiter\n";
		do {
		    printf "\tEnter options: ";
		    $inp = <STDIN>; chop $inp;
		    if (!length($inp)) { $okopt = 1; }
		    else { $okopt = &check_option($values{option},split(/,/,$inp)); }
		} until ($okopt);
		$database{$db,'option'} = $inp;
	    } elsif ($ans =~ /[Aa]/) { # access
		printf "\n\tChoose any of these values as access method.  Use a comma `:' as\n";
		printf "\tdelimiter.  Possible values: %s\n", $option{access};
		do {
		    printf "\tEnter new access methods: ";
		    $inp = <STDIN>; chop $inp;
		    if (!length($inp)) { $okopt = 1; }
		    else { $okopt = &check_option($values{access},split(/,/,$inp)); }
		} until ($okopt);
		$database{$db,'access'} = $inp;
	    }
	} until ($ans =~ /[Qq]/);
    }
}

sub menue
{
    local ($ans);
    $ans = '';
    do {
	printf "\nMain Menue\n\n";
	printf "\tG) eneral options\n";
	printf "\tD) atabase configuration\n";
	printf "\tW) rite configuration\n";
	printf "\tQ) uit (without saving)\n\n";
	do {
	    printf "\tSelect: ";
	    $ans = <STDIN>; chop $ans;
	} until ($ans =~ /[GgDdWwQq]/);
	do general_config() if ($ans =~ /[Gg]/);
	do db_config() if ($ans =~ /[Dd]/);
	do write_config() if ($ans =~ /[Ww]/);
    } until ($ans =~ /[Qq]/);
}

# --------------------- M A I N - P R O G R A M -------------------------

$msqluid = (getpwnam("msql"))[2]; 

if (!(($> == 0) || ($> == $msqluid))) {
    print "Sorry, only 'root' or 'msql' can do that.\n";
    exit 1;
}

$not_configured = 0;
$not_configured = 1 if (($#ARGV > -1) && ($ARGV[0] eq '--not-configured'));

printf "       --  Configuring mini SQL database engine  --\n";

do read_config();

if ($option{Configured} ne 'yes') {
    print "This is the first configuration on this machine so we start with the\n";
    print "basic stuff.\n";
    print "\n";

    do first_config();

    print "\n";
    print "You should now configure your databases.  This is normally done \n";
    print "by `msqlconfig' which has to be run as `root' or `msql'.\n";
}

do menue() if ($not_configured == 0);

print "\n";
print "If you have changed anything you should now reload the configuration\n";
print "via `msqladmin reload' as `root' or `msql'.\n";


