#!/usr/bin/perl -w
# This script (sophomorix-passwd) 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
                                 create_plain_password
                                 check_options
                                 config_sophomorix_read
                                 result_sophomorix_init
                                 result_sophomorix_add
                                 result_sophomorix_check_exit
                                 result_sophomorix_print
                                 );
use Sophomorix::SophomorixSambaAD qw(
                                 AD_get_unicodepwd
                                 AD_set_unicodepwd
                                 AD_school_create
                                 AD_bind_admin
                                 AD_unbind_admin
                                 AD_user_create
                                 AD_user_update
                                 AD_group_create
                                 AD_group_addmember
                                 AD_group_update
                                 AD_get_schoolname
                                 AD_get_name_tokened
                                 AD_dn_fetch_multivalue
                                 AD_object_search
                                 AD_login_test
                                 AD_debug_logdump
                                 AD_dns_get
                                 AD_computer_update
                                 AD_get_user
                                 AD_get_AD_for_check
                                 AD_get_full_groupdata
                                    );

my @arguments = @ARGV;
my @users=();

# option vars
$Conf::log_level=1;
my $help=0;
my $info=0;
my $json=0;

my $school="";
my $test_login="";
my $test_firstpassword=0;
my $set_firstpassword=0;

my $user="";
my $computer="";
my $clone_from_user="";
my $clone_to_user="";
my $class="";
my $projects="";
my $student=0;
my $teacher=0;
my $rooms="";
my $ws=0;

my $password="";
my $use_smbpasswd=0;
my $configfile=0;
my $reset=0;
my $common=0;
my $random=0;
my $all_characters=0;

my $smb_pw_m_change=2;

my $shell="";
my $show_help=0;

my $interactive=0;
my $nofirstpassupdate=0;
my $char_num=0;

my $show_password_expiry_days="";
my $set_password_noexpiry_all_sophomorix_users=0;

my $hide=0;
my $force=0;

# flag, if (1) user has specified ONE password for all (-p or --common)  , 
#    or if (2) password must be calculated for each user
my $password_given=0;

my $info_line="";

my $show_password_charlist=0;


# Parsen der Optionen
my $testopt=GetOptions(
           "help|h" => \$help,
           "info|i" => \$info,
           "json|j+" => \$json,
           "verbose|v+" => \$Conf::log_level,
           "users|user|u=s" => \$user,
           "computer=s" => \$computer,
           "clone-from-user=s" => \$clone_from_user,
           "clone-to-user=s" => \$clone_to_user,
           "class|c=s" => \$class,
#           "project|projects=s" => \$projects,
#           "student|students|s" => \$student,
#           "teacher|teachers|t" => \$teacher,
#           "room|rooms|r=s" => \$rooms,
#           "workstations|workstation|w" => \$ws,
           "password|passwd|pass=s" => \$password,
           "use-smbpasswd" => \$use_smbpasswd,
           "configfile" => \$configfile,
#           "reset" => \$reset,
           "common" => \$common,
           "random=i" => \$random,
           "interactive" => \$interactive,
#           "all-characters" => \$all_characters,
           "nofirstpassupdate" => \$nofirstpassupdate,
#           "plength=i" => \$char_num,
           "hide" => \$hide,
#           "samba-pwd-must-change!" => \$smb_pw_m_change,
#           "shell|loginshell=s" => \$shell,
#           "force" => \$force,
           "test-login=s" => \$test_login,
           "test-firstpassword" => \$test_firstpassword,
           "set-firstpassword" => \$set_firstpassword,
           "show-password-charlist" => \$show_password_charlist,
           "show-password-expiry-days=s" => \$show_password_expiry_days,
           "set-password-noexpiry-all-sophomorix-users" => \$set_password_noexpiry_all_sophomorix_users,
          );

my %sophomorix_result=&result_sophomorix_init("sophomorix-passwd");
# Prüfen, ob Optionen erkannt wurden, sonst Abbruch
&check_options($testopt,\%sophomorix_result,$json);

if ($use_smbpasswd==1){
    $use_smbpasswd="TRUE";
} else {
    $use_smbpasswd="TRUE"; # smbpasswd is the default
}


