[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cobalt-users] Auto provisioning services using shell-tools-1.1-1 - Scripting help



Hey guys here is something I hope you find Useful! But I need help
customizing. I'm a Perl Novice. The script is run using perl -w
ISPPower_Linux.pl which calls 4 pm files for the sub routines. I have
changed the following in ISPPower_Shared_Linux.pm. But when I try to create
an account it returns "error creating password" I don't know where its
getting this. I can create a user fine using the shell-tools using the
Format command from the prompt using the exact format shown (filling in the
$ for values). The scripts are called from a Signup server using an
encryption key. Very nice feature. I'm sure someone can figure this one out.

If you need all the files I can tar or zip them for you to try out.
any help would be greatly.... need I say more.
Johnc

my($USERADD) = "/usr/sbin/useradd"; to my($USERADD) =
"/usr/sbin/cobalt/adduser";
my($USERDEL) = "/usr/sbin/userdel"; to my($USERDEL) =
"/usr/sbin/cobalt/deluser";

# Format command
    $Command = "$USERADD -c \"$Name\" -d $Home -g $Gid -s $Shell -u $Uid
$Login"; to # Format command
    $Command = "$USERADD -f \"$Name\" -u $Login -p $Password -x";

Ok here is ISPPower_Shared_Linux.pm
#================================================================
# ISP Power Server Control Add-On
# Shared Linux Routines, Version 9809, December 17, 1998
# Copyright (c) 1997 ISP Power Corporation
# All rights reserved
#================================================================
# This software is licensed for use only as an integral part
# of the ISP Power Server Control Add-On.  All other uses are
# prohibited.  This software may be modified by a licensed
# end-user for their own internal use however all changes shall
# still be restricted by the original license.
#================================================================

   use strict;
   package ISPPower_Shared_Linux;
   use ISPPower_Interface;
   use Exporter ();

   @ISPPower_Shared_Linux::ISA = qw(Exporter);
   @ISPPower_Shared_Linux::EXPORT = qw(
       ForkChild
       AddUser ModifyUser DeleteUser SuspendUser UnsuspendUser
       GetNextUid CheckLogin LoginExists CheckName CheckUid UidExists
CheckPassword
       GetPasswordField EncryptPassword GetPassword ChangePassword
       GroupExists CheckGid GidExists
       LoadShell ShellExists
       CheckDirectory CheckFile CopyDirectory CopyFile BackupDirectory
BackupFile
       CreateFile DeleteFile GetFileMode
       LoadParmSettings SaveParmSettings
   );

$ISPPower_Shared_Linux::VERSION = "9809";

#================================================================
#=======================THIS SCRIPT SHOULD=======================
#======================NOT NEED TO BE EDITED=====================
#================================================================

# THIS SCRIPT SHOULD ONLY BE USED IF USING THE SHADOW PASSWORD
# IF SCRIPT MANIPULATIONS NEED TO BE MADE, YOU MUST HAVE ROOT ACCESS
# DAEMONS MUST BE LAUNCHED WITH ROOT ACCESS


# Location of Shadow File
   my($SHADOW) = "/etc/shadow";
# Location of Password File
   my($PASSWD) = "/usr/bin/passwd";

   my($SUSPEND) = "$PASSWD -l";
   my($UNSUSPEND) = "$PASSWD -u";
   my($USERADD) = "/usr/sbin/cobalt/adduser";
   my($USERMOD) = "/usr/sbin/usermod";
   my($USERDEL) = "/usr/sbin/cobalt/deluser";
   my($CHPASSWD) = "/usr/sbin/chpasswd -e";
   my($MKPASSWD) = "";

# Location of valid Shells files
   my($SHELLS) = "/etc/shells";
# Location of directory containing shell executables
   my($SHELL_DIR) = "/bin";
# This is for when there are no shells used, such as when using Telnet
   my($NO_SHELL) = "/bin/false";
# Location of valid Shells files
## my($SHELLS) = "/etc/shells";
# Location of directory containing shell executables
## my($SHELL_DIR) = "/bin";
# Location of tty file
   my($TTY) = "/dev/tty";

# Prefix appended to backed up files and directories for
# closed services
   my($BACKUP_PREFIX) = ".d.";

# Location of parameter file
   my($PARM_FILE) = "ISPPower_Server.ini";
# Location of last uid paramenter name
   my($PARM_LAST_UID) = "last_uid";

# Maximum length of logins
   my($LOGIN_MAX_LENGTH) = 16;

# Maximum length of password
   my($PASSWORD_MAX_LENGTH) = 8;

# Minimum and Maximum uid to use
   my($UID_MIN) = 10;
   my($UID_MAX) = 32760;

# Minimum and Maximum gid to use
   my($GID_MIN) = 10;
   my($GID_MAX) = 32760;

# The following constant is defined in sys/ioctl.h and may
# vary by UNIX version.  The value shown is known to be valid
# for Linux.

   sub TIOCNOTTY { return 0x5422 }

#================================================================
#=====================CONFIGURABLE SETTINGS======================
#============================END HERE============================
#================================================================

# Mask to check validity of logins (PERL REGULAR EXPRESSION)
   my($LOGIN_MASK) = '^[a-zA-Z][a-zA-Z0-9]*$';
# Mask to check validity of user names (PERL REGULAR EXPRESSION)
   my($NAME_MASK) = '^[^:]*$';
# Mask to check validity of uids (PERL REGULAR EXPRESSION)
   my($UID_MASK) = '^[0-9]+$';
# Mask to check validity of gids (PERL REGULAR EXPRESSION)
   my($GID_MASK) = '^[0-9]+$';
# Mask to check validity of directory names (PERL REGULAR EXPRESSION)
   my($DIR_MASK) = '^\/[^:]+$';
# Mask to check validity of file names (PERL REGULAR EXPRESSION)
   my($FILE_MASK) = '^\/[^:]+$';

   my($ShellsTime) = 0;
   my(%Shells);

# File locking constants
   my($LOCK_SH) = 1;
   my($LOCK_EX) = 2;
   my($LOCK_NB) = 4;
   my($LOCK_UN) = 8;


