#
# EventLog.pm
#
# Creates an object oriented interface to the Windows NT Evenlog 
# Written by Jesse Dougherty
#

package Win32::EventLog;

$VERSION = $VERSION = '0.03';

require Exporter;
require DynaLoader;
#use Win32;

die "The Win32::Eventlog module works only on Windows NT"
	unless Win32::IsWinNT();

@ISA= qw( Exporter DynaLoader );
@EXPORT = qw(
	EVENTLOG_AUDIT_FAILURE
	EVENTLOG_AUDIT_SUCCESS
	EVENTLOG_BACKWARDS_READ
	EVENTLOG_END_ALL_PAIRED_EVENTS
	EVENTLOG_END_PAIRED_EVENT
	EVENTLOG_ERROR_TYPE
	EVENTLOG_FORWARDS_READ
	EVENTLOG_INFORMATION_TYPE
	EVENTLOG_PAIRED_EVENT_ACTIVE
	EVENTLOG_PAIRED_EVENT_INACTIVE
	EVENTLOG_SEEK_READ
	EVENTLOG_SEQUENTIAL_READ
	EVENTLOG_START_PAIRED_EVENT
	EVENTLOG_SUCCESS
	EVENTLOG_WARNING_TYPE
);

sub AUTOLOAD {
    my($constname);
    ($constname = $AUTOLOAD) =~ s/.*:://;
    # reset $! to zero to reset any current errors.
    $!=0;
    my $val = constant($constname, @_ ? $_[0] : 0);
    if ($! != 0) {
	if ($! =~ /Invalid/) {
	    $AutoLoader::AUTOLOAD = $AUTOLOAD;
	    goto &AutoLoader::AUTOLOAD;
	}
	else {
	    my ($pack,$file,$line) = caller;
	    die "Unknown Win32::EventLog macro $constname, at $file line $line.\n";
	}
    }
    eval "sub $AUTOLOAD { $val }";
    goto &$AUTOLOAD;
}


#
# new()
#
#   Win32::EventLog->new("source name", "ServerName");
#
sub new
{
    my $c = shift;
    die "usage: PACKAGE->new(SOURCENAME[, SERVERNAME])\n" unless @_;
    my $source = shift;
    my $server = shift;
    my $handle;

    # Create new handle
    OpenEventLog($handle, $server, $source);
    return bless  {'handle' => $handle,
		   'Source' => $source,
		   'Computer' => $server }, $c;
}

#
# Open (the rather braindead old way)
# A variable initialized to empty must be supplied as the first
# arg, followed by whatever new() takes
#
sub Open {
    $_[0] = Win32::EventLog->new($_[1],$_[2]);
}

sub Backup
{
    $self = shift;
    die " usage: OBJECT->Backup(FILENAME)\n" unless @_ == 1;
    my $filename = shift;
    my $result;

    $result = BackupEventLog($self->{'handle'},$filename);
    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

# Read
# Note: the EventInfo arguement requires a hash reference.
sub Read
{
    $self = shift;

    die "usage: OBJECT->Read(FLAGS, RECORDOFFSET, HASHREF)\n" unless @_ == 3;

    my ($readflags,$recordoffset) = @_;
    my ($result, $datalength, $dataoffset, $sid, $length);
    my ($reserved, $recordnumber, $timegenerated, $timewritten, $eventid);
    my ($eventtype, $numstrings, $eventcategory, $reservedflags);
    my ($closingrecordnumber, $stringoffset, $usersidlength, $usersidoffset);

    # The following is stolen shamelessly from Wyt's tests for the registry. 

    $result = ReadEventLog($self->{'handle'},
			   $readflags,
			   $recordoffset,
			   $header,
			   $source,
			   $computer,
			   $sid,
			   $data,
			   $strings);

    ($length,
     $reserved,
     $recordnumber,
     $timegenerated,
     $timewritten,
     $eventid,
     $eventtype,
     $numstrings,
     $eventcategory,
     $reservedflags,
     $closingrecordnumber,
     $stringoffset,
     $usersidlength,
     $usersidoffset,
     $datalength,
     $dataoffset) = unpack('l6s4l6', $header);

    # make a hash out of the values returned from ReadEventLog.
    my %h = ( 'Source'			=> $source,
	      'Computer'		=> $computer,
	      'Length'			=> $datalength,
	      'Category'		=> $eventcategory,
	      'RecordNumber'		=> $recordnumber,
	      'TimeGenerated'		=> $timegenerated,
	      'Timewritten'		=> $timewritten,
	      'EventID'			=> $eventid,
	      'EventType'		=> $eventtype,
	      'ClosingRecordNumber'	=> $closingrecordnumber,
	      'Strings'			=> $strings,
	      'Data'			=> $data,
	    );

    if (ref($_[2]) eq 'HASH') {
	%{$_[2]} = %h;		# this needed for Read(...,\%foo) case
    }
    else {
	$_[2] = \%h;
    }
    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

sub Report
{
    my $self = shift;
    
    die "usage: OBJECT->Report( HASHREF )\n" unless @_ == 1;

    my $EventInfo = shift;
    my $result;

    if ( ref( $EventInfo)  eq "HASH" ) {
	my ($length, $reserved, $recordnumber, $timegenerated, $timewritten);
	my ($eventid, $eventtype, $numstrings, $eventcategory, $reservedflags);
	my ($closingrecordnumber, $stringoffset, $usersidlength);
	my ($usersidoffset, $source, $data, $strings);

	$eventcategory		= $EventInfo->{'Category'};
	$source			= $self->{'Source'};
	$computer		= $self->{'Computer'};
	$length			= $EventInfo->{'Length'};
	$recordnumber		= $EventInfo->{'RecordNumber'};
	$timegenerated		= $EventInfo->{'TimeGenerated'};
	$timewritten		= $EventInfo->{'Timewritten'};
	$eventid		= $EventInfo->{'EventID'};
	$eventtype		= $EventInfo->{'EventType'};
	$closingrecordnumber	= $EventInfo->{'ClosingRecordNumber'};
	$strings		= $EventInfo->{'Strings'};
	$data			= $EventInfo->{'Data'};

	$result = WriteEventLog($computer,
				$source,
				$eventtype,
				$eventcategory,
				$eventid,
				$reserved,
				$data,
				$strings);
    } 
    else {
	die "Win32::EventLog::Report requires a hash reference as arg 3\n";
    }

    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

sub GetOldest
{
    my $self=shift;
	    
    die "usage: OBJECT->GetOldest( SCALAREF )\n" unless @_ == 1;
    my $result = GetOldestEventLogRecord( $self->{'handle'},$_[0]);
    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

sub GetNumber
{
    my $self=shift;

    die "usage: OBJECT->GetNumber( SCALARREF )\n" unless @_ == 1;
    my $result = GetNumberOfEventLogRecords($self->{'handle'}, $_[0]);
    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

sub Clear
{
    my $self=shift;

    die "usage: OBJECT->Clear( FILENAME )\n" unless @_ == 1;
    my $filename = shift;
    my $result = ClearEventLog($self->{'handle'}, $filename);
    unless ($result) { $! = Win32::GetLastError() }
    return $result;
}

bootstrap Win32::EventLog;

1;
__END__