# --help
if ($help==1) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlsbeschreibung
   print('
sophomorix-passwd modifies passwords in the AD

Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose
  -i  / --info

Cloning a password-hash (unicodePwd and supplementalCredentials) from one user to another:
  --clone-from-user <user1> --clone-to-user <user2>

Testing stuff:
  --show-password-charlist                     (show characters used to create random passwords)
  --set-firstpassword                          (ALL users: set passwords back to sophomorixFirstPassword attribute)
  --set-firstpassword -u <user1>,<user2>, ...  (set passwords back to sophomorixFirstPassword attribute)

  --test-login <user> --password <password-to-test>

  --test-firstpassword                         (ALL users: Test sophomorixFirstPassword as password)
  --test-firstpassword -u <user1>,<user2>, ... (Test sophomorixFirstPassword as password)
    Users that still use their firstpassword are logged into 
     /var/lib/sophomorix/tmp_sophomorix-passwd/test-firstpassword.sh
    where their password can be set.


Options:
  --nofirstpassupdate (do not update AD attribute sophomorixFirstPassword)
  --hide (hide display of password in console output, when updating)

Create and set password:
  --pass <password>, --password <password>
  --pass <password>, --use-smbpasswd       (use smbpasswd to set the password, this is the default)
  --interactive                            (query new password twice on console)
  --random <num>                           (create random password of <num> characters for each user individually)
  --random <num> --common                  (create random password of <num> characters once and apply it to all users)
  --configfile                             (use *.school.conf of the user to create individual password)


Create userlist:
  -u <user1>,<user2>,...  /  --user  <user1>,<user2>,...
  -c <class1>,<class2>,... / --class  <class1>,<class2>,... (class members only, not admins)
Create computerlist
  --computer <computer1>,<computer2>,...

 Password expiry stuff:
  --show-password-expiry-days <user> (show info)
  --set-password-noexpiry-all-sophomorix-users (set all password of sophomorix users to nonexpiry)


Console examples:
  Interactively change password of a user (Always with --hide and --nofirstpassupdate)
    sophomorix-passwd --user schneima42 --interactive
  Create password according to *.school.conf settings
    sophomorix-passwd --configfile --user 

Webui examples:
  Change password for a user:
    sophomorix-passwd --user <user> --pass \'Muster!7\' --hide (--nofirstpassupdate) 
  Reset a password for a user from Schulkonsole:
    sophomorix-passwd --set-firstpassword -u <user1>,<user2>,...
  Test if the sophomorixFirstPassword is the valid password:
    sophomorix-passwd --test-firstpassword -u <user1>,<user2>,...


Todo:
Password change:
  --samba-pwd-must-change/nosamba-pwd-must-change
  --all-characters    (allow all characters in passwords,
                       only for developers, be careful!)
Create userlist:
  -s / --students
  -t / --teachers
  -w / --workstations (ExamAccouts)
  --project project1,project2,... 
  -r room1,room2,...   /  --rooms room1,room2,...

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


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);



# --info --user <>
if ($info==1 and $user ne "") {
    my @users=split(",",$user);
    foreach my $user (@users){
        my ($unicodepwd,$supplemental_credentials) = &AD_get_unicodepwd($user,\%sophomorix_config);
        print "\n";
        print "##### $user: #####\n";
        print "unicodePwd:: $unicodepwd\n";
        print "$supplemental_credentials \n";
    }
    exit;
} elsif ($info==1 and not $random==0){
    # go on
} elsif ($info==1){
    my $command="samba-tool domain passwordsettings show";
    print "\n# $command\n\n";
    system($command);
    print "\n";
    exit;
}



# Setting the filters

# school_filter
my $school_filter="";
if ($school ne ""){
    $school_filter="(sophomorixSchoolPrefix=$school)";
}


# list of users that are processed
my @userlist=();

# --user  for userlist
if ($user ne ""){
    my @users=split(",",$user);
    if ($#users==0){
	# single user
        push @userlist, $user; 
    } else {
	# multiple users
        foreach my $user (@users){
            push @userlist, $user; 
        }
    }
}



