#! perl -w
###########################################################################################
# FILE          : EventQuery.pl                                                           #
#                                                                                         #
# PURPOSE       : Enables an administrator to query/view all existing events              #
#                 in a given event log                                                    #
#                                                                                         #
# LAST MODIFIED : 30-August-2000                                                          #
#                                                                                         #
# AUTHOR        : RAVEENDRA M S                                                           #
###########################################################################################
use strict;      # Turn on script syntax checking
use Time::Local;
BEGIN
{
	if( &IsWmiModuleInstalled() )
	{
		require W2RK::WMI; import W2RK::WMI; # WMI library
	}
	else
	{
		print STDERR "ERROR: Wmi.pm is required to run the script.\n       Copy Wmi.pm from the Resource Kit directory to /Perl/site/lib/W2RK.\n";
		exit( 0 );
	}

	sub IsWmiModuleInstalled
	{
		foreach ( @INC )
		{
			my $dir = $_."/W2RK/Wmi.pm";
			if( -e $dir )
			{
				require W2RK::WMI; import W2RK::WMI; # WMI library
				return 1;
			}
		}
		return 0;
	}
}


# wmi constants
use constant CIMV2_NAMESPACE       => "root\\CIMV2";

#general constants
use constant DISPCONST             => 500;
use constant FIRSTX                => 1;
use constant LASTX                 => 2;
use constant RANGEX                => 3;
use constant WBEMPRIVILEGEBACKUP   => 16;
use constant WBEMPRIVILEGESECURITY => 7;
use constant TRUE                  => 1;
use constant FALSE                 => 0;
use constant OUTPUTCOUNT           => 25;
use constant FORWARDONLY_RETURNIMMEDIATELY => 48;

# error messages
use constant ERROR_INVALID_FORMAT_VALUE     => "ERROR: Invalid value '%s' for the format option.\n"; #7
use constant ERROR_INFO_NOT_AVAILABLE       => "ERROR: '%s' log entries not available.\n"; #18
use constant ERROR_INVALID_RANGE_EXPR       => "ERROR: Invalid value '%s' for the range option.\n"; #7
use constant ERROR_INVALID_LOGNAME          => "ERROR: Invalid log name specified.\n";#7
use constant ERROR_LOGINFO_NOT_AVAILABLE    => "ERROR: Unable to get '%s' log entries.\n"; #18
use constant ERROR_INVALID_FILTER_EXPR      => "ERROR: Invalid expression '%s' for the filter option.\nValid operators include =, !, >=, <=, >, and <\n"; #7
use constant ERROR_INVALID_FILTER_TYPE      => "ERROR: Invalid value '%s' for the filter option.\n"; #7
use constant ERROR_NO_INFO_RETREIVED        => "ERROR: No '%s' information retreived.\n"; #30
use constant ERROR_INFO_NO_FILTER           => "INFO: No '%s' log entries satisfy filter criteria.\n";
use constant ERROR_OPTION                   => "ERROR: Invalid value '%s' for the %s option.\n";  #7

# usage messages.
use constant USAGE_MAIN                     => "The syntax of the command is incorrect.\n".
                                               "Type \"EventQuery.pl -?\" for usage information.\n"; #8

