package SrvMngr::Controller::Remoteaccess;

#----------------------------------------------------------------------
# heading     : Network
# description : Remote access
# navigation  : 6000 400
#----------------------------------------------------------------------
#
# routes : end
#----------------------------------------------------------------------
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller';
use Locale::gettext;
use SrvMngr::I18N;
use SrvMngr qw(theme_list init_session ip_number subnet_mask get_reg_mask);
use esmith::ConfigDB;
use esmith::util;
use File::Basename;
use Exporter;
use Carp;
use Socket qw( inet_aton );

#our @ISA = qw(esmith::FormMagick Exporter);
our @EXPORT = qw( networkAccess_list passwordLogin_list get_ssh_permit_root_login get_ssh_access get_telnet_mode
    get_ftp_access  get_ftp_password_login_access
    get_value get_prop get_ssh_password_auth
    validate_network_and_mask ip_number_or_blank subnet_mask_or_blank
    get_ipsecrw_sessions pptp_and_dhcp_range
);

#		get_pptp_sessions
our $db = esmith::ConfigDB->open || warn "Couldn't open configuration database";

sub main {
    my $c = shift;
    $c->app->log->info($c->log_req);
    my $title     = $c->l('rma_FORM_TITLE');
    my $notif     = '';
    my %rma_datas = ();
    $db = esmith::ConfigDB->open || warn "Couldn't open configuration database";

    #$rma_datas{ipsecrwSess}  = $c->get_ipsecrw_sessions();
    #$rma_datas{pptpSessions} = $c->get_pptp_sessions();
    $rma_datas{sshAccess}                 = $c->get_ssh_access();
    $rma_datas{sshPermitRootLogin}        = $c->get_ssh_permit_root_login();
    $rma_datas{sshPasswordAuthentication} = $c->get_ssh_password_auth();
    $rma_datas{sshTCPPort}                = $c->get_ssh_port();
    $rma_datas{ftpAccess}                 = $c->get_ftp_access();
    $rma_datas{ftpPasswordAccess}         = $c->get_ftp_password_login_access();

    #$rma_datas{telnetAccess} = $c->get_telnet_access;
    $c->stash(title => $title, notif => $notif, rma_datas => \%rma_datas);
    $c->render(template => 'remoteaccess');
} ## end sub main

sub do_action {
    my $c = shift;
    $c->app->log->info($c->log_req);
    my $title = $c->l('rma_FORM_TITLE');
    my ($result, $res, $trt) = '';
    my %rma_datas = ();
    $db = esmith::ConfigDB->open || warn "Couldn't open configuration database";
    $rma_datas{ipsecrwSess}  = ($c->param('IpsecrwSess')  || '');
    $rma_datas{ipsecrwReset} = ($c->param('IpsecrwReset') || '');

    #$rma_datas{pptpSessions} = ($c->param ('PptpSessions') || '0');
    $rma_datas{validFromNetwork} = ($c->param('ValidFromNetwork') || '');
    $rma_datas{validFromMask}    = ($c->param('ValidFromMask')    || '');
##	my @remove = $q->param('validFromRemove');  ???????? the first one only !!
    my @vals = $c->param('Remove_nets');
    $rma_datas{remove_nets} = join ',', @vals;
    $rma_datas{sshaccess}                 = ($c->param('SshAccess')                 || 'off');
    $rma_datas{sshPermitRootLogin}        = ($c->param('SshPermitRootLogin')        || 'no');
    $rma_datas{sshPasswordAuthentication} = ($c->param('SshPasswordAuthentication') || 'no');
    $rma_datas{sshTCPPort}                = ($c->param('SshTCPPort')                || '22');
    $rma_datas{ftpAccess}                 = ($c->param('FtpAccess')                 || 'off');
    $rma_datas{ftpPasswordAccess}         = ($c->param('FtpPasswordAccess')         || 'private');
    $rma_datas{telnetAccess}              = ($c->param('TelnetAccess')              || 'off');

    # validate
    my $v = $c->validation;
    return $c->render('remoteaccess') unless $v->has_data;

    #$v->optional('PptpSessions')->num(0, 999)->is_valid;
    if ($c->param('ValidFromNetwork') ne "") {
        $v->optional('ValidFromNetwork')->size(7, 15)->is_valid;
    }

    if ($c->param('ValidFromMask') ne "") {
        $v->optional('ValidFromMask')->size(7, 15)->is_valid;
    }
    $v->required('SshTCPPort')->num(1, 65535)->is_valid;
    $result .= 'field validation error' if $v->has_error;

    if (!$result) {

        # controls
        #$res = pptp_and_dhcp_range( $c, $rma_datas{pptpSessions} );
        #$result .= $res . ' ' unless $res eq 'OK';
        $res = ip_number_or_blank($c, $rma_datas{validFromNetwork});
        $result .= $res . ' ' unless $res eq 'OK';
        $res = subnet_mask_or_blank($c, $rma_datas{validFromMask});
        $result .= $res . ' ' unless $res eq 'OK';
        $res = validate_network_and_mask($c, $rma_datas{validFromNetwork}, $rma_datas{validFromMask});
        $result .= $res . ' ' unless $res eq 'OK';

        #$result .= ' blocked for testing !' . $rma_datas{remove_nets};
    } ## end if (!$result)

    if (!$result) {
        $res = change_settings($c, %rma_datas);
        $result .= $res unless $res eq 'OK';
    }

    if ($result eq "") {
        $result = $c->l('rma_SUCCESS');
        $trt    = 'SUC';
    }
    $c->stash(title => $title, notif => $result, rma_datas => \%rma_datas);

    #return $c->render( template => 'remoteaccess' );
    if ($trt eq 'SUC') {
        $c->stash(title => $title, modul => $result);
        return $c->render(template => 'module');
    }
    return $c->render(template => 'remoteaccess');

    #$c->redirect_to('/remoteaccess');
} ## end sub do_action