# --class  for userlist
if ($class ne ""){
    my @classes=split(",",$class);
    foreach my $class (@classes){
        print "user of $class\n";
        my $ref_groups=&AD_get_full_groupdata({ldap=>$ldap,
                                               root_dse=>$root_dse,
                                               root_dns=>$root_dns,
                                               grouplist=>$class,
                                               sophomorix_config=>\%sophomorix_config,
                                             });
        @userlist=(@userlist,@{ $ref_groups->{'GROUPS'}{$class}{'sophomorixMembers'} });
    }
}



# create user filter
@userlist=uniq(@userlist);
@userlist = sort (@userlist);
my $userlist_count=$#userlist+1;
my $user_filter="";
my %user=();
if ($#userlist==0){
    $user_filter="(sAMAccountName=$user)";
} else {
    # multiple users
    $user_filter="(| ";
    foreach my $user (@userlist){
        $user_filter=$user_filter."(sAMAccountName=$user) ";
	$user{$user}="option";
    }
    $user_filter=$user_filter.")";
}


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

############################################################
# --clone-from-user <user1> --clone-to-user <user2>
if ( ($clone_from_user ne "" and $clone_to_user eq "") or
     ($clone_to_user ne "" and $clone_from_user eq "") ){
    print "\nERROR: --clone-from-user <user1> --clone-to-user <user2>\n\n";
    exit 88;
} elsif ($clone_from_user ne "" and $clone_to_user ne ""){
    &print_title("Cloning password-hash of $clone_from_user to $clone_to_user");
    my ($unicodepwd,$supplemental_credentials)= &AD_get_unicodepwd($clone_from_user,\%sophomorix_config);
    &AD_set_unicodepwd($clone_to_user,$unicodepwd,$supplemental_credentials,\%sophomorix_config);
    &log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
}



############################################################
# --test-login
if ($test_login ne "" and $password ne ""){
    $sophomorix_result{'LOGINTEST'}{$user}{'EXISTS'}="FALSE";
    my @users=split(",",$test_login);
    foreach my $user (@users){
	$user_filter="(sAMAccountName=$user)";
        my $filter="( &(objectclass=user) (cn=*) (sophomorixRole=*) $school_filter $user_filter)";
        my $mesg = $ldap->search(
                          base   => $root_dse,
                          scope => 'sub',
                          filter => $filter,
                          attr => ['cn']
                                );
        &AD_debug_logdump($mesg,2,(caller(0))[3]);

        my $max = $mesg->count;
        for( my $index = 0 ; $index < $max ; $index++) {
            my $entry = $mesg->entry($index);
            my $dn=$entry->dn();
            my $sam_account=$entry->get_value ('sAMAccountName');
            print "* $sam_account: $dn\n";
            my ($res,$testpassword,$res1,$res2)=&AD_login_test($ldap,$root_dse,$dn,$password);
            $sophomorix_result{'LOGINTEST'}{$sam_account}{'EXISTS'}="TRUE";
            $sophomorix_result{'LOGINTEST'}{$sam_account}{'PAM'}{'RETURN'}=$res1;
            $sophomorix_result{'LOGINTEST'}{$sam_account}{'KERBEROS'}{'RETURN'}=$res2;

            if ($res==0){
                $sophomorix_result{'LOGINTEST'}{$sam_account}{'LOGIN_OK'}="TRUE";
                print "   ---> Pam/Kerberos Login with password \"$password\" OK!\n";
            } else {
                $sophomorix_result{'LOGINTEST'}{$sam_account}{'LOGIN_OK'}="FALSE";
                print "   ---> ERROR logging in with password \"$password\": $res (pam: $res1, kerberos: $res2)\n";
            }
        }
    }
    &log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
}



