#!/usr/bin/perl -w
# $Id$
# 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@gmx.de


# Bibliotheken
use strict;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
use Sophomorix::SophomorixConfig;
use Sophomorix::SophomorixBase;
use Sophomorix::SophomorixAPI;
use DBI;
use Net::LDAP;
use Sophomorix::SophomorixPgLdap qw(show_modulename
                                    check_connections
                                    get_first_password
                                    update_user_db_entry
                                    check_sophomorix_user
                                    set_sophomorix_passwd
                                   );

my @arguments = @ARGV;


# ===========================================================================
# Variablen
# ==========================================================================

my @users=();

# ===========================================================================
# Optionen verarbeiten
# ==========================================================================

# Variablen für Optionen
$DevelConf::testen=0;
$DevelConf::system=0;
$Conf::log_level=1;
my $help=0;
my $info=0;
my $loginname="";
my $classes="";
my $projects="";
my $student=0;
my $teacher=0;
my $rooms="";
my $ws=0;

my $password="";
my $config=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 $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 $password_lehrer="";
my $password_other="";

my $info_line="";

# Parsen der Optionen
my $testopt=GetOptions(
           "users|user|u=s" => \$loginname,
           "class|classes|c=s" => \$classes,
           "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,
           "config" => \$config,
           "reset" => \$reset,
           "common" => \$common,
           "random" => \$random,
           "interactive" => \$interactive,
           "all-characters" => \$all_characters,
           "nofirstpassupdate" => \$nofirstpassupdate,
           "plength=i" => \$char_num,
           "verbose|v+" => \$Conf::log_level,
           "hide" => \$hide,
           "samba-pwd-must-change!" => \$smb_pw_m_change,
           "shell|loginshell=s" => \$shell,
           "force" => \$force,
           "info|i" => \$info,
           "help|h" => \$help
          );

# Prüfen, ob Optionen erkannt wurden, sonst Abbruch
&check_options($testopt);
&check_connections();

# --help
if ($help==1) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlsbeschreibung
   print('
sophomorix-passwd modifies users in the sophomorix database and the authentification system (see also sophomorix-usermod)

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

Create a password:
  --config  (use sophomorix.conf)
  --pass password, --password password
  --common
  --interactive
  --nofirstpassupdate
  --reset
  --random
  --plength number
  --hide

Password change:
  --samba-pwd-must-change/nosamba-pwd-must-change
  --all-characters    (allow all characters in passwords,
                       only for developers, be careful!)

Shell:
  --shell /bin/bash

Create userlist:
  -s / --students
  -t / --teachers
  -w / --workstations (ExamAccouts)
  -u user1,user2,...   /  --users  user1,user2,...
  -c class1,class2,... /  --class class1,class2,... 
  --project project1,project2,... 
  -r room1,room2,...   /  --rooms room1,room2,...

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




# add teachers to classes if option is given
if ($teacher==1){
    if ($classes eq ""){
        $classes=${DevelConf::teacher};
    } else {
        $classes=$classes.",".${DevelConf::teacher};
    }
}


if ($loginname eq "domadmin"){

}


# create the list of users
if ($loginname eq "domadmin"){
   @users=("domadmin");
} elsif ($force==1){
   @users=&create_userlist($loginname,$classes,0,$projects,0,$student,
                           $rooms,$ws,0,0);
} else {
   # check the users
   if($Conf::log_level>=2){
      &titel("Checking login names ...");
   }
   @users=&create_userlist($loginname,$classes,0,$projects,0,$student,
                           $rooms,$ws,0,1);
}

################################################################################
# Exit
################################################################################

# exit if no users specified
if ($#users+1==0){
    print "ERROR: No users specified. \n";
    exit;
}


# limit plength to 1 to 25
if ($char_num >=26 or $char_num < 0){
    print "Error: $char_num characters is too long/short for a password\n";
    exit;
}

if ($password ne "" and $char_num!=0){
    print "Error: What do you need --plength for? \n";  
    exit;
}

if ($char_num!=0 and($common==0 and $random==0)){
    print "Error: Options make no sense: add  --random or --common\n";  
    exit;
}

################################################################################
# Start
################################################################################

# --info, show only the users
if ($info==1) {
   &print_list_column(6,"Passwörter setzen für user ",@users);
   print "INFO: $info_line  \n";
   exit;
}

&log_script_start(@arguments);


# use the charlist of SophomorixBase
my @chars=&get_passwd_charlist();


#
# Create a password
#

# --password
if ($password ne ""){
   $info_line="Password will be '$password'";
   $password_given=1;
}

# --common
if ($common==1){
   if ($char_num==0){
      $password_lehrer=&get_plain_password(${DevelConf::teacher},@chars);
      $password_other=&get_plain_password("",@chars);
   } else {
      $password_lehrer=&get_random_password($char_num,${DevelConf::teacher},@chars);
      $password_other=&get_random_password($char_num,"",@chars);
   }
   $info_line="Passwords will be :\n".
              "   '$password_lehrer'  (for teachers) \n".
              "   '$password_other'  (for others) ";

}

# --reset
if ($reset==1){
   $info_line="Passwords will be looked up in the sophomorix database";
}

# --random
if ($random==1){
   $info_line="Every user will have its own a random password";
}



# Setting passwords interacive
if ($interactive==1){
    my $count_users=$#users+1;
    if ($count_users==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){
            # check for invalid characters
            &validate_password($password_1);
            # Passwort für user setzen
            &set_sophomorix_passwd($users[0],$password_1);
            # do NOT set password in the Database as FirstPassword
            &nscd_start();
            exit 0;
        } else {
            print "New passwords don't match!\n";
            &nscd_start();
            exit 10;
        }
    } else {
        print "ERROR: I can set password interactively only for one user\n";
        print "Unable to change password\n";
        # smbldap-passwd returns 0 (10 should be better)
        &nscd_start();
        exit 0;
    }

}


