#!/usr/bin/perl -w

use strict;

use lib 'lib';

use Cwd;
use MIME::Base64;
use Ocsinventory::Agent::Config;


my $old_unix_agent_dir = "/etc/ocsinventory-client";

my $config;
my @cacert;
my $binpath;
my $randomtime;
my $crontab;
my $cron_line;
my $option;
my $nowizard;
my $configdir;
my $remove_old_unix;
my $old_unix_config;
my $nossl;
my $download;
my $snmp;
my $now;
my $nosoftware;


for $option (@ARGV){
    if ($option=~/--nowizard$/){
        $nowizard = 1;
    } elsif($option=~/--server=(\S*)$/){
        $config->{server} = $1;
    } elsif($option=~/--basevardir=(\S*)$/){
        $config->{basevardir} = $1;
    } elsif($option=~/--configdir=(\S*)$/){
        $configdir = $1;
    } elsif($option=~/--user=(\S*)$/){
        $config->{user} = $1;
    } elsif($option=~/--password=(\S*)$/){
        $config->{password} = $1;
    } elsif($option=~/--realm=(\S*)$/){
        $config->{realm} = $1;
    } elsif($option=~/--tag=(\S*)$/){
        $config->{tag} = $1;
    } elsif($option=~/--crontab$/){
        $crontab = 1;
    } elsif($option=~/--get-old-unix-agent-config$/){
        $old_unix_config = 1;
    } elsif($option=~/--remove-old-unix-agent$/){
        $remove_old_unix = 1;
    } elsif($option=~/--debug$/){
        $config->{debug} = 1;
    } elsif($option=~/--logfile=(\S*)$/){
        $config->{logfile} = $1;
    } elsif($option=~/--nossl$/){
        $nossl = 1;
    } elsif($option=~/--ca=(\S*)$/){
        $config->{ca} = $1;
    } elsif($option=~/--download$/){
        $download = 1;
    } elsif($option=~/--nosoftware$/){
        $nosoftware = 1;
    } elsif($option=~/--snmp$/){
        $config->{snmp} = 1;
    } elsif($option=~/--now$/){
        $now = 1;
    } elsif($option=~/--help/ || $option=~/-h/) {
        print STDERR <<EOF;
Usage :
\t--nowizard                    launch this script without interaction
\t--server=url                  set OCS Inventory NG server address (e.g: http://ocsinventory-ng/ocsinventory) 
\t--basevardir=path             set OCS Inventory NG Unix Unified agent variables directory (e.g: /var/lib/ocsinventory-agent)
\t--configdir=path              set OCS Inventory NG Unix Unified configuration directory (e.g: /etc/ocsinventory-agent)
\t--user=username               set username for OCS Inventory server Apache authentication (if needed)
\t--password=password           set password for OCS Inventory NG server Apache authentication (if needed)
\t--realm=realm                 set realm name for OCS Inventory NG server Apache authentication (if needed)
\t--crontab                     set a crontab while installing OCS Inventory NG Unix Unified agent
\t--get-old-unix-agent-config   retrieve old OCS Inventory NG Unix agent configuration (if needed)
\t--remove-old-unix-agent       remove old OCS Inventory NG Unix agent from system (if needed)
\t--debug                       activate debug mode configuration option while installing OCS Inventory NG Unix Unified agent
\t--logfile=path                set OCS Inventory NG Unix Unified agent log file path (if needed) 
\t--nossl                       disable SSL CA verification configuration option while installing OCS Inventory NG Unix Unified agent (not recommended)
\t--ca=path                     set OCS Inventory NG Unix Unified agent CA certificates file path
\t--nosoftware                  disable software inventory
\t--download                    activate package deployment feature while installing OCS Inventory NG Unix Unified agent
\t--snmp                        activate SNMP scans feature while installing OCS Inventory NG Unix Unified agent
\t--now                         launch OCS Inventory NG Unix Unified agent after installation
\t-h --help                     display this help
EOF

        exit 0;
    }
}


loadModules (qw/XML::Simple ExtUtils::MakeMaker/);

############ Setting default values ############

my @default_configdirs = ("/etc/ocsinventory", "/usr/local/etc/ocsinventory", "/etc/ocsinventory-agent");

unless ($config->{basevardir}) {
    if ($^O =~ /solaris/) {
        $config->{basevardir} = '/var/opt/ocsinventory-agent';
    } elsif ($^O =~ /bsd/) {
        $config->{basevardir} = '/var/db/ocsinventory-agent';
    } else { 
        $config->{basevardir} = '/var/lib/ocsinventory-agent';
    }
}

# Default value of SNMP timeout
unless ($config->{snmptimeout}) {
    $config->{snmptimeout} = 3;
}

# Default value of SNMP retry
unless ($config->{snmpretry}) {
    $config->{snmpretry} = 2;
}


############ Asking for questions ##############
unless ($nowizard) {
    if (!ask_yn("Do you want to configure the agent?", 'y')) {
         exit 0;
    }
  
    unless ($configdir) {
        $configdir = getConfigDir (@default_configdirs);

        #If not found
        unless (-d $configdir) {
            $configdir = askConfigDir (@default_configdirs);
            unless (-d $configdir) {
                unless (ask_yn ("Do you want to create the directory ".$configdir."?", 'y')) {
                    die("Please create  ".$configdir." directory first.\n");
                }
            }
        }
    }

    #Old unix agent
    if (ask_yn("Should the old unix_agent settings be imported?", 'y')) {
        $old_unix_config=1;
    }

    #Getting agent configuration if exists
    if (-f $configdir."/ocsinventory-agent.cfg") {
        open (CONFIG, "<".$configdir."/ocsinventory-agent.cfg") or
        die "Can't open ".$configdir."/ocsinventory-agent.cfg: ".$!;

        foreach (<CONFIG>) {
            s/#.+//;
            if (/(\w+)\s*=\s*(.+)/) {
                 my $key = $1;
                 my $val = $2;
                 # Remove the quotes
                 $val =~ s/\s+$//;
                 $val =~ s/^'(.*)'$/$1/;
                 $val =~ s/^"(.*)"$/$1/;
                 $config->{$key} = $val unless $config->{$key};
            }
        }
        close CONFIG;
    }

    #Getting server name 
    print "[info] The config file will be written in $configdir/ocsinventory-agent.cfg,\n";
    $config->{server} = promptUser('What is the address of your ocs server',$config->{server});
    #$config->{server} = promptUser('What is the address of your ocs server', exists ($config->{server})?$config->{server}:'ocsinventory-ng');

    #Getting credentials if needed
    if (ask_yn ("Do you need credential for the server? (You probably don't)", 'n')) {
        $config->{user} = promptUser("user", $config->{user});
        $config->{user} = MIME::Base64::encode_base64($config->{user});
        $config->{password} = promptUser("password");
        $config->{password} = MIME::Base64::encode_base64($config->{password});
        print "[info] The realm can be found in the login popup of your Internet browser.\n[info] In general, it's something like 'Restricted Area'.\n";
        $config->{realm} = promptUser("realm");
    }

    #Getting tag
    unless ($config->{tag}){
        if (ask_yn('Do you want to apply an administrative tag on this machine?', 'y')) {
            $config->{tag} = promptUser("tag", $config->{tag});
        }
    }

    #Getting crontab
    if ($^O =~ /solaris/ || $^O =~ /bsd/) {
        if (ask_yn("Do yo want to install the cron task in current user crontab?", 'y')) {
            $crontab = 1;
        }
    } elsif (-d "/etc/cron.d") {
        if (ask_yn("Do yo want to install the cron task in /etc/cron.d?", 'y')) {
            $crontab = 1;
        }
    }

    #Getting basevardir
    $config->{basevardir} = promptUser('Where do you want the agent to store its files? (You probably don\'t need to change it)', $config->{basevardir}, '^\/\w+', 'The location must begin with /');

    unless (-d $config->{basevardir}) {
        unless (ask_yn ("Do you want to create the ".$config->{basevardir}." directory?\n", 'y')) {
            die("Please create the ".$config->{basevardir}." directory manually and relaunch postinst.pl script\n");
        }
    }

    #Remove old unix agent ?
    $remove_old_unix = ask_yn ("Should I remove the old unix_agent?", 'n') unless $remove_old_unix;

    #Enable debug option ?
    $config->{debug} = ask_yn("Do you want to activate debug configuration option?", 'y') unless $config->{debug};

    #Enable log file ?
    unless ($config->{logfile}) {
        if (ask_yn("Do you want to use OCS Inventory NG UNix Unified agent log file?", 'y')){ 
            $config->{logfile} = promptUser('Specify log file path you want to use', $config->{logfile}, '^\/\w+', 'The location must begin with /');
        }
    }

    #Disable SSL option ?
    unless ($nossl) {
        $nossl = ask_yn("Do you want disable SSL CA verification configuration option (not recommended)?", 'n');
    }

    #Set CA certificates file path ?
    unless ($config->{ca}) {
        if (ask_yn("Do you want to set CA certificates file path?", 'y')){
            $config->{ca} = promptUser('Specify CA certificates file path', $config->{ca}, '^\/\w+', 'The location must begin with /');
        }
    }

    #Disable software inventory ?
    unless ($nosoftware) {
        $nossl = ask_yn("Do you want disable software inventory?", 'n');
    }
    #Enable download feature ?
    $download = ask_yn("Do you want to use OCS-Inventory software deployment feature?", 'y') unless $download;

    #Enable SNMP feature ?
    $config->{snmp} = ask_yn("Do you want to use OCS-Inventory SNMP scans feature?", 'y') unless $config->{snmp};

    #Run agent after configuration ?
    $now = ask_yn("Do you want to send an inventory of this machine?", 'y') unless $now;

}


################ Here we go... ##############

#Old unix agent
if (-f $old_unix_agent_dir.'/ocsinv.conf' && $old_unix_config) {
  
    print STDERR "Getting old OCS Inventory NG Unix agent configuration...\n";
    my $ocsinv = XMLin($old_unix_agent_dir.'/ocsinv.conf');
    $config->{server} = mkFullServerUrl($ocsinv->{'OCSFSERVER'});

    if (-f $old_unix_agent_dir.'/cacert.pem') {
        open CACERT, $old_unix_agent_dir.'/cacert.pem' or die "Can't import the CA certificate: ".$!;
        @cacert = <CACERT>;
        close CACERT;
    }

    my $admcontent = '';

    if (-f "$old_unix_agent_dir/ocsinv.adm") {
        if (!open(ADM, "<:encoding(iso-8859-1)", "$old_unix_agent_dir/ocsinv.adm")) {
            warn "Can't open $old_unix_agent_dir/ocsinv.adm";
        } else {
            $admcontent .= $_ foreach (<ADM>);
            close ADM;
            my $admdata = XMLin($admcontent) or die;
            if (ref ($admdata->{ACCOUNTINFO}) eq 'ARRAY') {
                foreach (@{$admdata->{ACCOUNTINFO}}) {
                    $config->{tag} = $_->{KEYVALUE} if $_->{KEYNAME} =~ /^TAG$/;
                }
            } elsif (
                exists($admdata->{ACCOUNTINFO}->{KEYNAME}) &&
                exists($admdata->{ACCOUNTINFO}->{KEYVALUE}) &&
                $admdata->{ACCOUNTINFO}->{KEYNAME} eq 'TAG'
                ){
                    print $admdata->{ACCOUNTINFO}->{KEYVALUE}."\n";
                    $config->{tag} = $admdata->{ACCOUNTINFO}->{KEYVALUE};
            }
        }
    }
}


#Setting server uri
print STDERR "Setting OCS Inventory NG server address...\n";

$config->{server}="ocsinventory-ng" unless $config->{server};
$config->{server} = mkFullServerUrl($config->{server});

if (!$config->{server}) {
    print "Server is empty. Leaving...\n";
    exit 1;
}
my $uri;
if ($config->{server} =~ /^http(|s):\/\//) {
    $uri = $config->{server};
} else { # just the hostname
    $uri = "http://".$config->{server}."/ocsinventory"
}


#Is OCS agent well installed ?
print STDERR "Looking for OCS Invetory NG Unix Unified agent installation...\n";
chomp($binpath = `which ocsinventory-agent 2>/dev/null`);
if (! -x $binpath) {
    # Packaged version with perl and agent ?
    $binpath = $^X;
    $binpath =~ s/perl/ocsinventory-agent/;
}

if (! -x $binpath) {
    print "sorry, can't find ocsinventory-agent in \$PATH\n";
    exit 1;
} else {
    print "ocsinventory agent presents: $binpath\n";
}


#Setting crontab
$randomtime = int(rand(60)).' '.int(rand(24));
$cron_line = $randomtime." * * * root $binpath --lazy > /dev/null 2>&1\n";

if ($crontab) {

    print STDERR "Setting crontab...\n";

    if ($^O =~ /solaris/ || $^O =~ /bsd/) {
        my $cron = `crontab -l`;

        # Let's suppress Unix cron/anacron user column
        $cron_line =~ s/ root /  /;
        $cron .= $cron_line;

        open CRONP, "| crontab" || die "Can't run crontab: $!";
        print CRONP $crontab;
        close(CRONP);
    } elsif (-d "/etc/cron.d") {
        open DEST, '>/etc/cron.d/ocsinventory-agent' or die $!;
        # Save the root PATH
        print DEST "PATH=".$ENV{PATH}."\n";
        print DEST $randomtime." * * * root $binpath --lazy > /dev/null 2>&1\n";
        close DEST;
    }
}

#Creating basevardir
if (!-d $config->{basevardir}) {
    print STDERR "Creating $config->{basevardir} directory...\n";
    mkdir $config->{basevardir} or die $!;
}

#Disabling SSL verification if asked
$config->{ssl} = 0 if $nossl;

#Disabling software inventory 
if ($nosoftware) {
    $config->{nosoftware} = 1;
} else {
    $config->{nosoftware} = 0;
}

#Creating configuration directory 
$configdir = "/etc/ocsinventory-agent" unless $configdir;  #If not set in command line

if (grep (/$configdir/,@default_configdirs)) {
    $configdir = '/etc/ocsinventory-agent' unless $configdir;

    print STDERR "Creating $configdir directory...\n";

    unless (-d $configdir) {
        unless (mkdir $configdir) {
            die("Failed to create ".$configdir.". Are you root?\n");
        }
    }
    print STDERR "Writing OCS Inventory NG Unix Unified agent configuration\n";
    open CONFIG, ">$configdir/ocsinventory-agent.cfg" or die "Can't write the config file in $configdir: ".$!;
    print CONFIG $_."=".$config->{$_}."\n" foreach (keys %$config);
    close CONFIG;
    chmod 0600, "$configdir/ocsinventory-agent.cfg";
} else {
    die("Wrong configuration directory...please choose a directory supported by OCS Inventory NG agent !!!\n");
}


#Removing old unix agent if needed
if ($remove_old_unix) {
    print STDERR "Removing old OCS Inventory Unix agent...\n";
    foreach (qw#
        /etc/ocsinventory-client
        /etc/logrotate.d/ocsinventory-client
        /usr/sbin/ocsinventory-client.pl
        /etc/cron.d/ocsinventory-client
        /bin/ocsinv
        #) {
        print $_."\n";
        next;
        rmdir if -d;
        unlink if -f || -l;
    }
    print "done\n"
}

# Creating vardirectory for this server
my $dir = $config->{server};
$dir =~ s/\//_/g;
my $vardir = $config->{basevardir}."/".$dir;
print STDERR "Creating $vardir directory...\n";
recMkdir($vardir) or die "Can't create $vardir!";

if (@cacert) { # we need to migrate the certificate
    print STDERR "Copying cacert.pem in $vardir...\n";

    open CACERT, ">".$vardir."/cacert.pem" or die "Can't open ".$vardir.'/cacert.pem: '.$!;
    print CACERT foreach (@cacert);
    close CACERT;
    print "Certificate copied in ".$vardir."/cacert.pem\n";
}

#We copy custom XML for SNMP MIBs from sources
my $workdir = getcwd();
my $source_snmpdir = "$workdir/snmp";
my $snmpdir = "$vardir/snmp";

if ( -d $source_snmpdir && -d $vardir) {
    unless (-d $snmpdir) {
        print STDERR "Creating $snmpdir directory...\n";
        recMkdir($snmpdir) or die "Can't create $snmpdir!";
    }
    print STDERR "Copying SNMP MIBs XML files...\n";
    system("cp -r $source_snmpdir/* $snmpdir");
}

print STDERR "Activating modules if needed...\n";

open MODULE, ">$configdir/modules.conf" or die "Can't write modules.conf in $configdir: ".$!;
print MODULE "# this list of module will be load by the at run time\n";
print MODULE "# to check its syntax do:\n";
print MODULE "# #perl modules.conf\n";
print MODULE "# You must have NO error. Else the content will be ignored\n";
print MODULE "# This mechanism goal is to launch agent extension modules\n";
print MODULE "\n";
print MODULE ($download?'':'#');
print MODULE "use Ocsinventory::Agent::Modules::Download;\n";
print MODULE ($config->{snmp}?'':'#');
print MODULE "use Ocsinventory::Agent::Modules::SnmpScan;\n";
print MODULE "\n";
print MODULE "# DO NOT REMOVE THE 1;\n";
print MODULE "1;\n";
close MODULE;


#Prevent security risks by removing existing snmp_com.txt file which is no longer used
my $snmp_com_file = "$snmpdir/snmp_com.txt";
if ( -f $snmp_com_file ) {
    print STDERR "$snmp_com_file seems to exists...removing it to prevent security risks !\n";
    unlink $snmp_com_file;
}

# Launch agent if asked
if ($now) {
    print STDERR "Launching OCS Inventory NG Unix Unified agent...\n";

    system("$binpath --force");
    if (($? >> 8)==0) {
        print "   -> Success!\n";
    } else {
        print "   -> Failed!\n";
        print "You may want to launch the agent with the --verbose or --debug flag.\n";
    }
}

#End
print "New settings written! Thank you for using OCS Inventory\n";

######## Subroutines ################

sub loadModules {
    my @modules = @_;

    foreach (@modules) {
        eval "use $_;";
        if ($@) {
            print STDERR "Failed to load $_. Please install it and restart the postinst.pl script ( ./postinst.pl ).\n";
            exit 1;
        }
    }
}

sub ask_yn {
    my $promptUser = shift;
    my $default = shift;

    die unless $default =~ /^(y|n)$/;

    my $cpt = 5;
    while (1) {
        my $line = prompt("$promptUser\nPlease enter 'y' or 'n'?>", $default);
        return 1 if $line =~ /^y$/;
        return if $line =~ /^n$/;
        if ($cpt-- < 0) {
            print STDERR "too much user input, exit...\n";
            exit(0);
        }
    }
}

sub promptUser {
    my ($promptUser, $default, $regex, $notice) = @_;

    my $string = $promptUser;
    $string .= "?>";

    my $line;
    my $cpt = 5;

    while (1) {
        $line = prompt($string, $default);
        if ($regex && $line !~ /$regex/) {
            print STDERR $notice."\n";
        } else {
            last;
        }
        if ($cpt-- < 0) {
            print STDERR "too much user input, exit...\n";
            exit(0);
        }
    }
    return $line;
}

sub getConfigDir {
    my @choices = @_;

    foreach (@choices) {
        my $t = $_.'/ocsinventory-agent.cfg';
        if (-f $t) {
            print "Config file found are $t! Reusing it.\n";
            return $_; 
        }
    }
}

sub askConfigDir {
    my @choices = @_;

    print STDERR "Where do you want to write the configuration file?\n";
    foreach (0..$#choices) {
        print STDERR " ".$_." -> ".$choices[$_]."\n";
    }
    my $input = -1;
    my $configdir;
    while (1) {
        $input = prompt("?>");
        if ($input =~ /^\d+$/ && $input >= 0 && $input <= $#choices) {
            last;
        } else {
            print STDERR "Value must be between 0 and ".$#choices."\n";
        }
    }

    return $choices[$input];
}

sub recMkdir {
    my $dir = shift;

    my @t = split /\//, $dir;
    shift @t;
    return unless @t;

    my $t;
    foreach (@t) {
        $t .= '/'.$_;
        if ((!-d $t) && (!mkdir $t)) {
            return;
        }
    }
    1;
}

sub mkFullServerUrl {

    my $server = shift;

    my $ret = 'http://' unless $server =~ /^http(s|):\/\//;
    $ret .= $server;
   
    if ($server !~ /http(|s):\/\/\S+\/\S+/) {
        $ret .= '/ocsinventory';
    }

    return $ret;

}