############################################################
# --test-firstpassword
if ($test_firstpassword==1){
    my $count_tested=0;
    my $count_success=0;
    my $count_fail=0;
    my $count_nofirstpass=0;
    my $count_nonexisting=0;
    my @success=();
    my @fail=();
    my @nofirst=();
    my @nonexisting=();
    my $log_dir=$sophomorix_config{'INI'}{'PATHS'}{'LIB'}."/tmp_sophomorix-passwd";
    my $log_file=$log_dir."/test-firstpassword.sh";

    system("mkdir -p $log_dir");
    open (LOG, ">$log_file");
    print LOG "#!/bin/sh\n";
    print LOG "echo\n";
    print LOG "echo \"This script will change the password to a random password\"\n";
    print LOG "echo \"for all users with a working firstpassword\"\n";
    print LOG "echo\n";
    print LOG "echo \"Hit Strg+c to abort within 10 seconds\"\n";
    print LOG "echo\n";
    print LOG "sleep 10\n";

    print "Testing if firstpassword works for the following Accounts\n";

    my $filter="( &(objectclass=user) (cn=*) (sophomorixRole=*) $school_filter $user_filter)";
    if($Conf::log_level>=2){
        print "Filter to find users: $filter\n";
    }
    my $mesg = $ldap->search(
                      base   => $root_dse,
                      scope => 'sub',
                      filter => $filter,
                      attr => ['cn']
                            );
    &AD_debug_logdump($mesg,2,(caller(0))[3]);

    my $max = $mesg->count;
    for( my $index = 0 ; $index < $max ; $index++) {
        my $entry = $mesg->entry($index);
        my $dn=$entry->dn();
        my $sam_account=$entry->get_value ('sAMAccountName');
        my $sn=$entry->get_value ('sn');
        my $given_name=$entry->get_value ('givenName');
        my $class=$entry->get_value ('sophomorixAdminClass');
        $count_tested++;
        print "* $count_tested) $sam_account: $dn\n";
	$user{$sam_account}="existing";
        my ($res,$testpassword)=&AD_login_test($ldap,$root_dse,$dn,$password);
        if ($res==0){
            print "   * Login with sophomorixFirstPassword $testpassword OK!\n";
            $count_success++;
            push @success, "OK: $dn\n";
            print LOG "sophomorix-passwd --random 15 --nofirstpassupdate --user $sam_account   # $given_name $sn, $class\n";
        } elsif ( $res==-1) {
            print "   * No firstpassword found!\n";
            $count_nofirstpass++;
            push @nofirst, "No Password: $dn\n";
        } else {
            print "   * ERROR logging in with sophomorixFirstPassword $testpassword: $res\n";
            $count_fail++;
            push @fail, "Failed: $dn\n";
        }
    }

    foreach my $user (@userlist){
	if ($user{$user} eq "existing"){
            # thats fine
	} else {
            push @nonexisting, "NOT OK: Nonexisting user $user\n";
	    $count_nonexisting++;
	}
    }

    &print_title("Test result:");
    @success = sort @success;
    @fail = sort @fail;
    @nofirst = sort @nofirst;
    @nonexisting = sort @nonexisting;
    foreach my $line (@success,@fail,@nofirst,@nonexisting){
        print "$line";
    }
    &print_line;
    print "$userlist_count tested:  $count_success OK,",
          " $count_fail Failed Logins,",
	  " $count_nofirstpass Without Firstpassword Attribute,",
	  " $count_nonexisting Nonexisting",
	  "\n";
    # print the command
    print LOG "";
    close(LOG);
    system("chmod +x $log_file");
    &log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
}