my $errorcode = 0;
MAIN:
{
	my ($wmi, $format, $range, $verbose, @filter, $help, $sFilter,
		$collections, $count, @title, @table, $noheader, $rangeType,
		%lognames );
	my ($server,$user,$password);

	# instantiate the WMI object instance.
	$wmi = W2RK::WMI->new;

	# process the options specified on the command line.
	$wmi->ProcessOptions(
							"format=s"      => \$format, 
							"range=s"       => \$range, 
							"filter=s@"     => \@filter,
							"verbose"       => \$verbose,
							"h|?"           => \$help,
							"s=s"           => \$server,
							"u=s"           =>\$user,
							"p=s"           =>\$password
						);

	#Display the usage of the script if help option is specified.
	&usage() if (defined $help && !defined $format && !@filter 
				 && !defined $range && !defined $verbose && !@ARGV);

	# Display the error message and terminate, if logname(s) are not specified.
	fail (USAGE_MAIN, 8) if (!@ARGV);

	# Display the error message and terminate, if other suboptions are specified
	# with help option.
	fail (USAGE_MAIN, 8) if (defined $help && (defined $format || @filter 
				 || defined $range || defined $verbose || @ARGV));

	fail USAGE_MAIN,8 if ((!defined $server && defined $user) || (!defined $user && defined $password));

	#Validate the value specified with format option.
	$format = 'table' if (!defined $format);
	&ValidateFormatType(\$format) if (defined $format);

	# Validate the range input expression specified.
	$rangeType = &ValidateRange($range) if (defined $range);
	$rangeType = 0 if (!defined $range);

	# connect to the specified namespace of the WMI provider.
	$wmi->{server} = $server;
	$wmi->{username} = $user;
	$wmi->{password}= $password;

	$wmi->namespace(CIMV2_NAMESPACE); 
	eval { $wmi->Connect(); };
	if($@)
	{
		fail $@,9;
	}

	# Set the privileges.
	$wmi->services->{Security_}->{Privileges}->Add(WBEMPRIVILEGESECURITY);
	$wmi->services->{Security_}->{Privileges}->Add(WBEMPRIVILEGEBACKUP);

	@ARGV = &RemoveDuplicates( \@ARGV );

	# Validate the logonnames specified.
	&ValidateLogNames($wmi, \%lognames, @ARGV);

	my $filterdate = FALSE;
	my @datefilters;

	# Form the additional filter condition based on the filter expressions specified.
	if (defined @filter && @filter)
	{
		my $iter = FALSE;
		foreach my $filter ( @filter)
		{
			my ($EventType, $Operator, $EventValue);
			
			exit(7) if (ValidateFilter($wmi, $filter, \$EventType, \$Operator, \$EventValue, \$filterdate, \@datefilters) != 0);
			if (!$filterdate)
			{
				my $sSubFilter = &FormFilterString($EventType, $Operator, $EventValue);
				$sFilter .= " $sSubFilter " if( $iter == 0 and defined $sSubFilter);
				$sFilter .= " AND $sSubFilter" if ( $iter != 0 and defined $sSubFilter);
				$iter++;
			}
		}
	}

	# Populate the column header to be used in the display.
	@title=('Type', 'DateTime', 'Source', 'Category', 'Event ID', 'User', 'Computer',
			'Description', 'Data', 'LogName');
	push (@table, \@title);
	$noheader = 0;
	my $nologs = 1;
	my @instancearr;
	foreach my $logname (sort keys %lognames)
	{
		my ($totalcount, $begin, $end, $count, @startrecord, 
			@endrecord, $entriesfound);

		$nologs = 0;
		my $displayed = 0;

		# Store the total records available for the particular logtype
		$totalcount = $count = $lognames{$logname};

		if( !defined $sFilter )
		{
			# Given the rangetype and the available totalrecords, calculate
			# records range that is of interest to the user.    
			&CalculateRangeLimits($rangeType, $totalcount, \$begin, \$end, $range);
			my ($recFilter);

			# Form the filter string
			$recFilter = "RecordNumber>=$begin AND RecordNumber<=$end" ;

			# Query for the events log based on the filter conditions specified.
			$collections = &QueryEvents($recFilter, $logname,$wmi);
			if (!defined $collections)
			{
				printf STDERR ERROR_INFO_NOT_AVAILABLE, $logname;
				$errorcode = 18;
				next;
			}

			# Loop through the list of available instances of the event logs
			@instancearr = in $collections;
			if( $#instancearr == -1 )
			{
				printf STDERR ERROR_INFO_NOT_AVAILABLE, $logname;
				$errorcode = 18;
				next;
			}

			$begin = 0; $end = $#instancearr;
		}
		else
		{
			# Query for the events log based on the filter conditions specified.
			$collections = &QueryEvents($sFilter, $logname,$wmi);
			if (!defined $collections)
			{
				printf STDERR ERROR_INFO_NO_FILTER, $logname;
				$errorcode = 17;
				next;
			}
			@instancearr = in $collections;
			if( $#instancearr == -1 )
			{
				printf STDERR ERROR_INFO_NO_FILTER, $logname;
				$errorcode = 17;
				next;
			}

#			$range = $range > $#instancearr + 1  ? $#instancearr + 1 : $range;
			&CalculateRangeLimits($rangeType, $#instancearr + 1, \$begin, \$end, $range, 1 );
			$begin--; $end--;
		}

		for (my $count = $begin ; $count <= $end  ; $count++)
		{
			next if( !defined $instancearr[ $count ] );
			my $instance = $instancearr[$count];
			my @row = ('N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 
					  'N/A', 'N/A', 'N/A', 'N/A');
			$row[0] = ucfirst($instance->{Type}) if (defined $instance->{Type});
			$row[1] = $wmi->ConvertToDateTimeFormat($instance->{TimeGenerated}) if (defined $instance->{TimeGenerated});
			$row[2] = $instance->{SourceName} if (defined $instance->{SourceName});
			$row[3] = (defined $instance->{CategoryString}) ? $instance->{CategoryString} : "None";
			$row[4] = $instance->{EventCode} if (defined $instance->{EventCode});
			$row[5] = $instance->{User} if (defined $instance->{User});
			$row[6] = $instance->{ComputerName} if (defined $instance->{ComputerName});
			$row[7] = $instance->{Message} if (defined $instance->{Message});
			$row[8] = $instance->{Data} if (defined $instance->{Data});
			$row[9] = $instance->{Logfile} if (defined $instance->{Logfile});
			push (@table, \@row);

		   # To display the output in batches of size DISPCOUNT, so as facilitate
		   # early response to the user.
		   if (@table > 1)
		   {
			   if (@table > OUTPUTCOUNT)
			   {
				   &DisplayResults($wmi, \$noheader, $verbose, $format, \@table, \@datefilters, $filterdate, \$displayed);
				   @table = ($table[0]);
			   }
			   $entriesfound = TRUE;
		   }
		}
		
		if (@table > 1)
		{
			&DisplayResults($wmi, \$noheader, $verbose, $format, \@table, \@datefilters, $filterdate, \$displayed);
			@table = ($table[0]);
			$entriesfound = TRUE;
		}
		
		if (!$entriesfound or !$displayed)
		{
			if (!@filter)
			{
				printf STDERR (ERROR_NO_INFO_RETREIVED, $logname);
				$errorcode = 18; # no instances found.
			}
			else
			{
				printf STDERR (ERROR_INFO_NO_FILTER, $logname);;
				$errorcode = 30 ;
			}
		}
	}

	if ($nologs )
	{
		printf STDERR (ERROR_NO_INFO_RETREIVED,'EventQuery');;
		$errorcode = 30 ;
	}

exit($errorcode);
}

##############################################################################
# FUNCTION NAME: DisplayResults
#
# PURPOSE      : To display the output formatted using the ShowTable function of 
#                the WMI.pm 
#
# INPUTS       : $wmi - wmi provider instanceString
#                $noheader - flag to indicate whether the header should be printer
#                         or not.
#                $verbose - flag to indicate whether the output should be in verbose
#                        mode or non-verbose mode.
#                $format  - specifies the format of output.
#                @table - array containing the output to be displayed.
#
# RETURN VALUES : NONE
##############################################################################
sub DisplayResults()
{
    my ($wmi, $noheader, $verbose, $format, $table, $datefilter, $filterdate, $displayed) = @_;
    my ($hideheader);
	
	if ($filterdate)
	{
		my %FilterCfg = ("DateTime"  =>  { FilterName => 'DATETIME',   ValueType=>'time'});
		my $err = W2RK::WMI->ValidateFilter($datefilter, \%FilterCfg );
		fail sprintf(ERROR_OPTION, $err, "filter"),7 if( defined $err );
		W2RK::WMI->FilterResults($table, $datefilter, \%FilterCfg, undef); 
	}

	$hideheader = (!defined $verbose) ? 1 : 0;
    # Display our 'table' with the format and columns as listed below
	if (@$table > 1)
	{
	    $wmi->ShowTable($table,
	                        { Format => $format,  ColumnSeparator => '  ',
	                          HeaderDashes => (defined $$noheader) ? 1 : 0,
	                          NoHeader => (defined $$noheader) ? 0 : 1},
	                          'Type'        =>  { Align  => 'left', MinWidth => 20},
	                          'DateTime'    =>  { Align  => 'left', MinWidth => 22 },
	                          'Source'      =>  { Align  => 'left', MinWidth => 35},
	                          'Category'    =>  { Align  => 'left' , MinWidth => 10,  Formatter => \&DataFormatter},
	                          'Event ID'    =>  { Align  => 'left' , MinWidth => 15},
	                          'User'        =>  { Align  => 'left' , MinWidth => 20},
	                          'Computer'    =>  { Align  => 'left' , MinWidth => 20},
	                          'Description' =>  { Align  => 'left' , Hide => $hideheader, MinWidth => 20, Formatter => \&DataFormatter},
	                          'Data'        =>  { Align  => 'left' , Hide => $hideheader, MinWidth => 50},
	                          'LogName'     =>  { Align  => 'left' }
		          );
		$$displayed = 1;
	}
    undef $$noheader;
}

###############################################################################
# FUNCTION NAME : QueryEvents
#
# PURPOSE       : This function gets the events from the class 
#                 Win32_NTEventLog for user query
#
# INPUTS        : $sFilter - filter string, @args - (list of lognames)
#
# RETURN VALUES : $collections - Collection of Events
#
###############################################################################
sub QueryEvents()
{
    my($sFilter, $logname,$wmi) = @_;
    my ($query, $collection);
    $query = "SELECT * from Win32_NTLogEvent";
    $query = $query . " WHERE";
    my $query1 = " Logfile=\'$logname\' ";
    $query = $query . $query1;
    $query .= " AND $sFilter" if (defined $sFilter);

#    eval { $collections = $wmi->{_services}->ExecQuery($query,"WQL",48)};
    eval { $collection = $wmi->ExecQuery($query, FORWARDONLY_RETURNIMMEDIATELY) };
    if ($@ || !defined $collection)
    {
        fail (sprintf(ERROR_LOGINFO_NOT_AVAILABLE, $logname), 18);
    }

    return $collection;
}

##############################################################################################################
#
#   PURPOSE :       This function formats the data by removing the \n and \r characters.
#
#   INPUTS:         Data to be formatted.
#
#   RETURN VALUES:  Formatted Data.
#
##############################################################################################################
sub DataFormatter
{
	my $data = $_[0];
	$data =~ s/[\n\r]//g;
	return $data;
}

##############################################################################################################
#
#   PURPOSE :       This function validates the lognames entered by user
#
#   INPUTS:         @ARGV - array of lognames
#
#   RETURN VALUES:  None
#
##############################################################################################################
sub ValidateLogNames()
{
    my ($wmi, $lognames, @args) = @_;
    my ($instances, $count);

    # Obtain the instances of the Win32_NTEventLogFile
    eval { $instances = $wmi->InstancesOf("Win32_NTEventLogFile", FORWARDONLY_RETURNIMMEDIATELY); };
    if ($@ || !defined $instances)
    {
        fail (ERROR_LOGINFO_NOT_AVAILABLE, 18);
    }

    $count = 0;
    for my $instance (in $instances)
    {
        for (my $lv = 0; $lv < @args; $lv++)
        {
            my $logname = $args[$lv];
            &SubstitutePatterns(\$logname);
            if($instance->{LogfileName} =~ /^$logname$/i)
            {
                if (defined $instance->{NumberOfRecords})
                {
                    $$lognames{$instance->{LogfileName}} = $instance->{NumberOfRecords};
                }
                else
                {
                    $$lognames{$instance->{LogfileName}} = 0;
                }
                $count++;
                last;
            }
        }#inner for
    }#outer for
    return if (@args == 1 && $args[0] eq "*");
    if($count < @args) 
    {
        fail(ERROR_INVALID_LOGNAME, 7);
    }
}

###############################################################################################################
#
#   PURPOSE :       this function  splits the month, day, year, hour and minutes form the datetime 
#                   in the format mm/dd/yy:hh:mm
#
#   INPUTS:         $format - DateTime in the format mm/dd/yy:hh:mm
#
#   RETURN VALUES:  returns splitted month, day, year, hour and minute.
#
###############################################################################################################
sub GetAllFieldsInDateTime
{
    my $format = $_[0];
    my @fields1 = split("\/", $format);
    my @fields2 = split(":", $fields1[2]);
    return ($fields1[0], $fields1[1], $fields2[0], $fields2[1], $fields2[2]);
}

##########################################################################################
# FUNCTION NAME     :   ValidateFormatType
#
# PURPOSE           :   This function validates the value passed with -format option
#
# INPUT PARAMETERS  :   PARAMETER       DESCRIPTION                         PASS BY 
#                                                                           VALUE/REF   
#                     --------------------------------------------------------------------
#                       $format         Format type                          VALUE
#########################################################################################
sub ValidateFormatType()
{
    my ($format) = @_;  
    if ($$format !~ /^TABLE$|^LIST$|^CSV$/i)
    {
        fail(sprintf(ERROR_INVALID_FORMAT_VALUE, $$format),7);
    }
    $$format = 'normal' if ($$format =~ /^table$/i); 
    $$format = lc($$format);
}

##########################################################################################
# FUNCTION NAME     : ValidateRange
#
# PURPOSE           : This function validates the value passed with -range option
#
# INPUT PARAMETERS  :   PARAMETER         DESCRIPTION                       PASS BY 
#                                                                           VALUE/REF
#                     --------------------------------------------------------------------
#                        $range           range expression                   VALUE
#########################################################################################
sub ValidateRange()
{
    my ($range) = @_;
    #test whether the range is one of the following allowed formats:
    #1. dd1-dd2 (entries in the range dd1 and dd2) 
    #2. -dd (last 'dd' log entries) 
    #3. dd (first 'dd' log entries)
    # where dd is decimal digit 
    if ($range !~ /^(\d+)(-)(\d+)$|^(-)(\d+)$|^(\d+)$/)
    {
        fail(sprintf(ERROR_INVALID_RANGE_EXPR, $range),7);
    }
    return RANGEX if ($range =~ /^(\d+)(-)(\d+)$/);
    return LASTX  if ($range =~ /^(-)(\d+)$/);
    return FIRSTX if ($range =~ /^(\d+)$/);
}

###############################################################################################################
# FUNCTIONNAME : ValidateFilter
#
# PURPOSE      : Validates the filter, entered bu user for the option -filter
#                Format of the filter is <EventType(OPER)EventValue>. It validates the filter as follows
#                   1. Checks whether EventType is proper one.
#                   2. Operator is valid one, some valid operators may not valid for some EventTypes
#                   3. Finally EventValue, For some EventTypes, EventValue should be one of the set of values 
#                      Eg. For EventType is type, EventValues should be one of (ERROR, WARNING, INFORMATION ..).
#
# INPUTS:         $DateTime - DateTime in the format mm/dd/yyyy:hh:mm
#                 $timezone - Current gmt time zone
#                 $EventType - (OUT) This function initializes EventType
#                 $oper - (OUT) This function initializes Operator
#                 $EventValue - (OUT) This function initializes EventValue
#
# RETURN VALUES : Returns  0 for SUCCESS 
#                 -1 for FAILURE
###############################################################################################################
sub ValidateFilter
{
    my ($wmi, $filter,$type, $operator, $value, $filterdate, $datefilters) = @_;
    my ($oper, $EventType, $EventValue, $pos, @operators, @ValidTypes);
    #valid operators are >=, =<, >, <, =, !
    @operators = ( ">=", "<=", ">", "<", "=", "!");

    #Valid Event Types are DATETIME, TYPE, USER, COMPUTER, CATEGORY, ID
    #make the array of valid event types
    @ValidTypes = ("DATETIME", "TYPE",  "COMPUTER", "CATEGORY", "ID", "SOURCE");

    $pos = -1;

    for my $oper_val (@operators)
    {
        $pos = index($filter, $oper_val, $pos);
        if ($pos != -1) 
        {
            $oper = $oper_val;
            last;
        }
    }

    if($pos == -1) 
    {
        printf STDERR (ERROR_INVALID_FILTER_EXPR, $filter);
		$errorcode = 7;
        return -1;
    }

    #Get the Event Type from the filter string
    $EventType = substr($filter, 0, $pos);
        
    #check whether $EventType is one of the entry in the array @ValidTypes
    my $found = 0;
    for my $type (@ValidTypes)
    {
        if($type =~ /^$EventType$/i)
        {
            $found = 1;
            last;
        }
    }

    if (!$found) 
    {
        printf STDERR (ERROR_INVALID_FILTER_TYPE,$EventType);
		$errorcode = 7;
        return -1;
    }

    #Get the Event Value from the filter string
    my $str = substr($filter, $pos+1, 1);

    $pos++ if (($str eq "=") || ($str eq "<"));
    $EventValue = substr($filter, $pos+1);

    #The operators >=, =<, <, > are applicable only when the type is ID.
    #other types, valid operators are either = or !
    if($EventType !~ /^ID$/i && $EventType !~ /^DATETIME$/i)
    {
        if ($oper ne "=" && $oper ne "!") 
        {
             printf STDERR (ERROR_INVALID_FILTER_TYPE, $filter);
	    	 $errorcode = 7;
             return -1;
        }
    }

    #check whether the $EventValue is valid for some event types
    SWITCH: 
    {
        if ($EventType =~ /^TYPE$/i)
        {
            if ( $EventValue !~ (/^ERROR$|^INFORMATION$|^WARNING$|^SUCCESSAUDIT$|^FAILUREAUDIT$/i))
            {
                printf STDERR (ERROR_INVALID_FILTER_TYPE, $filter);
				$errorcode = 7;
                return -1;
            }
            last SWITCH;
        }

        if ($EventType =~ /^ID$/i)
        {
            if ($EventValue !~ /^\d+$/)
            {
                printf STDERR ERROR_INVALID_FILTER_TYPE, $EventValue;
				$errorcode = 7;
                return -1;
            }
            last SWITCH;
        }

#		if ($EventType =~ /^USER$/i)
#		{
#			printf( "Event Value is $EventValue \n" );
#           if ($EventValue =~ /^ANONYMOUS LOGON$|^BATCH$|^DIALUP$|^INTERACTIVE$|^SERVICE$|^SYSTEM$|^TERMINAL SERVER USER$|^PROXY$|^ENTERPRISE DOMAIN CONTROLLERS$|^RESTRICTED$|^SELF$/i)
#            {
#                $EventValue = "NT AUTHORITY\\\\$EventValue";
#            }
#            elsif ($EventValue =~ /^Administrators$|^Backup Operators$|^Guests$|^Power Users$|^Replicator$|^Users$/i)
#            {
#                $EventValue = "BUILTIN\\\\$EventValue";
#            }
#			elsif($EventValue !~ /^Everyone$|^CREATOR OWNER$|^CREATOR GROUP$/i)
#			{
#				$EventValue = Win32::NodeName . "\\\\$EventValue";
#			}
#		}
        #date pattern mm/dd/yyyy:hh:mm
        if ($EventType =~ /^DATETIME$/i)
        {
			
			push @$datefilters, $filter;
			$$filterdate = TRUE;
            my $wmidatetime = $wmi->ConvertToWmiDateFormat($EventValue);
            if(!defined($wmidatetime))
            {
                printf STDERR (ERROR_INVALID_FILTER_TYPE,$filter);
				$errorcode = 7;
                return -1;
            }
#            $EventValue = $wmidatetime;
            last SWITCH;
        }
        my $nothing = 1;
    } #end of SWITCH

    #filter is valid string, send back the values of $EventType, $oper and $EventValue to main
    $$type = $EventType;
    $$operator = $oper;
    $$value = $EventValue;
    return 0;
}

##########################################################################################
# FUNCTION NAME     : FormFilterString
#
# PURPOSE           : Given the Eventtype, operator and eventvalue as input parameters, 
#                     this function forms a filter string.
#
# INPUT PARAMETERS  : PARAMETER        DESCRIPTION                         PASS BY 
#                                                                          VALUE/REF
#                    ----------------------------------------------------------------
#                      $EventType      event type                           VALUE
#                      $Operator       operator                             VALUE
#                      $EventValue     event value                          VALUE
#########################################################################################
sub FormFilterString()
{
    my ($EventType, $Operator, $EventValue) = @_;
    my $sFilterCond;
    
    $EventType = "EventCode" if ($EventType =~ /^ID$/i);
    $EventType = "SourceName" if ($EventType =~ /^SOURCE$/i);
    $EventType = "ComputerName" if ($EventType =~ /^COMPUTER$/i);
#    $EventType = "TimeGenerated" if ($EventType =~ /^DATETIME$/i);
    $EventType = "User" if ($EventType =~ /^USER$/i);

    $Operator = "!=" if ($Operator eq "!");
    if ($EventType =~ /^CATEGORY$/i)
    {
		$EventType = "CategoryString";
		if ( $Operator eq "=" )
		{
			$sFilterCond = $EventType . ">=" . "\"" . $EventValue . "\"" . " AND ";
			my $chr = substr($EventValue,-1,1);
			$chr++;
			substr($EventValue,-1,1) = $chr;
			$sFilterCond = $sFilterCond . $EventType . "<" . "\"" . $EventValue . "\"";
		}
		elsif( $Operator eq "!=" )
		{
			$sFilterCond = "(" . $EventType . "<" . "\"" . $EventValue . "\"" . " OR ";
			my $chr = substr($EventValue,-1,1);
			$chr++;
			substr($EventValue,-1,1) = $chr;
			$sFilterCond = $sFilterCond . $EventType . ">" . "\"" . $EventValue . "\"" . ")";
		}
    }
    else 
    {
		
        $sFilterCond = $EventType . $Operator . "\"" . $EventValue . "\"";
    }
    return (defined $sFilterCond) ? $sFilterCond : undef;
}

#**********************************************************************************
# Name          : SubstitutePatterns    
#
# Purpose       : This function nulltifies the speicial interpretation of the 
#                 metacharacters except for *.
#
# Input         : log name.
#
# Return  Value : None
#**********************************************************************************
sub SubstitutePatterns()
{
    my ($name) = @_;
    $$name =~ s/\\/\\\\/g; $$name =~ s/\?/\\\?/g; $$name =~ s/\./\\\./g; 
    $$name =~ s/\$/\\\$/g; $$name =~ s/\^/\\\^/g; $$name =~ s/\(/\\\(/g; 
    $$name =~ s/\[/\\\[/g; $$name =~ s/\)/\\\)/g; $$name =~ s/\|/\\\|/g;
    $$name =~ s/\+/\\\+/g; $$name =~ s/\*/.\*/g; 
}

#**********************************************************************************
# Name          : CalculateRangeLimits
#
# Purpose       : Based on the rangetype and the available totalrecords, this function
#                 calculates the records range that is of interest to the user. 
#
# Input         : $rangeType, $totalcount, $range, $being, $end
#
# Return  Value : None
#**********************************************************************************
sub CalculateRangeLimits()
{
    my ($rangeType, $totalcount, $begin, $end, $range, $filterflag) = @_;

    SWITCH:
    {
		# If First X record are needed.
		if( $rangeType == FIRSTX )
		{
			if( $filterflag  )
			{
				$$begin = 1;
				$$end = ( $range > $totalcount ) ? $totalcount : $range;
#				$$begin = ( ( $totalcount - $range  ) < 0 ) ? 1 : ( $totalcount - $range + 1);
#				$$end = $totalcount;
			}
			else
			{
				$$begin = ( ( $totalcount - $range  ) < 0 ) ? 1 : ( $totalcount - $range + 1);
				$$end = $totalcount;
#				$$begin = 1;
#				$$end = ( $range > $totalcount ) ? $totalcount : $range;
			}
			last SWITCH;
		}

		# If the Last X records are required.
		if( $rangeType == LASTX )
		{
			$range =~ /-/;
			my $postmatch = $';
			if( $filterflag )
			{
				$$begin = ( ( $totalcount - $postmatch  < 0 ) ) ? 1 : ( $totalcount - $postmatch + 1 );
				$$end = $totalcount;
#				$$begin = 1;
#				$$end   = ( $postmatch > $totalcount ) ? $totalcount : $postmatch;
			}
			else
			{
				$$begin = 1;
				$$end   = ( $postmatch > $totalcount ) ? $totalcount : $postmatch;
#				$$begin = ( ( $totalcount - $postmatch  < 0 ) ) ? 1 : ( $totalcount - $postmatch + 1 );
#				$$end = $totalcount;
			}
			last SWITCH;
		}

        # If the x records in the given range are needed.
        if ($rangeType == RANGEX)
        {
            $range =~ /-/;
			if( $filterflag )
			{
				$$begin = $`;
				$$end = $';
				if ($$end < $$begin) 
				{
					my $temp = $$begin;
					$$begin = $$end;
					$$end = $temp;
				}
				$$end = $totalcount if( $$end > $totalcount );

#				$$begin = $totalcount - $' + 1;
#				$$end = $totalcount - $`+ 1;
			}
			else
			{
				$$begin = $totalcount - $' + 1;
				$$end = $totalcount - $`+ 1;

#				$$begin = $`;
#				$$end = $';
#				if ($$end < $$begin) 
#				{
#					my $temp = $$begin;
#					$$begin = $$end;
#					$$end = $temp;
#				}
#				$$end = $totalcount if( $$end > $totalcount );
			}

            last SWITCH;
        }
        # If range option is not specified.
        $$begin = 1;
        $$end = $totalcount;
    }
}

# ****************************************************************************
# Function Name		: RemoveDuplicates
#
# Purpose			: Removes the duplicate elements from array
#
# Inputs			: Array
#
# Return Value(s)	: Array 
# ****************************************************************************
sub RemoveDuplicates
{
	my %hash;
	map { $hash{ lc( $_ ) } = 1; } @{ $_[ 0 ] };
	return keys( %hash );
} # End of sub RemoveDuplicates

######################################################################################
#   FUNCTIONNAME    : usage
#
#   PURPOSE         : Prints the usage and exits the program
#
#   INPUTS          : None
#
#   RETURN VALUES   : None
#
######################################################################################
sub usage
{
print <<EOF;

EventQuery.pl [-s machine [-u user [-p password]]]
              [-filter filter]
              [-format format]
              [-range range] 
              [-verbose]
              logname...

Description:
      Enables an administrator to list the events and event
      properties from one or more event logs.

Parameter List:
      -s       machine        Specifies a remote system to connect to.

      -u       user           Specifies a user name (user context) to run the
                              script commands under.

      -p       password       Specifies the user password.

      -filter  filter         Specifies the types of events to filter in or
                              out of the query.

      -format  format         Specifies the display/output format to use.
                              Valid formats are: TABLE, LIST or CSV.

      -range   range          Specifies a range of events to be listed.

      -verbose                Display detailed/verbose event property 
                              information.

      default                 Specifies the log(s) to query event list for.

Note:
1.  Following are the valid filters that are applicable.

  FilterName     Valid Operators                   Valid Value types
  ----------     ---------------                   -------------------------
  DATETIME       '<', '>', '<=', '>=', '!', '='    Date:Time**
  TYPE           '!', '='                          ERROR | INFORMATION |
                                                   WARNING | SUCCESSAUDIT |
                                                   FAILUREAUDIT
  COMPUTER       '!', '='                          String
  SOURCE         '!', '='                          String
  CATEGORY       '!', '='                          String
  ID             '<', '>', '<=', '>=', '!', '='    Non-negative integer

  *  - Supports Wildcard * 
  ** - Valid format for date:time is "mm/dd/yy[:hh:mm:ss[pm|am]]"

2.  If more than one filter is specified then a logical AND is assumed.

Examples:   
       EventQuery.pl -s machine -u user -p password -filter TYPE=ERROR 
                     -format TABLE -verbose *
       EventQuery.pl -s machine -u user -p "" -filter TYPE=ERROR 
                     -format TABLE -verbose *
       EventQuery.pl -range 10-200 Application System
       EventQuery.pl -filter "ID<=12" -format TABLE -verbose Application
       EventQuery.pl -filter "DATETIME>01/23/2000:10:10:14AM" System
       EventQuery.pl -filter ID=100 -filter CATEGORY=SVC
                     -filter SOURCE=MSDTC Application 
       EventQuery.pl -filter TYPE=SUCCESSAUDIT -filter COMPUTER=ALPHA
                     System Application
       EventQuery.pl *

EOF
exit(0);
}
###############################################################################
#                            End of the Script                                #
###############################################################################
