#!/usr/bin/perl
#
#
$BENNO_USERADMIN::VERSION = '2.1.0';

use strict;
use Carp;
use Getopt::Std;
use DBI;
use Digest::MD5;

our(%opts);
getopts('haDdElsvC:c:e:n:p:r:u:', \%opts);

my $help        = $opts{'h'};
my $list        = $opts{'l'};
my $configfile  = $opts{'c'} || '/etc/benno-web/benno.conf';

my $disable     = $opts{'d'};
my $enable      = $opts{'E'};
my $delete      = $opts{'D'};

my $password    = $opts{'p'};
my $uid         = $opts{'u'};
my $name        = $opts{'n'};
my $email       = $opts{'e'};

my $role        = $opts{'r'};
my $container   = $opts{'C'};

my $version     = $opts{'v'};


$version && print_version();
$help    && print_help()     &&  exit;

my $config;
eval {
    $config = read_config($configfile);
};
if ($@) {
    print STDERR "$@\n";
    print_help();
    exit 2;
}
my $DBH = db_connect($config);

# no option
if (! $list &! $uid) {
    print_help();
    exit 1;
}

my $exit_cmd = 1;
if ($password || $name || $email || $container || $role) {
    $exit_cmd = 0; 
}

$list       && list_users()         && exit; 
$enable     && disable_user($uid,0) && $exit_cmd && exit;
$disable    && disable_user($uid,1) && $exit_cmd && exit;
$delete     && delete_user($uid)    && exit;

if ($password || $name || $email || $role || $container) {
    # change or add user
    eval {
        change_user($uid,$password,$name,$email,$role,$container);
    };
    if ($@) {
        add_user($uid,$password,$name,$email,$role,$container,$disable);
    }
    exit;
}
else {
    # show user details
    show_user($uid);
    exit;
}

print_help();
exit 0;

### SUBS #######################################################################
# list_users
sub list_users
{
    my $sql = 'SELECT id FROM user';

    my $sth = $DBH->prepare($sql);
    my $rv  = $sth->execute;
    while (my @row = $sth->fetchrow_array) {
        print "$row[0]\n";
    }
    return 1;
}


