#!/usr/bin/perl

#  af-arch installer: a simple perl script to install af-arch
#  Copyright (C) 2004  Advanced Software Production Line, S.L.
#
#  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

$host     = "dolphin.aspl.es"; 
$version  = "v0.2.4";
$log      = enable;

sub error {
    my $exit = $_[1];
    print "[\e[31merror\e[0m]: $_[0]\n";
    if ($exit eq false) {
        return;
    }
    exit;
}

sub msg {
    print "[\e[32mmsg\e[0m]: $_[0]\n";
}

sub check_program {
    @list_of_programs = @_;
    foreach $program (@list_of_programs) {
        print "checking for $program..";
        if (system "which $program > /dev/null") {
            print "\n";
            error "$program not found. You will need this to run this installer.";
        }else {
            print "ok\n";
        }
    }
    return;
}

sub check_pkgconfig {
    @list_of_packages = @_;
    foreach $dependency (@list_of_packages) {
        print "checking for $dependency..";
        if (! system "pkg-config --exists $dependency") {
            print "ok\n";
        }else{
            print "\n";
            error "unable to find $dependency installation. You will need to install this package to run this installer.";
        }
    }
    return;
}

sub execute {
    my $command = $_[0];
    my $message = $_[1];
    my $exit    = $_[2];
    my $result;

    if ($log eq disabled) {
        $redirect = "> /dev/null 2>&1";
    }else {
        $redirect = "";
    }
    $result = system "$command $redirect";

    if ($result) {
        if (defined ($message)) {
            error "unable to execute '$command' $message", $exit;
        }else {
            error "unable to execute '$command'", $exit;
        }
    }
    return $result;
}

sub get_svn_host {
    print "where is located the svn host[$host] ?\n";
    print "--> ";
    chomp ($newhost = <STDIN>);
    if (length ($newhost) > 0) { 
        $host = $newhost;
    }

    print "ok, using $host as svn host.\n\n";
}

sub download_af_arch_from_svn {

    get_svn_host ();

    $svn_path = "https://$host/svn/publico/af-arch";

    msg "Downloading af-arch components";
    execute "svn co $svn_path", "maybe you don't have svn installed";

    return;
}

sub get_prefix_directory {
    print "what prefix directory should I use [/usr] ?\n";
    print "--> ";
    chomp ($prefix = <STDIN>);
    if (length ($prefix) == 0) { 
        $prefix = "/usr"; 
    }

    print "ok, using $prefix as prefix directory.\n\n";
}

sub get_sysconf_directory {
    print "what config directory should I use [/etc] ?\n";
    print "--> ";
    chomp ($sysconf = <STDIN>);
    if (length ($sysconf) == 0) { 
        $sysconf = "/etc"; 
    }
    print "ok, using $sysconf as sysconf directory\n\n";
}

sub get_log_file {
    print "Where should I install log file [/var/log/af-kernel.log] ?\n";
    print "--> ";
    chomp ($logfile = <STDIN>);
    if (length ($logfile) == 0) { 
        $logfile = "/var/log/af-kernel.log"; 
    }
    print "ok, using $logfile as log\n\n";
}

sub get_server_name {
    my $newvalue;
    chomp ($hostname = `hostname`);
    print "Setting visible name for the af-kernel:\n\n";
    print "The visible name will be used by the af-arch clients to reach the af-kernel server.\n";
    print "You can also use ip addresses or auto to let af-kernel to figure out what are the\n";
    print "names for the running computer. If doubt use: auto.\n\n";
    print "What is the visible server name [$hostname] ?\n";
    print "--> ";
    chomp ($newvalue = <STDIN>);
    if (length ($newvalue) > 0) {
        $hostname = $newvalue;
    }

    return;
}

sub get_server_passwd {
    my $newvalue;
    print "What is the password that af-kernel is going to use to connect to Postgresql?\n";

    while (1) {
        system "stty -echo";
        print "--> ";
        chomp ($newvalue = <STDIN>);
        system "stty echo";
        print "\n";

        if (length ($newvalue) == 0) {
            print "password cannot be empty. Try again\n";
            next;
        }
    
        
        system "stty -echo";
        print "Repeat: \n";
        print "--> ";
        chomp ($newvalue2 = <STDIN>);
        system "stty echo";
        print "\n";


        if ($newvalue2 eq $newvalue) {
            $hostpass = $newvalue2;
            print "password ok\n";
            last;
        }else {
            print "password missmatch. Try again\n";
        }
    }

    return;
}

sub get_server_port {
    my $newvalue;
    $hostport = "55000";
    print "what is the server tcp port [$hostport] ?\n";
    print "--> ";
    chomp ($newvalue = <STDIN>);
    if (length ($newvalue) > 0) {
        $hostport = $newvalue;
    }

    return;
}

