# 
# $Header: rfsGetFileList.pl 06-feb-2006.12:38:10 sreddy Exp $
#
# rfsGetFileList.pl
# 
# Copyright (c) 2004, 2006, Oracle. All rights reserved.  
#
#    NAME
#      rfsGetFileList.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      Perl script to get file list for Remote File Selector Page
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    sreddy      02/06/06 - Backport sreddy_bug-4968435 from main 
#    ajere       12/10/05 - XbranchMerge ajere_bug-4779290 from main 
#    ajere       04/14/05 - Add interpretPattern subroutine 
#    ajere       01/31/05 - Windows: Get contents starting from My Computer 
#    ajere       01/05/05 - Add timeout 
#    ajere       10/05/04 - Support search patterns *, % and _
#    ajere       07/02/04 - Creation
# 

require "emd_common.pl";
use strict;

#Global variables
my $windowsOsCmd  = "cmd";
my $winRoot       = "MC";
my $unixOsCmd     = "/bin/sh";
my $timeout       = 3;

#Subroutine declarations
sub getFileList();
sub getFileSeparator($);
sub getPermissions($);
sub readOpenedDir($$$);
sub getFileDetails($$$$\@);
sub interpretPattern($);

###################################################
# getFileList()
# Get the file list in a specified directory
# Last line contains result status :
# 0 - OK
# 1 - There are more files than maxFiles returned
# 2 - Operation timed out
###################################################
sub getFileList() {
  my $osCmd        =  $ARGV[0];
  my $dirName       = $ARGV[1];
  my $fetchFileType = $ARGV[2];
  my $maxFiles      = $ARGV[3];
  my $searchPattern = $ARGV[4];
  
  #If the directory is null, return
  if ($dirName eq '') {
    EMD_PERL_ERROR("[rfsGetFileList.pl] Input directory is empty!\n");
    exit 1;
  }
  
  #For Windows OS, execute exec_commonenv.bat if mount_shares.bat
  #and unmount_shares.bat files exist

  if (($osCmd eq $windowsOsCmd) && 
       !defined($ENV{EM_AGENT_COMMONENV_BAT_EXEC}) &&
       (-e "$ENV{EMDROOT}/bin/mount_shares.bat") &&
       (-e "$ENV{EMDROOT}/bin/unmount_shares.bat"))
  {
    #save STDERR into STDERZR_ORIG. Upon failure, skip exec_commonenv.bat step
    if (open (STDERR_ORIG, ">&STDERR"))
    {
      #send STDERR to null device. Upon failure, skip exec_commonenv.bat step
      if (open (STDERR, ">/dev/null"))
      {
        my $beginToken="BEGIN_REMOTE_OP_RESULT_SET_AFTER_COMMONENV_BAT";
        my $endToken="END_REMOTE_OP_RESULT_SET_AFTER_COMMONENV_BAT";
        my $beginTokenFound=0;
        my $line;

        #Batch file exec_commonenv.bat sets EM_AGENT_COMMONENV_BAT_EXEC and
        #call this script again after network shares from mount_shares.bat
        #are mounted. This block won't get executed then and the array 
        #scriptResult will contain actual output within start and end tokens.

        my @scriptResult=`$ENV{EMDROOT}\\bin\\exec_commonenv.bat rfsGetFileList.pl @ARGV`;

        #restore STDERR to STDERR_ORIG
        close(STDERR);
        open (STDERR, ">&STDERR_ORIG")
          or EMD_PERL_ERROR("rfsGetFileList.pl: Failed to restore STDERR"); 

        #Note: There is no good reason why the above open will fail. Upon failure
        #there is no need to exit since result will be sent on STDOUT anyways
 
        #Extract actual output of this script
        chomp @scriptResult;
        foreach $line (@scriptResult)
        {
          last if ($line =~ $endToken);
          print "$line\n" if ($beginTokenFound eq 1);
          $beginTokenFound=1 if ($line =~ $beginToken);
        }
        exit 0;
      }
      else
      {
        EMD_PERL_ERROR("rfsGetFileList.pl: Failed to redirect STDERR to null device");
      }
    }
    else
    {
      EMD_PERL_ERROR("rfsGetFileList.pl: Failed to save STDERR into STDERR_ORIG");
    }
  }

  #Defaults
  if($maxFiles eq '') {
    $maxFiles = 500;
  }
  
  #Variables
  my $dirHandle;
  my @allFiles       = ();
  my $fileSeparator  = "";
  my @outFileList    = ();
  
  #For Windows OS: Get available drives, if started from My Computer
  if (($osCmd eq $windowsOsCmd) && ($dirName eq $winRoot))
  {
    my $line           = "";
    my $drive          = "";
    my $driveSize      = "";
    my $oneRow         = "";
    my $tokenSeparator = "\t";
    my @driveList      = ();

    #Bug 4779290 fix - Following will return all drives, including network drives
    $ENV{EM_MONITOR_ALL_DISKS} = "TRUE";
    my $nmupmOut  = `$ENV{EMDROOT}\\bin\\nmupm filesystems`;
    #print "nmupm drive list = $nmupmOut\n";

    #Clean up invalid entries
    @driveList = cleanUpDriveListNT($nmupmOut);
    #print "cleaned up drive list = @driveList\n";
    #End of bug 4779290 fix

    for $line (@driveList) {
      $drive = (split /=/, (split /\|/, $line)[0])[1];
      #Hard drive size in KB
      $driveSize = (split /\|/, $line)[1]*1024;
      push(@outFileList, join($tokenSeparator, ($drive, $driveSize, "\n")));
    }
    return @outFileList;
  }

  #All other Windows directories and all non-Windows directories
  #Append the directory with separator, if not done already
  $fileSeparator = getFileSeparator($osCmd);
  if($dirName !~ /$fileSeparator$/) {
    $dirName .= $fileSeparator;
  }

  #Open the directory for reading
  if (!opendir(dirHandle, $dirName)) { 
    EMD_PERL_ERROR("[rfsGetFileList.pl] Unable to open the directory $dirName for reading!\n");
    exit 1;
  } 
  
  #print "searchPattern = $searchPattern\n";
  
  #Escape non-alphanumeric chars, [A-Za-z_0-9]
  $searchPattern = quotemeta($searchPattern);
  $searchPattern = interpretPattern($searchPattern);

  #print "searchPattern = $searchPattern\n";

  #Read the directory contents
  @allFiles = readOpenedDir($dirHandle, $osCmd, $searchPattern);
  
  #Check for timeout
  if($allFiles[0] =~ /Timed out/) {
   $outFileList[0] = "2\n";

   #Close the directory
   closedir(dirHandle);

   return @outFileList;
  }
  
  #Get detailed info for each file
  @outFileList = getFileDetails($osCmd, $dirName, $fetchFileType, $maxFiles, @allFiles);

  #Close the directory
  closedir(dirHandle);
  
  #print "@allFiles\n";
  #print "@outFileList";

  return @outFileList;
}

