#!/usr/bin/perl -w
# This script (sophomorix-add) is maintained by Rüdiger Beck
# It is Free Software (License GPLv3)
# If you find errors, contact the author
# jeffbeck@web.de  or  jeffbeck@linuxmuster.net

# modules
use strict;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
use Sophomorix::SophomorixConfig;
use List::MoreUtils qw(uniq);
use Net::LDAP;
use JSON;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Useqq = 1;
$Data::Dumper::Terse = 1; 
use Sophomorix::SophomorixBase qw(
                                 print_line
                                 print_title
                                 unlock_sophomorix
                                 lock_sophomorix
                                 log_script_start
                                 log_script_end
                                 log_script_exit
                                 backup_auk_file
                                 get_passwd_charlist
                                 get_plain_password
                                 check_options
                                 config_sophomorix_read
                                 result_sophomorix_init
                                 result_sophomorix_add
                                 result_sophomorix_add_summary
                                 result_sophomorix_check_exit
                                 result_sophomorix_print
                                 run_hook_scripts
                                 json_dump
                                 read_sophomorix_add
                                 );
use Sophomorix::SophomorixSambaAD qw(
                                 AD_school_create
                                 AD_bind_admin
                                 AD_dns_get
                                 AD_unbind_admin
                                 AD_user_create
                                 AD_group_create
                                 AD_group_addmember
                                 AD_group_update
                                 AD_get_schoolname
                                 AD_get_name_tokened
                                 AD_dn_fetch_multivalue
                                 AD_object_search
                                 AD_get_passwd
                                    );

my @arguments = @ARGV;

my $user_count=0;
my $login_check_ok="";
my $sophomorix_first_password="";
my @users_for_quota=();


############################################################
# options
############################################################
$Conf::log_level=1;
my $help=0;
my $info=0;
my $json=0;
my $password="";
my $lock=0;
my $unlock=0;

# selection lookup hash
my $school_opt="";
my $class_opt="";
my $user_opt="";
my %selection=();

my $testopt=GetOptions(
           "help|h" => \$help,
           "info|i" => \$info,
           "json|j+" => \$json,
           "verbose|v+" => \$Conf::log_level,
           "lock" => \$lock,
           "unlock" => \$unlock,
           "password" => \$password,
           "user|u=s" => \$user_opt,
           "class=s" => \$class_opt,
           "school=s" => \$school_opt,
          );

my %sophomorix_result=&result_sophomorix_init("sophomorix-add");
&check_options($testopt,\%sophomorix_result,$json);

# Reading Configuration
my ($ldap,$root_dse) = &AD_bind_admin(\@arguments,\%sophomorix_result,$json);
my $root_dns=&AD_dns_get($root_dse);
my %sophomorix_config=&config_sophomorix_read($ldap,$root_dse,\%sophomorix_result);
my ($smb_admin_pass)=&AD_get_passwd($DevelConf::sophomorix_file_admin,
                                     $DevelConf::secret_file_sophomorix_file_admin);
my $add_file=$sophomorix_config{'INI'}{'PATHS'}{'CHECK_RESULT'}."/sophomorix.add";
my $add_file_new=$sophomorix_config{'INI'}{'PATHS'}{'CHECK_RESULT'}."/sophomorix.add.new";