#================================================================
# Execution Control Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Fork a child process to act as the server.
# Returns:      Returns 1 from the child process and 0 from the
#               parent process.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub ForkChild {

    my($pid);

    # Is program not running as root?
    if ($> != 0) {
        print "Program must be run as root\n";
        return 0;
    }

    # Fork child
    if (not defined($pid = fork())) {
        print "Unable to fork child process\n";
        return 0;
    }

    # Is this the parent process?
    if ($pid > 0) {
        return 0;
    }

    # Become process group leader to avoid propagated signals
    setpgrp();

    # Close open filehandles to free resources
    close(STDIN);
    close(STDOUT);
    close(STDERR);

    # Detach from controlling terminal
    open(TTY, "<$TTY");
    ioctl(TTY, TIOCNOTTY, 0);
    close(TTY);

    # Set umask
    umask(077);

    # Change name of process
    $0 = "ISPPower";

    return 1;

}

#================================================================
# Password Info Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Add a user.
# Returns:      Returns a null string if the operation is
#               successful or an error message otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub AddUser {

    my($Login, $Password, $Name, $Uid, $Gid, $Home, $Shell) = @_;
    my($Note, $Command);

    # Check fields
    if ($Note = &CheckLogin($Login)) {
        return $Note;
    }
    if (&LoginExists($Login)) {
        return "Login is already in use";
    }
    if ($Note = &CheckPassword($Password)) {
        return $Note;
    }
    if ($Note = &CheckName($Name)) {
        return $Note;
    }
    if ($Note = &CheckUid($Uid)) {
        return $Note;
    }
    if (&UidExists($Uid)) {
        return "User ID is already in use";
    }
    if ($Note = &CheckGid($Gid)) {
        return $Note;
    }
    if (not &GidExists($Gid)) {
        return "Group ID does not exist";
    }
    if (not &CheckDirectory($Home)) {
        return "Home directory $Home is invalid";
    }
    if (-e $Home) {
        return "Home directory $Home already exists";
    }

    # Is shell missing directory?
    if ($Shell !~ /^\//) {
        # Prepend shell directory
        $Shell = $SHELL_DIR . "/" . $Shell;
    }

    # Check shell
    if (not &ShellExists($Shell)) {
        return "Shell does not exist";
    }

    # Format command
    $Command = "$USERADD -f \"$Name\" -u $Login -p $Password -x";

    # Add user
    if (system($Command) != 0) {
        return "Error adding login";
    }

    # Set password
    if (not &ChangePassword($Login, 1, $Password)) {
        return "Error setting password";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Modify a user.
# Returns:      Returns a null string if the operation is
#               successful or an error message otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub ModifyUser {

    my($OldLogin, $NewLogin, $Password, $Name, $Uid, $Gid, $Home, $Shell) =
@_;
    my($Options, $Command, $Note);

    # Get old login info
    my($OldName, $OldPassword, $OldUid, $OldGid, $OldQuota, $OldComment,
$OldGecos, $OldHome, $OldShell) = getpwnam($OldLogin);

    # Clear options
    $Options = "";

    # Check fields
    if ($OldName eq "") {
        return "Old login does not exist";
    }
    if (($NewLogin ne "") && ($NewLogin ne $OldLogin)) {
        if ($Note = &CheckLogin($NewLogin)) {
            return $Note;
        }
        if (&LoginExists($NewLogin)) {
            return "Login is already in use";
        }
    }
    if (defined($Password)) {
        if ($Note = &CheckPassword($Password)) {
            return $Note;
        }
    }
    if (defined($Name)) {
        if ($Note = &CheckName($Name)) {
            return $Note;
        }
        $Options = $Options . " -c \"$Name\"";
    }
    if (($Uid ne "") && ($Uid != $OldUid)) {
        return "Cannot change User ID";
    }
    if (($Gid ne "") && ($Gid != $OldGid)) {
        return "Cannot change Group ID";
    }
    if ($Home ne "") {
        if (not &CheckDirectory($Home)) {
            return "Home directory $Home is invalid";
        }
        $Options = $Options . " -d $Home";
    }

    # Was shell specified?
    if ($Shell ne "") {
        # Is shell missing directory?
        if ($Shell !~ /^\//) {
            # Prepend shell directory
            $Shell = $SHELL_DIR . "/" . $Shell;
        }
        # Check shell
        if (not &ShellExists($Shell)) {
            return "Shell does not exist";
        }
        $Options = $Options . " -s $Shell";
    }

    # Has login changed?
    if (($NewLogin ne "") && ($NewLogin ne $OldLogin)) {
        # Get current login info
        if (not defined($Password)) {
            if (not defined($Password = &GetPassword($OldLogin))) {
                return "Unable to get current password";
            }
        } else {
            $Password = &EncryptPassword($Password);
        }
        if (not defined($Name)) {
            $Name = $OldComment;
        }
        $Uid = $OldUid;
        $Gid = $OldGid;
        if ($Home eq "") {
            $Home = $OldHome;
        }
        if ($Shell eq "") {
            $Shell = $OldShell;
        }
        # Format command
        $Command = "$USERADD -c \"$Name\" -d $Home -g $Gid -s $Shell -u
$Uid -o $NewLogin";
        # Create new login
        if (system($Command) != 0) {
            return "Error creating new login";
        }
        # Set password
        if (not &ChangePassword($NewLogin, 0, $Password)) {
            return "Error setting password";
        }
        # Delete old login
        if (system("$USERDEL $OldLogin") != 0) {
            return "Error deleting login";
        }
    } else {
        # Change specified fields
        if ($Options ne "") {
            if (system("$USERMOD $Options $OldLogin") != 0) {
                return "Error modifying login";
            }
        }
        # Change password
        if (defined($Password)) {
            if (not &ChangePassword($OldLogin, 1, $Password)) {
                return "Error changing password";
            }
        }
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Delete a user.
# Returns:      Returns a null string if the operation is
#               successful or an error message otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub DeleteUser {

    my($Login) = @_;

    # Does login not exist?
    if (not &LoginExists($Login)) {
        return "Login does not exist";
    }

    # Delete user
    if (system("$USERDEL $Login") != 0) {
        return "Error deleting login";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Suspend a user.
# Returns:      Returns a null string if the operation is
#               successful or an error message otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub SuspendUser {

    my($Login) = @_;

    # Does login not exist?
    if (not &LoginExists($Login)) {
        return "Login does not exist";
    }

    # Suspend user
    if (system("$SUSPEND $Login") != 0) {
        return "Error suspending login";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Unsuspend a user.
# Returns:      Returns a null string if the operation is
#               successful or an error message otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub UnsuspendUser {

    my($Login) = @_;

    # Does login not exist?
    if (not &LoginExists($Login)) {
        return "Login does not exist";
    }

    # Unsuspend user
    if (system("$UNSUSPEND $Login") != 0) {
        return "Error unsuspending login";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Get next available user id.
# Returns:      Returns the next available user id.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub GetNextUid {

    my($LastUid, %Parms);

    # Set default uid
    $LastUid = $UID_MIN;

    # Load parameter file
    if (&LoadParmSettings($PARM_FILE, \%Parms)) {
        # Get parameter from parameter file
        if (exists($Parms{$PARM_LAST_UID})) {
            $LastUid = $Parms{$PARM_LAST_UID};
        }
    }

    # Find next available uid
    for (;;) {
        # Make sure uid is in range
        if ($LastUid < $UID_MIN) {
            $LastUid = $UID_MIN;
        }
        if ($LastUid > $UID_MAX) {
            $LastUid = $UID_MIN;
        }
        # Is uid available?
        if (not &UidExists($LastUid)) {
            last;
        }
        # Increment uid
        $LastUid++;
    }

    # Update parameter file
    $Parms{$PARM_LAST_UID} = $LastUid;
    &SaveParmSettings($PARM_FILE, \%Parms);

    return $LastUid;

}

#----------------------------------------------------------------
# Purpose:      Check a login.
# Returns:      Returns an error message if the field is invalid
#               otherwise returns a null string.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckLogin {

    my($Login) = @_;

    # Is login too long?
    if (length($Login) > $LOGIN_MAX_LENGTH) {
        return "Login is too long";
    }

    # Is login in an invalid format?
    if ($Login !~ /$LOGIN_MASK/o) {
        return "Login is in an invalid format";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Check if a login exists.
# Returns:      Returns 1 if the login exists or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub LoginExists {

    my($Login) = @_;

    if (getpwnam($Login)) {
        return 1;
    } else {
        return 0;
    }

}

#----------------------------------------------------------------
# Purpose:      Check a password.
# Returns:      Returns an error message if the field is invalid
#               otherwise returns a null string.
# History:
# 10/05/97 BMK  Released.
#----------------------------------------------------------------
sub CheckPassword {

    my($Password) = @_;

    # Is password too long?
    if (length($Password) > $PASSWORD_MAX_LENGTH) {
        return "Password is too long";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Check an account name.
# Returns:      Returns an error message if the field is invalid
#               otherwise returns a null string.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckName {

    my($Name) = @_;

    # Is name in an invalid format?
    if ($Name !~ /$NAME_MASK/o) {
        return "Account name is in an invalid format";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Check a user id.
# Returns:      Returns an error message if the field is invalid
#               otherwise returns a null string.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckUid {

    my($Uid) = @_;

    # Is user id out of range?
    if (($Uid < $UID_MIN) || ($Uid > $UID_MAX)) {
        return "User ID is out of range";
    }

    # Is user id in an invalid format?
    if ($Uid !~ /$UID_MASK/o) {
        return "User ID is in an invalid format";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Check if a uid exists.
# Returns:      Returns 1 if the uid exists or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub UidExists {

    my($Uid) = @_;

    if (getpwuid($Uid)) {
        return 1;
    } else {
        return 0;
    }

}

#----------------------------------------------------------------
# Purpose:      Get a password field for a login.
# Returns:      Returns the password field value if the login
#               exists or undef otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub GetPasswordField {

    my($Login, $Field) = @_;
    my($Name, $Password, $Uid, $Gid, $Quota, $Comment, $Gecos, $Home,
$Shell) = getpwnam($Login);

    # Format field name
    $Field = uc($Field);

    # Does login exist?
    if (defined($Name)) {
        # Return field value
        return $Password if ($Field eq "PASSWORD");
        return $Uid if ($Field eq "UID");
        return $Gid if ($Field eq "GID");
        return $Quota if ($Field eq "QUOTA");
        return $Comment if ($Field eq "COMMENT");
        return $Gecos if ($Field eq "GECOS");
        return $Home if ($Field eq "HOME");
        return $Shell if ($Field eq "SHELL");
    }

    return undef;

}

#----------------------------------------------------------------
# Purpose:      Encrypt a password.
# Returns:      Returns the encrypted password.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub EncryptPassword {

    my($Password, $Salt) = @_;

    # Was a salt specified?
    if (defined($Salt)) {
        return crypt($Password, $Salt);
    } else {
        return crypt($Password, rand 65536);
    }

}

#----------------------------------------------------------------
# Purpose:      Get the password for a login.
# Returns:      Returns the password if the login exists or
#               undef otherwise.
# History:
# 06/13/97 BMK  Released.
#----------------------------------------------------------------
sub GetPassword {

    my($Login) = @_;
    my($Line, $ShadowLogin, $ShadowPassword);

    # Open shadow password file
    if (not open(SHADOW, $SHADOW)) {
        &ISPPower_Log("Unable to open shadow password file $SHADOW");
        return undef;
    }

    # Read file
    while ($Line = <SHADOW>) {
        # Strip end-of-line character(s)
        chomp($Line);
        # Extract parameter and value
        if (($ShadowLogin, $ShadowPassword) = ($Line =~
(/^([^:]*):([^:]*)/))) {
            if ($ShadowLogin eq $Login) {
                close(SHADOW);
                return $ShadowPassword;
            }
        }
    }

    # Close file
    close(SHADOW);

    return undef;

}

#----------------------------------------------------------------
# Purpose:      Change the password for a login.
# Returns:      Returns 1 if the password is changed or 0
#               otherwise.
# History:
# 10/05/97 BMK  Released.
#----------------------------------------------------------------
sub ChangePassword {

    my($Login, $Encrypt, $Password) = @_;

    # Ignore SIGPIPE signal
    local $SIG{'PIPE'} = 'IGNORE';

    # Open pipe to chpasswd command
    if (not open(CHPASSWD, "| $CHPASSWD")) {
        &ISPPower_Log("Error opening pipe to $CHPASSWD");
        return 0;
    }

    # Encrypt password if requested
    if ($Encrypt) {
        $Password = &EncryptPassword($Password);
    }

    # Send login and password to change
    print CHPASSWD "$Login:$Password\n";

    # Close pipe
    if (not close(CHPASSWD)) {
        &ISPPower_Log("Error closing pipe to $CHPASSWD");
        return 0;
    }

    # Do password DBM files need to be updated?
    if ($MKPASSWD ne "") {
        # Update password DBM files
        if (system($MKPASSWD) != 0) {
            &ISPPower_Log("Error running $MKPASSWD");
            return 0;
        }
    }

    return 1;

}

#================================================================
# Group Info Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Check if a group exists.
# Returns:      Returns 1 if the group exists or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub GroupExists {

    my($Group) = @_;

    if (getgrnam($Group)) {
        return 1;
    } else {
        return 0;
    }

}

#----------------------------------------------------------------
# Purpose:      Check a group id.
# Returns:      Returns an error message if the field is invalid
#               otherwise returns a null string.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckGid {

    my($Gid) = @_;

    # Is group id out of range?
    if (($Gid < $GID_MIN) || ($Gid > $GID_MAX)) {
        return "Group ID is out of range";
    }

    # Is group id in an invalid format?
    if ($Gid !~ /$GID_MASK/o) {
        return "Group ID is in an invalid format";
    }

    return "";

}

#----------------------------------------------------------------
# Purpose:      Check if a gid exists.
# Returns:      Returns 1 if the gid exists or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub GidExists {

    my($Gid) = @_;

    if (getgrgid($Gid)) {
        return 1;
    } else {
        return 0;
    }

}

#================================================================
# Shell File Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Load shell file.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/13/97 BMK  Released.
#----------------------------------------------------------------
sub LoadShell {

    my($Time) = @_;

    # Is shells file already loaded?
    if (defined($Time) && ($Time <= $ShellsTime)) {
        return 1;
    }

    # Clear existing info
    %Shells = ();

    # Was a shell file specified?
    if ($SHELLS ne "") {
        # Open shells file
        if (open(SHELLS, "$SHELLS")) {
            # Read file
            while (<SHELLS>) {
                # Trim end-of-line character
                chomp;
                # Is line not a comment and not blank?
                if (($_ !~ /^#/) && ($_ =~ /\S/)) {
                    # Save shell
                    $Shells{$_} = "";
                }
            }
            # Close file
            close(SHELLS);
        }
    }

    # Update timestamp
    if (defined($Time)) {
        $ShellsTime = $Time;
    }

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Check if a shell exists.
# Returns:      Returns 1 if the shell exists or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub ShellExists {

    my($Shell) = @_;

    # Is shell array empty?
    if (not %Shells) {
        return 1;
    }

    # Is shell missing directory?
    if ($Shell !~ /^\//) {
        # Prepend shell directory
        $Shell = $SHELL_DIR . "/" . $Shell;
    }

    # Does shell exist?
    if (exists($Shells{$Shell})) {
        return 1;
    } elsif ($Shell eq $NO_SHELL) {
        return 1;
    } else {
        return 0;
    }

}

#================================================================
# Directory/File Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Check a directory.
# Returns:      Returns 1 if the directory is valid or 0
#               otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckDirectory {

    my($Dir) = @_;

    # Is directory in a valid format?
    if ($Dir =~ /$DIR_MASK/o) {
        return 1;
    } else {
        return 0;
    }

}

#----------------------------------------------------------------
# Purpose:      Check a file.
# Returns:      Returns 1 if the file is valid or 0 otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CheckFile {

    my($File) = @_;

    # Is file in a valid format?
    if ($File =~ /$FILE_MASK/o) {
        return 1;
    } else {
        return 0;
    }

}

#----------------------------------------------------------------
# Purpose:      Copy a directory.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CopyDirectory {

    my($Uid, $Gid, $Source, $Dest, $Recursive) = @_;
    my($Mode, @Files, $File, $DestFile);

    # Does source directory not exist?
    if (not ((-e $Source) && (-d $Source))) {
        &ISPPower_Log("Source directory $Source does not exist");
        return 0;
    }

    # Does destination directory already exist?
    if (-e $Dest) {
        &ISPPower_Log("Destination directory $Dest already exists");
        return 0;
    }

    # Get mode of source directory
    $Mode = &GetFileMode($Source);

    # Make destination directory
    if (not mkdir($Dest, $Mode)) {
        &ISPPower_Log("Unable to make destination directory $Dest");
        return 0;
    }

    # Change mode of destination directory
    if (not chmod($Mode, $Dest)) {
        &ISPPower_Log("Unable to change permissions of destination directory
$Dest");
        return 0;
    }

    # Change ownership of destination directory
    if (not chown($Uid, $Gid, $Dest)) {
        &ISPPower_Log("Unable to change ownership of destination directory
$Dest");
        return 0;
    }

    # Open source directory
    if (not opendir(DIR, $Source)) {
        &ISPPower_Log("Unable to open source directory $Source");
        return 0;
    }

    # Get files in directory
    @Files = readdir(DIR);

    # Close directory
    closedir(DIR);

    # Copy files in directory
    foreach $File (@Files) {
        # Is this a file?
        if (-f "$Source/$File") {
            # Strip any "dot." or "local." prefixes from destination
filename
            $DestFile = $File;
            $DestFile =~ s/^dot\./\./;
            $DestFile =~ s/^local\./\./;
            # Copy file to destination directory
            if (not &CopyFile("$Source/$File", "$Dest/$DestFile")) {
                return 0;
            }
            # Get mode of source file
            $Mode = &GetFileMode("$Source/$File");
            # Change mode of destination file
            if (not chmod($Mode, "$Dest/$DestFile")) {
                &ISPPower_Log("Unable to change permissions of destination
file $Dest/$DestFile");
                return 0;
            }
            # Change ownership of destination file
            if (not chown($Uid, $Gid, "$Dest/$DestFile")) {
                &ISPPower_Log("Unable to change ownership of destination
file $Dest/$DestFile");
                return 0;
            }
        }
        # Is this a subdirectory?
        elsif ((-d "$Source/$File") && ($File !~ /^\./)) {
            # Should subdirectories be processed?
            if ($Recursive) {
                # Strip any "dot." prefixes from destination directory name
                $DestFile = $File;
                $DestFile =~ s/^dot\./\./;
                # Copy subdirectory
                if (not &CopyDirectory($Uid, $Gid, "$Source/$File",
"$Dest/$DestFile", 1)) {
                    return 0;
                }
            }
        }
    }

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Copy a file.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CopyFile {

    my($Source, $Dest) = @_;

    # Open source file
    if (not open(SOURCE, $Source)) {
        &ISPPower_Log("Unable to open source file $Source");
        return 0;
    }

    # Open destination file
    if (not open(DEST, ">$Dest")) {
        &ISPPower_Log("Unable to open destination file $Dest");
        close(SOURCE);
        return 0;
    }

    # Copy source file to destination file
    while (<SOURCE>) {
        print DEST;
    }

    # Close files
    close(SOURCE);
    close(DEST);

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Backup a directory.
# Returns:      Returns the new directory name if the operation
#               is successful or a null string otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub BackupDirectory {

    my($Dir) = @_;

    return &BackupFile($Dir);

}

#----------------------------------------------------------------
# Purpose:      Backup a file.
# Returns:      Returns the new filename if the operation is
#               successful or a null string otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub BackupFile {

    my($File) = @_;
    my($OldFile, $NewFile, $Dir, $Suffix, $i);
    my($Sec, $Min, $Hour, $Day, $Month, $Year) = localtime(time());

    # Does file not exist?
    if (not (-e $File)) {
        return "";
    }

    # Setup variables
    $File =~ /([^\/]*)$/;
    $OldFile = $1;
    $NewFile = sprintf("%s%s.%04d%02d%02d", $BACKUP_PREFIX, $OldFile,
        $Year + 1900, $Month + 1, $Day);
    $Dir = substr($File, 0, length($File) - length($OldFile));
    $Suffix = "";
    $i = 0;

    # Find first open backup filename
    while (-e $Dir . $NewFile . $Suffix) {
        $i++;
        $Suffix = "." . $i;
    }

    # Rename file
    if (not rename($Dir . $OldFile, $Dir . $NewFile . $Suffix)) {
        return "";
    }

    return $Dir . $NewFile . $Suffix;

}

#----------------------------------------------------------------
# Purpose:      Create a file.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub CreateFile {

    my($Uid, $Gid, $Mode, $File) = @_;

    # Does file already exist?
    if (-e $File) {
        &ISPPower_Log("File $File already exists");
        return 0;
    }

    # Open file
    if (not open(FILE, ">$File")) {
        &ISPPower_Log("Unable to open file $File");
        return 0;
    }

    # Close file
    close(FILE);

    # Change mode of file
    if (not chmod($Mode, $File)) {
        &ISPPower_Log("Unable to change permissions of file $File");
        return 0;
    }

    # Change ownership of file
    if (not chown($Uid, $Gid, $File)) {
        &ISPPower_Log("Unable to change ownership of file $File");
        return 0;
    }

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Delete a file.
# Returns:      Returns 1 if the operation is successful, 0 if
#               the file is busy, or -1 if there is an error.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub DeleteFile {

    my($File, $Timeout) = @_;
    my($EndTime);

    # Does file not exist?
    if (not (-e $File)) {
        return 1;
    }

    # Open file
    if (not open(FILE, $File)) {
        &ISPPower_Log("Unable to open file $File");
        return -1;
    }

    # Calculate ending time
    $EndTime = time + $Timeout;

    # Loop until file is free or ending time reached
    for (;;) {
        # Lock file
        if (flock(FILE, $LOCK_EX | $LOCK_NB)) {
            last;
        }
        # Has ending time been reached?
        if (time >= $EndTime) {
            close(FILE);
            return 0;
        }
        # Wait and process messages
        &ISPPower_Wait(1);
    }

    # Delete file
    if (not unlink($File)) {
        &ISPPower_Log("Unable to delete file $File");
        close(FILE);
        return -1;
    }

    # Close file
    close(FILE);

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Get the mode of a file.
# Returns:      Returns the file mode (permissions).
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub GetFileMode {

    my($File) = @_;

    return ((stat($File))[2] & 07777);

}

#================================================================
# Parameter File Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Load parameter file settings.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/13/97 BMK  Released.
#----------------------------------------------------------------
sub LoadParmSettings {

    my($ParmFile, $ParmArray) = @_;
    my($Line, $Parm, $Value);

    # Was a parameter file not specified?
    if ($ParmFile eq "") {
        &ISPPower_Log("Parameter file not specified");
        return 0;
    }

    # Does parameter file not exist?
    if (not (-e $ParmFile)) {
        return 0;
    }

    # Open parameter file
    if (not open(PARM, $ParmFile)) {
        &ISPPower_Log("Unable to open parameter file $ParmFile");
        return 0;
    }

    # Read file
    while ($Line = <PARM>) {
        # Strip end-of-line character(s)
        chomp($Line);
        # Extract parameter and value
        if (($Parm, $Value) = ($Line =~ (/^\s*([^=]+)=\s*(.*)/))) {
            # Strip trailing whitespace
            $Parm =~ s/\s*$//;
            $Value =~ s/\s*$//;
            # Add parameter to parameter array
            $$ParmArray{$Parm} = $Value;
        }
    }

    # Close file
    close(PARM);

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Save parameter file settings.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/13/97 BMK  Released.
#----------------------------------------------------------------
sub SaveParmSettings {

    my($ParmFile, $ParmArray) = @_;
    my($Parm);

    # Was a parameter file not specified?
    if ($ParmFile eq "") {
        &ISPPower_Log("Parameter file not specified");
        return 0;
    }

    # Open parameter file
    if (not open(PARM, ">$ParmFile")) {
        &ISPPower_Log("Unable to open parameter file $ParmFile");
        return 0;
    }

    # Output parameter settings
    foreach $Parm (sort keys(%$ParmArray)) {
        print PARM "$Parm=$$ParmArray{$Parm}\n";
    }

    # Close file
    close(PARM);

    return 1;

}

1;
and here is the ISPPower_Shell.pm
#================================================================
# ISP Power Server Control Add-On
# Linux Shell Resource Manager, Version 9809, December 17, 1998
# Copyright (c) 1997, 1998 ISP Power Corporation
# All rights reserved
#================================================================
# This software is licensed for use only as an integral part
# of the ISP Power Server Control Add-On.  All other uses are
# prohibited.  This software may be modified by a licensed
# end-user for their own internal use however all changes shall
# still be restricted by the original license.
#================================================================

#================================================================
#=======================THIS SCRIPT SHOULD=======================
#======================NOT NEED TO BE EDITED=====================
#================================================================

   use strict;
   package ISPPower_Shell_Linux;
   use ISPPower_Interface;
   use ISPPower_Shared_Linux;

# Predefined field names
   my($FIELD_SERVICETYPE) = "Service Type";
   my($FIELD_LOGIN) = "Login";
   my($FIELD_PASSWORD) = "Password";
   my($FIELD_NAME) = "Account Name";
# Optionally defined field names
   my($FIELD_UID) = "User ID";
   my($FIELD_GID) = "Group ID";
   my($FIELD_HOME) = "Home Directory";
   my($FIELD_SHELL) = "Shell";
   my($FIELD_MAIL) = "Mail File";

# Defaults
# Default gid to use for user accounts
   my($DEFAULT_GID) = 100;
# Default base directory to use for user home
# directories
   my($DEFAULT_HOME_BASE) = "/home";
# Default skeleton directory to use as a model
# for new user home directories
   my($DEFAULT_HOME_SKELETON) = "/etc/skel";
# Default shell
   my($DEFAULT_SHELL) = "csh";
# Default base directory to use for user mail files
   my($DEFAULT_MAIL_BASE) = "/var/spool/mail";
# Default mode for user mail files
   my($DEFAULT_MAIL_MODE) = 0600;


# Closed service settings
# Closed service settings
# Uid to use for closed accounts
   my($CLOSED_UID) = 65535;
# Gid to use for closed accounts
   my($CLOSED_GID) = 100;
# Mode to use for the files and directories owned
# by closed accounts
   my($CLOSED_MODE) = 0600;

# PASSWORD TIMEOUT
# Time-out interval in seconds; how long server should
# wait to lock password files before aborting operation
# and returning an error status
   my($PASSWORD_TIMEOUT) = 60;

# Time-out interval in seconds; how long the server should
# wait to delete a user POP mail file before aborting
# operation and returning an error status
   my($POP_TIMEOUT) = 300;
# Format string to use for creating the pathname to user POP
# mail files
   my($POP_FORMAT) = "/var/spool/mail/.%s.pop";

# Rhosts filename
   my($RHOSTS_FILE) = ".rhosts";
# Rhosts backup filename
   my($RHOSTS_BACKUP) = ".rhosts.bak";

#================================================================
#==================IF MODIFICATIONS MADE, DO NOT=================
#=========================EDIT BELOW THIS========================
#================================================================


#================================================================
# Object Methods
#================================================================

#----------------------------------------------------------------
# Purpose:      Create a new object.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub new {

    my($Class) = shift;
    my($Self) = {};

    bless $Self, $Class;

    return $Self;

}

#----------------------------------------------------------------
# Purpose:      Initialize object.
# Returns:      Returns 1 if the operation is successful or 0
#               otherwise.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Initialize {

    my($Time) = @_;

    # Load shells
    if (not &LoadShell($Time)) {
        return 0;
    }

    return 1;

}

#----------------------------------------------------------------
# Purpose:      Terminate object.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Terminate {

}

#----------------------------------------------------------------
# The following object methods all share common inputs, outputs,
# and return values:
#
# Inputs:       $EventID       - Event ID
#               $ServiceArray  - Reference to associative array
#                                for service info
#               $ChangesArray  - Reference to associative array
#                                for field changes
# Outputs:      $UpdatesArray  - Reference to associative array
#                                for returning updates
#               $NoteRef       - Reference to note describing
#                                processing result
# Returns:      Returns a status code which may be one of the
#               following:
#
#               SUCCESS - Message processed successfully
#               WARNING - Message processed with warning
#               FAIL    - Message could not be processed
#               IGNORED - Message ignored
#----------------------------------------------------------------

#----------------------------------------------------------------
# Purpose:      Open a new service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Open {

    my($Self) = shift;
    my($EventID, $ServiceArray, $UpdatesArray, $NoteRef) = @_;
    my($Login, $Password, $Name, $Uid, $Gid, $Home, $Shell, $Mail);

    # Is login not specified?
    if (($Login = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Login is not specified";
        return FAIL;
    }

    # Is password not specified?
    if (($Password = $$ServiceArray{$FIELD_PASSWORD}) eq "") {
        $$NoteRef = "Password is not specified";
        return FAIL;
    }

    # Get other fields
    $Name = $$ServiceArray{$FIELD_NAME};
    $Uid = $$ServiceArray{$FIELD_UID};
    $Gid = $$ServiceArray{$FIELD_GID};
    $Home = $$ServiceArray{$FIELD_HOME};
    $Shell = $$ServiceArray{$FIELD_SHELL};
    $Mail = $$ServiceArray{$FIELD_MAIL};

    # Use defaults if not specified
    if ($Uid eq "") {
        $Uid = &GetNextUid();
        $$UpdatesArray{$FIELD_UID} = $Uid;
    }
    if ($Gid eq "") {
        $Gid = $DEFAULT_GID;
        $$UpdatesArray{$FIELD_GID} = $Gid;
    }
    if ($Home eq "") {
        $Home = "$DEFAULT_HOME_BASE/$Login";
        $$UpdatesArray{$FIELD_HOME} = $Home;
    }
    if ($Shell eq "") {
        $Shell = $DEFAULT_SHELL;
        $$UpdatesArray{$FIELD_SHELL} = $Shell;
    }
    if ($Mail eq "") {
        $Mail = "$DEFAULT_MAIL_BASE/$Login";
        $$UpdatesArray{$FIELD_MAIL} = $Mail;
    }

    # Check mail file
    if (not &CheckFile($Mail)) {
        $$NoteRef = "Mail file $Mail is invalid";
        return FAIL;
    }
    if (-e $Mail) {
        $$NoteRef = "Mail file $Mail already exists";
        return FAIL;
    }

    # Add user
    if ($$NoteRef = &AddUser($Login, $Password, $Name, $Uid, $Gid, $Home,
$Shell)) {
        return FAIL;
    }

    # Create home directory
    if (not &CopyDirectory($Uid, $Gid, $DEFAULT_HOME_SKELETON, $Home, 1)) {
        $$NoteRef = "Unable to create home directory $Home";
        return FAIL;
    }

    # Create mail file
    if (not &CreateFile($Uid, $Gid, $DEFAULT_MAIL_MODE, $Mail)) {
        $$NoteRef = "Unable to create mail file $Mail";
        return FAIL;
    }

    return SUCCESS;

}

#----------------------------------------------------------------
# Purpose:      Modify an existing service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Modify {

    my($Self) = shift;
    my($EventID, $ServiceArray, $ChangesArray, $UpdatesArray, $NoteRef) =
@_;
    my($OldLogin, $NewLogin, $Password, $Status);

    # Is old login not specified?
    if (($OldLogin = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Old login is not specified";
        return FAIL;
    }

    # Does old login not exist?
    if (not &LoginExists($OldLogin)) {
        $$NoteRef = "Old login does not exist";
        return FAIL;
    }

    # Perform changes
    if (($Status = &PerformChanges($ServiceArray, $ChangesArray,
$UpdatesArray, $NoteRef)) ne SUCCESS) {
        return $Status;
    }

    return SUCCESS;

}

#----------------------------------------------------------------
# Purpose:      Close an existing service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Close {

    my($Self) = shift;
    my($EventID, $ServiceArray, $ChangesArray, $UpdatesArray, $NoteRef) =
@_;
    my($Login, $Home, $Mail, $POPFile);

    # Is login not specified?
    if (($Login = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Login is not specified";
        return FAIL;
    }

    # Does login not exist?
    if (not &LoginExists($Login)) {
        $$NoteRef = "Login does not exist";
        return FAIL;
    }

    # Get home directory and mail file
    $Home = &GetPasswordField($Login, "Home");
    if (($Mail = $$ServiceArray{$FIELD_MAIL}) eq "") {
        $Mail = "$DEFAULT_MAIL_BASE/$Login";
    }

    # Check home directory and mail file
    if (not (-e $Home)) {
        $$NoteRef = "Home directory $Home does not exist";
        return FAIL;
    }
    if (not (-e $Mail)) {
        $$NoteRef = "Mail file $Mail does not exist";
        return FAIL;
    }

    # Delete user
    if ($$NoteRef = &DeleteUser($Login)) {
        return FAIL;
    }

    # Change mode and ownership of home directory
    if (not chmod($CLOSED_MODE, $Home)) {
        $$NoteRef = "Unable to change permissions of home directory $Home";
        return FAIL;
    }
    if (not chown($CLOSED_UID, $CLOSED_GID, $Home)) {
        $$NoteRef = "Unable to change ownership of home directory $Home";
        return FAIL;
    }

    # Backup home directory
    if (($Home = &BackupDirectory($Home)) eq "") {
        $$NoteRef = "Unable to backup home directory $Home";
        return FAIL;
    }

    # Format POP filename
    $POPFile = sprintf($POP_FORMAT, $Login);

    # Delete POP file to make sure there is no active POP session
    if (&DeleteFile($POPFile, $POP_TIMEOUT) <= 0) {
        $$NoteRef = "Unable to delete POP file $POPFile";
        return FAIL;
    }

    # Change mode and ownership of mail file
    if (not chmod($CLOSED_MODE, $Mail)) {
        $$NoteRef = "Unable to change permissions of mail file $Mail";
        return FAIL;
    }
    if (not chown($CLOSED_UID, $CLOSED_GID, $Mail)) {
        $$NoteRef = "Unable to change ownership of mail file $Mail";
        return FAIL;
    }

    # Backup mail file
    if (($Mail = &BackupFile($Mail)) eq "") {
        $$NoteRef = "Unable to backup mail file $Mail";
        return FAIL;
    }

    # Return updated fields
    $$UpdatesArray{$FIELD_HOME} = $Home;
    $$UpdatesArray{$FIELD_MAIL} = $Mail;

    return SUCCESS;

}

#----------------------------------------------------------------
# Purpose:      Suspend an existing service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Suspend {

    my($Self) = shift;
    my($EventID, $ServiceArray, $ChangesArray, $UpdatesArray, $NoteRef) =
@_;
    my($Login, $Home, $Status);

    # Is login not specified?
    if (($Login = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Login is not specified";
        return FAIL;
    }

    # Perform changes
    if (($Status = &PerformChanges($ServiceArray, $ChangesArray,
$UpdatesArray, $NoteRef)) ne SUCCESS) {
        return $Status;
    }

    # Suspend user
    if ($$NoteRef = &SuspendUser($Login)) {
        return FAIL;
    }

    # Get home directory
    $Home = &GetPasswordField($Login, "Home");

    # Does rhosts file exist?
    if (-e $Home . "/" . $RHOSTS_FILE) {
        # Does backup file already exist?
        if (-e $Home . "/" . $RHOSTS_BACKUP) {
            # Delete file
            if (not unlink($Home . "/" . $RHOSTS_BACKUP)) {
                $$NoteRef = "Unable to delete rhosts backup file
$RHOSTS_BACKUP";
                return FAIL;
            }
        }
        # Rename file to prevent its use
        if (not rename($Home . "/" . $RHOSTS_FILE, $Home . "/" .
$RHOSTS_BACKUP)) {
            $$NoteRef = "Unable to rename rhosts file $RHOSTS_FILE";
            return FAIL;
        }
    }

    return SUCCESS;

}

#----------------------------------------------------------------
# Purpose:      Unsuspend an existing service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub Unsuspend {

    my($Self) = shift;
    my($EventID, $ServiceArray, $ChangesArray, $UpdatesArray, $NoteRef) =
@_;
    my($Login, $Status);

    # Is login not specified?
    if (($Login = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Login is not specified";
        return FAIL;
    }

    # Unsuspend user
    if ($$NoteRef = &UnsuspendUser($Login)) {
        return FAIL;
    }

    # Perform changes
    if (($Status = &PerformChanges($ServiceArray, $ChangesArray,
$UpdatesArray, $NoteRef)) ne SUCCESS) {
        return $Status;
    }

    return SUCCESS;

}

#================================================================
# Private Routines
#================================================================

#----------------------------------------------------------------
# Purpose:      Perform changes to service.
# History:
# 06/15/97 BMK  Released.
#----------------------------------------------------------------
sub PerformChanges {

    my($ServiceArray, $ChangesArray, $UpdatesArray, $NoteRef) = @_;
    my($OldLogin, $OldHome, $OldMail);
    my($NewLogin, $Password, $Name, $Uid, $Gid, $NewHome, $Shell, $NewMail);
    my($POPFile);

    # Is old login not specified?
    if (($OldLogin = $$ServiceArray{$FIELD_LOGIN}) eq "") {
        $$NoteRef = "Old login is not specified";
        return FAIL;
    }

    # Get old home directory and mail file
    $OldHome = &GetPasswordField($OldLogin, "Home");
    if (($OldMail = $$ServiceArray{$FIELD_MAIL}) eq "") {
        $OldMail = "$DEFAULT_MAIL_BASE/$OldLogin";
    }

    # Get other account info
    $$UpdatesArray{$FIELD_UID} = &GetPasswordField($OldLogin, "UID");
    $$UpdatesArray{$FIELD_GID} = &GetPasswordField($OldLogin, "GID");
    $$UpdatesArray{$FIELD_SHELL} = &GetPasswordField($OldLogin, "Shell");

    # Get changes
    $NewLogin = $$ChangesArray{$FIELD_LOGIN};
    $Password = $$ChangesArray{$FIELD_PASSWORD};
    $Name = $$ChangesArray{$FIELD_NAME};
    $Uid = $$ChangesArray{$FIELD_UID};
    $Gid = $$ChangesArray{$FIELD_GID};
    $NewHome = $$ChangesArray{$FIELD_HOME};
    $Shell = $$ChangesArray{$FIELD_SHELL};
    $NewMail = $$ChangesArray{$FIELD_MAIL};

    # Has login changed?
    if (($NewLogin ne "") && ($NewLogin ne $OldLogin)) {
        # Was a new home directory not specified?
        if ($NewHome eq "") {
            # Set new home directory from old home directory
            $NewHome = $OldHome;
            $NewHome =~ s#\/$OldLogin$#\/$NewLogin#;
        }
        # Was new mail file not specified?
        if ($NewMail eq "") {
            # Set new mail file from old mail file
            $NewMail = $OldMail;
            $NewMail =~ s#\/$OldLogin$#\/$NewLogin#;
        }
    }

    # Has home directory changed?
    if (($NewHome ne "") && ($NewHome ne $OldHome)) {
        # Does old home directory not exist?
        if (not (-e $OldHome)) {
            $$NoteRef = "Old home directory $OldHome does not exist";
            return FAIL;
        }
        # Does new home directory already exist?
        if (-e $NewHome) {
            $$NoteRef = "New home directory $NewHome already exists";
            return FAIL;
        }
    }

    # Has mail file changed?
    if (($NewMail ne "") && ($NewMail ne $OldMail)) {
        # Does old mail file not exist?
        if (not (-e $OldMail)) {
            $$NoteRef = "Old mail file $OldMail does not exist";
            return FAIL;
        }
        # Does new mail already exist?
        if (-e $NewMail) {
            $$NoteRef = "New mail file $NewMail already exists";
            return FAIL;
        }
    }

    # Modify user
    if ($$NoteRef = &ModifyUser($OldLogin, $NewLogin, $Password, $Name,
$Uid, $Gid, $NewHome, $Shell)) {
        return FAIL;
    }

    # Has home directory changed?
    if (($NewHome ne "") && ($NewHome ne $OldHome)) {
        # Rename home directory
        if (not rename($OldHome, $NewHome)) {
            $$NoteRef = "Unable to rename home directory $OldHome to
$NewHome";
            return FAIL;
        }
        # Return new home directory
        $$UpdatesArray{$FIELD_HOME} = $NewHome;
    } else {
        # Return old home directory
        $$UpdatesArray{$FIELD_HOME} = $OldHome;
    }

    # Has mail file changed?
    if (($NewMail ne "") && ($NewMail ne $OldMail)) {
        # Format POP filename
        $POPFile = sprintf($POP_FORMAT, $OldLogin);
        # Delete POP file to make sure there is no active POP session
        if (&DeleteFile($POPFile, $POP_TIMEOUT) <= 0) {
            $$NoteRef = "Unable to delete POP file $POPFile";
            return FAIL;
        }
        # Rename mail file
        if (not rename($OldMail, $NewMail)) {
            $$NoteRef = "Unable to rename mail file $OldMail to $NewMail";
            return FAIL;
        }
        # Return new mail file
        $$UpdatesArray{$FIELD_MAIL} = $NewMail;
    } else {
        # Return old mail file
        $$UpdatesArray{$FIELD_MAIL} = $OldMail;
    }

    return SUCCESS;

}

1;