#
# Copyright (c) 2004, 2007, Oracle. All rights reserved.  
#
#    NAME
#      conditionContext.pl
#
#    DESCRIPTION
#      This script contains generic functions for host-metric-collection scripts, like getConditionContext etc
#       
#    MODIFIED    (MM/DD/YY)
#      sreddy     05/08/07 - Backport sreddy_bug-5139454 from main
#      sreddy     05/02/07 - fix Windows Platform Handling
#      sreddy     05/02/07 - stop handling _ as a regexp character
#                            Fix for bug#5917834 removed _ as regexp character
#                            from the EM OMS/Agent framework as well
#      sacgoyal   01/24/05 - .? changed to . for regexp meaning of _ wild 
#                            character 
#      sreddy     10/05/04 - 
#      sreddy     10/04/04 - add expandPath routine for UNIX systems 
#      sacgoyal   07/22/04 - Creation for Agent Condition Context, Enterprise Manager, 10.2
#

use strict;

#--------------getConditionContext subroutine--------------
#
# This takes value of following agentConditionContext environment variables-
# EMAgentConditionContextNum  [n]
# EMAgentConditionContext0    [column_name1;operator1;criticalThreshold1;warningThreshold1;key1,operator,key_value01;key2,operator,key_value02]
# EMAgentConditionContext1    [column_name1;operator1;criticalThreshold1;warningThreshold1;key1,operator,key_value11;key2,operator,key_value12]  
# ...so on
# +
# It also takes a list of names as input that should be interpreted
# as perl regular expressions, not sql expressions
#
# Returns an aref of hrefs, where each href has the following keys
#  conditionColumnName - value is scalar
#  conditionOperator - value is scalar
#  criticalThreshold - value is scalar
#  warningThreshold - value is scalar
#  keyColumnAref - value is an aref of hrefs, where each href has the keys below
#     keyName - value is scalar
#     keyOperator - value is scalar
#     keyValueToReturn - value is scalar
#     keyValueToMatch - value is scalar
#   
# Where INTERPRETED_key_values are :->
#       - URLdecoded, (ie. %25 %26 %3b converted to % & ; respectively)
#       - PERL special characters are escaped
#       - And if operator is "LIKE" then "% and _" are interpreted to
#         their special meaning of ".* and ."
#
##############################################################################