# --help
if ($help==1) {
   # get scriptname
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   print('
sophomorix-add adds users from the file sophomorix.add to the sophomorix 
database and the authentification system.

Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose
  -i  / --info
  --u user / --users user
  -c class / --class class
  --lock / --unlock

Add all users:
  sophomorix-add

Add users from some schools only:
  sophomorix-add --school <schoolname1>,<schoolname2>, ...

Add some users only:
  sophomorix-add --user <user1>,<user2>, ...

Add users in some classes only:
  sophomorix-add --class <class1>,<class2>, ...

Please see the sophomorix-add(8) man pages for full documentation
');
   print "\n";
   exit;
}



# --unlock
if ($unlock==1) {
    &unlock_sophomorix();
    exit;
}



# --lock
if ($lock==1) {
    &lock_sophomorix("lock",0,\@arguments);
    exit;
}



# --info
if ($info==1) {
    my $ref_add=&read_sophomorix_add({sophomorix_config=>\%sophomorix_config});
    &json_dump({json => $json,
                jsoninfo => "ADDFILE",
                jsoncomment => "sophomorix.add",
                log_level => $Conf::log_level,
                object_name => $school_opt,
                hash_ref=>$ref_add,
                sophomorix_config=>\%sophomorix_config,
              });
   exit;
}



# --user
if ($user_opt ne ""){
    my @users=split(/,/,$user_opt);
    foreach my $us (@users){
        $selection{'SELECTION'}{'USER'}{$us}="adduser";
    }
}



# --school
if ($school_opt ne ""){
    my @schools=split(/,/,$school_opt);
    foreach my $sch (@schools){
        $selection{'SELECTION'}{'SCHOOL'}{$sch}="addschool";
    }
}



# --class
if ($class_opt ne "") {
    my @classes=split(/,/,$class_opt);
    foreach my $cl (@classes){
        $selection{'SELECTION'}{'CLASS'}{$cl}="addclass";
    }
}



&result_sophomorix_check_exit(\%sophomorix_result,\%sophomorix_config,$json);

################################################################################
# Start
################################################################################
&log_script_start(\@arguments,\%sophomorix_result,\%sophomorix_config);

# create students and teachers
my @password_chars=&get_passwd_charlist();

# logging sophomorix.add
&backup_auk_file("add","before",\%sophomorix_config);

# file for users that are NOT added
open(STILLTOADD,">$add_file_new") 
    || die "ERROR: $!";

# the add file
if (not -f $add_file){
    &result_sophomorix_add(\%sophomorix_result,"ERROR",-1,"",$add_file." not found!");
    &result_sophomorix_add_summary({
        NAME=>"ADDED", 
        RESULT=>0, 
        RESULT_TYPE => "integer",
        DESCRIPTION_POST => "users added", 
        DESCRIPTION_PRE => "added users", 
        FORMAT_TYPE => 1,
        sophomorix_result=>\%sophomorix_result,
	});
    &log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
}

# calulate users to add
my $max_user_count=`cat $sophomorix_config{'INI'}{'PATHS'}{'CHECK_RESULT'}/sophomorix.add | wc -l`;
chomp($max_user_count);

# open add file
open(SOPHOMORIXADD,"$add_file") 
    || die "ERROR: $!";

# loop through all users in sophomorix.add
while(<SOPHOMORIXADD>){
    if(/^\#/){ # skip commented lines
        next;
    }

    chomp();
    my ($file,
        $class_group,
        $identifier,
        $login_wish,
        $password_wish,
        $uidnumber_migrate,
        $gidnumber_migrate,
        $unid,
        $school,
        $role,
        $surname_utf8,
        $firstname_utf8,
        $status,
        $creationdate,
        $tolerationdate,
        $deactivationdate,
        $sambantpassword,
        $userpassword,
        $mail,
        $webui_permissions_calculated_string,
       )=split("::");

    my ($surname_ascii,$firstname_ascii,$birthdate)=split(";", $identifier);
    my @webui_permissions_calculated=split(",", $webui_permissions_calculated_string);

    # login
    if ($login_wish eq "---" or $login_wish eq ""){
        print "\n   ERROR: >$login_wish< seems to be wrong\n\n"; 
        exit 88;
    } else {
        # accept login
        $login_check_ok=$login_wish;
    }


    # deselecting users
    if ($user_opt ne ""){
        if (not exists $selection{'SELECTION'}{'USER'}{$login_wish}){
            print "   * Skipping $login_wish in $class_group in $school (Reason: $login_wish)\n";
            print STILLTOADD "$_\n";
            next;
        }
    }

    if ($class_opt ne ""){
        if (not exists $selection{'SELECTION'}{'CLASS'}{$class_group}){
            print "   * Skipping $login_wish in $class_group in $school (Reason: $class_group)\n";
            print STILLTOADD "$_\n";
            next;
        }
    }

    if ($school_opt ne ""){
        if (not exists $selection{'SELECTION'}{'SCHOOL'}{$school}){
            print "   * Skipping $login_wish in $class_group in $school (Reason: $school)\n";
            print STILLTOADD "$_\n";
            next;
        }
    }

    # Plain password
    my $random_pwd;
    if (exists $sophomorix_config{'FILES'}{'USER_FILE'}{$file}{'RANDOM_PWD'}){
        $random_pwd=$sophomorix_config{'FILES'}{'USER_FILE'}{$file}{'RANDOM_PWD'};
    }
    my $pwd_length;
    if (exists $sophomorix_config{'FILES'}{'USER_FILE'}{$file}{'PWD_LENGTH'}){
        $pwd_length=$sophomorix_config{'FILES'}{'USER_FILE'}{$file}{'PWD_LENGTH'};
    }

    if ($password_wish eq "---") {
        $sophomorix_first_password=&get_plain_password(
                        $role,
                        $file,
                        $random_pwd, # TRUE/FALSE/birthday/dice
                        $pwd_length, # length of random pwd
                        $birthdate,
                        \%sophomorix_config,
                        @password_chars);
    } else {
        $sophomorix_first_password=$password_wish;
    }

    # Status
    if ($status eq "---") {
        $status="U";
    }
    # creationdate
    if ($creationdate eq "---"){
        $creationdate=$sophomorix_config{'DATE'}{'LOCAL'}{'TIMESTAMP_AD'};
    }
    # 0. rember login to set quota later
    push @users_for_quota, $login_check_ok;

    # add user
    ##############################
    $user_count++;

    # --- -> default-school
    $school=&AD_get_schoolname($school);

    # 1. Entry in AD
    &AD_school_create({ldap=>$ldap,
                       root_dse=>$root_dse,
                       root_dns=>$root_dns,
                       school=>$school,
                       smb_admin_pass=>$smb_admin_pass,
                       sophomorix_config=>\%sophomorix_config,
                       sophomorix_result=>\%sophomorix_result,
                     });

    my $group_token=&AD_get_name_tokened($class_group,$school,"adminclass");
    my $login_token=&AD_get_name_tokened($login_check_ok,$school,$role);

    my $type=$sophomorix_config{'FILES'}{'USER_FILE'}{$file}{'sophomorixType'};

    &AD_group_create({ldap=>$ldap,
                      root_dse=>$root_dse,
                      root_dns=>$root_dns,
                      group=>$group_token,
                      group_basename=>$class_group,
                      school=>$school,
                      status=>"P",
                      type=>$type,
                      joinable=>"TRUE",
                      description=>$group_token,
                      gidnumber_migrate=>$gidnumber_migrate,
                      file=>$file,
                      smb_admin_pass=>$smb_admin_pass,
                      sophomorix_config=>\%sophomorix_config,
                      sophomorix_result=>\%sophomorix_result,
                    });

    &AD_user_create({ldap=>$ldap,
                     root_dse=>$root_dse, 
                     root_dns=>$root_dns, 
                     user_count=>$user_count,
                     max_user_count=>$max_user_count,
                     identifier=>$identifier,
                     login=>$login_token,
                     group=>$group_token,
                     group_basename=>$class_group,
                     firstname_ascii=>$firstname_ascii,
                     surname_ascii=>$surname_ascii,
                     firstname_utf8=>$firstname_utf8,
                     surname_utf8=>$surname_utf8,
                     birthdate=>$birthdate,
                     sophomorix_first_password=>$sophomorix_first_password,
                     unid=>$unid,
                     uidnumber_migrate=>$uidnumber_migrate,
                     role=>$role,
                     type=>$type,
                     school=>$school,
                     creationdate=>$creationdate,
                     tolerationdate=>$tolerationdate,
                     deactivationdate=>$deactivationdate,
                     status=>$status,
                     file=>$file,  
                     mail=>$mail,  
                     smb_admin_pass=>$smb_admin_pass,
                     json=>$json,
                     webui_permissions_calculated=>\@webui_permissions_calculated,
                     sophomorix_config=>\%sophomorix_config,
                     sophomorix_result=>\%sophomorix_result,
                   });
}

close(SOPHOMORIXADD);
close(STILLTOADD);

# logging sophomorix.add
&backup_auk_file("add","after",\%sophomorix_config);
rename("$add_file_new","$add_file" );

&result_sophomorix_add_summary({
                     NAME=>"ADDED", 
                     RESULT=>$user_count, 
                     RESULT_TYPE => "integer",
                     DESCRIPTION_POST => "users added", 
                     DESCRIPTION_PRE => "added users", 
                     FORMAT_TYPE => 1,
                     sophomorix_result=>\%sophomorix_result,
			       });

&AD_unbind_admin($ldap);

# Unlock sophomorix in order to update quotas
if (-e $DevelConf::lock_file){
    unlink $DevelConf::lock_file;
    &print_title("Removing lock in $DevelConf::lock_file");
}

if ($user_count>0) {
    my $users=join(",",@users_for_quota);
    system("${DevelConf::path_sbin}/sophomorix-quota --user $users");
} else {
    print "NOT setting quota (0 users added).\n";
}

if ($user_count>0){
    &run_hook_scripts("ADD_HOOK_DIR",\%sophomorix_result,\%sophomorix_config,"TRUE");
} else {
    print "No users added -> No hook scripts run.\n";
}

&log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