# show_users
sub show_user
{
    my $uid = shift;
    my @user = fetch_user($uid); 
    ($#user == 4) or error_exit('User not found.');

    print "Userid:    $user[0]\n";
    print "Name:      $user[1]\n";
    print "Container: $user[3]\n";
    print "Role:      $user[4]\n";
    print "E-Mail:    ";
    my $adresslist;
    foreach my $address (fetch_adresses($uid)) {
        $adresslist .= $address.', ';
    }
    $adresslist =~ s/, $//;
    print "$adresslist\n";

    if ($user[2] =~ /^DISABLED_/) {
        print "Enabled:   NO\n"
    }
}


# add_user
sub add_user
{
    my $uid       = shift;
    my $pass      = shift;
    my $name      = shift;
    my $email     = shift;
    my $role      = shift || 'USER';
    my $container = shift || 'BennoContainer';
    my $disable   = shift;

    # check if user exists
    my @user = fetch_user($uid); 
    ($#user == 4) && error_exit('User alredy exists.');
    (!$email)     && error_exit('No email address / filter for user given.');

    # first, insert user
    my $sql = 'INSERT INTO user (id,archive,role) VALUES (?,?,?)';
    my $rows = $DBH->do($sql,undef,$uid,$container,$role) or error_exit($DBH->errstr);

    $pass   && update_password($uid,$pass,$disable);
    $name   && update_name($uid,$name);
    $email  && update_email($uid,$email);
}


# change_user
sub change_user
{
    my $uid   = shift;
    my $pass  = shift;
    my $name  = shift;
    my $email = shift;
    my $role  = shift;
    my $container = shift;

    # check if user exists
    my @user = fetch_user($uid); 
    ($#user == 4) || die "User does not exist.\n";

    $pass   && update_password($uid,$pass);
    $name   && update_name($uid,$name);
    $email  && update_email($uid,$email);
    $role   && update_role($uid,$role);
    $container && update_container($uid,$container);
}


# delete_user
sub delete_user
{
    my $uid  = shift;

    # check if user exists
    my @user = fetch_user($uid); 
    ($#user == 4) || error_exit('User does not exist.');

    # first, insert user
    my $sql = 'DELETE FROM user WHERE id=?';
    my $rows = $DBH->do($sql,undef,$uid) or error_exit($DBH->errstr);

    $sql = 'DELETE FROM address WHERE id=?';
    $rows = $DBH->do($sql,undef,$uid) or error_exit($DBH->errstr);
}


# update_password
sub update_password
{
    my $uid  = shift;
    my $pass = shift;
    my $disable = shift;

    my $ctx = Digest::MD5->new;
    $ctx->add($pass);
    my $pwdigest = $ctx->hexdigest;
    $pwdigest = 'DISABLED_'.$pwdigest if $disable;

    my $sql = 'UPDATE user SET password=? WHERE id=?';
    my $rows = $DBH->do($sql,undef,$pwdigest,$uid) or error_exit($DBH->errstr);
}


# update_name
sub update_name
{
    my $uid  = shift;
    my $name = shift;

    my $sql = 'UPDATE user SET name=? WHERE id=?';
    my $rows = $DBH->do($sql,undef,$name,$uid) or error_exit($DBH->errstr);
}


# update_email
sub update_email
{
    my $uid  = shift;
    my $email = shift;
    my $sql;

    my @addresslist = split(/,/,$email);

    local $DBH->{RaiseError} = 0;
    local $DBH->{PrintError} = 0;

    $DBH->begin_work;       # start transaction
    $sql = 'DELETE FROM address WHERE id=?';
    my $rows = $DBH->do($sql,undef,$uid) or error_exit($DBH->errstr);

    $sql = 'INSERT INTO address (id,address) VALUES(?,?)';
    my $sth = $DBH->prepare($sql);
    foreach my $address (@addresslist) {
        $address =~ s/\s//g;
        my $rv  = $sth->execute($uid,$address);
    }
    $DBH->commit;       # end transaction
}


# update_role
sub update_role
{
    my $uid  = shift;
    my $role = shift;

    my $sql = 'UPDATE user SET role=? WHERE id=?';
    my $rows = $DBH->do($sql,undef,$role,$uid) or error_exit($DBH->errstr);
}


# update_container
sub update_container
{
    my $uid       = shift;
    my $container = shift;

    my $sql = 'UPDATE user SET archive=? WHERE id=?';
    my $rows = $DBH->do($sql,undef,$container,$uid) or error_exit($DBH->errstr);
}


# enable_user(1|0)
sub disable_user
{
    my $uid     = shift;
    my $disable = shift;

    my $sql = 'SELECT password FROM user WHERE id = ?';
    my $sth = $DBH->prepare($sql);
    my $rv  = $sth->execute($uid);
    my $pwd = ($sth->fetchrow_array)[0];
    if ($disable) {
        $pwd = 'DISABLED_'.$pwd;
    }
    else {
        $pwd =~ s/^DISABLED_//;
    }
    $sql = 'UPDATE user SET password=? WHERE id=?';
    my $rows = $DBH->do($sql,undef,$pwd,$uid) or error_exit($DBH->errstr);
}


# fetch_user
sub fetch_user
{
    my $uid = shift;

    my $sql = 'SELECT * FROM user WHERE id = ?';

    my $sth = $DBH->prepare($sql);
    my $rv  = $sth->execute($uid);
    my @user = $sth->fetchrow_array;

    return @user;
}


# fetch_adresses
sub fetch_adresses
{
    my $uid = shift;

    my @adresses;

    my $sql = 'SELECT address FROM address WHERE id = ?';
    my $sth = $DBH->prepare($sql);
    my $rv  = $sth->execute($uid);
    while (my @row = $sth->fetchrow_array) {
        push @adresses,$row[0];
    }

    return @adresses;
}


# db_connect
sub db_connect
{   
    my $config = shift;

    my $dbtype = $config->{DBTYPE} || 'sqlite:////var/lib/benno-web/bennoweb.sqlite';
    my $db     = $config->{DATABASE};
    my $dbhost = $config->{DBHOST} || 'localhost';
    my $dbport = $config->{DBPORT} || 3306;
    my $dbuser = $config->{DBUSER};
    my $dbpass = $config->{DBPASS};
    my $DBH;
    if ($dbtype eq 'mysql') {
        eval {
            my $dsn = "DBI:mysql:database=$db;host=$dbhost;port=$dbport";
            $DBH = DBI->connect($dsn,$dbuser,$dbpass);
        };
        if ($@) {
            print STDERR "ERROR: MySQL Driver not installed.\n";
            exit 1;
        }
    }
    else {
        if ($dbtype =~ /^sqlite:\/\/(\/.\S+)$/) {
            my $dsn = "dbi:SQLite:dbname=$1","","";
            $DBH = DBI->connect($dsn);
        }
    }
    return $DBH;
}


# error_exit
sub error_exit
{
    my $err = shift;
    print STDERR "$err\n";
    exit 1;
}

# print_version
sub print_version
{
    print "benno-useradmin version $BENNO_USERADMIN::VERSION\n";
    exit 0;
}


# read_config
sub read_config
{
    my $configfile = shift;

    my $config = {};
    open CONF, $configfile or die "Cannot open config file $configfile. $!\n";
    foreach my $line (<CONF>) {
        next if $line =~ /^$/;
        next if$line  =~ /^#/;
        chomp $line;
        my ($param,$value) = split(/\s*=\s*/,$line,2);
        $config->{$param} = $value;
    }

    return $config;
}


# print_help
sub print_help
{
    print "Usage: $0 [-h] [-l] [-u <username>] [-d|-E] [-D] [-p <password>]\n";
    print "  [-n <name>] [-e <email1,email2, ...>] -C [<container>]\n";
    print "\n";
    print "  -c <file>          config file (default /etc/benno-web/benno.conf)\n";
    print "  -l                 list all users\n";
    print "\n";
    print "  -u <username>      username (shows user data if no other options given)\n";
    print "\n";
    print "\n";
    print "Add or change user data\n";
    print "  -p <password>      password of the user\n";
    print "  -n <name>          display name of the user\n";
    print "  -e <email,email>   comma separated list of email addresses ('' deletes all)\n";
    print "  -r <role>          user role (USER, ADMIN, REVISOR, default USER)\n";
    print "  -C <container>     container name (default BennoContainer)\n";
    print "\n";
    print "  -d                 disable user\n";
    print "  -E                 enable user\n";
    print "  -D                 delete user\n";
    print "  -h                 this help\n";
    print "  -v                 print version number\n";
    print "\n";
}


