Title: Message
My "belief" is the correct belief as per the Win32 documentation. I also believe that you guys are not taking into account your timezone. The logon hours stored in the account profile is based on GMT.
 
I am attaching the DumpAccount.pl script from my second book (http://www.roth.net/books/handbook/). It will dump the entire account information including logon hours *properly formatted* for your timezone.
 
Note that the script requires Win32::API::Prototype (http://www.roth.net/perl/packages/win32-api-prototype.ppd). You could modify the script to only use Win32::API easily enough.
 
The script yields logon hours displayed as:
 
logon hours:      Sunday: 00-23
                  Monday: 00-23
                  Tuesday: 00-23
                  Wednesday: 00-23
                  Thursday: 00-23
                  Friday: 00-23
                  Saturday: 00-23
 
The root of the script (regarding logon hours) is:
 
# Vicious hack to get the time zone
if( ApiLink( 'kernel32.dll',
             'DWORD GetTimeZoneInformation( PVOID pTZInfo )' ) )
{
   my $pTZInfo = NewString( 200 );
   my $Result = GetTimeZoneInformation( $pTZInfo );
   if( 0 == $Result || 2 == $Result )
   {
      $TZOffset = unpack( "l", $pTZInfo ) / 60;
   }
}
and
 
{
                # Determine which hours on what days of the week this user has
                # permission to logon
                my $DayIndex = 0;
                my $BinaryHours = $Value;
                my $HourString = unpack( "b*", $BinaryHours );
                $Value = "";               
               
                # We need to offset the logon hours string by the number
                # of hours we are either before or after GMT.
                if( 0 > $TZOffset )
                {
                   $TZOffset *= -1;
                   $HourString =~ s/^(.*)(.{$TZOffset})$/$2$1/;
                }
                else
                {
                   $HourString =~ s/^(.{$TZOffset})(.*)$/$2$1/;
                }
 
                # Now walk through each day and determine what
                # hours the user is allowed to log on.
                foreach my $Day ( $HourString =~ /.{24}/g )
                {
                    my @Hours;
                    my $Display = "";
                    my $Index = 0;
                   
                    $Day .= "0";
                    foreach my $HourValue ( split( "", $Day ) )
                    {
                        if( $HourValue )
                        {
                            if( "" eq $Display )
                            {
                                $Display = sprintf( "%02d", $Index );
                            }
                        }
                        else
                        {
                            if( "" ne $Display )
                            {
                                if( $Display < $Index - 1 )
                                {
                                    $Display .= sprintf( "-%02d", $Index - 1 );
                                }   
                                push( @Hours, $Display );
                                $Display = "";
                            }
                        }
                        $Index++;
                    }
                    $Value .= sprintf( "%- 50s", "$WEEKDAYS[$DayIndex++]: "
                              . join( ",", @Hours ) ), "\n";
                }
            }
 
-----Original Message-----
From: DePriest, Jason R. [mailto:[EMAIL PROTECTED]]
Sent: Tuesday, December 24, 2002 1:23 PM
To: 'Steven Manross'; Perl-Win32-Admin ([EMAIL PROTECTED])
Subject: RE: Win32::AdminMisc - user flag USER_LOGON_HOURS

Thanks, Steven.

That helped me pull the data out to be a reasonable value (i.e. 21-bytes instead of 1-byte).  I had glanced at the pack/unpack functions but didn't actually try to use them.

However, I am trying to use the UserGetMiscAttributes module instead of the UserSetMiscAttributes module.
From your example, I think I'll still have to create a huge switch statement to get the 1's and 0's into text strings.

-----Original Message-----
From: Steven Manross [mailto:[EMAIL PROTECTED]]
Sent: Tuesday, December 24, 2002 3:04 PM
To: 'DePriest, Jason R.'; Perl-Win32-Admin ([EMAIL PROTECTED])
Subject: RE: Win32::AdminMisc - user flag USER_LOGON_HOURS


I don't know why but contrary to Dave's belief, I had to set this up
this way to make it work, where the first line is the last few hours on
Sunday and then the next lines are Monday, Tuesday, etc...

%Logon_Hours = ('special' =>
[(1,0,0,0,0,0,0,
 
0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
 
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
 
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
 
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
 
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
 
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                                   0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
                                  )],
                     'normal' =>
[(1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                                   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                                  )],
                    );
  my $packed_hours = pack ( 'b*' ,
join('',@{$Logon_Hours{$hours_identifier}}) ) ;
  if (Win32::AdminMisc::UserSetMiscAttributes ($pdc, $username,
                                                USER_LOGON_HOURS =>
$packed_hours )) {
    print "  Succeeded setting Logon Hours for $username!\n";
  } else {
    print "  Failed setting Logon Hours for $username!\nERROR:
",Win32::GetLastError(),"\n";
  }

Steven
-----Original Message-----
From: DePriest, Jason R. [ mailto:[EMAIL PROTECTED]]
Sent: Tuesday, December 24, 2002 1:55 PM
To: Perl-Win32-Admin ([EMAIL PROTECTED])
Subject: Win32::AdminMisc - user flag USER_LOGON_HOURS


Dave Roth's documentation and website say this value points to a 21-byte
(168-bit) binary string in which each bit represents one hour of the
week.

Has anyone bothered to parse this flag out into english?  The only way I
can think of is to write a pseudo-switch statement with a 168 'if'
statements that push the results into an array.

Plus, if I 'ord' the value from the flag (and no restrictions are set),
I get 255 and not 2E+167.
Thank you!
Jason R DePriest

#  DumpAccount.pl
#  Example 2.2:
#  ----------------------------------------
#  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::NetAdmin;
use Win32::AdminMisc;
use Win32::API::Prototype;

%FLAGS = (
    eval UF_TEMP_DUPLICATE_ACCOUNT      =>  "UF_TEMP_DUPLICATE_ACCOUNT",
    eval UF_NORMAL_ACCOUNT              =>  "UF_NORMAL_ACCOUNT",
    eval UF_INTERDOMAIN_TRUST_ACCOUNT   =>  "UF_INTERDOMAIN_TRUST_ACCOUNT",
    eval UF_WORKSTATION_TRUST_ACCOUNT   =>  "UF_WORKSTATION_TRUST_ACCOUNT",
    eval UF_SERVER_TRUST_ACCOUNT        =>  "UF_SERVER_TRUST_ACCOUNT",
    eval UF_DONT_EXPIRE_PASSWD          =>  "UF_DONT_EXPIRE_PASSWD",
    eval UF_SETTABLE_BITS               =>  "UF_SETTABLE_BITS",
    eval UF_SCRIPT                      =>  "UF_SCRIPT",
    eval UF_ACCOUNTDISABLE              =>  "UF_ACCOUNTDISABLE",
    eval UF_HOMEDIR_REQUIRED            =>  "UF_HOMEDIR_REQUIRED",
    eval UF_LOCKOUT                     =>  "UF_LOCKOUT",
    eval UF_PASSWD_NOTREQD              =>  "UF_PASSWD_NOTREQD",
    eval UF_PASSWD_CANT_CHANGE          =>  "UF_PASSWD_CANT_CHANGE",
);

%AUTH_FLAGS = (
    eval AF_OP_PRINT                    =>  "AF_OP_PRINT",
    eval AF_OP_COMM                     =>  "AF_OP_COMM",
    eval AF_OP_SERVER                   =>  "AF_OP_SERVER",
    eval AF_OP_ACCOUNTS                 =>  "AF_OP_ACCOUNTS",
);

%PRIVILEGES = (    
    eval USER_PRIV_GUEST                =>  "USER_PRIV_GUEST",
    eval USER_PRIV_USER                 =>  "USER_PRIV_USER",
    eval USER_PRIV_ADMIN                =>  "USER_PRIV_ADMIN",
);                

@WEEKDAYS = qw(
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
);
                
# Vicious hack to get the time zone
if( ApiLink( 'kernel32.dll', 
             'DWORD GetTimeZoneInformation( PVOID pTZInfo )' ) )
{
   my $pTZInfo = NewString( 200 );
   my $Result = GetTimeZoneInformation( $pTZInfo );
   if( 0 == $Result || 2 == $Result )
   {
      $TZOffset = unpack( "l", $pTZInfo ) / 60;
   }
}

Configure( \%Config );
if( $Config{help} )
{
    Syntax();
    exit();
}

if( "" ne $Config{domain} )
{
    # Find the primary domain controller for the specified domain.
    $Config{machine} = "";
    Win32::NetAdmin::GetDomainController( '', 
                                          $Config{domain}, 
                                          $Config{machine} );
}
elsif( "" eq $Config{machine} )
{
    # Find the primary domain controller for the current domain.
    $Config{machine} = "";
    Win32::NetAdmin::GetDomainController( '', 
                                          Win32::DomainName(), 
                                          $Config{machine} );    
}

# Expand any user account wildcards
foreach my $Account ( @{$Config{accounts}} )
{
    if( $Account =~ /\*$/ )
    {
        my( $Prefix ) = ( $Account =~ /^(.*)\*$/ );
        my @Users;
        Win32::AdminMisc::GetUsers( $Config{machine}, $Prefix, \@Users );
        push( @AccountList, @Users );
    }
    else
    {
        push( @AccountList, $Account );
    }
}

$~ = Attributes;
foreach my $Account ( sort( @AccountList ) )
{
    my %Info;
    print "Displaying $Config{machine}\\$Account:\n";
    if( Win32::AdminMisc::UserGetMiscAttributes( $Config{machine},
                                                 $Account, 
                                                 \%Info ) )
    {
        foreach $Key ( sort( keys( %Info ) ) )
        {
            $Value = $Info{$Key};
            ( $DisplayKey ) = ( ( lc $Key ) =~ /^.*?_(.*)/i );
            $DisplayKey =~ s/_/ /g;
            if( $Key =~ /_age/i )
            {
                # Break apart the C timestamp into years, days, 
                # hours and minutes
                # There are 31536000 seconds in a year...
                # There are 86400 seconds in a day...
                # There are 3600 seconds in an hour...
                # And of course 60 seconds in a minute.
                my $Time = $Info{$Key};
                my $Years   = int( $Time / 31536000 );
                my $Days    = int( ( $Time % 31536000 ) / 86400 );
                my $Hours   = int( ( ( $Time % 31536000 ) % 86400 ) / 3600 );
                my $Minutes = int( ( ( ( $Time % 31536000 ) % 86400 ) % 3600 ) / 60 );
                $Value = sprintf( "%01d Years %02d Days %02d Hours %02d Minutes", 
                                  $Years, $Days, $Hours, $Minutes );
            }
            elsif( $Key =~ /LAST/i || $Key =~ /ACCT_EXPIRES/i )
            {
                if( 1 > $Value )
                {
                    $Value = "No date specified";
                }
                else
                {    
                    $Value = scalar localtime( $Info{$Key} );
                }    
            }
            elsif( $Key =~ /AUTH_FLAGS/i )
            {
                # Break apart the flags attribute into individual flag strings
                $Value = DecodeBits( $Value, \%AUTH_FLAGS );      
            }
            elsif( $Key =~ /FLAGS/i )
            {
                # Break apart the flags attribute into individual flag strings
                $Value = DecodeBits( $Value, \%FLAGS );
            }
            elsif( $Key =~ /PASSWORD$/i )
            {
                $Value = "<Unknown>";
            }
            elsif( $Key =~ /PRIV$/i )
            {
                $Value = $PRIVILEGES{$Value};
            }
            elsif( $Key =~ /MAX_STORAGE$/i )
            {
                if( -1 == $Value )
                {
                    $Value = "Unlimited storage";
                }
                else
                {
                    # Format the number to include commas as in 123,456,789
                    while( $Value =~ s/^(-?\d+)(\d{3})/$1,$2/ ){};
                    $Value .= " bytes";
                }
            }
            elsif( $Key =~ /LOGON_HOURS/i )
            {
                # Determine which hours on what days of the week this user has
                # permission to logon
                my $DayIndex = 0;
                my $BinaryHours = $Value;
                my $HourString = unpack( "b*", $BinaryHours );
                $Value = "";                
                
                # We need to offset the logon hours string by the number
                # of hours we are either before or after GMT.
                if( 0 > $TZOffset )
                {
                   $TZOffset *= -1;
                   $HourString =~ s/^(.*)(.{$TZOffset})$/$2$1/;
                }
                else
                {
                   $HourString =~ s/^(.{$TZOffset})(.*)$/$2$1/;
                }

                # Now walk through each day and determine what
                # hours the user is allowed to log on.
                foreach my $Day ( $HourString =~ /.{24}/g )
                {
                    my @Hours;
                    my $Display = "";
                    my $Index = 0;
                    
                    $Day .= "0";
                    foreach my $HourValue ( split( "", $Day ) )
                    {
                        if( $HourValue )
                        {
                            if( "" eq $Display )
                            {
                                $Display = sprintf( "%02d", $Index );
                            }
                        }
                        else
                        {
                            if( "" ne $Display )
                            {
                                if( $Display < $Index - 1 )
                                {
                                    $Display .= sprintf( "-%02d", $Index - 1 );
                                }    
                                push( @Hours, $Display );
                                $Display = "";
                            }
                        }
                        $Index++;
                    }
                    $Value .= sprintf( "%- 50s", "$WEEKDAYS[$DayIndex++]: " 
                              . join( ",", @Hours ) ), "\n";
                }
            }
            write;           
        }
    }
    else
    {
        print "failure: ";
        print Win32::FormatMessage( Win32::AdminMisc::GetError() );
    }
    print "\n";
}

sub DecodeBits
{
    my( $Bits, $FlagList ) = @_;
    my $Result = "";
    
    foreach my $Flag ( keys( %{$FlagList} ) )
    {
        # Add spaces so that the format breaks lines (formats will not
        # break lines on carriage returns)
        $Result .= $FlagList->{$Flag} . " " x 40 . "\n" if( $Bits & $Flag );
    }
    return( $Result );
}
    
sub Configure
{
    my( $Config ) = @_;

    Getopt::Long::Configure( "prefix_pattern=(-|\/)" );
    $Result = GetOptions( $Config, 
                            qw(
                                machine|m=s
                                domain|d=s
                                help|?
                            )
                        );

    $Config->{help} = 1 if( ! $Result );
    if( "" ne $Config->{machine} )
    {
      $Config->{machine} = "\\\\$Config->{machine}";
      $Config->{machine} =~ s/^(\\\\)+/\\\\/;
    }
    push( @{$Config->{accounts}}, @ARGV );
    $Config->{help} = 1 if( ! scalar @{$Config->{accounts}} );
}

sub Syntax
{
    my( $Script ) = ( Win32::GetLongPathName( $0 ) =~ /([^\\\/]*?)$/ );
    my( $Line ) = "-" x length( $Script );

    print <<EOT;

$Script
$Line
Displays an account's configuration.

Syntax:
    perl $Script [-d Domain | -m Machine] Account [Account2 ...]
        -m Machine..Specify a machine where the user account lives.
        -d Domain...Specify a domain where the user account lives.
        Account.....The name of the account (the userid).
                    This account can end with a * char to indicate
                    all accounts that begin with the specified string.
        If no domain or machine is specified then the current domain
        is used.
EOT
}

format Attributes = 
   @<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   "$DisplayKey:",   $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
~                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                     $Value
.

Reply via email to