sub initial_greetings {
    print "\n\Af-arch installer $version: a simple perl script to install ar-arch\n";
    print "Copyright (C) 2004 Advanced Software Production Line, S.L.\n\n";

    print "To install Af-arch consist of serveral steps that must be taken.\n";
    print "You can let this program to follow all steps until the end. But,\n";
    print "if something goes wrong you can relaunch all steps or relaunch \n";
    print "each step manually in order to get more control over the\n";
    print "installation.\n\n";
    print "During the process there will apper some questions to guide the\n";
    print "installation. This questions have defaults answers inside\n";
    print "[brackets] that you may choose by simply pressing Enter.\n";
    print "There are also questions like: (Y/n). Here capital letter is the\n";
    print "default options also selected by pressing Enter.\n\n";
    print "Remember:\n";
    print "If you have any problem, questions or suggestions mail us now at:\n";
    print "  <aspl-fact-devel\@lists.aspl.es>\n\n";

    print "Press Enter to show the help any time you want.\n";
    print "Press Enter to continue the process.\n";
    
    return;
}

sub compile_and_install_af_arch {
    msg "Now, we are going to compile af-arch..\n";
    
    get_prefix_directory ();
    
    get_sysconf_directory ();
    
    get_csharp_binding_generation ();

    get_doxygen_generation ();

    msg "configuring af-arch";
    execute "cd af-arch/trunk; ./autogen.sh --prefix=$prefix --sysconfdir=$sysconf $doxygen $csharp_bindings $af_arch_gtk", "you have to satisfy all dependencies: \n\n  readline\n  glib2.0 (at least 2.6)\n  openssl\n  bison\n  flex\nLater, at run-time you will need a postgresql running";
    msg "compiling af-arch";
    execute "cd af-arch/trunk; make";
    msg "installing af-arch";
    execute "cd af-arch/trunk; make install", "you don't have enough permissions, maybe you need to be root";
    
    msg "af-arch installed";
}

sub configure_af_kernel {
    msg "configuring af-kernel";
    $file1 = "/etc/af-arch/af-kernel.cfg";
    $file2 = "/etc/af-arch/af-kernel.cfg.example";
    if (-e $file1) {
        print "\nIt seems that you already have an af-kernel config file inside\n";
        print "$file1.\n";
        print "Should I reinstall it? (Y/n)\n";
        
        print "--> ";
        chomp ($line = <STDIN>);
        if ($line =~ /n|N|No|no/) {
            msg "leaving af-kernel actual configuration";
            return;
        }
    }
    msg "installing new af-kernel config file: $file1";
    rename $file2, $file1;

    get_server_name ();

    get_server_port ();

    get_server_passwd ();

    get_log_file ();

    open CFG, "< $file1" or error "unable to open $file1 for read";
    @file = <CFG>;
    close CFG;

    @new_file = ();
    foreach (@file) {
        $_ =~ s/listening port = (.*)/listening port = 55000/;
        $_ =~ s/listening hostname = (.*)/listening hostname = $hostname/;
        $_ =~ s/database password = (.*)/database password = $hostpass/;
        $_ =~ s/log file = (.*)/log file = $logfile\n/;

        push @new_file, $_;
    }

    open CFG, "> $file1" or error "unable to open $file1 to write";
    print CFG @new_file;
    close CFG;
    
    msg "setting permissions to $file1";
    execute "chmod 0600 $file1";

    msg "af-kernel configured";
    
}