sub networkAccess_list {
    my $c = shift;
    return [
        [ $c->l('rma_NO_ACCESS')         => 'off' ],
        [ $c->l('NETWORKS_ALLOW_LOCAL')  => 'private' ],
        [ $c->l('NETWORKS_ALLOW_PUBLIC') => 'public' ]
    ];
} ## end sub networkAccess_list

sub passwordLogin_list {
    my $c = shift;
    return [ [ $c->l('rma_PASSWORD_LOGIN_PRIVATE') => 'private' ], [ $c->l('rma_PASSWORD_LOGIN_PUBLIC') => 'public' ] ];
}

sub get_prop {
    my ($c, $item, $prop) = @_;
    warn "You must specify a record key"    unless $item;
    warn "You must specify a property name" unless $prop;
    my $record = $db->get($item) or warn "Couldn't get record for $item";
    return $record ? $record->prop($prop) : undef;
} ## end sub get_prop

sub get_value {
    my $c    = shift;
    my $item = shift;
    return ($db->get($item)->value());
} ## end sub get_value

sub get_ftp_access {
    my $status = get_prop('', 'ftp', 'status') || 'disabled';
    return 'off' unless $status eq 'enabled';
    my $access = get_prop('', 'ftp', 'access') || 'private';
    return ($access eq 'public') ? 'normal' : 'private';
} ## end sub get_ftp_access

#sub get_pptp_sessions {
#  my $status = get_prop('','pptpd','status');
#  if (defined($status) && ($status eq 'enabled')) {
#    return(get_prop('','pptpd','sessions') || 'no');
#  return '0';
#}
sub get_ssh_permit_root_login {
    return (get_prop('', 'sshd', 'PermitRootLogin') || 'no');
}

sub get_ssh_password_auth {
    return (get_prop('', 'sshd', 'PasswordAuthentication') || 'yes');
}

sub get_ssh_access {
    my $status = get_prop('', 'sshd', 'status');

    if (defined($status) && ($status eq 'enabled')) {
        my $access = get_prop('', 'sshd', 'access');
        $access = ($access eq 'public') ? 'public' : 'private';
        return ($access);
    } else {
        return ('off');
    }
} ## end sub get_ssh_access

sub get_ssh_port {
    return (get_prop('$c', 'sshd', 'TCPPort') || '22');
}

sub get_ftp_password_login_access {
    my $status = get_prop('', 'ftp', 'status') || 'disabled';
    return 'private' unless $status eq 'enabled';
    my $access = get_prop('', 'ftp', 'LoginAccess') || 'private';
    return ($access eq 'public') ? 'public' : 'private';
} ## end sub get_ftp_password_login_access