############################################################
# --set-firstpassword
if ($set_firstpassword==1){
    my @nonexisting=();
    my $count_nonexisting=0;
    # search for all users
    my $filter="( &(objectclass=user) (cn=*) (sophomorixRole=*) $school_filter $user_filter)"; 
    if($Conf::log_level>=2){
        print "Filter to find users: $filter\n";
    }
    my $mesg = $ldap->search(
                      base   => $root_dse,
                      scope => 'sub',
                      filter => $filter,
                      attr => ['sAMAccountName','cn','sophomorixFirstPassword']
                            );
    &AD_debug_logdump($mesg,2,(caller(0))[3]);
    my $max = $mesg->count;
    for( my $index = 0 ; $index < $max ; $index++) {
        my $entry = $mesg->entry($index);
        my $dn=$entry->dn();
        my $sam_account=$entry->get_value ('sAMAccountName');
        my $firstpassword=$entry->get_value ('sophomorixFirstPassword');
        my $user_count=$index+1;
	$user{$sam_account}="existing";
        &AD_user_update({ldap=>$ldap,
                     root_dse=>$root_dse,
                     dn=>$dn,
                     user=>$sam_account,
                     user_count=>$user_count,
                     max_user_count=>$max,
                     hide_pwd=>$hide,
#                     firstpassword=>$firstpassword,
                     sophomorix_first_password=>$firstpassword,
                     smbpasswd=>$use_smbpasswd,
                     json=>$json,
                     sophomorix_config=>\%sophomorix_config,
                     sophomorix_result=>\%sophomorix_result,
                   });
    }
    foreach my $user (@userlist){
        if ($user{$user} eq "existing"){
            # thats fine
	} else {
            push @nonexisting, "User $user does not exist";
	    $count_nonexisting++;
	}
    }
    if ($#nonexisting>-1){
        foreach my $error_message (@nonexisting){
            print "WARNING: $error_message\n";
	    &Sophomorix::SophomorixBase::result_sophomorix_add(\%sophomorix_result,"WARNING",-1,"",$error_message);
        }
        print "\n";
    }
    &log_script_end(\@arguments,\%sophomorix_result,\%sophomorix_config,$json);
}



############################################################
# creating passwords
############################################################
my @password_chars=();



# --random
if ($random!=0){
    @password_chars=&get_passwd_charlist();
    if ($random<$sophomorix_config{'samba'}{'domain_passwordsettings'}{'Minimum_password_length'}){
        print "\nERROR: Random password length $random is shorter than required minimun of ",
              "$sophomorix_config{'samba'}{'domain_passwordsettings'}{'Minimum_password_length'}\n\n";
        exit;
    }
    if ($info==1){
        print "100 valid passwords:\n";
	my $i=1;
	for ($i=1;$i<=100;$i++){
	    $password=&create_plain_password("TRUE",$random,"",@password_chars);
	    print "$i)  $password\n";
	    #if (not $password=~m/[a-z]/){
	    #    print "Password contains no small letters\n";
	    #}
	}
	exit;
    }
}



# --configfile
if ($configfile==1){
    @password_chars=&get_passwd_charlist();
}



# --common
if ($common==1){
    @password_chars=&get_passwd_charlist();
    $password=&create_plain_password("TRUE",$random,"",@password_chars);
}



# --show-password-charlist
if ($show_password_charlist==1){
    @password_chars=&get_passwd_charlist();
    print "Valid characters in a randomly generated password:\n";
    foreach my $char (@password_chars){
        print " $char";
    }
    print "\n";
    exit;
}



# --show-password-expiry-days <user>
if ($show_password_expiry_days ne ""){
    my $user=$show_password_expiry_days;
    my $ldb_command="ldbsearch -H ldap://localhost -UAdministrator%`cat /etc/linuxmuster/.secret/administrator` ".
                    "-b $root_dse sAMAccountname=".
                    $user.
                    " msDS-UserPasswordExpiryTimeComputed".
                    " | grep --color=never msDS-UserPasswordExpiryTimeComputed";
    #print "$ldb_command\n";
    my $line=`$ldb_command`;
    if ($line eq ""){
        print "\nNo result found: Does user \"$user\" exist?\n\n";
        exit 88;
    }
    my ($tmp,$wintime)=split(":",$line);
    chomp($wintime);
    $wintime=&Sophomorix::SophomorixBase::remove_embracing_whitespace($wintime);
    my $unix_expire_time=$wintime/10000000-11644473600;
    my $days=($unix_expire_time-$sophomorix_config{'DATE'}{'LOCAL'}{'EPOCH'})/86400;
    print "\nPassword of user $user expires in $days days\n\n";
    exit;
}



# --set-password-noexpiry-all-sophomorix-users
if ($set_password_noexpiry_all_sophomorix_users==1){
    my ($ref_AD) = &AD_get_AD_for_check({ldap=>$ldap,
                                         root_dse=>$root_dse,
                                         root_dns=>$root_dns,
                                         admins=>"FALSE",
                                         sophomorix_config=>\%sophomorix_config,
                                        });
    #print Dumper($ref_AD->{'sAMAccountName'});
    foreach my $sam (keys %{ $ref_AD->{'sAMAccountName'} } ) {
        my $command="samba-tool user setexpiry ".$sam." --noexpiry";
        print "\n";
        print "$sam:\n";
        print "   $command\n";
        system($command);
    }
    print "\n";
}