sub install_af_kernel_sql () {

    # ask for user 
    print "\nNow, to install all af-kernel SQL schema, I need a valid\n";
    print "PostgresSQL user to make a tcp localhost connection.\n";

  init_af_kenel_sql:
    while (1) {
        chomp ($whoami = `whoami`); 
        print "Postgres user to install sql schema? [$whoami] "; 
        chomp ($user = <STDIN>); 
        if (length ($user) == 0) { 
            $user = $whoami; 
        } 
        $tries = 0;
      list_databases:
        msg "checking actual database installed..";
        $command = "psql -l -h localhost -U $user 2>&1";
        @db_list = `$command`;
        
        foreach (@db_list) {
            if ($_ =~ /FATAL: (.*) /) {
                error "unable to get actual database installed on the system:\n    error reported: $1", false;
                $tries++;
                if ($tries == 3) {
                    error "  unable to connect to the database using: \n  $command\n", false;
                    print " HOW TO SOLVE THE PROBLEM:\n";
                    print "  Seems that user configuration provided or current postgres installation\n";
                    print "  doesn't work. You have to check that the user is correct and you pg_hba.conf\n";
                    print "  file allows to configure the next command:\n\n";
                    print "      $command\n\n";
                    print "  which should return the list of current databases installed.\n";
                    print "  Once you have successfully executed previous command, restart again the\n";
                    print "  Af-Arch configuration process. \n\n";
                    exit 0;
                }
                goto list_databases;
            }
            if ($_ =~ /af_kernel/) {
              ask_question:
                msg "af-kernel database detected, what should I do?";
                print "Sking af-kernel database installation and use the already found or drop\n";
                print "the actual installation to install a new af-kernel clean install?\n";
                print "Choose drop or skip (no default option): ";
                chomp ($drop = <STDIN>); 
                if (length ($drop) == 0 || ($drop !~ /skip/ && $drop !~ /drop/ )) {
                    print "Please, you have to chose \"drop\" or \"skip\", try again.\n";
                    goto ask_question;
                }
                if ($drop =~ /skip/) {
                  ask_question_2:
                    msg "leaving actual af-kernel database";
                    print "Do you want to install the af-kernel database schema or skip this step.\n";
                    print "Chose \"leave\" or \"install\": ";

                    chomp ($schemma = <STDIN>); 
                    if ($schemma =~ /install/) {
                        goto install_schemma;
                    }
                    if ($schemma =~ /leave/) {
                        msg "leaving actual af-kernel database schemma";
                        return;
                    }
                    
                    print "It seems you didn't select a valid option, try again..\n";
                    goto ask_question_2;

                    last;


                }

                if ($drop =~ /drop/) {
                    msg "uninstalling items..";
                    $uninstall_file = "af-arch/trunk/af-kernel/data/uninstall.sql";
                    if (! (-e $uninstall_file)) {
                        $uninstall_file = "af-kernel/data/uninstall.sql";
                        if (! (-e $uninstall_file)) {
                            error "unable to find uninstall.sql file for af-kernel";
                        }
                    }
                    
                    
                    $result = execute "psql -d template1 -f  $uninstall_file -h localhost -U $user";
                    if ($result) {
                        print "failed to uninstall items.. the af-kernel database, try again.\n";
                        goto ask_question;
                    }else {
                        msg "previous af-kernel database item uninstalled..";
                    }

                    msg "droping database..";
                    $result = execute "dropdb -U $user af_kernel";
                    if ($result) {
                        print "failed to drop the af-kernel database, try again.\n";
                        goto ask_question;
                    }else {
                        msg "previous af-kernel database dropped";
                    }
                }
                
                   
            }
        }
        msg "installing af-kernel sql schema";  
        $result =  execute "createdb -h localhost -U $user af_kernel", "", false;
        if ($result) {
            print "failed to create af_kernel database. Try again\n";
        }else {
            msg "af_kernel database created";
            last;
        }
    }

  install_schemma:
    # we need to configure af-kernel password into install.sql
    print "searching for af-kernel sql schema..";
    $data = "af-arch/trunk/af-kernel/data/install.sql";
    if (-e $data) {
        goto install_af_kernel_sql;
    }
    $data = "af-kernel/data/install.sql";
    if (-e $data) {
        goto install_af_kernel_sql;
    }

install_af_kernel_sql:
    print "using $data\n";

    open CFG, "< $data" or error "unable to open $data for read";
    @file = <CFG>;
    close CFG;

    @new_file = ();
    foreach (@file) {
        $_ =~ s/PASSWORD \'(.*)\'/PASSWORD \'$hostpass\'/;
        push @new_file, $_;
    }

    open CFG, "> $data" or error "unable to open $data to write";
    print CFG @new_file;
    close CFG;

    print "Installing sql schema for af_kernel...\n"; 
    $result = 1;
    while ($result) {
        print "retype the same password, ";
        $result = execute "psql -d af_kernel -h localhost -U $user -f $data", "", false;
        if ($result) {
            error "unable to install af_kernel sql data", false;
        }
    }
    
    msg "sql schema sucessfully installed";
}

sub uninstall_af_kernel_sql () {

    msg "uninstalling af-kernel sql schema";
    # ask for user 
    print "\nNow, to uninstall all af-kernel SQL schema, I need a valid\n";
    print "PostgresSQL user to make a tcp localhost connection.\n";

    while (1) {
        chomp ($whoami = `whoami`); 
        print "Postgres user to uninstall sql schema? [$whoami] "; 
        chomp ($user = <STDIN>); 
        if (length ($user) == 0) { 
            $user = $whoami; 
        } 

        # we need to configure af-kernel password into install.sql
        $data = "af-arch/trunk/af-kernel/data/uninstall.sql";
        if (-e $data) {
            goto unistall_af_kernel_sql;
        }
        $data = "af-kernel/data/uninstall.sql";
        if (-e $data) {
            goto uinstall_af_kernel_sql;
        }

      uinstall_af_kernel_sql:
        print "Uninstalling af-kenel sql schema..."; 
        $result = execute "psql -d af_kernel -h localhost -U $user -f $data", "", false;
        if ($result) {
            print "failed to af-kernel sql schema database. Try again\n";
        }else {
            msg "af-kernel sql schema uninstalled";
            last;
        }
    }

    $result = 1;
    while ($result) {
        print "retype the same password, ";
        $result =  execute "dropdb -h localhost -p 5432 -U $user af_kernel", "", false;
        if ($result) {
            error "unable to uninstall af-kernel database", false;
        }
    }
    msg "sql schema sucessfully uninstalled";
}