#############################################
# getFileSeparator()
# Get the file separator based on the OS -
# / for UNIX flavors and \ for Windows
#############################################
sub getFileSeparator($)
{
  my $fileSeparator = "/";
  if($_[0] eq $windowsOsCmd) {
    $fileSeparator = "\\";
  }
  return $fileSeparator;
}

#############################################
# getPermissions()
# Get the file permissions in a UNIX format
#############################################
sub getPermissions($)
{
  my @permArr = ();

  my $permissions = unpack("B32", pack("N", shift));
  $permissions =~ s/^0+(?=\d)//;   # otherwise we'll get leading zeros

  #Replace 0 by -
  $permissions =~ s/0/-/g;

  #Parse remaining bits
  @permArr = split //, $permissions;
  for(my $i=0; $i< @permArr.length; ++$i) {
    if((($i==0)||($i==3)||($i==6))&&(@permArr[$i]==1)) {
      @permArr[$i] = 'r';
    }
    if((($i==1)||($i==4)||($i==7))&&(@permArr[$i]==1)) {
      @permArr[$i] = 'w';
    }
    if((($i==2)||($i==5)||($i==8))&&(@permArr[$i]==1)) {
      @permArr[$i] = 'x';
    }
  }
  return join "", @permArr;
}

#############################################
# readOpenedDir()
# Get files from opened directory
#############################################
sub readOpenedDir($$$)
{
  my @allFiles      = ();
  my $dirHandle     = $_[0];
  my $osCmd         = $_[1];
  my $searchPattern = $_[2];

  #Timeout mechanism for UNIX only
  if($osCmd eq $unixOsCmd) {
    #UNIX OS
    #Register to receive alarm
    $SIG{ALRM} = sub {

      #Reset alarm
      alarm 0;

      #Break
      die "Timed out"; 
    };

    eval {
      #Activate alarm
      alarm ($timeout);

      #Read contents of the directory
      @allFiles = grep(/$searchPattern/, readdir(dirHandle));

      #Reset alarm
      alarm 0;
    };

    #Opertion timed out!
    if($@ =~ /Timed out/) {
      @allFiles = $@;
    }
  } else {
    #Windows OS
    #Read contents of the directory
    @allFiles = grep(/$searchPattern/, readdir(dirHandle));
  }

  return @allFiles;
}