sub get_telnet_mode {
    my $telnet = $db->get('telnet');
    return ('off') unless $telnet;
    my $status = $telnet->prop('status') || 'disabled';
    return ('off') unless $status eq 'enabled';
    my $access = $telnet->prop('access') || 'private';
    return ($access eq "public") ? "public" : "private";
} ## end sub get_telnet_mode

sub get_ipsecrw_sessions {
    my $status = $db->get('ipsec')->prop('RoadWarriorStatus');

    if (defined($status) && ($status eq 'enabled')) {
        return ($db->get('ipsec')->prop('RoadWarriorSessions') || '0');
    } else {
        return ('0');
    }
} ## end sub get_ipsecrw_sessions

sub get_ipsecrw_status {
    return undef unless ($db->get('ipsec'));
    return $db->get('ipsec')->prop('RoadWarriorStatus');
}

sub pptp_and_dhcp_range {
    my $c           = shift;
    my $val         = shift || 0;
    my $dhcp_status = $db->get_prop('dhcpd', 'status') || 'disabled';
    my $dhcp_end    = $db->get_prop('dhcpd', 'end') || '';
    my $dhcp_start  = $db->get_prop('dhcpd', 'start') || '';

    if ($dhcp_status eq 'enabled') {
        my $ip_start = unpack 'N', inet_aton($dhcp_start);
        my $ip_end   = unpack 'N', inet_aton($dhcp_end);
        my $ip_count = $ip_end - $ip_start;
        return 'OK' if ($val < $ip_count);
        return $c->l('rma_NUMBER_OF_PPTP_CLIENTS_MUST_BE_LESSER_THAN_NUMBER_OF_IP_IN_DHCP_RANGE');
    } else {
        return 'OK';
    }
} ## end sub pptp_and_dhcp_range

sub _get_valid_from {
    my $c   = shift;
    my $rec = $db->get('httpd-admin');
    return undef unless ($rec);
    my @vals = (split ',', ($rec->prop('ValidFrom') || ''));
    return @vals;
} ## end sub _get_valid_from

sub ip_number_or_blank {
    my $c  = shift;
    my $ip = shift;

    if (!defined($ip) || $ip eq "") {
        return 'OK';
    }
    return ip_number($c, $ip);
} ## end sub ip_number_or_blank

sub subnet_mask_or_blank {
    my $c    = shift;
    my $mask = shift;

    if (!defined($mask) || $mask eq "") {
        return "OK";
    }
    chomp $mask;
    return (subnet_mask($mask) ne 'OK')
        ? $c->l('rma_INVALID_SUBNET_MASK') . " (" . $mask . ")"
        : 'OK';
} ## end sub subnet_mask_or_blank

sub validate_network_and_mask {
    my $c    = shift;
    my $net  = shift || "";
    my $mask = shift || "";

    if ($net xor $mask) {
        return $c->l('rma_ERR_INVALID_PARAMS' . " (" . $net . "/" . $mask . ")");
    }
    return 'OK';
} ## end sub validate_network_and_mask