sub create_initial_user {
    print "\nCreating the Af-arch root user. This user will have permissions to\n";
    print "access all af-kernel services, which means, this user has the power!.\n";
    print "This user must be used to create other users, manage permission, \n";
    print "servers, etc. This user shouldn't be used for any other purpose.\n";

    while (1) {
        print "Enter the user name: \n";
        print "--> ";
        chomp ($line = <STDIN>);
        
        $result = execute "af-kernel --add-superuser $line";
        if ($result) {
            print "there was an error while creating the super user. Try again\n";
        }else {
            msg "super user $line created";
            last;
        }
    }



}

sub get_csharp_binding_generation () {
    print "Do you want to build AF Arch C# bindings? (Y/n)\n";
    print "--> ";
    chomp ($line = <STDIN>);
    if ($line =~ /y|Y|Yes|yes/ or length ($line) == 0) {
        $csharp_bindings = "--enable-af-arch-sharp";
    }else {
        $csharp_bindings = "--disable-af-arch-sharp";
    }
}

sub get_doxygen_generation () {
    print "Do you want to build AF Arch Documentation? (Y/n)\n";
    print "--> ";
    chomp ($line = <STDIN>);
    if ($line =~ /y|Y|Yes|yes/ or length ($line) == 0) {
        $doxygen = "--enable-af-arch-doc";
    }else {
        $doxygen = "--disable-af-arch-doc";
    }
}

sub show_help {
    print "\n";

    print "  all    - Install af-arch taking all steps (recommended).\n";
    print "           Select this option if you want to install Af-arch, \n";
    print "           install sql schema, configure af-kernel parameters and\n";
    print "           create an initial Af-arch root user.\n";
    print "  source - only install af-arch source code. No sql install,\n";
    print "           no af-kernel configure and no root user creation takes\n";
    print "           place. This is useful if you have Af-arch already\n";
    print "           installed and working and you only want to upgrade the\n";
    print "           source code with out modifiying config and sql.\n";
    print "           This can also be used if you already have a server \n";
    print "           installation and you need the af-arch client side libraries.\n";
    print "  config - only launch the configure process, the sql install and\n";
    print "           the initial root user creation\n";
    print "  uninst - uninstall af-kernel sql.\n";
    print "  quit   - exit from the installer (same as Control-C)\n";
    print "  log    - enable/disable console log. By default console log is \n";
    print "           disable. If you want to know what's really happening \n";
    print "           use this.\n";
    print "\n";
}

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

msg "checking enviroment conditions";
check_program "svn", 
    "wget", 
    "pkg-config", 
    "aclocal", 
    "autoheader", 
    "automake", 
    "autoconf", 
    "libtoolize", 
    "psql",
    "make",
    "flex",
    "bison",
    "indent";

check_pkgconfig "glib-2.0",
    "gthread-2.0",
    "gobject-2.0";

initial_greetings ();

while (1) {
    print "--> ";
    chomp ($line = <STDIN>);

    if (length ($line) == 0) {
        show_help ();
        next;
    }

    if ($line =~ /all/) {
        download_af_arch_from_svn ();

        compile_and_install_af_arch ();

        configure_af_kernel ();

        install_af_kernel_sql ();

        create_initial_user ();

        msg "Af-arch installation done";

        next;
    }

    if ($line =~ /source/) {

        download_af_arch_from_svn ();



        compile_and_install_af_arch ();

        msg "Af-arch source code installation done";

        next;
    }

    if ($line =~ /config/) {

        configure_af_kernel ();
        
        install_af_kernel_sql ();
        
        create_initial_user ();

        msg "Af-arch configuration done";

        next;
    }

    if ($line =~ /uninst/) {
        uninstall_af_kernel_sql ();
    }
    if ($line =~ /log/) {
        if ($log eq enable) {
            $log = disabled;
            msg "log disabled";
        }else {
            $log = enable;
            msg "log enabled";
        }
    }

    if ($line =~ /quit/) {
        exit 0;
    }
}