# --shell
#if ($shell ne ""){
#   foreach my $user (@users){
#      print "  Setting shell of user $user to $shell\n";
#      &update_user_db_entry($user,"LoginShell=$shell");
#   }
#}



# without option
# Setting the passwords noninteractive
foreach my $user (@users){
    # gruppe ermitteln
    my ($home,$type,$gecos,$group)=
        &Sophomorix::SophomorixPgLdap::fetchdata_from_account($user);

    # --shell
    if ($shell ne ""){
       print "  Setting shell of $user in $group ($type) to $shell\n";
       &update_user_db_entry($user,"LoginShell=$shell");
    } else {
       if($Conf::log_level>=2){
          print "  Not changing shell for $user in $group ($type)\n";
       }
    }

    # --samba-pwd-must-change/samba-pwd-must-change
    if ($smb_pw_m_change==1){
       # Must change
       print "  Setting user $user to sambaPwdMustChange=Yes\n";
       &update_user_db_entry($user,"sambaPwdMustChange=Yes");
    } elsif ($smb_pw_m_change==0){
       # NOT Must change
       print "  Setting user $user to sambaPwdMustChange=No\n";
       &update_user_db_entry($user,"sambaPwdMustChange=No");
    } else {
       if($Conf::log_level>=2){
          print "  Not changing sambaPwdMustChange for $user in $group ($type)\n";
       }
    }

    # which password to set
    if ($password_given==1){
        # use $password, do nothing
    } elsif ($reset==1){
        # lookup old password
        $password=&get_first_password($user);
    } elsif ($common==1){
       # set password
       if ($group eq ${DevelConf::teacher}){ 
           $password=$password_lehrer;
       } else {
	   $password=$password_other;
       }
    } elsif ($random==1){
       # set password randomly
       $password=&get_random_password($char_num,$group,@chars);
    } elsif ($config==1){
       # nothing specified: use sophomorix.conf
       $password=&get_plain_password($group,@chars)
    } elsif ($shell eq "" and $smb_pw_m_change==2){
       $show_help=1;
       if($Conf::log_level>=2){
          print "  Not setting password for $user in $group ($type)\n";
       }
       next;
    } else {
       # no options at all
       next;
    }

    if ($hide==1){
       print "  Setting password for user $user in ".
             "$group ($type) to xxxxxxxx\n";
    } else {
       print "  Setting password for user $user in ".
             "$group ($type) to $password\n";
    }
    # check for invalid characters
    &validate_password($password);
    # Passwort für user setzen
    &set_sophomorix_passwd($user,$password);
    # updating the database
    if ($nofirstpassupdate==0){
        &update_user_db_entry($user,"FirstPass=$password");
    }
}

# show mor help when no password could be generated
# and no --shell option was given
#if ($no_shell_no_pass==1 and $no_pw_m_change_no_pass=1){
if ($show_help==1){
    print "\nUse one of the following options:\n";
    print "   A) one of the password options\n";
    print "   B) the --shell option\n";
    print "   C) the --samba-pwd-must-change/nosamba-pwd-must-change option\n";
    print "\n";
}


&log_script_end(@arguments);



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