sub getConditionContext ()
{
  my @conditions = ();

  if (!defined($ENV{EMAgentConditionContextNum}) ||
      $ENV{EMAgentConditionContextNum} == 0)
  {
    EMD_PERL_DEBUG("getConditionContext: No user specified conditions passed in");
    return \@conditions;
  }

  my @perlRegExpColumns = @_;
  EMD_PERL_DEBUG("getConditionContext: perlRegExpColumns = @perlRegExpColumns");

  
  for (my $i = 0; $i < $ENV{EMAgentConditionContextNum}; $i++)
  {

    my $envName = "EMAgentConditionContext" . $i;
    EMD_PERL_DEBUG("getConditionContext: User specified condition context is $ENV{$envName}");
    next if (!defined($ENV{$envName}) && EMD_PERL_ERROR("$envName not set\n"));
    my @conditionInfo = split (';', $ENV{$envName});
    my @currentKeys = ();

    for (my $j = 4; $j <= $#conditionInfo; $j++)
    {
      my @keyColumnInfo = split ('&', $conditionInfo[$j]);
      my $keyValueToReturn = &decodeUrl($keyColumnInfo[2]);
      my $keyValueToMatch = $keyValueToReturn;
      my $isPerlRegExpColumn = 0;

      foreach my $perlRegExpColumn (@perlRegExpColumns)
      {
        if ($perlRegExpColumn eq $keyColumnInfo[0])
        {
          $isPerlRegExpColumn = 1;
          last;
        }
      }

      if (!$isPerlRegExpColumn)
      {
        # Need to support SQL reg expressions for this column

        #####################################################
        # quotemeta() escapes all PERL's special characters.
        # sql specific conversions include the following
        #        % => \%
        #        \ => \\
        #####################################################

        $keyValueToMatch = quotemeta($keyValueToMatch);

        if (uc($keyColumnInfo[1]) eq "1" )  #LIKE is defined as 1
        { 
          $keyValueToMatch = &interpretPattern($keyValueToMatch);
        }
      }

      if (!defined($keyValueToMatch) || $keyValueToMatch eq "")
      {
        $keyValueToMatch = ".*";
      }

      my %currentKey = ("keyName" => $keyColumnInfo[0],
                        "keyOperator" => $keyColumnInfo[1],
                        "keyValueToReturn" => $keyValueToReturn,
                        "keyValueToMatch" => $keyValueToMatch);

      push(@currentKeys, \%currentKey); 
    }
    my %currentCondition = ("conditionColumnName" => $conditionInfo[0],
                             "conditionOperator" => $conditionInfo[1],
                             "criticalThreshold" => $conditionInfo[2],
                             "warningThreshold" => $conditionInfo[3],
                             "keyColumnAref" => \@currentKeys);

    push @conditions, \%currentCondition;
  }
  return \@conditions;
}

#############################################################################
#---------------------sub interpretPattern-----------------------------------
#
#  It does following conversions
#        empty string => .*
#        \\\% ==> %                   # match literal %
#        \\\\ ==> \\                  # match literal \
#        \\[?] ==> \\[?]
#        \% ==> .*                    # match 0 or more characters
#                              
#############################################################################

sub interpretPattern {
  my $pattern1 = shift @_;
  my $intpString = "";
  my @pattern = split("",$pattern1);
  
  if($pattern1 eq "")
  {
    $intpString = ".*"; #interpreting Empty string to .*
  }
  else 
  {
    while(@pattern) 
    {
      my $char1 = shift @pattern;
      if( $char1 eq "\\") {
        my $char2 = shift @pattern;
        if( $char2 eq "%") {
          $intpString = $intpString.".*";      #converting \% to .*
        } elsif( $char2 eq "\\" ) {
          my $char3 = shift @pattern;
          if( $char3 eq "\\" ) {
            my $char4 = shift @pattern;
            if( $char4 eq "%" ) {
              $intpString = $intpString."%";  #converting  \\\% to %
            } elsif($char4 eq "\\") {
              $intpString = $intpString."\\\\";    #converting \\\\ to \\
            } else {
              $intpString = $intpString."\\\\\\$char4";
            }
          } else {
            $intpString = $intpString."\\\\$char3";
          }
        } else {
          $intpString = $intpString."\\$char2";
        }
      } else {
        $intpString = $intpString."$char1";
      }    
    } #end while
  }
  return $intpString; 
}#end sub

#############################################################################
#---------------------sub decodeUrl-----------------------------------
#  
# It does following conversions-> [%25 -> %] [ %26 -> &]  [%3b -> ;]                             
#############################################################################

sub decodeUrl 
{
  my $decodedUrl = shift @_;
  
  $decodedUrl =~ s/%25/%/g;  #converting %25 to %
  $decodedUrl =~ s/%26/&/g;  #converting %26 to &
  $decodedUrl =~ s/%3b/;/g;  #converting %3b to ;
  return $decodedUrl; 
}

##########################################################################
# ------------- expandPath subroutine ----------------------------------
# Inputs:
#   $pathToExpand - path pattern to be expanded on the target host
#                 - should be the first parameter in the list passed
#                 - path can contain SQL regexp characters (%, _)
#                 - % matches 0 or more chacters of the given directory content
#                 - _ matches any one character of the directory content
#                 - (%, _, \) can be escaped with \
# Outputs:
#   Returns an array of paths matching $pathToExpand that exist on the host
#
# Note:  This routine is implemented for UNIX systems at this point.
##########################################################################

sub expandPath
{
  my ($pathToExpand) = shift @_;

  return expandWindowsPath($pathToExpand)
    if (($^O eq "MSWin32") || ($^O eq "Windows_NT"));

  my @expandedPaths = ('');
  my $fpSep = '/';
  my $skipDirEntries = '(\.)|(\.\.)';

#  EMD_PERL_DEBUG ("expandPath: pathToExpand = $pathToExpand");

  my @splitPath = splitIntoTokens ($pathToExpand , $fpSep);

#  EMD_PERL_DEBUG ("expandPath: #splitPath = $#splitPath");

  for (my $i = 0; $i <= $#splitPath; $i++)
  {
    my $subPath = $splitPath[$i];
    my @curExpandedPaths = @expandedPaths;

#    EMD_PERL_DEBUG ("expandPath: subPath = $subPath");

    next if ($subPath eq "");
    @expandedPaths = ();

    if ($subPath eq $fpSep)
    {
      foreach my $curExpandedPath (@curExpandedPaths)
      {
        my $newPath = $curExpandedPath . $subPath;
        push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
      }
      next;
    }

    my $filePattern = $subPath;
    $filePattern = quotemeta($subPath);
    $filePattern = interpretPattern($filePattern);

#    EMD_PERL_DEBUG ("expandPath: filePattern = $filePattern");
    if ($subPath eq $filePattern)
    {
      foreach my $curExpandedPath (@curExpandedPaths)
      {
        my $newPath = $curExpandedPath . $subPath;
        push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
      }
      next;
    }

    foreach my $curExpandedPath (@curExpandedPaths)
    {
      opendir(DH, $curExpandedPath);
      my @present_files = readdir(DH); 
      closedir(DH);
      foreach my $file (@present_files)
      {
#        EMD_PERL_DEBUG ("expandPath: file = $file");
        next if $file =~ /^$skipDirEntries$/;
        if ($file =~ /^$filePattern$/)
        {
          my $newPath = $curExpandedPath . $file;
          push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
        }
      }
    }
  }
#  foreach my $path (@expandedPaths)
#  {
#    EMD_PERL_DEBUG("expanded_paths: $pathToExpand: $path\n");
#  }
  return @expandedPaths;
}

sub expandWindowsPath
{
  my ($pathToExpand) = shift @_;
  my @expandedPaths = ('');
  my $fpSep = '\\';
  my $skipDirEntries = '(\.)|(\.\.)';

#  EMD_PERL_DEBUG ("expandWindowsPath: pathToExpand = $pathToExpand");

  if ($pathToExpand =~ /^\\\\/)
  {
    EMD_PERL_ERROR("Invalid Path[0]: UNC path expansion not supported: $pathToExpand");
    return @expandedPaths;
  }

  if ($pathToExpand =~ /^\\/)
  {
    EMD_PERL_ERROR("Invalid Path[1]: Path must start with a valid drive: $pathToExpand");
    return @expandedPaths;
  }

  my @splitPath = splitIntoTokens ($pathToExpand , $fpSep);

  if ($#splitPath < 0)
  {
    EMD_PERL_ERROR("Invalid Path[2]: $pathToExpand");
    return @expandedPaths;
  }


  if ($splitPath[0] eq "" ||
      $splitPath[0] eq $fpSep)
  {
    EMD_PERL_ERROR("Invalid Path[3]: $pathToExpand");
    return @expandedPaths; #this should never happen
  }

#  EMD_PERL_DEBUG ("expandWindowsPath: #splitPath = $#splitPath");

  for (my $i = 0; $i <= $#splitPath; $i++)
  {
    my $subPath = $splitPath[$i];
    my @curExpandedPaths = @expandedPaths;

#    EMD_PERL_DEBUG ("expandWindowsPath: subPath = $subPath");

    next if ($subPath eq "");
    @expandedPaths = ();

    if ($subPath eq $fpSep)
    {
      foreach my $curExpandedPath (@curExpandedPaths)
      {
        my $newPath = $curExpandedPath . $subPath;
        push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
      }
      next;
    }

    my $filePattern = $subPath;
    $filePattern = quotemeta($subPath);
    $filePattern = interpretPattern($filePattern);

#    EMD_PERL_DEBUG ("expandWindowsPath: filePattern = $filePattern");
    if ($subPath eq $filePattern)
    {
      if ($i == 0)
      {
        my $drive = $subPath . '\\';
#        EMD_PERL_DEBUG ("expandPath: matching Drive: $drive") and
          push (@expandedPaths, $drive) 
            if (-d $drive);
      }
      else
      {
        foreach my $curExpandedPath (@curExpandedPaths)
        {
          my $newPath = $curExpandedPath . $subPath;
          push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
        }
      }
      next;
    }

    foreach my $curExpandedPath (@curExpandedPaths)
    {
      if ($i == 0)
      {
        my @drives = getWindowsDrives();
        foreach my $drive (@drives)
        {
#          EMD_PERL_DEBUG ("drive: $drive, filePattern: $filePattern");
          if ($drive =~ /^$filePattern$/)
          {
            my $newPath = $drive . '\\';
#            EMD_PERL_DEBUG ("matched drive: $drive") and
              push (@expandedPaths, $drive) 
                if (-d $newPath);
          }
        }
      }
      else
      {
        opendir(DH, $curExpandedPath);
        my @present_files = readdir(DH); 
        closedir(DH);
        foreach my $file (@present_files)
        {
#          EMD_PERL_DEBUG ("expandWindowsPath: file = $file");
          next if $file =~ /^$skipDirEntries$/;
          if ($file =~ /^$filePattern$/)
          {
            my $newPath = $curExpandedPath . $file;
            push (@expandedPaths, $newPath) if ($newPath eq "" || -e $newPath);
          }
        }
      }
    }
  }
#  foreach my $path (@expandedPaths)
#  {
#    EMD_PERL_DEBUG("expanded_paths: $pathToExpand: $path\n");
#  }
  return @expandedPaths;
}

sub splitIntoTokens($$)
{
  my ($path, $matchCharacter) = @_;
  my @result = ();
  my $word="";
  my $pending = 1;
  my $escapedLiteralPercent = '\\%';
  my $escapedLiteralBackslash = '\\\\';
  my $literalBackslash = '\\';
  my $literalPercent = '%';
  my $encodeLiteralPercent = 'P1E2R3C4E5N6T7';
  my $encodedLiteralBackslash = 'B1A2C3K4S5L6A7S8H9';
  my $savedPath = $path;
  my $qEscapedLiteralPercent = quotemeta($escapedLiteralPercent);
  my $qEscapedLiteralBackslash = quotemeta($escapedLiteralBackslash);

  $path =~ s/$qEscapedLiteralBackslash/$encodedLiteralBackslash/g;
  $path =~ s/$qEscapedLiteralPercent/$encodeLiteralPercent/g;
  $path =~ s/$encodedLiteralBackslash/$literalBackslash/g;

  my @letters = split ("", $path);
 
  if ($#letters == -1)
  {
    return @result;
  }

  for (my $i = 0; $i <= $#letters; $i++)
  {
    if ($letters[$i] eq $matchCharacter)
    {
      $word =~ s/$encodeLiteralPercent/$escapedLiteralPercent/g;
      push @result, $word;
      push @result, $matchCharacter;
      $word = "";
      $pending = 0;
      next;
    }
    $word .= $letters[$i];
    $pending = 1;
  }

  if ($pending)
  {
    $word =~ s/$encodeLiteralPercent/$escapedLiteralPercent/g;
    push @result, $word;
  }

  my $buffer="";
  foreach (@result)
  {
    $buffer .= "$_ ";
  }
  EMD_PERL_DEBUG("splitIntoTokens[$savedPath]: $buffer");
  return @result;
}

my $initWindowsDrives=0;
my @windowsDrives=();

sub getWindowsDrives()
{
  return @windowsDrives
    if ($initWindowsDrives == 1);

  $initWindowsDrives=1;


  if (($^O eq "MSWin32") || ($^O eq "Windows_NT"))
  {
    my $nmupm = $ENV{ORACLE_HOME}.'\\bin\\nmupm.exe';
    if ( -f $nmupm )
    {
      $ENV{EM_MONITOR_ALL_DISKS}="TRUE";
      my @results = `$nmupm filesystems`;
      if ($#results >= 0)
      {
        foreach my $result (@results)
        {
#          EMD_PERL_DEBUG("$result");
          if ($result =~ /^em_result=/)
          {
            my @parts = split('=',$result);
            if ($#parts == 1)
            {
              my @subParts = split(':',$parts[1]);
              if ($#subParts == 1)
              {
                my $drive = $subParts[0];
                push @windowsDrives, "$drive:"
#                  and EMD_PERL_DEBUG("add $drive:")
                    if ( -d "$drive:\\" );
              }
            }
          }
        }
      }
    }
    else
    {
      EMD_PERL_ERROR("$nmupm does not exist");
    }
  }
  return @windowsDrives;
}

sub isRegexpValid($)
{
  my ($path) = @_;
  my $escapedLiteralPercent = '\\%';
  my $escapedLiteralBackslash = '\\\\';
  my $literalBackslash = '\\';
  my $qEscapedLiteralPercent = quotemeta($escapedLiteralPercent);
  my $qEscapedLiteralBackslash = quotemeta($escapedLiteralBackslash);
  my $qLiteralBackSlash = quotemeta($literalBackslash);

  my $encodeLiteralPercent = 'P1E2R3C4E5N6T7';
  my $encodedLiteralBackslash = 'B1A2C3K4S5L6A7S8H9';

  # replace '\\\\' with 'B1A2C3K4S5L6A7S8H9'
  $path =~ s/$qEscapedLiteralBackslash/$encodedLiteralBackslash/g;

  # replace '\\%' with 'P1E2R3C4E5N6T7'
  $path =~ s/$qEscapedLiteralPercent/$encodeLiteralPercent/g;

  if ($path =~ /$qLiteralBackSlash/)
  {
    # It is an error to have single backslash in the path at this point
    return 0;
  }
  return 1;
}
1;