############################################################
# setting passwords from userlist
############################################################

# set a password from userlist
if ($userlist_count>0 and ($password ne "" or 
                           $random!=0 or
                           $configfile==1)){
    my $user_count=0;
    foreach my $userlist_user (@userlist){
	$user_count++;
	my $filter="( &(objectclass=user) (sAMAccountName=$userlist_user) )"; 
        my $mesg = $ldap->search(
                          base   => $root_dse,
                          scope => 'sub',
                          filter => $filter,
                          attr => ['sAMAccountName','cn','dn','sophomorixFirstPassword']
                                );

        &AD_debug_logdump($mesg,2,(caller(0))[3]);
        my $max = $mesg->count;
	if ($max==1){
            # create pasword for each user
            if ($random!=0 and $common==0){
                $password=&create_plain_password("TRUE",$random,"",@password_chars);
            } elsif ($configfile==1){
                my ($firstname_utf8_AD,$lastname_utf8_AD,$adminclass_AD,$existing_AD,$exammode_AD,$role_AD,
                    $home_directory_AD,$user_account_control_AD,$toleration_date_AD,$deactivation_date_AD,
                    $school_AD,$status_AD,$firstpassword_AD,$unid_AD,$firstname_ASCII_AD,$lastname_ASCII_AD,
                    $firstname_initial_AD,$lastname_initial_AD,$user_token_AD,$file_AD,$birthdate_AD)=
                    &AD_get_user({ldap=>$ldap,
                                  root_dse=>$root_dse,
                                  root_dns=>$root_dns,
                                  user=>$userlist_user,
                                });
                    $password=&get_plain_password(
                        $role_AD,
                        $file_AD,
                        $sophomorix_config{'FILES'}{'USER_FILE'}{$file_AD}{'RANDOM_PWD'}, # yes|no|birthdate|dice
                        $sophomorix_config{'FILES'}{'USER_FILE'}{$file_AD}{'PWD_LENGTH'}, # length of random pwd
                        $birthdate_AD,
                        \%sophomorix_config,
                        @password_chars);
            }
            my $entry = $mesg->entry(0);
            my $dn=$entry->dn();
            if ($nofirstpassupdate==1){
                &AD_user_update({ldap=>$ldap,
                                 root_dse=>$root_dse,
                                 dn=>$dn,
                                 user=>$userlist_user,
                                 user_count=>$user_count,
                                 max_user_count=>$userlist_count,
                                 hide_pwd=>$hide,
                                 sophomorix_first_password=>$password,
                                 smbpasswd=>$use_smbpasswd,
                                 # firstpassword=>$password, # not updated
                                 json=>$json,
                                 sophomorix_config=>\%sophomorix_config,
                                 sophomorix_result=>\%sophomorix_result,
			        });
	    } else {
                &AD_user_update({ldap=>$ldap,
                                 root_dse=>$root_dse,
                                 dn=>$dn,
                                 user=>$userlist_user,
                                 user_count=>$user_count,
                                 max_user_count=>$userlist_count,
                                 hide_pwd=>$hide,
                                 sophomorix_first_password=>$password,
                                 firstpassword=>$password,
                                 smbpasswd=>$use_smbpasswd,
                                 json=>$json,
                                 sophomorix_config=>\%sophomorix_config,
                                 sophomorix_result=>\%sophomorix_result,
			        });
	    }
	} else {
            print "\n";
            print "WARNING: Skipping user \"$userlist_user\" ($max users found instead of 1)\n\n";
	}
    }
} elsif ($password eq "" and $userlist_count>0) {
    print "\n";
    print "WARNING: No password given, but $userlist_count users in userlist\n";
    print "         I do not know what password you want!\n\n";        
}



############################################################
# setting passwords from computerlist
############################################################
my @computerlist=();
# --computer
if ($computer ne ""){
    @computerlist=split(",",$computer);
}
my $computerlist_count=$#computerlist+1;

