#!/usr/bin/perl -w
# This script (sophomorix-kill) 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 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
                                 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_kill
                                 remove_from_list
                                 );
use Sophomorix::SophomorixSambaAD qw(
                                 AD_bind_admin
                                 AD_unbind_admin
                                 AD_dns_get
                                 AD_user_kill
                                 AD_get_passwd
                                 AD_get_name_tokened
                                 AD_get_AD_for_check
                                 AD_get_sessions
                                 AD_session_manage
                                    );

my @arguments = @ARGV;

my $user_count=0;

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

my $killuser="";

# 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,
           "skiplock" => \$skiplock,
           "killuser=s" => \$killuser,
           "user|u=s" => \$user_opt,
           "class=s" => \$class_opt,
           "school=s" => \$school_opt,
          );

my %sophomorix_result=&result_sophomorix_init("sophomorix-kill");
&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 $kill_file=$sophomorix_config{'INI'}{'PATHS'}{'CHECK_RESULT'}."/sophomorix.kill";
my $kill_file_new=$sophomorix_config{'INI'}{'PATHS'}{'CHECK_RESULT'}."/sophomorix.kill.new";



# --help
if ($help==1) {
   # get scriptname
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   print('
sophomorix-kill removes users from the sophomorix database and deletes their data for good.

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

Deleting users shown by sophomorix-kill -i:

  Delete all users:
    sophomorix-kill

  Delete users from some schools only:
    sophomorix-kill --school <schoolname1>,<schoolname2>, ...

  Delete some users only:
    sophomorix-kill --user <user1>,<user2>, ...

  Delete users in some classes only:
    sophomorix-kill --class <class1>,<class2>, ...

Deleting users by force:

  Delete a user by force (must not deleletable):
    --killuser user (Kill a user, even if she is not in sophomorix.kill)

Please see the sophomorix-kill(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_kill=&read_sophomorix_kill({sophomorix_config=>\%sophomorix_config});
    &json_dump({json => $json,
                jsoninfo => "KILLFILE",
                jsoncomment => "sophomorix.kill",
                log_level => $Conf::log_level,
                object_name => $school_opt,
                hash_ref=>$ref_kill,
                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);

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

# --killuser
if ($killuser ne ""){
    # killing users given by option --killuser
    my @userlist=split /,/,$killuser;
    my $max_user_count=$#userlist+1;
    foreach my $user_to_kill (@userlist){
        $user_count++;
        my $identifier_to_kill="";
        &AD_user_kill({ldap=>$ldap,
                       root_dse=>$root_dse,
                       root_dns=>$root_dns,
                       login=>$user_to_kill,
                       identifier=>$identifier_to_kill,
                       user_count=>$user_count,
                       max_user_count=>$max_user_count,
                       smb_admin_pass=>$smb_admin_pass,
                       json=>$json,
                       sophomorix_config=>\%sophomorix_config,
                       sophomorix_result=>\%sophomorix_result,
                        });
    }
} else {
    ### using the kill file

    # file for users that are NOT killed
    open(STILLTOKILL,">$kill_file_new") || die "ERROR: $!";

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

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

    # open kill file
    open(KILL,"$kill_file") || die "ERROR: $!";

    while(<KILL>){
        if(/^\#/){ # skip commented lines
 	    next;
        }

        chomp();
        my ($identifier_to_kill,
            $login_name_to_kill,
            $class_to_kill,
            $school_to_kill)=split(/::/);

        # deselecting users
        if ($user_opt ne ""){
            if (not exists $selection{'SELECTION'}{'USER'}{$login_name_to_kill}){
                print "   * Skipping $login_name_to_kill in $class_to_kill in $school_to_kill (Reason: $login_name_to_kill)\n";
                print STILLTOKILL "$_\n";
                next;
            }
         }

        if ($class_opt ne ""){
            if (not exists $selection{'SELECTION'}{'CLASS'}{$class_to_kill}){
                print "   * Skipping $login_name_to_kill in $class_to_kill in $school_to_kill (Reason: $class_to_kill)\n";
                print STILLTOKILL "$_\n";
                next;
            }
        }

        if ($school_opt ne ""){
            if (not exists $selection{'SELECTION'}{'SCHOOL'}{$school_to_kill}){
                print "   * Skipping $login_name_to_kill in $class_to_kill in $school_to_kill (Reason: $school_to_kill)\n";
                print STILLTOKILL "$_\n";
                next;
            }
        }

        $user_count++;
        &AD_user_kill({ldap=>$ldap,
                       root_dse=>$root_dse,
                       root_dns=>$root_dns,
                       login=>$login_name_to_kill,
                       identifier=>$identifier_to_kill,
                       user_count=>$user_count,
                       max_user_count=>$max_user_count,
                       smb_admin_pass=>$smb_admin_pass,
                       json=>$json,
                       sophomorix_config=>\%sophomorix_config,
                       sophomorix_result=>\%sophomorix_result,
                     });
    }
    close(KILL);
    close(STILLTOKILL);
}



# remove nonexisting users from sessions
&print_title("START: Removing nonexisting users from sessions");
my %sessions=&AD_get_sessions($ldap,$root_dse,$root_dns,$json,"all","allsupervisors",$smb_admin_pass,0,0,\%sophomorix_config);
#print Dumper(%sessions{'NONEXISTING_PARTICIPANTS_by_session'});
if (defined $sessions{'NONEXISTING_PARTICIPANTS_by_session'}){
    foreach my $session ( keys %{ $sessions{'NONEXISTING_PARTICIPANTS_by_session'} }){
        my $supervisor=$sessions{'NONEXISTING_PARTICIPANTS_by_session'}{$session}{'SUPERVISOR'};

	# create new list of participants without the nonexisting users
        my @participants_old=split(/,/,$sessions{'ID'}{$session}{'PARTICIPANTSTRING'});
        my @participants_new = @participants_old;
        foreach my $user ( keys %{ $sessions{'NONEXISTING_PARTICIPANTS_by_session'}{$session}{'PARTICIPANTS'} }){
            @participants_new = &remove_from_list($user,@participants_new);
        }
        my $participants_new=join(",",@participants_new);

        # update the requested session
        &AD_session_manage({ldap=>$ldap,
                            root_dse=>$root_dse,
                            supervisor=>$supervisor,
                            create=>"FALSE",
                            kill=>"FALSE",
                            session=>$session,
                            participants=>$participants_new,
                            sessions_ref=>\%sessions,
                            sophomorix_config=>\%sophomorix_config,
                        });
    }
} else {
    print "   * no users to remove from sesssions\n";
}
&print_title("END: Removing nonexisting users from sessions.");



# logging sophomorix.kill
&backup_auk_file("kill","after",\%sophomorix_config);
rename("$kill_file_new","$kill_file");

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

&AD_unbind_admin($ldap);

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

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