############################################################
# Script       : Adduser.pl
#
# Language     : Perl 5.004_02 windows NT Intel
#
# Description  : This Perl script reads a file and creates
#                groups and user accounts on the Windows NT
#                domain where the current user is logged on.
#
# Dependencies : CACLS.EXE          (standard Windows NT command line utility)
#                GetOpt::Long       (standard Perl module)
#                Win32::NetAdmin    (Windows NT specific Perl module)
#                Win32::NetResource (Windows NT specific Perl module)
#                BLAT.EXE           a freeware command-line mail utility
#
# Usage        : perl adduser.pl -i[nput]=<filename>
#
# Author       : Reinoud van Leeuwen (reinoud@xs4all.nl)
# Status       : Freeware : use, change, learn, steal in any way you like :-)
# Version      : $Revision: 1.6 $
# Date         : $Date: 1997/12/18 14:39:50 $
#
# TO DO        : - specify full name of user with Perl function
#                - set NTFS rights using Perl function
#                - specify user profile location using Perl function
#                - enable interactive use when no inputfile givven
#                - create SQL logins
#                - put users in existing LOCAL groups
#                - logfile
#
# Revision History:
# $Log: adduser.pl,v $
# Revision 1.6  1997/12/18 14:39:50  DB_ADM
# added extended reporting.
# added mail option
#
# Revision 1.5  1997/12/11 13:39:16  DB_ADM
# - improved regular expressions
# - added counter for created profile directories
#
# Revision 1.4  1997/12/04 16:41:23  DB_ADM
# added random password function
#
# Revision 1.3  1997/12/04 15:09:21  DB_ADM
# removed inactive code
#
# Revision 1.2  1997/12/04 14:22:35  DB_ADM
# Fixed some bugs with directory creation and setting of user rights
#
############################################################
use Getopt::Long ;      # needed for parsing of command-line
use Win32::NetAdmin;    # needed for administration of Windows NT domain
use Win32::NetResource; # needed for adding remote shares
use Win32;              # for loginname and station name

##############################################################
# Constants that may be edited
# beware that special characters like '\' or '$' have to be escaped!
##############################################################
my $passwordlength      = 8;                              #
my $passwordAge         = 0;                              #
my $homedirserver       = "\\\\SERVERNAME";               # The server where the home directories reside
my $homedir_root_share  = "home\$";                       # the sharename where the home directories are created
my $profileserver       = "\\\\SERVERNAME";               # The server where the user profile  directories reside
my $profile_root_share  = "profile\$";                    # the sharename where the user profile  directories are created
my $scriptpath          = "logon.bat";                    # The name of the login script
my $dflt_group_comment  = "Global group to put users in"; #
my $privilege           = USER_PRIV_USER;                 # privilige of new accounts
my $flags               = UF_SCRIPT & UF_NORMAL_ACCOUNT;  #
my $illegal_characters  = ' \!#$%&()*+,-./:;<=>?@{}'."'"; # illegal characters in usernames
my $mailprogram         = "blat.exe";                     # specify full path when not in search path
my $smtpserver_ip       = "127.0.0.1";                    # IP address of SMTP mail server
my $receiver            = "user\@domain.com";             #

##############################################################
# get version information from auto-generated fields by RCS version control
##############################################################
my $version_number = (split ' ', '$Revision: 1.6 $')[1];
my $version_date   = (split ' ', '$Date: 1997/12/18 14:39:50 $')[1];
print "Adduser $version_number $version_date\n\n";
print &timestamp, " script $0 started\n";

##############################################################
# parse command-line options
##############################################################
GetOptions ("input=s","Help")  or $printusage=1;

if ( !$opt_input || $opt_help || $printusage)
{
    print <<"EOF";
Synopsis:   Reads in a textfile and creates groups and users on the current
            Windows NT domain.
Usage:      $0 -i[nput]=<filename>
            $0 -H[elp]   print this information
Inputfile:  # marks a comment line
            GROUPS group1,group2
               the users following this line will be added to global group1 and
               group2. If group1 or group2 don't exist, they will be created.
            username, full name, comment
               create useraccount on the NT domain.

EOF
    if ( !$opt_input )  {
        print "ERROR: No inputfile specified, nothing done.\n";   }
    exit;
}

##############################################################
# define arrays to hold information for reporting
##############################################################
my @username;
my @fullname;
my @password;
my @homedir;

