#!/usr/bin/perl -w


# This perl script will add several accounts.  It can be called thusly:
#
# addusers foo
#
# foo should contain one line per account to be added.  The format of each
# line is two comma delimited fields, as follows:
#
#     GECOS name
#     username ($USERIDLEN characters or less)
#
# The script attempts to create the accounts, add file quotas (be careful
# check that the prototype user exists and is on the same filesystem),
# and create a public_html directory.
#
# The script creates the file foo.pw which is foo with the account passwords.
#
# For now, the quota system is not in use.
#
# Passwords are randomly generated and consist of ($PWLEN-2) alpha
# characters, followed by 2 non-alpha characters (numerals, math symbols,
# punctuation, etc.).


$PWLEN = 8;                     # Password length.
$USERIDLEN = 8;                 # Max length of userid.
$PASSWD = "/etc/passwd";        # Location of password file.


# Locations of various system utilities

$USERADD = "/usr/sbin/useradd";
$EDQUOTA = "/usr/sbin/edquota";
#$MKDIR = "/usr/bin/mkdir";
#$CHMOD = "/usr/bin/chmod";
#$CHOWN = "/usr/bin/chown";


# Command line arguments for useradd.

$GROUP = "-g student";
$GECOS = "-c";
#$HOMEPATH = "/export/home";
$HOME = "-m";
$SHELL = "-s /bin/bash";
#$SHELL = "-s /usr/local/shells/noshell";
$PASSWDARG = "-p";
#$SKEL = "-k /usr/local/skel";

$QUOTA = "-p quotprot";         # Quota prototype user.

@SCHARS = ("a".."z","A".."Z","0".."9",".","/"); # Salt characters.
$SSIZE = scalar(@SCHARS);       # Length of salt array.

######################################################################

umask 022;
srand(time|$$);                 # Generate random seed.

if ($#ARGV != 0) { die "$0: expecting an account file name.\n"; }

if (-f "$ARGV[0].pw") { die "$0: $ARGV[0].pw already exists.\n"; }


# Read account records.

open(ACCOUNTS, "<$ARGV[0]") || die "$0: couldn't open $ARGV[0].\n";
@accounts = <ACCOUNTS>;
close(ACCOUNTS);
chop(@accounts);


# Read password file so that we can check that we aren't trying to
# recreate existing accounts.

open(OLDIDS, "<$PASSWD") || die "$0: couldn't open $PASSWD.\n";
$i = 0;
while (<OLDIDS>)
{
    ($oldids[$i]) = split(/:/, $_, 2);
    ++$i;
}
close(OLDIDS);

$i = 0;
foreach (@accounts)             # Split records apart.
{
    ($names[$i], $ids[$i]) = split(/,/, $_, 2); # Split on comma delimiter.
    $ids[$i] = substr($ids[$i], 0, $USERIDLEN); # Chop, if necessary.

    while (substr($names[$i], -1, 1) eq " " # Remove trailing whitespace.
           || substr($names[$i], -1, 1) eq "\t")
    {
        chop($names[$i]);
    }

    while (substr($ids[$i], -1, 1) eq " " # Ditto.
           || substr($ids[$i], -1, 1) eq "\t")
    {
        chop($ids[$i]);
    }

    $passwords[$i] = &genpwd;   # Create the password.

    $encrypts[$i] = crypt($passwords[$i], &salt); # Encrypt the password
                                # with a random salt.

#    $_ .= "\t\t" . $passwords[$i]; # Append password to accounts.
    $_ .= "," . $passwords[$i]; # Append password to accounts.

    ++$i;
}


# Write an account file containing passwords.

if (-f "$ARGV[0].pw") { die "$0: $ARGV[0].pw already exists.\n"; }

open(ACCOUNTS, ">$ARGV[0].pw") || die "$0: couldn't open $ARGV[0].pw.\n";

# Create the accounts.

for ($i = 0, $limit = scalar(@accounts); $i < $limit; ++$i)
{
    if (&match($ids[$i]))       # Make sure the account isn't already
                                # in use.
    {
        print "$ids[$i] already has an account.\n";
        $status = 1;            # 1 actually means fail.
    }
    else
    {
        print ACCOUNTS "$names[$i]\t\t$ids[$i]\t\t$passwords[$i]\n";

        $status = system("$USERADD $GROUP " .
                     "$GECOS \"$names[$i]\" " .
                     "$PASSWDARG \"$encrypts[$i]\" " .
#                     "$HOME $SHELL $SKEL " .
                     "$HOME $SHELL " .
                     "$ids[$i] > /dev/null 2>&1");
    }

    if ($status)
    {
        print "Couldn't create account for $names[$i].\n";
    }
    else
    {
        print "Added account for $names[$i].\n";

        $status = system("$EDQUOTA $QUOTA $ids[$i]");

       if ($status)
       {
           print "Couldn't create storage quotas for $names[$i].\n";
       }
    }
}

close(ACCOUNTS);
chmod(0600, "$ARGV[0].pw");
chown((getpwnam("kelliher"))[2,3], "$ARGV[0].pw");

exit 0;

######################################################################
# Generate a random password.

sub genpwd()
{
    my $i;
    my $password = "";
    my $numPunct = "!#\$%&()*+,-./0123456789:;<=>?@[]{}";
    my $numPunctLen = length($numPunct);
    my $alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    my $alphaLen = length($alpha);

    for ($i = 0; $i < $PWLEN - 2; ++$i)
    {
	$password .= substr($alpha, int(rand($alphaLen)), 1);
    }

    for ($i = 0; $i < 2; ++$i)
    {
	$password .= substr($numPunct, int(rand($numPunctLen)), 1);
    }

    $password;
}

######################################################################
# Generate a random salt for encryting a password.

sub salt
{
    my $salt;

    $salt = "$SCHARS[int(rand($SSIZE))]$SCHARS[int(rand($SSIZE))]";
}

######################################################################
# Make sure a userid isn't already in use.  @oldids must have
# the old ids in it.

sub match
{
    foreach (@oldids)
    {
        if ($_ eq $_[0])
        {
            return 1;
        }
    }

    0;
}