###########################
# getFileDetails()
# Get all the file details
###########################
sub getFileDetails($$$$\@)
{
  #Input params
  my $osCmd         = $_[0];
  my $dirName       = $_[1];
  my $fetchFileType = $_[2];
  my $maxFiles      = $_[3];
  my ($allFilesRef) = @_[4];
  my @allFiles      = @{$allFilesRef};

  my @outFileList    = ();
  my $oneRow         = "";
  my $fileCount      = 0;
  my @fileDetails    = ();
  my $tokenSeparator = "\t";

  #Table columns
  my $name         = "";
  my $permissions  = "";
  my $owner        = "";
  my $group        = "";
  my $size         = "";
  my $lastModified = "";
  
  for $name (@allFiles) {
    #Skip directories . and ..
    next if(($name eq ".") || ($name eq ".."));

    #Skip based on on fetchFileType
    if($fetchFileType eq "RFS_DIRS") {
      #Skip files
      next if(!(-d $dirName.$name));
    }
  
    #Get all details about this file
    @fileDetails = stat($dirName.$name);

    #Permissions
    if(-d $dirName.$name) {
      $permissions = 'd';
    } 
    else {
      $permissions = '-';
    }

    $permissions .= getPermissions($fileDetails[2] & 0777);

    #Owner
    if($osCmd eq $unixOsCmd) {
      #UNIX OS
      $owner = getpwuid($fileDetails[4]);
    } else {
      #Windows OS
      $owner = $fileDetails[4];
    }
  
    #Group
    if($osCmd eq $unixOsCmd) {
      #UNIX OS
      $group = getgrgid($fileDetails[5]);
    } else {
      #Windows OS
      $group = $fileDetails[5];
    }
  
    #Size
    $size = $fileDetails[7];
  
    #Last modified time (milliseconds)
    $lastModified = $fileDetails[9]*1000;
  
    $oneRow = join($tokenSeparator, ($name, $permissions, $owner, $group, $size, $lastModified, "\n"));
  
    #Store for output
    push(@outFileList, $oneRow);
  
    ++$fileCount;

    #Exit the loop, if maxFiles has been reached
    last if $fileCount >= $maxFiles;
  }
  
  #Last line always tells whether returning with less than maxFiles
  if($fileCount >= $maxFiles) {
    push(@outFileList, "1\n");
  }
  else {
    push(@outFileList, "0\n");
  }
  
  return @outFileList;
}

#############################################################################
#---------------------sub interpretPattern-----------------------------------
#
#  It does following conversions
#        empty string => .*
#        \\\* ==> *    ## added for rfs
#        \\\% ==> %
#        \\\\ ==> \
#        \\_ ==> _
#        \\[!_] ==> \\[!_]
#        \* ==> .*    ## added for rfs
#        \% ==> .*
#        _ ==> .
#############################################################################

sub interpretPattern($)
{
  my $pattern1 = shift @_;
  my $intpString = "";
  my @pattern = split("",$pattern1);
  
  return ".*" if($pattern1 eq "");

  while(@pattern) 
  {
    my $char1 = shift @pattern;
    if($char1 eq "_") 
    {
      $intpString = $intpString.".";   #converting _ to .
    } elsif( $char1 eq "\\") {
      my $char2 = shift @pattern;
      if( $char2 eq "%") {
        $intpString = $intpString.".*";      #converting \% to .*
      } elsif( $char2 eq "*" ) {
        $intpString = $intpString.".*";      #converting \* to .*
      } elsif( $char2 eq "\\" ) {
        my $char3 = shift @pattern;
        if( $char3 eq "_" ) {
          $intpString = $intpString."_";   #converting \\_ to _
        } elsif( $char3 eq "\\" ) {
          my $char4 = shift @pattern;
          if( $char4 eq "%" ) {
            $intpString = $intpString."%";  #converting  \\\% to %
          } elsif($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

##################################################################
# cleanUpDriveListNT()
# Clean up invalid entries received from nmupm filesystems command
##################################################################
sub cleanUpDriveListNT($)
{
  my $nmupmOut = shift @_;
  my @nmupmOutArr = split("\n", $nmupmOut);
  my @driveList   = ();
  my $line        = "";
  my $tokenSeparator = "";
  
  for $line (@nmupmOutArr) {
    chomp($line);
    if($line =~ /em_result/) {
      push(@driveList, join($tokenSeparator, ($line, "\n")));
    }
  } 

  return @driveList;
}

#For Windows OS only
if ($ARGV[0] eq $windowsOsCmd && defined($ENV{EM_AGENT_COMMONENV_BAT_EXEC}))
{
  my @outFileList = getFileList();
  print "@outFileList";
}

return 1;