sub change_settings {
    my ($c, %rma_datas) = @_;

    #------------------------------------------------------------
    # good; go ahead and change the access.
    #------------------------------------------------------------
    my $rec = $db->get('telnet');

    if ($rec) {
        if ($rma_datas{telnetAccess} eq "off") {
            $rec->set_prop('status', 'disabled');
        } else {
            $rec->set_prop('status', 'enabled');
            $rec->set_prop('access', $rma_datas{telnetAccess});
        }
    } ## end if ($rec)
    $rec = $db->get('sshd') || $db->new_record('sshd', { type => 'service' });
    $rec->set_prop('TCPPort', $rma_datas{sshTCPPort});
    $rec->set_prop('status', ($rma_datas{sshaccess} eq "off" ? 'disabled' : 'enabled'));
    $rec->set_prop('access',                 $rma_datas{sshaccess});
    $rec->set_prop('PermitRootLogin',        $rma_datas{sshPermitRootLogin});
    $rec->set_prop('PasswordAuthentication', $rma_datas{sshPasswordAuthentication});
    $rec = $db->get('ftp');

    if ($rec) {
        if ($rma_datas{ftpAccess} eq "off") {
            $rec->set_prop('status',      'disabled');
            $rec->set_prop('access',      'private');
            $rec->set_prop('LoginAccess', 'private');
        } elsif ($rma_datas{ftpAccess} eq "normal") {
            $rec->set_prop('status',      'enabled');
            $rec->set_prop('access',      'public');
            $rec->set_prop('LoginAccess', $rma_datas{ftpPasswordAccess});
        } else {
            $rec->set_prop('status',      'enabled');
            $rec->set_prop('access',      'private');
            $rec->set_prop('LoginAccess', $rma_datas{ftpPasswordAccess});
        }
    } ## end if ($rec)

    #	if ($rma_datas{pptpSessions} == 0) {
    #	$db->get('pptpd')->set_prop('status', 'disabled');
    #    } else {
    #	$db->get('pptpd')->set_prop('status', 'enabled');
    #	$db->get('pptpd')->set_prop('sessions', $rma_datas{pptpSessions});
    #    }
    if ($rma_datas{validFromNetwork} && $rma_datas{validFromMask}) {

        unless (add_new_valid_from($c, $rma_datas{validFromNetwork}, $rma_datas{validFromMask})) {
            return $c->l('rma_ERROR_UPDATING_CONFIGURATION') . 'new net';
        }
    } ## end if ($rma_datas{validFromNetwork...})

    if ($rma_datas{remove_nets}) {
        unless (remove_valid_from($c, $rma_datas{remove_nets})) {
            return $c->l('rma_ERROR_UPDATING_CONFIGURATION') . 'del net';
        }
    } ## end if ($rma_datas{remove_nets...})

    # reset ipsec roadwarrior CA,server,client certificates
    if ($rma_datas{ipsecrwReset}) {
        system('/sbin/e-smith/roadwarrior', 'reset_certs') == 0
            or return $c->l('rma_ERROR_UPDATING_CONFIGURATION') . 'rst ipsec';
    }

    if ($rma_datas{ipsecrwSess}) {
        set_ipsecrw_sessions($c, $rma_datas{ipsecrwSess});
    }

    unless (system("/sbin/e-smith/signal-event", "remoteaccess-update") == 0) {
        return $c->l('rma_ERROR_UPDATING_CONFIGURATION');
    }
    return 'OK';
} ## end sub change_settings

sub set_ipsecrw_sessions {
    my $c        = shift;
    my $sessions = shift;

    if (defined $sessions) {
        $db->get('ipsec')->set_prop('RoadWarriorSessions', $sessions);

        if (int($sessions) > 0) {
            $db->get('ipsec')->set_prop('RoadWarriorStatus', 'enabled');
        }
    } ## end if (defined $sessions)
    return '';
} ## end sub set_ipsecrw_sessions

sub add_new_valid_from {
    my $c    = shift;
    my $net  = shift;
    my $mask = shift;

    # we transform bit mask to regular mask
    $mask = get_reg_mask($net, $mask);
    my $rec = $db->get('httpd-admin');
    return $c->error('ERR_NO_RECORD') unless $rec;
    my $prop = $rec->prop('ValidFrom') || '';
    my @vals = split /,/, $prop;
    return '' if (grep /^$net\/$mask$/, @vals);    # already have this entry

    if ($prop ne '') {
        $prop .= ",$net/$mask";
    } else {
        $prop = "$net/$mask";
    }
    $rec->set_prop('ValidFrom', $prop);
    return 1;
} ## end sub add_new_valid_from

sub remove_valid_from {
    my $c           = shift;
    my $remove_nets = shift;
    my @remove      = split /,/, $remove_nets;

    #	my @remove = $c->param('Remove_nets');
    my @vals = $c->_get_valid_from();

    foreach my $entry (@remove) {
        return undef unless $entry;
        my ($net, $mask) = split(/\//, $entry);

        unless (@vals) {
            print STDERR "ERROR: unable to load ValidFrom property from conf db\n";
            return undef;
        }

        # what if we don't have a mask because someone added an entry from
        # the command line? by the time we get here, the panel will have
        # added a 32 bit mask, so we don't know for sure if the value in db
        # is $net alone or $net/255.255.255.255. we have to check for both
        # in this special case...
        @vals = (grep { $entry ne $_ && $net ne $_ } @vals);
    } ## end foreach my $entry (@remove)
    my $prop;

    if (@vals) {
        $prop = join ',', @vals;
    } else {
        $prop = '';
    }
    $db->get('httpd-admin')->set_prop('ValidFrom', $prop);
    return 1;
} ## end sub remove_valid_from
1;