#  AddUser.pl
#  Example 2.1:
#  ----------------------------------------
#  From "Win32 Perl Scripting: Administrators Handbook" by Dave Roth
#  Published by New Riders Publishing.
#  ISBN # 1-57870-215-1
#
print "From the book 'Win32 Perl Scripting: The Administrator's Handbook' by Dave Roth\n\n";


use Win32;
use Getopt::Long;
use Win32::Perms;
use Win32::Lanman;

# Change these values to match your network
%PATH = (
    home    =>  "\\\\server\\Home\$",
    profile =>  "\\\\server\\Profiles\$",
    script  =>  "logon.pl",
);

# Change these values to match your needs
%Permission = (
    '<user>'        =>  {
        dir     =>  CHANGE_DIR,
        file    =>  CHANGE_FILE,
    },
    administrators  =>  {
        dir     =>  FULL_CONTROL_DIR,
        file    =>  FULL_CONTROL_FILE,
    },
);

%Config = (
    flags   =>  UF_SCRIPT | UF_NORMAL_ACCOUNT,
    priv    =>  USER_PRIV_USER,
    machine =>  Win32::NodeName(),
);

Configure( \%Config );
if( $Config{help} )
{
    Syntax();
    exit();
}

if( "" ne $Config{domain} )
{
    Win32::Lanman::NetGetDCName( '', 
                                 $Config{domain}, 
                                 \$Config{machine} );
}
elsif( "" eq $Config{machine} )
{
    Win32::Lanman::NetGetDCName( '', 
                                 Win32::DomainName(), 
                                 \$Config{machine} );
}

print "Creating $Config{machine}\\$Config{name}...";
CreateAccount( \%Config );

sub CreateAccount
{
    my( $Config ) = @_;
    my $Result = 0;
    my %Account = (
        name        =>  $Config->{name},
        password    =>  lc $Config->{name},
        home_dir    =>  "$PATH{home}\\$Config->{name}",
        comment     =>  $Config->{comment},
        usr_comment =>  '',
        flags       =>  $Config->{flags},
        params      =>  '',
        script_path =>  $PATH{script},
        full_name   =>  $Config->{fullname},
        workstations=>  "",
        profile     =>  "$PATH{profile}\\$Config->{name}",
        acct_expires=>  -1,
        # logon_hours is a binary bitmap where each bit represents an
        # hour in a week (168 bits).
        logon_hours =>  pack( "b168", "11111111" x 21 ),
        home_dir_drive  => "Z:",
        password_expired=> 1,
    );

    if( Win32::Lanman::NetUserAdd( $Config->{machine}, \%Account ) )
    {
        my( $Domain, $Sid, $SidType );

        print "Success\n";
        next unless( Win32::LookupAccountName( $Config->{machine},
                                               $Config->{name},
                                               $Domain,
                                               $Sid,
                                               $SidType ) );
        $Result = 1;
        foreach my $Group ( @{$Config->{groups}} )
        {
            # First try the global group...
            if( ! Win32::Lanman::NetGroupAddUser( $Config->{machine},
                                                  $Group,
                                                  $Config->{name} ) )
            {
                # Global group failed try local group...
                Win32::Lanman::NetLocalGroupAddMember( $Config->{machine},
                                                       $Group,
                                                       $Sid );
            }
        }

        MakeDir( $Account{profile} );
        if( MakeDir( $Account{home_dir} ) )
        {
            # The home directory exists, apply permissions...
            if( ApplyPermissions( "$Config{machine}\\$Account{name}", 
                                  $Account{home_dir} ) )
            {
                print "success\n";
            }
            else
            {
                print "failed to apply permissions.\n";
            }
        }
    }
    return( $Result );
}

sub MakeDir
{
    my( $Dir ) = @_;
    my @DirList;
    my $DirCount;
    my $Result = 1;
    my( $Root, $Path ) 
        = ( $Dir =~ /^(\w:\\?|\\\\.+?\\.+?\\|\\)?(.*)$/ );

    return( 1 ) if( -d $Dir );
    @DirList = split( /\\/, $Path );
    $Path = $Root;

    while( $Result && scalar @DirList )
    {
        $Path .= ( shift @DirList ) . "\\";

        next if( -d $Path );
        $Result = mkdir( $Path, 0777 );
    }
    return( $Result );
}

sub ApplyPermissions
{
    my( $User, $Path ) = @_;
    my $Perm = new Win32::Perms( $Path ) || return( 0 );
    my $Result = 0;

    $Perm->Remove( -1 );

    $Perm->Owner( $User );
    foreach my $Account ( keys( %Permission ) )
    {
        my $UserName = $Account;
        $UserName = $User if( "<user>" eq $Account );

        # Apply file permissions
        if( defined $Permission{$Account}->{file} )
        {
            $Perm->Allow( $UserName, 
                          $Permission{$Account}->{file}, 
                          FILE );
        }
        
        # Apply dir permissions
        if( defined $Permission{$Account}->{dir} )
        {
            $Perm->Allow( $UserName, 
                          $Permission{$Account}->{dir}, 
                          DIR );
        }
    }
    print "Applying permissions to '$Path'...";
    $Result = $Perm->Set();
    return( $Result );
}

sub Configure
{
    my( $Config ) = @_;

    Getopt::Long::Configure( "prefix_pattern=(-|\/)" );
    $Result = GetOptions( $Config, 
                            qw(
                                machine|m=s
                                groups|g=s@
                                domain|d=s
                                fullname|f=s
                                comment|c=s
                                help|?
                            )
                        );

    $Config->{help} = 1 if( ! $Result );
    if( "" ne $Config->{machine} )
    {
      $Config->{machine} = "\\\\$Config->{machine}";
      $Config->{machine} =~ s/^(\\\\)+/\\\\/;
    }
    $Config->{name} = shift @ARGV;
    $Config->{help} = 1 if( "" eq $Config->{name} );
}

sub Syntax
{
    my( $Script ) = ( Win32::GetLongPathName( $0 ) =~ /([^\\\/]*?)$/ );
    my( $Line ) = "-" x length( $Script );
    my( $WhiteSpace ) = " " x length( $Script );

    print <<EOT;

$Script
$Line
Create an account.

Syntax:
    perl $Script AccountName [-d Domain | -m Machine][-c Comment]
         $WhiteSpace [-f FullName] [-g Group]
        -c..........Specify the account's comment.
        -f..........Specify the account's full name.
        -g..........A group the account is a member of.
                    This is only applicable to user accounts.
                    Specify this switch as many times as needed.
        -m Machine..Specify a machine where the user account will live.
        -d Domain...Specify a domain where the user account will live.
        AccountName.The name of the account (the userid).
EOT
}