# set a password from computerlist
if ($password ne "" and $computerlist_count>0){
    my $computer_count=0;
    foreach my $computerlist_user (@computerlist){
	$computer_count++;
        $computerlist_user=~tr/a-z/A-Z/;
        $computerlist_user=&Sophomorix::SophomorixBase::append_dollar($computerlist_user);
        my %update_computer=();
        $update_computer{$computerlist_user}{'REPLACE'}{'unicodePwd'}=$password;
        $update_computer{$computerlist_user}{'COUNT'}=1;
	my $filter="( &(objectclass=user) (sAMAccountName=$computerlist_user) )";
        my $mesg = $ldap->search(
                          base   => $root_dse,
                          scope => 'sub',
                          filter => $filter,
                          attr => ['sAMAccountName','cn','dn','sophomorixFirstPassword']
                                );
        &AD_debug_logdump($mesg,2,(caller(0))[3]);
        my $max = $mesg->count;
	if ($max==1){
            my $entry = $mesg->entry(0);
            my $dn=$entry->dn();
        &AD_computer_update({ldap=>$ldap,
                             root_dse=>$root_dse,
                             computer=>$computerlist_user,
                             computer_count=>$computer_count,
                             attrs_count=>$update_computer{$computerlist_user}{'COUNT'},
                             replace=>\%update_computer,
                             sophomorix_first_password=>$password,
                             hide_pwd=>$hide,
                             max_computer_count=>$computerlist_count,
                             json=>$json,
                             sophomorix_config=>\%sophomorix_config,
                             sophomorix_result=>\%sophomorix_result,
                           });
	} else {
            print "\nERROR: $max users $computerlist_user found instead of 1\n\n";
	}
    }
} elsif ($password eq "" and $computerlist_count>0) {
    print "\n";
    print "WARNING: No password given, but $computerlist_count users in computerlist\n";
    print "         I do not know what password you want!\n\n";
}





############################################################
# Setting passwords interacive
############################################################
if ($interactive==1){
    if ($userlist_count==1){
        # ask for password
        use Term::ReadKey;
        ReadMode('noecho');
        # ask once
        print "New password : ";
        my $password_1 = ReadLine(0);
        print "\n";
        chomp($password_1);

        # ask again
        print "Retype new password : ";
        my $password_2 = ReadLine(0);
        print "\n";
        chomp($password_2);
        # reset to echo
        ReadMode('normal');

        # Look if they match
        if ($password_1 eq $password_2){
            print "New passwords match!\n";
 	    my $filter="( &(objectclass=user) (sAMAccountName=$user) )"; 
            my $mesg = $ldap->search(
                              base   => $root_dse,
                              scope => 'sub',
                              filter => $filter,
                              attr => ['sAMAccountName','dn']
                                     );
            &AD_debug_logdump($mesg,2,(caller(0))[3]);
            my $max = $mesg->count;
	    if ($max==1){
                my $entry = $mesg->entry(0);
                my $dn=$entry->dn();
                &AD_user_update({ldap=>$ldap,
                                 root_dse=>$root_dse,
                                 dn=>$dn,
                                 user=>$user,
                                 user_count=>"1",
                                 max_user_count=>"1",
                                 hide_pwd=>"1",
                                 sophomorix_first_password=>$password_1,
                                 smbpasswd=>$use_smbpasswd,
                                 # firstpassword=>$password, # not updated
                                 json=>$json,
                                 sophomorix_config=>\%sophomorix_config,
                                 sophomorix_result=>\%sophomorix_result,
                                });
	    }
            exit 0;
         } else {
             print "\nERROR: New passwords don't match!\n\n";
             exit 88;
         }
     } else {
         print "\nERROR: I can set password interactively only for one user\n";
         print "Unable to change password\n\n";
         exit 88;
     }

}



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



# sub validate_password {
#     my ($password) = @_;
#     # - and $ escaped
#     if ($password=~/[^0-9A-Za-z@!\$%&?_\-:;.,]/ and $all_characters==0){
#         print "New password contains unallowed characters!\n";
#         &nscd_start();
#         exit 5;
#     }
# }