##############################################################
# get the domain controller for the domain this computer belongs to
##############################################################
my $localpc=Win32::NodeName();     # NetBIOS computer name
my $domain=$ENV{USERDOMAIN};
Win32::NetAdmin::GetDomainController ($localpc,
                                      $domain,
                                      $domaincontroller)
   || die "cannot find domain controller for domain $domain. $!";

##############################################################
# check if the current user (the one that started this script) has sufficient rights
##############################################################
my $curuser=Win32::LoginName();
Win32::NetAdmin::UserGetAttributes($domaincontroller, $curuser,    $curpassword, $curpasswordAge,
                                   $curprivilege,     $curhomeDir, $curcomment,  $curflags,
                                   $curscriptPath);
if ( !($curprivilege & USER_PRIV_ADMIN) ){
    die "Error: user $curuser has not got administrative privileges on domain $domain\n"; }

##############################################################
# determine the local pathname of the root of the homedirectories
##############################################################
my $homedir_root_local_path = "";
if (Win32::NetResource::NetShareGetInfo($homedir_root_share, $shareinfo, $homedirserver)) {
    $homedir_root_local_path = $shareinfo->{'path'}; }
else {
    die "Fatal error while checking root for homedirectories ($homedirserver\\$homedir_root_share): $!\n"; }

##############################################################
# define and initialize counters
##############################################################
my $users_created_OK                  = 0;
my $groups_created_OK                 = 0;
my $users_added_to_groups_OK          = 0;
my $home_directories_created_OK       = 0;
my $profile_directories_created_OK    = 0;
my $users_created_ERROR               = 0;
my $groups_created_ERROR              = 0;
my $users_added_to_groups_ERROR       = 0;
my $home_directories_created_ERROR    = 0;
my $profile_directories_created_ERROR = 0;

##############################################################
# proces inputfile
##############################################################
open (INPUT, $opt_input) or die "Could not open input file $opt_input : $!";

while ( <INPUT> ) {
    chomp;
    if  ( /(^\s*#)|(^\s*$)/ ) {                  # skip lines that start with a # or consist of only whitespace
       print "*** ignore line: \"$_\"\n"; }

    elsif ( s/^GROUPS//i ) {                     # handle lines starting with the word "GROUPS" &  remove keyword groups
                                                 # check groups and create global group if not present
        foreach $group (@groups =  split ",") {
            $group =~ s/^\s*(.*?)\s*$/$1/;   # remove leading and trailing blanks from name

            if(( !Win32::NetAdmin::LocalGroupGetAttributes($domaincontroller, $group, $comment) ) &&
               ( !Win32::NetAdmin::GroupGetAttributes     ($domaincontroller, $group, $comment) )  )  {
                if (Win32::NetAdmin::GroupCreate($domaincontroller, $group, $dflt_group_comment)) {
                    print "-   Global group \"$group\" created.\n";
                    $groups_created_OK++;
                }
                else {
                    print "*** creation of group \"$group\" failed!\n";
                    $groups_created_ERROR++;
                }
            }
        }
    } # elseif ( /^GROUPS/ )

    else { # assume user details are on this line
        $accountexist=0;
        $account_created=0;
        ($username,$fullname,$comment) = split ",";

        $username =~ s/^\s*(.*?)\s*$/$1/; # remove leading and trailing blanks by matching greedy for the
        $fullname =~ s/^\s*(.*?)\s*$/$1/; #  leading and trailing whitespace, while matching non-greedy the middle part
        $comment  =~ s/^\s*(.*?)\s*$/$1/; #  (the '(.*?)' part). This middle part is returned (with $1)

        print "\n--> user: $username -------------------\n";
        # check for invalid usernames
        if ( $username =~ /([$illegal_characters]+)/ ) {
            print "*** Error: username \"$username\" contains invalid character(s) \"$1\"\n";
            $users_created_ERROR++;
        }

        # check for existence of useraccount
        elsif (Win32::NetAdmin::UserGetAttributes ($domaincontroller, $username, $password, $passwordAge, $privilege,
                                                   $homeDir,          $comment,  $flags,    $scriptPath)) {
             # user does exist
             print "*** user \"$username\" already exists on domain $domain\n";
             $accountexist=1;
        }
        else {
            # create useraccount
            $password = &randpassword ($passwordlength);
            if (Win32::NetAdmin::UserCreate ($domaincontroller, $username, $password, $passwordAge, $privilege,
                                             "$homedirserver\\$username\$", $comment,  $flags,    $scriptpath)) {
                print "-   user \"$username\" created with password \"$password\".\n";
                $users_created_OK++;
                $accountexist=1;
                $account_created=1;
                push (@username , $username);
                push (@fullname , $fullname);
                push (@password , $password);
                if ( $fullname !~ /^[ \s]*$/ ) { # test on empty fullname
                    system ("cmd /c net user \"$username\" /fullname:\"$fullname\" /DOMAIN > NUL"); }
            }
            else {
                print "*** Error creating user \"$username\" : $!\n";
                $users_created_ERROR++;
            }
        }

        # put user in appropriate groups
        if ($accountexist) {
            foreach $group (@groups) {
                if ( !Win32::NetAdmin::GroupIsMember($domaincontroller, $group, $username) ) {
                    if ( Win32::NetAdmin::GroupAddUsers($domaincontroller, $group, $username) ) {
                        print "-   user \"$username\" added to group \"$group\".\n";
                        $users_added_to_groups_OK++;
                    }
                    else {
                        print "*** Error adding user \"$username\" to group \"$group\": $!\n";
                        $users_added_to_groups_ERROR++;
                    }
                }
                else  {
                    print "*** user \"$username\" is already member of group \"$group\".\n"; }
            }
        }
        # create home directory for user
        if ($accountexist) {
             if (!mkdir ("$homedirserver\\$homedir_root_share\\$username",777)) {
                  print "*** error creating $homedirserver\\$homedir_root_share\\$username : $!\n";
                 $home_directories_created_ERROR++;
             }
             else {
                 print "-   created home directory $homedirserver\\$homedir_root_share\\$username\n";
                 $home_directories_created_OK++;
                 # set rights on created directory
                 system ("cmd /c echo y|cacls \"$homedirserver\\$homedir_root_share\\$username\" /G SYSTEM:F Administrators:F > NUL");
                 system ("cmd /c echo y|cacls \"$homedirserver\\$homedir_root_share\\$username\" /E /G \"$domain\\$username\":F > NUL");
             }
        }

        # share the home directory
        if ($accountexist) {
            $shareinfo->{'path'}    = "$homedir_root_local_path\\$username";
            $shareinfo->{'netname'} = "$username\$";
            $shareinfo->{'remark'}  = "Home directory for user $username";

            if (!Win32::NetResource::NetShareAdd($shareinfo, $errmsg, $homedirserver)) {
                print "*** error sharing $homedirserver\\$homedir_root_local_path\\$username as $homedirserver\\$username\$: $errmsg\n"; }
            else {
                print "-   shared home directory $homedir_root_local_path\\$username as $homedirserver\\$username\$\n";
                push (@homedir , "$homedirserver\\$username\$");
            }
        }

        # create profile directory for user
        if ($accountexist) {
            if (!mkdir ("$profileserver\\$profile_root_share\\$username",777)) {
                print "*** error creating $profileserver\\$profile_root_share\\$username : $!\n";
                $profile_directories_created_ERROR++; }
            else {
                print "-   created profile directory $profileserver\\$profile_root_share\\$username\n";
                $profile_directories_created_OK++;
                # set rights on created directory
                system ("cmd /c echo y|cacls \"$profileserver\\$profile_root_share\\$username\" /G SYSTEM:F Administrators:F > NUL");
                system ("cmd /c echo y|cacls \"$profileserver\\$profile_root_share\\$username\" /E /G \"$domain\\$username\":F > NUL");
            }
            system ("cmd /c net user \"$username\" /PROFILEPATH:$profileserver\\$profile_root_share\\$username /DOMAIN > NUL");
        }
        # TO DO create login on SQL server
    }
 }
 print "\nSummary of actions:\t\tSucces\tError\n-------------------\n";
 print "Users created:\t\t\t$users_created_OK\t$users_created_ERROR\n";
 print "Groups created:\t\t\t$groups_created_OK\t$groups_created_ERROR\n";
 print "Users added to groups:\t\t$users_added_to_groups_OK\t$users_added_to_groups_ERROR\n";
 print "Home directories created:\t$home_directories_created_OK\t$home_directories_created_ERROR\n";
 print "Profile directories created:\t$profile_directories_created_OK\t$profile_directories_created_ERROR\n\n";


 ##############################################################
 # report to file and mail
 ##############################################################

 my $tmpfile=$ENV{TEMP}."\\adduser.pl.tmpfile";
 for ( $usercnt=0; $usercnt <= $#username; $usercnt++ )
 {
     open (OUTFILE, ">$tmpfile");
     $report = printuserinfo ($username [$usercnt], $fullname [$usercnt], $password [$usercnt], $homedir [$usercnt]);
     print OUTFILE "$report\n";
     close (OUTFILE);
     print "sending mail for username ",$username [$usercnt],"...";
     $cmd = "cmd /c $mailprogram $tmpfile -t $receiver -server $smtpserver_ip -i \"adduser script\" -S \"adduser: user ".$username [$usercnt]." created\" > NUL";
     if (system ($cmd)) {
        print "failed!\n";
     }
     else {
        print "Succeeded\n";
     }
 };
 unlink ($tmpfile);

 print "\n", &timestamp, " script $0 ended normally\n";

 ############################################################################
 sub timestamp      # print current date and time
 ############################################################################
 {
    @time = localtime (time);
    sprintf ("%4.4d/%2.2d/%2.2d %2.2d:%2.2d.%2.2d", (1900+$time[5]), ++$time[4], $time[3], $time[2], $time[1], $time[0]);
 }  # sub timestamp

 ############################################################################
 sub randpassword        # construct random password of givven length
 ############################################################################
 {
     my $count = ($_[0] > 0 ? $_[0] : 8);
     my $localcounter;
     # to avoid confusion, the characters O,0,o,1,l,L,I have been left out of the alphabet
     my $alphabet = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789!@#$% ";
     my $pass = "";

     for ( $localcounter=1; $localcounter<=$count; $localcounter++ ){
         $pass = $pass . substr ($alphabet, rand(length($alphabet)),1);}
     return $pass;
 }   # sub randpassword

 ############################################################################
 sub spellstring     # spell a string
 ############################################################################
 {
   my $string = $_[0];
   my $out = "";
   my $localcounter;
   my %phonetic = ('A' => 'Alpha',    'B' => 'Bravo',      'C' => 'Charlie',        'D' => 'Delta',
                   'E' => 'Echo',     'F' => 'Foxtrot',    'G' => 'Golf',           'H' => 'Hotel',
                   'I' => 'India',    'J' => 'Juliet',     'K' => 'Kilo',           'L' => 'Lima',
                   'M' => 'Mike',     'N' => 'November',   'O' => 'Oscar',          'P' => 'Papa',
                   'Q' => 'Quebec',   'R' => 'Romeo',      'S' => 'Sierra',         'T' => 'Tango',
                   'U' => 'Uniform',  'V' => 'Victor',     'W' => 'Whiskey',        'X' => 'Xray',
                   'Y' => 'Yankee',   'Z' => 'Zulu');

   for ( $localcounter=0; $localcounter<length($string);$localcounter++ )
   {
       $out = $out . "   " . ($c = substr ($string, $localcounter, 1)) . " = ";
       if    ($c =~ /[a-z]/) { $out = $out . "Lowercase " }
       elsif ($c =~ /[A-Z]/) { $out = $out . "UPPERCASE " }
       elsif ($c =~ /\s/   ) { $out = $out . "Space     " }
       elsif ($c =~ /[0-9]/) { $out = $out . "Number    $c"}
       else                  { $out = $out . "Character $c"};
       $c =~ s/[a-z]/\u$&/g;
       $out = $out . $phonetic {$c} . "\n";
   }
   sprintf $out;
 }   ##spellstring

 ############################################################################
 sub printuserinfo       #return report for created user
 ############################################################################

 {
     my $username = $_[0];
     my $fullname = $_[1];
     my $password = $_[2];
     my $homedir  = $_[3];
     my $out = "";

     $out = $out .  "mail sent by $0 version $version_number\n";
     $out = $out .  "--------------------------------\n\n";
     $out = $out .  " A Windows NT domain useraccount has been created for $fullname\n\n";
     $out = $out .  " Account details:\n ----------------\n";
     $out = $out .  " Username : $username\n";
     $out = $out .  " Fullname : $fullname\n";
     $out = $out .  " Domain   : $domain\n";
     $out = $out .  " Password : \"$password\" (without quotes!)\n";
     $out = $out .  " home dir : $homedir\n";
     $out = $out .  " NOTE: The password is CASE SENSITIVE\n\n";
     $out = $out .  " it is :\n";
     $out = $out .  &spellstring ($password);
     $out = $out .  "\n\n have a nice day\n\n";
     sprintf $out;
 }   ##printuserinfo
