#!/usr/bin/perl
#
# SNMPTT v1.0
#
# Copyright 2002 Alex Burger
# alex_b@users.sourceforge.net
# 4/11/2002
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
##############################################################################
#
# http://www.sourceforge.net/projects/snmptt
#
# This script is a snmp trap handler for use with the NET-SNMP / UCD-SNMP 
# snmptrapd program.
#
# The script is called by defining a 'traphandle' in snmptrapd.conf.  
# For example:
#
# traphandle default /sbin/snmptt
# 
# SNMPTRAPD feeds details about the trap to the launched program's standard
# input in the following format (see snmptrapd.conf man page for a complete
# descriptipon)
#
# HOSTNAME: 	The name of the host in question  that  sent the  trap
# IPADDRESS: 	The IP address of the  host  that  sent  the trap
# VARBINDS: 	A  list  of  variable bindings that describe the trap and 
# 		the variables enclosed  in  it.  
#
# See the SNMPTT documentation (readme.html) for more information.
#
##############################################################################
#

$snmptt_version = "v1.0";

sub showversion
{
	print "\nSNMPTT $snmptt_version\n";
	print "(c) 2002 Alex Burger\n";
	print "http://snmptt.sourceforge.net\n\n";
}

##############################################################################
# Process command line arguments

use Getopt::Long;

$version = 0;
$daemon = 0;
$debug = 0;
$debugfile = '';
$dump = 0;
$help = 0;
$time = 0;
$ini = '';

GetOptions 	('version' => \$version, 
		'daemon' => \$daemon,
		'debug:i' => \$debug,
		'debugfile=s' => \$debugfile,
		'dump' => \$dump,
		'help' => \$help,
		'ini=s' => \$ini,
		'time' => \$time);  

if ($version == 1)
{
	&showversion;
	exit(0);
}

if ($help == 1)
{
$USAGE = qq/Usage:
    snmptt [<options>] 
Options:
    --daemon                             Run as a daemon
    --debug=n                            Set debug level (1 or 2)
    --debugfile=filename                 Set debug output file
    --dump                               Dump (display) defined traps
    --help                               Display this message
    --ini=filename                       Set configuration file to load
    --version                            Display author and version information
    --time                               Use to see how long it takes to load and 
                                         process trap file (eg: time snmptt --time)
/;
		
	&showversion;
	print $USAGE;

	exit(0);
}

if ($debug >= 1)
{
  $DEBUGGING = $debug;	
  $debugcmdline = 1;
}
else
{
  $DEBUGGING = 0;	
  $debugcmdline = 0;
}

if ($daemon == 1)
{
  	# $daemon = 1;	
	$daemoncmdline = 1;
}
else
{
 	$daemoncmdline = 0;
}

if ($dump ==1)
{
	$DEBUGGING = 2;
	$debugcmdline = 1;
	&findsnmpttini;
	&loadsnmpttini;
	&loadsnmpttconf;		# Load SNMPTT.CONF file
	print ("\n\n");
	exit(0);
}

if ($time ==1)
{
	&findsnmpttini;
	&loadsnmpttini;
	&loadsnmpttconf;		# Load SNMPTT.CONF file
	exit(0);
}

if ($debugfile ne '') 
{
	$DEBUGGING_FILE = $debugfile;	# commandline overpowers snmptt script
	$debugfilecmdline = 1;
}
else
{
  $debugfilecmdline = 0;
}

##############################################################################
#
# Load config file 
#

&findsnmpttini;
&loadsnmpttini;
		
##############################################################################

use Text::ParseWords;
use POSIX qw(strftime);

$debug_file_used = 0;

my $g_start_time = time();

if ($DEBUGGING >= 1)
{
	if ($DEBUGGING_FILE ne '') 
	{
	  if (open DEBUGFILE, ">>$DEBUGGING_FILE")
	  {
	    select DEBUGFILE;	# change default output to debug file
	    $debug_file_used = 1;
	  }
	  else
	  {
	    warn "could not open debug output file ($!)";
	  }
	}
	
	# print out time
	print "********** SNMPTT $snmptt_version started: ",scalar(localtime($g_start_time))," **********\n\n"
}

if ($syslog_enable == 1 || $syslog_system_enable == 1)
{
  eval 'require Sys::Syslog;';
  if ($@) {
    warn $@;
    print "\nCould not load Perl module Sys::Syslog!  If syslog_system_enable or\n";
    print "syslog_enable is enabled then the Sys::Syslog module is required.  Please\n";
    print "see snmptt.html for system requirements.\n\n";
    die "died";
  }
  require Sys::Syslog;
}

if ($eventlog_system_enable == 1 || $eventlog_enable == 1)
{
  eval 'require Win32::EventLog;';
  if ($@) {
    warn $@;
    print "\nCould not load Perl module Win32::EventLog!  If eventlog_system_enable or\n";
    print "eventlog_enable is enabled, then the Win32::EventLog module is required.  \n";
    print "Please see snmptt.html for system requirements.\n\n";
    die "died";
  }
  require Win32::EventLog;
  
  # Constants not available when using 'require' (!)
  $eventlog_error = 1;		# EVENTLOG_ERROR_TYPE
  $eventlog_warning = 2;		# EVENTLOG_WARNING_TYPE
  $eventlog_information = 4;	# EVENTLOG_INFORMATION_TYPE
}


if ($syslog_system_enable == 1 && $daemon == 1)
{
  syslog_system("SNMPTT $snmptt_version started");
}

if ($eventlog_system_enable == 1 && $daemon == 1)
{
  eventlog_system("SNMPTT $snmptt_version started",0,$eventlog_information);
}

if ($net_snmp_perl_enable == 1)
{
  eval 'require SNMP;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module SNMP!  If net_snmp_perl_enable is\n";
    print "enabled then the SNMP module is required.  Please see snmptt.html\n";
    print "for system requirements.  Note:  SNMPTT uses the Net-SNMP package's\n";
    print "SNMP module, NOT the CPAN Net::SNMP module!\n\n";
    die "died";
  }
  require SNMP;
  if (defined ($mibs_environment))
  {
    $ENV{'MIBS'} = $mibs_environment;
  }
  my $noperlwarning;
  if ($description_mode == 2) {
    $SNMP::save_descriptions = 1;
  }
  &SNMP::initMib();
  
  $SNMP::best_guess = $net_snmp_perl_best_guess;
  $noperlwarning = $SNMP::best_guess;  # Just to get rid of the Perl warning that var used only once
  
  $SNMP::use_long_names = 0;	# Set to 0, otherwise getType may fail when using a long name on 4.2.x
  $noperlwarning = $SNMP::use_long_names;  # Just to get rid of the Perl warning that var used only once
  
  if ($DEBUGGING >= 1)
  {
    print "********** Net-SNMP version $SNMP::VERSION Perl module enabled **********\n\n";
    if (defined ($mibs_environment))
    {
      print "********** MIBS: $mibs_environment **********\n\n";
    }
  }
}

if ($dns_enable == 1)
{
  eval 'require Socket;';
  if ($@) {
    warn $@;
    print "\nCould not load the Net-SNMP Perl module Socket!  If dns_enable\n";
    print "is enabled then the Socket module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require Socket;
  if ($DEBUGGING >= 1)
  {
    print "********** DNS enabled **********\n\n";
  }
}

if ($mysql_dbi_enable == 1)
{
  eval 'require DBI;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module DBI!  If mysql_dbi_enable\n";
    print "is enabled then the DBI module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require DBI;

  eval 'require DBD::mysql;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module DBD::mysql!  If mysql_dbi_enable\n";
    print "is enabled then the DBD::mysql module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require DBD::mysql;
}

if ($postgresql_dbi_enable == 1)
{
  eval 'require DBI;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module DBI!  If postgresql_dbi_enable\n";
    print "is enabled then the DBI module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require DBI;

  if ($postgresql_dbi_module == 0)
  {
    eval 'require DBD::PgPP;';
    if ($@) {
      warn $@;
      print "\nCould not load the Perl module DBD::PgPP!  If postgresql_dbi_module\n";
      print "is set to 0 then the DBD::PgPP module is required.  Please see snmptt.html\n";
      print "for system requirements.\n\n";
      die "died";
    }
    require DBD::PgPP;
  }
  else
  {
    eval 'require DBD::Pg;';
    if ($@) {
      warn $@;
      print "\nCould not load the Perl module DBD::Pg!  If postgresql_dbi_module\n";
      print "is set to 1 then the DBD::Pg module is required.  Please see snmptt.html\n";
      print "for system requirements.\n\n";
      die "died";
    }
    require DBD::Pg;
  }
}    

if ($dbd_odbc_enable == 1)
{
  eval 'require DBI;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module DBI!  If dbd_odbc_enable\n";
    print "is enabled then the DBI module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require DBI;

  eval 'require DBD::ODBC;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module DBD::ODBC!  If dbd_odbc_enable\n";
    print "is enabled then the DBD::ODBC module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require DBD::ODBC;
}

if ($sql_win32_odbc_enable == 1)
{
  eval 'require Win32::ODBC;';
  if ($@) {
    warn $@;
    print "\nCould not load the Perl module Win32::ODBC!  If sql_win32_odbc_enable\n";
    print "is enabled then the Win32::ODBC module is required.  Please see snmptt.html\n";
    print "for system requirements.\n\n";
    die "died";
  }
  require Win32::ODBC;
}


##############################################################################
####  MAIN SECTION START

# Pull in passed SNMP info from snmptrapd via STDIN and place in the array @tempvar

# Global variables for statistics
my $g_total_traps_received = 0;
my $g_total_traps_translated = 0;
my $g_total_traps_ignored = 0;
my $g_total_traps_unknown = 0;
my $g_last_statistics_logged = $g_start_time;

# Global variables for SQL ping
my $g_last_mysql_ping = $g_start_time;
my $g_last_postgresql_ping = $g_start_time;
my $g_last_dbd_odbc_ping = $g_start_time;

if ($daemon == 1)
{
	$SIG{HUP} = \&signal_handler_reload;

	$SIG{TERM} = \&signal_handler_die;

	$SIG{USR1} = \&signal_log_statistics;
        
	$timetoreload = 0;
	$timetodie = 0;
        $timetologstatistics = 0;

	&loadsnmpttconf;	# Load SNMPTT.CONF file

        # Only fork to the background if not Win32
	if (($^O ne "MSWin32") && ($daemon_fork==1))
	{
	        use POSIX qw(setsid);
		use Cwd;

		my $working_dir = cwd;
		print "cwd: $working_dir\n";
	
		chdir '/'
		   or die "Can't chdir to /: $!";
		open STDIN, '/dev/null'
		   or die "Can't read /dev/null: $!";
		open STDOUT, '>>/dev/null'
		   or die "Can't write to /dev/null: $!";
		open STDERR, '>>/dev/null'
		   or die "Can't write to /dev/null: $!";
		defined(my $pid = fork)
		   or die "Can't fork: $!";

		if ($pid)
		{		  
	 	  if ( ! (open(PID, ">/var/run/snmptt.pid") ))
  		  {
  		    open(PID, ">$working_dir/snmptt.pid");
  		  }
		  
		  if (defined (PID))
  		  {
  		    print(PID "$pid\n");
  		    close(PID);
  		  }
		  exit;
		}

		POSIX:setsid
		   or die "Can't start a new session: $!";
		umask 0;
	}

	# Change user if not Windows, and daemon_uid ini parameter not blank
	if ($^O ne "MSWin32" && $daemon_uid ne '')
	{
	  my $daemon_uid_name = '';
	  
	  if ($daemon_uid =~ /\D/)
	  {
	    # no numbers found, so assume it's a textual name
	    
	    $daemon_uid_name = $daemon_uid;
	    
	    $daemon_uid = getpwnam($daemon_uid_name);
	    if (!defined($daemon_uid))
	    {
	      syslog_system("Could not convert user id \'$daemon_uid_name\' to a numeric UID\n");
	    }
	  }
	  
	  # Change current uid to new_uid
	  if (defined($daemon_uid))
	  {
	    if ($daemon_uid_name ne '')
	    {
	      syslog_system("Changing to UID: $daemon_uid_name \($daemon_uid\)");
	      if ($DEBUGGING >= 1)
	      {
		print "Changing to UID: $daemon_uid_name \($daemon_uid\)\n";
	      }
	    }
	    else
	    {
	      syslog_system("Changing to UID: $daemon_uid");
	      if ($DEBUGGING >= 1)
	      {
		print "Changing to UID: $daemon_uid\n";
	      }
	    }
	    
	    # Close debug file before changing users and re-open after
	    if (defined (DEBUGFILE))
	    {
	      close(DEBUGFILE);
	    }
	    
	    $> = $daemon_uid;
	    
	    # Re-open debug file (if needed) as the new user
	    if ($DEBUGGING >= 1)
	    {
	      if ($DEBUGGING_FILE ne '') 
	      {
		if (open DEBUGFILE, ">>$DEBUGGING_FILE")
		{
		  select DEBUGFILE;	# change default output to debug file
		  $debug_file_used = 1;
		}
		else
		{
		  warn "could not open debug output file ($!)";
		}
	      }
	    }
	  }
	}
        
        # Open connections to MySQL, PostresQL, ODBC etc
        # (Do this after switch user IDs when daemon_uid is defined)
        create_db_connections();
        
	while (!$timetodie)
	{
		if (! (chdir($spool_directory)))
		{
		  if ($DEBUGGING >= 1)
		  {
		    print "Unable to enter spool dir $spool_directory:$!\n";
		  }
		  warn "Unable to enter spool dir $spool_directory:$!\n";
		  
		  if ($syslog_system_enable == 1)
		  {
		    syslog_system("Unable to enter spool dir $spool_directory");
		  }
		  if ($eventlog_system_enable == 1)
		  {
		    eventlog_system("Unable to enter spool dir $spool_directory",3,$eventlog_error);
		  }
		}
		elsif (! (opendir(DIR, ".")))
		{
		  if ($DEBUGGING >= 1)
		  {
		    print "Unable to open $spool_directory:$!\n";
		  }
		  warn "Unable to open spool dir $spool_directory:$!\n";

		  if ($syslog_system_enable == 1)
		  {
		    syslog_system("Unable to open spool dir $spool_directory");
		  }
		  if ($eventlog_system_enable == 1)
		  {
		    eventlog_system("Unable to open spool dir $spool_directory",4,$eventlog_error);
		  }
		}
		elsif (! (@filenames = readdir(DIR)))
		{
		  if ($DEBUGGING >= 1)
		  {
		    print "Unable to read spool dir $spool_directory:$!\n";
		  }
		  warn "Unable to read spool dir $spool_directory:$!\n";

		  if ($syslog_system_enable == 1)
		  {
		    syslog_system("Unable to read spool dir $spool_directory");
		  }
		  if ($eventlog_system_enable == 1)
		  {
		    eventlog_system("Unable to read spool dir $spool_directory",5,$eventlog_error);
		  }		  
		}
		else
		{
		  closedir(DIR);
		  
		  @filenames = sort (@filenames);		# Sort list of filenames to ensure they
		  						# are processed in the order they were
		  						# received
		  
		  foreach my $file (@filenames)
		  {
		    next if ($file eq ".");
		    next if ($file eq "..");

		    if (lc($file) eq "!reload")
		    {
		      $timetoreload = 1;
		      unless (unlink($file))		      
		      {
			if ($DEBUGGING >= 1)
			{
			  print "Unable to delete !reload file from spool dir:$!\n";
			}
			warn "Unable to delete !reload file from spool dir:$!\n";
			
			if ($syslog_system_enable == 1)
			{
			  syslog_system("Unable to delete !reload file from spool dir");
			}
			if ($eventlog_system_enable == 1)
			{
			  eventlog_system("Unable to delete !reload file from spool dir",20,$eventlog_error);
			}
		      }
		      next;
		    }

                    if (lc($file) eq "!statistics")
		    {
		      $timetologstatistics = 1;
		      unless (unlink($file))		      
		      {
			if ($DEBUGGING >= 1)
			{
			  print "Unable to delete !statistics file from spool dir:$!\n";
			}
			warn "Unable to delete !statistics file from spool dir:$!\n";
			
			if ($syslog_system_enable == 1)
			{
			  syslog_system("Unable to delete !statistics file from spool dir");
			}
			if ($eventlog_system_enable == 1)
			{
			  eventlog_system("Unable to delete !statistics file from spool dir",21,$eventlog_error);
			}
		      }
		      next;
		    }


		    if ($DEBUGGING >= 1)
		    {
		      print "Processing file: $file\n";
		    }
		    
		    $filesuccess = 1;
		    unless (open FILE, $spool_directory.$file)
		    {
		      if ($DEBUGGING >= 1)
		      {
			print "Could not open trap file $spool_directory$file: ($!)\n";
		      }
		      warn "Could not open trap file $spool_directory$file: ($!)\n";
		      
		      if ($syslog_system_enable == 1)
		      {
			syslog_system("Could not open trap file $spool_directory$file");
		      }
		      if ($eventlog_system_enable == 1)
		      {
			eventlog_system("Could not open trap file $spool_directory$file",6,$eventlog_error);
		      }
		      
		      $filesuccess = 0;
		    }
		    
		    $input = 'FILE';
		    		
		    &readtrap;			# Read trap from STDIN or file
		    
		    &searchfortrap;		# Search for trap snmptt.conf (array)
		    
		    close FILE;
		    
		    if ($filesuccess == 1)
		    {
		      if ($keep_unlogged_traps == 0 || $trap_successfully_logged == 1)
		      {
			unless (unlink($file))
			{
			  if ($DEBUGGING >= 1)
			  {
			    print "Unable to delete trap file $file from spool dir:$!\n";
			  }
			  warn "Unable to delete trap file $file from spool dir:$!\n";
			  
			  if ($syslog_system_enable == 1)
			  {
			    syslog_system("Unable to delete trap file $file from spool dir");
			  }
			  if ($eventlog_system_enable == 1)
			  {
			    eventlog_system("Unable to delete trap file $file from spool dir",7,$eventlog_error);
			  }
			}
		      }
		    }
		  }
		}

                if ($statistics_interval > 0) {
                  if (time() >= ($g_last_statistics_logged + $statistics_interval)) {
                    &log_statistics();
                  }
                }

                if ($mysql_dbi_enable == 1 && $mysql_ping_interval > 0) {
                  if (time() >= ($g_last_mysql_ping + $mysql_ping_interval)) {
                    &mysql_ping();
                  }                  
                }

                if ($postgresql_dbi_enable == 1 && $postgresql_ping_interval > 0) {
                  if (time() >= ($g_last_postgresql_ping + $postgresql_ping_interval)) {
                    &postgresql_ping();
                  }                  
                }

                if ($dbd_odbc_enable == 1 && $dbd_odbc_ping_interval > 0) {
                  if (time() >= ($g_last_dbd_odbc_ping + $dbd_odbc_ping_interval)) {
                    &dbd_odbc_ping();
                  }                  
                }
                
		if ($timetologstatistics == 1)
		{
                  &log_statistics();
                  $timetologstatistics = 0;
                }
                
                if ($DEBUGGING >= 1)
		{
			print "Sleeping for $sleep seconds\n\n";
		}
		sleep $sleep;

		if ($timetoreload == 1)
		{
			if ($DEBUGGING >= 1)
			{
				print "Reloading configuration file\(s\)\n\n";
			}
			
			if ($syslog_system_enable == 1)
			{
			  syslog_system("Reloading configuration file\(s\)");
			}
			if ($eventlog_system_enable == 1)
			{
			  eventlog_system("Reloading configuration file\(s\)",8,$eventlog_information);
			}

			&loadsnmpttini;		# Load ini file
			&loadsnmpttconf;	# Load SNMPTT.CONF file
			$timetoreload = 0;
		}
	}
	if ($DEBUGGING >= 1)
	{
		print "SNMPTT $snmptt_version shutdown: ",scalar(localtime),"\n\n";
                print "Total traps received:    $g_total_traps_received\n";
                print "Total traps translated:  $g_total_traps_translated\n";                
                print "Total traps ignored:     $g_total_traps_ignored\n";                
                print "Total unknown traps:     $g_total_traps_unknown\n\n";
	}
	
	if ($syslog_system_enable == 1 &&  $daemon == 1)
	{
	  syslog_system("SNMPTT $snmptt_version shutdown");
          syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown");                
          #syslog_system("Total traps received:    $g_total_traps_received");
          #syslog_system("Total traps translated:  $g_total_traps_translated");
          #syslog_system("Total traps ignored      $g_total_traps_ignored");
          #syslog_system("Total unknown traps:     $g_total_traps_unknown");                
	}
	if ($eventlog_system_enable == 1 &&  $daemon == 1)
	{
	  my $message = "SNMPTT $snmptt_version shutdown\n\n" . \
          "Total traps received:    $g_total_traps_received\n" . \
          "Total traps translated:  $g_total_traps_translated\n" . \
          "Total traps ignored:     $g_total_traps_ignored\n" . \
          "Total unknown traps:     $g_total_traps_unknown\n";
          
          eventlog_system($message,1,$eventlog_information);
	}
}
else
{
  # NOT daemon mode...

  # Open connections to MySQL, PostresQL, ODBC etc
  create_db_connections();
  
  $input = 'STDIN';
  
  &readtrap;		# Read trap from STDIN or file
  
  &loadsnmpttconf;	# Load SNMPTT.CONF file
  
  &searchfortrap;		# Search for trap snmptt.conf (array)
}

&close_db_connections();

exit();

####  MAIN SECTION END

sub processtrap
{
	# Variables of trap received by SNMPTRAPD:
	# 
	# $var[0]		hostname
	# $var[1] 		ip address
	# $var[2] 		uptime
	# $var[3] 		trapname / OID
	# $var[4] 		ip address from trap agent
	# $var[5] 		trap community string
	# $var[6] 		enterprise
	#
	# $entvarname[0]	passed variable name 1
	# $entvarname[1]	passed variable name 2
	#
	# $entvar[0]		passed variable 1
	# $entvar[1]		passed variable 2
	# .
	# .
	# etc..
	# 
	# $event hash for each trapped defined in snmptt.conf with following values:
	# 
	# 0: name of trap
	# 1: category
	# 2: severity
	# 3: FORMAT string
	# 4: EXEC string
	# 5: NODES string
	# 6: REGEX array
	# 7: MATCH array
	# 8: DESC array
	#
	# Example: 	To access FORMAT string for received trap:
	# 		$event{"$var[3]"}[3]
	#
	#		or
	# 		
	# 		$event{"$receivedtrap_entry"}[3]	
	###############################################################################
	# if no nodes list, then $hostmatch = 1 so trap is logged etc, otherwise it is not

	$l = 1;	# Start with first event entry

	$multiple_event_passes = 0;	# Below, if =1, then abort because we already found a match for
					# this trap.  This would only happen if you have a trap
					# defined multiple times in the config file to allow 
					# different machines to have different actions based on 
					# the node name.
	
	while (defined($event{$receivedtrap_entry}[$l+1]))
	{
	  if (($multiple_event == 0) && ($multiple_event_passes > 0))
	  {
	    if ($DEBUGGING >= 1)
	    {
	      print "  multiple_event = $multiple_event, multiple_event_passes = " .
	            "$multiple_event_passes so don't process any more entries\n";
            }
	    last;	# Don't look any further - stop
	  }
	  if ($DEBUGGING >= 1)
		{
			print  "Working with EVENT entry: $receivedtrap_entry => $event{$receivedtrap_entry}[0+$l],$event{$receivedtrap_entry}[1+$l],$event{$receivedtrap_entry}[2+$l],$event{$receivedtrap_entry}[5+$l]\n";
		}
		
		$hostmatch = 0;
		$nodesmatch = 0;	# Match from NODES
		my $match_found = 0;	# Match from MATCH
	
		# $event is the hash of events defined in the config file
		# $event2 is a copy of the matching event
	
		# Flush out @event2
		@event2 = ();
		
		# Flush out @nodes
		#@nodes = ();
		@nodes2 = ();
	
		#	$event2[0]=$event{"$receivedtrap_entry"}[0];	# 0: name of trap	
		#	$event2[1]=$event{"$receivedtrap_entry"}[1];	# 1: category
		#	$event2[2]=$event{"$receivedtrap_entry"}[2];	# 2: severity
		#	$event2[3]=$event{"$receivedtrap_entry"}[3];	# 3: FORMAT string
		#	$event2[4]=$event{"$receivedtrap_entry"}[4];	# 4: EXEC string
		#	$event2[5]=$event{"$receivedtrap_entry"}[5];	# 5: NODES string
		
		$event2[0]=$event{"$receivedtrap_entry"}[0+$l];	# 0: name of trap	
		$event2[1]=$event{"$receivedtrap_entry"}[1+$l];	# 1: category
		$event2[2]=$event{"$receivedtrap_entry"}[2+$l];	# 2: severity
		$event2[3]=$event{"$receivedtrap_entry"}[3+$l];	# 3: FORMAT string
		$event2[4]=$event{"$receivedtrap_entry"}[4+$l];	# 4: EXEC string
		$event2[5]=$event{"$receivedtrap_entry"}[5+$l];	# 5: NODES string
		$event2[6]=$event{"$receivedtrap_entry"}[6+$l];	# 6: REGEX array
		$event2[7]=$event{"$receivedtrap_entry"}[7+$l];	# 7: MATCH array
		$event2[8]=$event{"$receivedtrap_entry"}[8+$l];	# 7: DESC array
		
		$l+=10;
			
		if ($event2[5] eq '')
		{
		  $nodesmatch = 1;
		  #$hostmatch = 1;		# No nodes defined, so default to all nodes
		  #$multiple_event_passes++;
		  #$processed = 1;
		  if ($DEBUGGING >= 1)
		  {
		    print "  No nodes defined for this entry so all nodes will match\n";
		  }
		}
		else
		{
			if ($DEBUGGING >= 1)
			{
			  print "  Nodes defined for this entry...\n";
			}	

			@nodes2 = &process_nodes($event2[5]);
			
			if ($DEBUGGING >= 1)
			{
			  print "  NODES entries: @nodes2\n";
			}

			foreach my $a (@nodes2)
			{	
				if ($a =~ /^(\d+\.\d+\.\d+\.\d+)/) # NODES entry is an IP address / cidr / range
				{
				  #if ( $a eq $var[1])     # compare against agent ip
				  if ( checkip($var[4], $a) == 1)
				  {				  
				    $nodesmatch = 1;
				    #$hostmatch = 1;
				    #$processed = 1;
				  }
				}
				elsif ($dns_enable == 1)	# Resolve NODES entry to IP address, and compare
				{
				  my $temp = gethostbyname($a);
				  if (defined($temp))
				  {
				    $temp = Socket::inet_ntoa(scalar($temp));
				    if ($DEBUGGING => 1)
				    {
				      print "    NODES entry ($a) resolved to: $temp\n";
				    }
				    if ( checkip($var[4], $temp) == 1)
				    {
				      $nodesmatch = 1;
				    }
				  }
				  else
				  {
				    print "    NODES entry ($a) could NOT be resolved.\n";
				  }
				}
	                        elsif (lc $a eq lc $agent_dns_name)	# NODES entry is a host name.  Do lowercase compare
				{
				  $nodesmatch = 1;
				  #$hostmatch = 1;
				  #$processed = 1;
				}
			}
			#if ($hostmatch == 1)
			if ($DEBUGGING >= 1)
			{			  
			  if ($nodesmatch == 1)
 			  {
			    print "  Node found in list\n";
			  }
			  else
			  {
			    print "  Node NOT found in list\n";
			  }
			}
		}

		# Check for MATCH entries
		if ($nodesmatch == 1 && defined($event2[7][0]))	# Only if NODES has matched and 
		{						# we have some MATCH statements
		  my @match = ();
		  my $match_type;
		  		  
		  for (my $i=0; defined($event2[7][$i]); $i++)
		  {
		    my $match_temp = $event2[7][$i];
		    $match_temp =~ s/\s//g;	# Remove any white space from $match
		    if ($match_temp =~ /^mode=(.*)/i)
		    {
		      $match_type = lc($1);
		      last;
		    }
		  }
		  if ($match_type ne 'and' && $match_type ne 'or')
		  {
		    $match_type = 'or';
		  }
		  if ($DEBUGGING >= 2)
		  {
		    print "  MATCH statements found\n";
		    print "    MATCH mode: " . uc($match_type) . "\n";
		  }
		  
		  for (my $i=0; defined($event2[7][$i]); $i++)
		  {
		    my $match_temp = $event2[7][$i];

                    # If it's a regex (has ()) then remove white space before and after ()s.  
		    # Otherwise, remove all white space
		    #print "!$match_temp!\n";
		    if ($match_temp =~ /\(|\)/)
		    {
			$match_temp =~ s/\s*(\(.*\))\s*/$1/g;	# Remove any white space from before and after ()'s

			$match_temp =~ s/\)\s*(\i)\s*/\)$1/g;	# Remove any white space from before and after i modifier
			                                        # if there is one
			
			$match_temp =~ s/\s*(\!.*)/$1/g;	# Remvoe any white space in front of the ! if there is one
		    }
		    else
		    {
		      $match_temp =~ s/\s//g;	# Remove any white space from $match
		    }
		    #print "!$match_temp!\n";
		    
		    # DEFAULT= line
		    if ($match_temp =~ /^mode=(.*)/i)
		    {
		      next;
		    }

		    if (match($match_temp) == 1)
		    {
		      $match_found = 1;
		      if ($match_type eq "or")
		      {
			last;
		      }
		    }
		    else
		    {
		      $match_found = 0;
		      if (lc($match_type) eq "and")
		      {
			last;
		      }
		    }
		  }
		  if ($DEBUGGING >= 1)
		  {
		    if ($match_found == 1)
		    {
		      print "  MATCH statement(s) final result = true...\n\n";
		    }
		    else 
		    {
		      print "  MATCH statement(s) final result = false...\n\n";
		    }
		  }
		}
		else
		{
		  if ($DEBUGGING >= 1)
		  {
		    print "  No MATCH entries defined for this entry\n";
		  }
		  $match_found = 1;	# Default to match_found if no MATCH entries
		}

		if ($nodesmatch == 1 && $match_found == 1)
		{
		  $multiple_event_passes++;
		  $hostmatch = 1;
		  $processed = 1;
		}
		
		if ($hostmatch == 1 && $event2[1] ne "IGNORE")
		{
                  $message_short = "";

			# Trap received exists in our list of trap definitions
			
                        # Statistics
                        $g_total_traps_translated++;
                        
                        if ($DEBUGGING >= 1)
			{
				print "\nTrap defined, processing...\n\n";
				print "\n\nFORMAT line:\n";				
			}

			if ($event2[3] ne '')   # if FORMAT string has been defined
			{		
			# Variable substitution for FORMAT string
			
			$_ = $event2[3];	# FORMAT string
		
			# Translate if enabled, and if we can
			$receivedtrap_trans = &translate_log_trap_oid_sub($receivedtrap);

			$enterprise_trans = &translate_enterprise_oid_format_sub($var[6]);

			&substitute;
		
			$message_short = $_;
			
			$message = "$receivedtrap_trans $event2[2] \"$event2[1]\" $agent_dns_name - $message_short\n";
			
			if ($stdout_enable == 1)
			{
			  #select STDOUT;
			  print STDOUT "$message";

			  #if ($debug_file_used == 1)
			  #{
			  #  select DEBUGFILE;
			  #}
			}

			if ($DEBUGGING >= 1)
			{
				print "$message_short\n";
				print "\n$message";
			}
			
			if ($log_enable == 1)
			{
				$trap_attempted_to_log++;	
				
				if (open LOGFILE, ">>$log_file")
				{
					print LOGFILE $trap_date_time." $message";
					close LOGFILE;
					$trap_successfully_logged++;
				}
				else
				{
				  warn "Can not open log file $log_file: $!";

				  if ($syslog_system_enable == 1)
				  {
				    syslog_system("Can not open log file $log_file");
				  }

				  if ($eventlog_system_enable == 1)
				  {
				    eventlog_system("Can not open log file $log_file",14,$eventlog_information);
				  }
				}
			}
	
			if ($syslog_enable == 1)
			{
				$trap_attempted_to_log++;

				my $syslog_level_temp = 99;
				my $temp;
				
				foreach $temp (@syslog_level_debug)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'debug' }
				}
				foreach $temp (@syslog_level_info)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'info' }
				}
				foreach $temp (@syslog_level_notice)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'notice' }
				}
				foreach $temp (@syslog_level_warning)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'warning' }
				}
				foreach $temp (@syslog_level_err)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'err' }
				}
				foreach $temp (@syslog_level_crit)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'crit' }
				}
				foreach $temp (@syslog_level_alert)
				{
				  if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'alert' }
				}
				
				if ($syslog_level_temp == 99)
				{
				  $syslog_level_temp = $syslog_level;
				}

				#if (Sys::Syslog::openlog('TRAPD', '',$syslog_facility) )
				if (Sys::Syslog::openlog('snmptt[' . $> . ']', '',$syslog_facility) )
				{
					Sys::Syslog::syslog ($syslog_level_temp, $message,1);
					Sys::Syslog::closelog();
					$trap_successfully_logged++;
				}
				else
				{
					warn "Can not open log_file: $!";
				}
			}
			
			if ($eventlog_enable == 1)
			{
			  $trap_attempted_to_log++;
			  
			  my $eventlog_type_temp = 99;
			  my $temp;

			  foreach $temp (@eventlog_type_information)
			  {
			    if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_information }
			  }

			  foreach $temp (@eventlog_type_warning)
			  {
			    if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_warning }
			  }
			  
			  foreach $temp (@eventlog_type_error)
			  {
			    if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_error }
			  }
			  
			  if ($eventlog_type_temp == 99)
			  # Use the default eventlog type
			  {
			    if ($eventlog_type =~ /INFORMATION/s)
			    {
			      if (eventlog_system($message,2,$eventlog_information) == 0)
			      {
				$trap_successfully_logged++;
			      }
			    }
			    elsif ($eventlog_type =~ /ERROR/s)
			    {
			      if (eventlog_system($message,2,$eventlog_error) == 0)
			      {
				$trap_successfully_logged++;
			      }
			    }
			    else
			    {
			      if (eventlog_system($message,2,$eventlog_warning) == 0)
			      {
				$trap_successfully_logged++;
			      }
			    }
			  }
			  else
			  {
			    if (eventlog_system($message,2,$eventlog_type_temp) == 0)
			    {
			      $trap_successfully_logged++;
			    }
			  }
			}

			if ($net_snmp_perl_enable == 1 && $db_translate_enterprise == 1)
			{
			  $db_enterprise = $enterprise_trans;
			}
			else
			{
			  $db_enterprise = $var[6];
			}

			if ($mysql_dbi_enable == 1)
			{
                          $trap_attempted_to_log++;	

                          # Make sure the connection is up
                          if ($mysql_ping_on_insert == 1) {
                            &mysql_ping();
                          }

                          if (defined ($dbh_mysql)) {
                            # Backslash any quotes
                            my $message_short2 = $message_short;
                            $message_short2 =~ s(\')(\\\')g;   #'
                            $message_short2 =~ s(\")(\\\")g;   #"
                            
                            my $community = $var[5];
                            $community =~ s(\')(\\\')g;   #'
                            $community =~ s(\")(\\\")g;   #"				

                              
                            unless (defined ($dbh_mysql->do(
                                  "INSERT INTO $mysql_dbi_table (eventname, eventid, trapoid, enterprise, 
                                  community, hostname, agentip, category, severity, uptime, traptime, 
                                  formatline) VALUES (
                                  \'$event2[0]\',\'$receivedtrap_entry'\,\'$receivedtrap_trans'\,\'$db_enterprise\',
                                  \'$community\',\'$agent_dns_name\',\'$var[4]\',
                                  \'$event2[1]\',\'$event2[2]\',\'$var[2]\',\'".$trap_date_time."\',
                                  \'$message_short2\')" ) ) )
                            {
                              my $msg = "MySQL error " . $dbh_mysql->{'errno'} . ": Unable to perform INSERT INTO: ".$dbh_mysql->errstr;                             
                              warn $msg, "\n";
                              
                              if ($DEBUGGING >= 1)
                              {
                                print $msg, "\n";
                              }
                              
                              if ($syslog_system_enable == 1)
                              {
                                syslog_system($msg);
                              }
                              
                              if ($eventlog_system_enable == 1)
                              {
                                eventlog_system($msg,15,$eventlog_error);
                              }
                            }
                            else
                            {
                              $trap_successfully_logged++;
                            }
                          }
			}

			if ($postgresql_dbi_enable == 1)
			{
                          $trap_attempted_to_log++;	
                         
                          # Make sure the connection is up
                          if ($postgresql_ping_on_insert == 1) {
                            &postgresql_ping();
                          }

                          if (defined ($dbh_postgresql)) {
                            # Backslash any quotes
                            my $message_short2 = $message_short;
                            $message_short2 =~ s(\')(\\\')g;   #'
                            $message_short2 =~ s(\")(\\\")g;   #"
                              
                            my $community = $var[5];
                            $community =~ s(\')(\\\')g;   #'
                            $community =~ s(\")(\\\")g;   #"
                              
                            unless (defined ($dbh_postgresql->do(
                                  "INSERT INTO $postgresql_dbi_table (eventname, eventid, trapoid, enterprise, 
                                  community, hostname, agentip, category, severity, uptime, traptime, 
                                  formatline) VALUES (
                                  \'$event2[0]\',\'$receivedtrap_entry'\,\'$receivedtrap_trans'\,
                                  \'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
                                  \'$event2[1]\',\'$event2[2]\',\'$var[2]\',\'".$trap_date_time."\',
                                  \'$message_short2\')" ) ) )
                            {
                              my $msg = "Postgres error: Unable to perform INSERT INTO: ".$dbh_postgresql->errstr;
                              
                              warn $msg, "\n";

                              if ($DEBUGGING >= 1)
                              {
                                print $msg, "\n";
                              }
                              
                              if ($syslog_system_enable == 1)
                              {
                                syslog_system($msg);
                              }
                              
                              if ($eventlog_system_enable == 1)
                              {
                                eventlog_system($msg,19,$eventlog_error);
                              }
                            }
                            else
                            {
                              $trap_successfully_logged++;
                            }
                          }
			}
			
			if ($dbd_odbc_enable == 1)                       
			{
                          $trap_attempted_to_log++;

                          # Make sure the connection is up
                          if ($dbd_odbc_ping_on_insert == 1) {
                            &dbd_odbc_ping();
                          }
                          
                          if (defined ($dbh_odbc))
                          {
                            # Double any single quotes
                            my $message_short2 = $message_short;
                            $message_short2 =~ s(\')('')g;     #'
                            
		            my $community = $var[5];
                            $community =~ s(\')('')g;   #'

  			    unless (defined ($dbh_odbc->do(
                                  "INSERT INTO $dbd_odbc_table (eventname, eventid, trapoid, enterprise, 
                                  community, hostname, agentip, category, severity, uptime, traptime, 
                                  formatline) VALUES (
                                  \'$event2[0]\',\'$receivedtrap_entry'\,\'$receivedtrap_trans'\,
                                  \'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
                                  \'$event2[1]\',\'$event2[2]\',\'$var[2]\',\'".$trap_date_time."\',
                                  \'$message_short2\')" ) ) )
                            {
                              my $msg = warn "DBI DBD::ODBC error: Unable to perform INSERT INTO: ".$dbh_odbc->errstr;
                              warn $msg, "\n";

                              if ($DEBUGGING >= 1)
                              {
                                print $msg, "\n";
                              }
                              
                              if ($syslog_system_enable == 1)
                              {
                                syslog_system($msg);
                              }
                              
                              if ($eventlog_system_enable == 1)
                              {
                                eventlog_system($msg,16,$eventlog_error);
                              }
                            }
                            else
                            {
                              $trap_successfully_logged++;
                            }
                          }
			}				 
			
			if ($sql_win32_odbc_enable)
			{
                          $trap_attempted_to_log++;	
                          
                          if (defined ($dbh_win32_odbc) )
                          {
                            # Double any single quotes
                            my $message_short2 = $message_short;
                            $message_short2 =~ s(\')('')g;     #'
                              
                            my $community = $var[5];
                            $community =~ s(\')('')g;   #'
                              
                            # if defined, there's an error
                            if (defined ($dbh_win32_odbc->Sql
                                ("INSERT INTO $sql_win32_odbc_table (eventname, eventid, trapoid, enterprise, 
                                  community, hostname, agentip, category, severity, uptime, traptime, 
                                  formatline) VALUES (
                                  \'$event2[0]\',\'$receivedtrap_entry'\,\'$receivedtrap_trans'\,
                                  \'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
                                  \'$event2[1]\',\'$event2[2]\',\'$var[2]\',\'".$trap_date_time."\',
                                  \'$message_short2\')" ) ) )
                            {					
                              warn "Unable to perform INSERT INTO: ".Win32::ODBC::Error()."\n";
                              
                              if ($eventlog_system_enable == 1)
                              {
                                eventlog_system("Win32::ODBC error: Unable to perform INSERT INTO: ".Win32::ODBC::Error(),
                                  17,$eventlog_error);
                              }
                            }
                            else
                            {
                              $trap_successfully_logged++;
                            }
                          }
			}	

		        } # end block for if ($event2[3] ne '')
			else
			{
			  if ($DEBUGGING >= 1)
			  {
			    print "  FORMAT line not defined\n";
			  }
			}


			
			#
			# Variable substitution for EXEC string
			#

			if ($DEBUGGING >= 1)
			{
				print "\n\nEXEC line(s):\n";
			}
			
			if (defined ($event2[4][0])) # if EXEC string has been defined
			{
                          if (defined ($event2[4][0]) && $event2[1] ne "LOGONLY")
                          {
                            for (my $i=0; defined($event2[4][$i]); $i++)
                            {
                              $_ = $event2[4][$i];
                                                           
                              &substitute($message_short);
                              
                              my $command = $_;
                              
                              if ($exec_enable == 1)
                              {
                                if ($DEBUGGING >= 1)
                                {	
                                  print "EXEC command:$command\n";
                                }
                                # Execute command
                                system $command;
                              }
                            }
                          }
                          elsif ($DEBUGGING >= 1)
                          {
                            if ($event2[1] eq "LOGONLY")
                            {
                              print "\n\nTrap set to LOGONLY...\n\n";
                            }
                          }
                          
                        } # end of block if ($event2[4] ne '')
			else
			{
			  if ($DEBUGGING >= 1)
			  {
			    print "  EXEC line not defined\n";
			  }
			}

		}
		else
		{
                  if ($event2[1] eq "IGNORE") {
                    # Statistics
                    $g_total_traps_ignored++;
                  }

                  if ($DEBUGGING >= 1)
                  {
                    if ($event2[1] eq "IGNORE")
                    {
                      print "\nTrap set to IGNORE...\n\n";
                    }
                    else
                    {
                      print "\nTrap defined, but NODES or MATCH check failed - skipping this EVENT entry..\n\n";
                    }
                  }
                }
	
	} #while defined..
}

sub substitute
{
  my $format_line = shift;      # Used for $Fz variable substitution for EXEC line.
  
  # Perform substitution on $_ variable

  # Wildcards - $*, $+*, $-*
  # Expand to $1 $2 $3 etc
  my $temp_wildcard1 = ();
  my $temp_wildcard2 = ();
  my $temp_wildcard3 = ();
  for($i=1;$i <= $#entvar+1; $i++)
  {
    $temp_wildcard1 = $temp_wildcard1 . "\$$i" . $wildcard_expansion_separator;
    $temp_wildcard2 = $temp_wildcard2 . "\$\+$i" . $wildcard_expansion_separator;
    $temp_wildcard3 = $temp_wildcard3 . "\$-$i" . $wildcard_expansion_separator;
  }
  
  # Chop off last space
  if (defined ( $temp_wildcard1) )
  {
    chop ($temp_wildcard1);
    chop ($temp_wildcard2);
    chop ($temp_wildcard3);
  }
  
  #s(\$\*)($temp_wildcard1)g;
  &substitute2 ("\$\*", $temp_wildcard1);

  s(\$\+\*)($temp_wildcard2)g;
  &substitute2 ("\$\+\*", $temp_wildcard2);

  s(\$-\*)($temp_wildcard3)g;
  &substitute2 ("\$-\*", $temp_wildcard3);
  
  # $v - Names of variable-bindings 
  # Count down backwards to make sure 10 is not mistaken for $1
  for($i=$#entvarname+1;$i > 0; $i--)
  {
    if ($net_snmp_perl_enable == 1)
    {
      my $temp = my_translateObj($entvarname[$i-1],$translate_varname_oid_format);
      if (!defined ($temp) )
      {
	$temp = $entvarname[$i-1];
      }
      #s(\$v$i)($temp)g;
      &substitute2 ("\$v$i", $temp);
    }
    else
    {
      #s(\$v$i)($entvarname[$i-1])g;
      &substitute2 ("\$v$i", $entvarname[$i-1]);
    }
  }
  
  # $n - Variable-bindings
  # Count down backwards to make sure 10 is not mistaken for $1
  for($i=$#entvar+1;$i > 0; $i--)
  {
    my $val = $entvar[$i-1];
    
    if ($DEBUGGING >= 2)
    {
      print "\nVariable $entvarname[$i-1] with value $entvar[$i-1]\n";
    }
    
    # This will translate (if enabled) any OIDs found in the VALUE (not the variable name) to the textual  
    # name if possible.  For example, if the value was 'A UPS Alarm (.1.3.6.1.4.1.534.1.7.12) has cleared.', 
    # it could be translated to: 'A UPS Alarm (xupsBuildingAlarm) has cleared.'
    if ($translate_value_oids > 0 && $net_snmp_perl_enable == 1)
    {
      $val = &translate_value_oids_sub($val);
    }

    if ($dns_enable == 1 && $resolve_value_ip_addresses == 1 && $net_snmp_perl_enable == 1)
    {
      $val = &resolve_value_ip_addresses_sub($val);
    }
    
    if ($translate_integers == 1 && $net_snmp_perl_enable == 1)
    {
      if (! ($entvar[$i-1] =~ /\D+/))				# Check to see if value is numerical
      {
	if ($DEBUGGING >= 2)
	{
	  print "  Value is numerical\n";
	}
	
	my $temp_OID_type = uc (my_getType($entvarname[$i-1]));
	if ($temp_OID_type eq "INTEGER32" || $temp_OID_type eq "INTEGER")  # Make sure it's a SNMP INTEGER type
	{
	  if ($DEBUGGING >= 2)
	  {
	    print "  Value is defined as an INTEGER in the mib - will attempt to translate\n";
	  }
	  my $val2 = my_mapEnum($entvarname[$i-1], $entvar[$i-1]);
	  if ($val2 ne '')					# Make sure we got something
	  {
	    $val = $val2;
	    
	    if ($DEBUGGING >= 2)
	    {
		    print "    Translated to: $val2\n";
	    }    
          }
	  else
	  {
	    $val = $entvar[$i-1];
	    if ($DEBUGGING >= 2)
	    {
		    print "    Could not translate\n";
	    } 
	  }
	}
	else
	{
	  if ($DEBUGGING >= 2)
	  {
	    print "  Value is NOT defined as an INTEGER or Integer32 in the mib\n";
	  }
	}
      }
    }
    
    if ($net_snmp_perl_enable == 1)
    {
      #s(\$$i)($val)g;			# $n	      
      &substitute2 ("\$$i", $val);

      # Translate for $+n and $-n
      my $temp = my_translateObj($entvarname[$i-1],$translate_varname_oid_format);
      if (!defined ($temp) )		
      {
	$temp = $entvarname[$i-1];
      }

      my $temp2 = &my_getType($entvarname[$i-1]);
      if (!defined ($temp2))
      {
	$temp2 = "unknown";
      }
            
      #s(\$\+$i)($temp:$val)g;		# $+n
      &substitute2 ("\$\+$i", "$temp:$val");

      #s(\$-$i)($temp ($temp2):$val)g;	# $-n
      &substitute2 ("\$-$i", "$temp ($temp2):$val");
    }
    else
    {
      #s(\$$i)($val)g;				# $n
      &substitute2 ("\$$i", $val);
      
      #s(\$\+$i)($entvarname[$i-1]:$val)g;		# $+n
      &substitute2 ("\$\+$i", "$entvarname[$i-1]:$val");      

      #s(\$-$i)($entvarname[$i-1] (unknown):$val)g;	# $-n
      &substitute2 ("\$-$i", "$entvarname[$i-1] (unknown):$val");
    }
  }
  
  # $c - Category
  #s(\$c)($event2[1])g;
  &substitute2 ("\$c", $event2[1]);
  
  # $C - Trap community string
  #s(\$C)($var[5])g;
  &substitute2 ("\$C", $var[5]);
  
  # $E - Enterprise trap (symbolic)
  #s(\$E)($enterprise_trans)g;
  &substitute2 ("\$E", $enterprise_trans);
  
  # $e - Enterprise trap (numerical)
  #s(\$e)($var[6])g;
  &substitute2 ("\$e", $var[6]);
  
  # $N - Event name
  #s(\$N)($event2[0])g;
  &substitute2 ("\$N", $event2[0]);

  # $i - Event OID
  #s(\$i)($receivedtrap_entry)g;
  &substitute2 ("\$i", $receivedtrap_entry);
  
  # $# - Number of variable-bindings in the trap
  my $entvar_temp = $#entvar+1;
  #s(\$#)($entvar_temp)g;
  &substitute2 ("\$#", $entvar_temp);
   
  # $O - Received trap (symbolic)
  if ($net_snmp_perl_enable == 1)
  {
    if ($DEBUGGING >= 2) {
      print "\nOID of received trap: $receivedtrap.  Will attempt to translate to text\n";
    }
    my $temp = my_translateObj("$receivedtrap",$translate_trap_oid_format);
    if (defined ($temp) ) {
      if ($DEBUGGING >= 2) {
	print "  Translated to $temp\n";
      }
      #s(\$O)($temp)g;
      &substitute2 ("\$O", $temp);
    }
    else 
    {
      # Could not translate default to numeric
      if ($DEBUGGING >= 2) {
	print "  Could not translate - defaulting to numeric\n";
      }
      #s(\$O)($temp)g;
      &substitute2 ("\$O", $temp);
    }
  }
  else
  {
    #s(\$O)($receivedtrap)g;
    &substitute2 ("\$O", $receivedtrap);
  }

  # $o - Received trap (numerical)
  #s(\$o)($receivedtrap)g;
  &substitute2 ("\$o", $receivedtrap);  
  
  # $s - Severity
  #s(\$s)($event2[2])g;
  &substitute2 ("\$s", $event2[2]);
  
  # $AR, $ar - IP address
  #s(\$aR)($var[1])g;
  &substitute2 ("\$aR", $var[1]);
  
  #s(\$ar)($var[1])g;
  &substitute2 ("\$ar", $var[1]);
  
  # $R, $r - Hostname
  #s(\$R)($var[0])g;
  &substitute2 ("\$R", $var[0]);
  
  #s(\$r)($var[0])g;
  &substitute2 ("\$r", $var[0]);
  
  # $A, $a - Trap agent IP address
  #s(\$aA)($var[4])g;		# IP address
  &substitute2 ("\$aA", $var[4]);
  
  #s(\$A)($agent_dns_name)g;	# hostname
  &substitute2 ("\$A", $agent_dns_name);
  
  # $T - Uptime
  #s(\$T)($var[2])g;
  &substitute2 ("\$T", $var[2]);
  
  # $x - Current date
  #s(\$x)($trap_date)g;	
  &substitute2 ("\$x", $trap_date);
  
  # $X - Current time
  #s(\$X)($trap_time)g;	
  &substitute2 ("\$X", $trap_time);
  
  # $@ - Current time since epoch (Jan 1, 1904 for Mac, Jan 1, 1900 for most others)
  #s(\$@)($trap_date_time_epoch)g;
  &substitute2 ("\$@", $trap_date_time_epoch);
  
  # iso(1) org(3) dod(6) internet(1) snmpV2(6) snmpModules(3) snmpMIB(1) snmpMIBObjects(1) 5
  # SNMPv2 Generic?  1=coldStart....5=authenticationFailure, 6=egpNeighborLoss so 0=enterprise?
  if ($receivedtrap =~ /^.1.3.6.1.6.3.1.1.5/)
  {
    # It's a generic trap
      
    # $S - Specific trap number - 0
    #s(\$S)(0)g;
    &substitute2 ("\$S", "0");
    
    # $G - Generic trap number - last #
    my $temp = $_; 		# Save old $_
    $_ = $receivedtrap;
    /(\d+)$/;
    my $temp2 = $1;
    $_ = $temp;		# Restore old $_
    #s(\$G)($temp2)g;
    &substitute2 ("\$G", $temp2);
  }
  else
  {
    # It's an enterprise trap
    
    # $S - Specific trap number - last #
    my $temp = $_; 		# Save old $_
    $_ = $receivedtrap;
    /(\d+)$/;
    my $temp2 = $1;
    $_ = $temp;		# Restore old $_
    #s(\$S)($temp2)g;
    &substitute2 ("\$S", $temp2);
    
    # $G - Generic trap number - 0
    s(\$G)(0)g;	  
    &substitute2 ("\$G", "0");
  }	

  # $D - Description from MIB file (NOT from snmptt.conf)
  if ($net_snmp_perl_enable == 1 && $description_mode == 2) {
    my $description_temp = $SNMP::MIB{$event2[0]}->{description};
    if ($description_clean == 1) {
      $description_temp =~ s/\n\s+/\n/g;
    }
    s(\$D)($description_temp)g;
    &substitute2 ("\$D", $description_temp);
  }
  elsif ($description_mode == 1) {
    for (my $i=0; defined($event2[8][$i]); $i++)
    {
      $description_temp = $description_temp . $event2[8][$i] . "\n";
    }
    if ($description_clean == 1) {
      $description_temp =~ s/\n\s+/\n/g;
    }
    &substitute2 ("\$D", $description_temp);
  }
  else {
    #s(\$D)()g;
    &substitute2 ("\$D", "");
  }

  # Formatting
  # alarm (bell)          (BEL)
  &substitute2 ("\$Fa", "\a");

  # form feed             (FF)
  &substitute2 ("\$Ff", "\f");

  # newline               (LF, NL)
  &substitute2 ("\$Fn", "\n");

  # return                (CR)
  &substitute2 ("\$Fr", "\r");

  # tab                   (HT, TAB)
  &substitute2 ("\$Ft", "\t");

  # FORMAT line (only possible when doing EXEC line)
  &substitute2 ("\$Fz", "$format_line");

  # $$ - Print a $
  s(\$\$)(\$)g;

  # Do user defined REGEX on $message
  
  if ($DEBUGGING >= 2 && defined($event2[6][0]))
  {
    print "\n$_\n";
  }
  
  for (my $i=0; defined($event2[6][$i]); $i++)
  {
    my $regex_temp = $event2[6][$i];
    
    #print "!!!!!!$regex_temp\n";
    # Remove starting and ending ()
    #$regex_temp =~ /^\((.*)\)$/;
    $regex_temp =~ /^\((.*)\)(.*)$/;
    $regex_temp = $1;
    my $modifiers = $2;
    
    # Split using )(
    my @regex_temp2 = split(/\)\(/,$regex_temp,2);
    #print "0:$regex_temp2[0]\n";
    #print "1:$regex_temp2[1]\n";
    #print "Modifiers: $modifiers\n";

    if ($regex_temp2[0] ne '')
    {
      # allowed modifiers: any combination of: i, g, e, ee+ not permitted
      my $modifiers2 = 0;
      if ($modifiers =~ /i/) { $modifiers2 = $modifiers2 | 1; }
      if ($modifiers =~ /g/) { $modifiers2 = $modifiers2 | 2; }
      if ($modifiers =~ /e/) 
      { 
	if ($allow_unsafe_regex == 1)
	{
	  $modifiers2 = $modifiers2 | 4;
	  
	  # Append a package declaration to the beginning to prevent global variables from being
	  # visible.  Also remove any attempts to access variables from the main:: package.
	  $regex_temp2[1] = "package regexisoloate;$regex_temp2[1]";
	  $regex_temp2[1] =~ s/\$main::/\$/gi;	# Remove attempts to access the main package
	}
	else
	{
	  if ($DEBUGGING >= 2)
          {
	    print "  REGEX detected with e modifier, but allow_unsafe_regex is disabled.  Removing e modifier\n";
          }
	}
      }

      #print "modifiers2 = $modifiers2\n";
      
      # 0	none
      # 1	i
      # 2	g
      # 3	ig
      # 4	e
      # 5	ie
      # 6	ge
      # 7	ige

      if ($DEBUGGING >= 2)
      {
	print "  Doing REGEX: s($regex_temp2[0])($regex_temp2[1])$modifiers\n";
      }

      # If unsafe is off, then don't use the e modifier.  This means that variable interopolation
      # will not work on the right side.  If the user puts a $1 on the right, it will print out $1.
      # 
      # If unsafe is on, then we use the /ee modifier.
      # 
      # With a normal regular expression in a program, the e modifier is not requried for variable 
      # interopolation to work.  If you want to have code executed, then you need at least one e
      # modifier in a regular program.
      # 
      # Because we are passing the right side to a perl program instead of hard-coding the right side
      # *inside* of the program, we need to handle it differently.  These are the rules:
      # 
      # -for interopolation and / or code execution to work, we need to use the /ee modifier inside the 
      #  program (snmptt)
      # -if we only want interop and no code execution (same behaviour as a program with no e modifier),
      #  then we:
      #   -escape all backslashes
      #   -escape all double quotes
      #   -escape all single quotes
      #   -put quotes around the whole thing
      # - One would assume that if we didn't put quotes around everything, and used one /e instead of two, then
      #   it would work, but it does not..
      #   
      # -if we want both interop and code to execute (same behaviour as a program with one e modifier), 
      #  then we:
      #   -do nothing extra (no escaping or putting quotes around it)
      # 
      # With unsafe off, or unsafe no with NO e modifier, whatever the user puts in the right side of the 
      # REGEX will display exactly, with the only exception of the $n variable interop working with unsafe on.
      #
      # With unsafe on and one e modifier, the right side is evaluated so any text needs to be in quotes, and
      # printable quotes need to be escaped etc just like a normal program.  
      #
      # Example1:
      # 
      # "5 + 5 is ".(5+5)."and \"this is quoted\""
      # will output:
      # 5+5 is 10 and "this is quoted"
      #
      # If unsafe was off, it would output:
      #
      # "5 + 5 is ".(5+5)."and \"this is quoted\""
      #
      # Example2:
      # 
      # "5 + 5 is ".sprintf("%d",(5+5))."and \"this is quoted\""
      # will output:
      # 5+5 is 10 and "this is quoted"
      #
      # If unsafe was off, it would output:
      #
      # "5 + 5 is ".sprintf("%d",(5+5))."and \"this is quoted\""

      if ($allow_unsafe_regex == 0)
      {
	# unsafe_regex is OFF
	if    ($modifiers2 == 0 ) { s($regex_temp2[0])($regex_temp2[1]); }
        elsif ($modifiers2 == 1)  { s($regex_temp2[0])($regex_temp2[1])i; }
        elsif ($modifiers2 == 2)  { s($regex_temp2[0])($regex_temp2[1])g; }
        elsif ($modifiers2 == 3)  { s($regex_temp2[0])($regex_temp2[1])ig; }
      }
      else
      {
	# unsafe_regex is ON
	
	# If unsafe is on, use two e's.  If user does not specify an e, 
	# escape back slashes and quotes, and put quotes around the whole string
	# to prevent functions from being executed
	if (! ($modifiers =~ /e/))
	{
	  # If it contains a \, then escape it
	  $regex_temp2[1] =~ s/\\/\\\\/g;
	  # Escape each quote
	  $regex_temp2[1] =~ s/\"/\\\"/g;	# double quotes
	  $regex_temp2[1] =~ s/\'/\\\'/g;	# single quotes
	  # put quotes around the whole thing
	  $regex_temp2[1] = "\"$regex_temp2[1]\"";
	}      
	#print "1:$regex_temp2[1]\n";
	if    ($modifiers2 == 0 ) { s($regex_temp2[0])($regex_temp2[1])ee; }
        elsif ($modifiers2 == 1)  { s($regex_temp2[0])($regex_temp2[1])eei; }
        elsif ($modifiers2 == 2)  { s($regex_temp2[0])($regex_temp2[1])eeg; }
        elsif ($modifiers2 == 3)  { s($regex_temp2[0])($regex_temp2[1])eeig; }
        elsif ($modifiers2 == 4)  { s($regex_temp2[0])($regex_temp2[1])ee; }
        elsif ($modifiers2 == 5)  { s($regex_temp2[0])($regex_temp2[1])eei; }
        elsif ($modifiers2 == 6)  { s($regex_temp2[0])($regex_temp2[1])eeg; }
        elsif ($modifiers2 == 7)  { s($regex_temp2[0])($regex_temp2[1])eeig; }
      }
    }
  }
}

# sub substitute2 (left, right)
#
# left:  inside of double quotes with escapes.  eg: "\$a"
# right: replacement string eg: $name, "hello", "hi$namebye"
#
# Only substitute if there is an odd number of $ or \ symbols before the 
# substitute variable (eg: a in $a).  If there is an even number (0,2,4 etc)
# then do NOT substitute.  If there is an odd number (1,3,5 etc) then
# substitute.  For example, if $a converted to x:
# $a      = x
# $$a     = $a
# $$$a    = $x
# $$$$a   = $$a
# $$$$$a  = $$x
# 
# We need variable length look behind pattern matching to do this, but Perl
# does not support it.  As an alternative we can reverse the string, do a
# substitute with look ahead, and reverse it back.
#
sub substitute2 {
  my $left = shift;
  my $right = shift;

  $string_r = reverse $_;
  $left_r = reverse $left;
  $left_r =~ s/\\/\\\\/g;               # escape \
  $left_r =~ s/\$/\\\$/g;               # escape $
  $left_r =~ s/\*/\\\*/g;               # escape *
  $left_r =~ s/\+/\\\+/g;               # escape +
  $right_r = reverse $right;

  # The format is:
  # s/a\$((?=(\$\$)*(?!\$)))/b/g;
  # where 'a\$' is the reverse of what to look for and 'b' is what to replace 
  # it with (reversed).

  $string_r =~ s/$left_r((?=(\$\$)*(?!\$)))/$right_r/g;

  $_ = reverse $string_r;
}

sub loadsnmpttconf
{
	#
	# snmptt.conf loader.
	# This section loads the snmptt.conf which defines the traps and text strings
	# that will be used for variable substitution

	# Flush variables in case this is a re-load during run
	@snmpttconf = ();
	# @snmptt_conf_files = ();
	undef %event;
	undef %nodes_file_hash;		# NODES entries from NODES files
	
	foreach $snmpttconffile (@snmptt_conf_files)
	{
	  if ($DEBUGGING >= 1)
	  {
	    print "\n\tLoading $snmpttconffile\n";
	  }
	  
	  if ($syslog_system_enable == 1 && $daemon == 1)
	  {
	    syslog_system("Loading $snmpttconffile");
	  }
	  if ($eventlog_system_enable == 1 && $daemon == 1)
	  {
	    eventlog_system("Loading $snmpttconffile",9,$eventlog_information);
	  }
	  
	  unless (open SNMPTTCONF, $snmpttconffile)
	  {
	    warn "Could not open configuration file: $snmpttconffile ($!)";
	    if ($DEBUGGING >= 1)
	    {
	      print "\n\tCould not open configuration file: $snmpttconffile\n";
	    }
	    
	    if ($syslog_system_enable == 1 && $daemon == 1)
	    {
	      syslog_system("Could not open configuration file: $snmpttconffile");
	    }
	    if ($eventlog_system_enable == 1 && $daemon == 1)
	    {
	      eventlog_system("Could not open configuration file: $snmpttconffile",10,$eventlog_error);
	    }
	    
	    next;
	  }
	  $tempcount = 0;
	  while (<SNMPTTCONF>)
	  {
	    chomp;				#remove <cr> at end of line	
	    s/\015//;                       	# Remove any DOS carriage returns
	    push(@snmpttconf, $_);		#add to each line to @trapconf array
	    $tempcount++;
	  }
	  
	  if ($DEBUGGING >= 1)
	  {
	    print "\tFinished loading $tempcount lines from $snmpttconffile\n";
	  }
	  if ($syslog_system_enable == 1 && $daemon == 1)
	  {
	    syslog_system("Finished loading $tempcount lines from $snmpttconffile");
	  }
	  if ($eventlog_system_enable == 1 && $daemon == 1)
	  {
	    eventlog_system("Finished loading $tempcount lines from $snmpttconffile",11,$eventlog_information);
	  }
	}
	
	if ($DEBUGGING >= 1)
	{
		print "\nFinished loading configuration files\n";
	}
	
	# Process each line of snmptt.conf loaded into @snmpttconf array.
	# Add each EVENT to %event hash
	
	if ($DEBUGGING >= 1)
	{
		print "\nProcessing memory copy of configuration files\n";
	}
	
	$currentline=0;
	$tempcount=0;
	$tempcount2=0;
	
	while ($currentline <= $#snmpttconf)
	{
		$line = $snmpttconf[$currentline];
	
		if ($line =~ /^EVENT/)
		{
			#EVENT name .x.x.x.x "category" severity  (note: .x.x. is FULL OID!)
			#0      1       2        3         4
			
			# use shellwords to divide the two space separated values
			# without messing up quoted text and re-join using :: as the
			# delimiter
			#
			# hash elements:
			# 0: name of trap
			# 1: category
			# 2: severity
			# 3: FORMAT string
			# 4: EXEC string
			# 5: NODES string
                	# 6: REGEX array
                	# 7: MATCH array
                	# 8: DESC array
			
			$tempcount++;		# Temporary counter of the number of EVENTS found
			
			# Divide up line taking into consideration strings in quotes
			$temp = join "::::", shellwords($line);                

			#print "line is: $temp\n";
	
			# Split line up into multiple elements of an array using the new delimiter
			@temp = split ("::::",$temp);
			
			if (! ($temp[2] =~ /^(\.\d+)+$/) && ! ($temp[2] =~ /^(\.\d+)+\.\*$/))
			{
			  # OID contains text, so it's not fully numerical
			  if ($net_snmp_perl_enable == 1)
			  {
			    # Try to convert to numerical
			    if ($DEBUGGING >= 2)
			    {
			      print "Non-numeric OID: $temp[2].  Will attempt to translate to numerical\n";
			    }
			    my $temp2 = SNMP::translateObj("$temp[2]",0);
			    if (defined ($temp2) )
			    {
			      if ($DEBUGGING >= 2)
			      {
				print "  Translated to $temp2\n";
			      }
			      $temp[2] = $temp2;
			    }
			    else
			    {
			      # Could not translate, so skip this entry
			      if ($DEBUGGING >= 2)
			      {
				print "  Could not translate - skipping entry\n";
			      }
			      $currentline++; # Increment to the next line
			      next;
			    }
			  }
			  else
			  {
			    # Net-SNMP Perl module not enabled, and OID is not numeric so skip this entry
			    if ($DEBUGGING >= 2)
			    {
			      print "Non-numeric OID: $temp[2].  Net-SNMP Perl module not enabled - skipping entry\n";
			    }
			    $currentline++; # Increment to the next line
			    next;
			  }
			}
			
			# Clear out the FORMAT, EXEC and NODES lines in case they are not found.  Don't want old
			# data in them..
			$lineformat = '';
			$lineexec = '';
			$linenodes = '';
			$lineregex = '';
			@exec = ();
			@regex = ();
			@match = ();
                        @desc = ();
	
			$currentline++; # Increment to the next line which should be a FORMAT, EXEC or NODES
			$line2 = $snmpttconf[$currentline];
			
			while ( ($currentline <= $#snmpttconf) && !($line2 =~ /^EVENT/) )
			{
				# Keep going through the file until the next EVENT or the end of snmptt.conf
				# is reached
			
				# Check to see if next line is a FORMAT line (it should be!)
				if ($line2 =~ /^FORMAT/)
				{
					# It's a FORMAT line
					
					$lineformat = substr($line2,7);
				}
				elsif ($line2 =~ /^EXEC/)
				{
					# It's an EXEC line
					
					$lineexec = substr($line2,5);

					push (@exec,$lineexec);
				}
				elsif ($line2 =~ /^NODES/)
				{
					# It's a NODES line
					# Allow for multiple NODES lines - concatenate them

					$linenodes = $linenodes . substr($line2,6) . " ";
				}
				elsif ($line2 =~ /^REGEX/)
				{
					# It's a REGEX line
					
					$lineregex = substr($line2,6);

					# Remove spaces before and after
					$lineregex =~ /^\s*(.*?)\s*$/;
					$lineregex = $1;

					# Make sure it looks like ()() before accepting
					if ($lineregex =~ /\(.*?\)\(.*?\)/ )
					{
					  push (@regex,$lineregex);
					}
				}
				elsif ($line2 =~ /^MATCH/)
				{
					# It's a MATCH line
					
					$linematch = substr($line2,6);

					push (@match,$linematch);
				}
				elsif ( ($line2 =~ /^SDESC/) && ($description_mode == 1) )
				{
					# It's a DESC line
                                        
                                        $currentline++; # Increment to the next line
                                        $line2 = $snmpttconf[$currentline]; # Get next line

                                        while ( ($currentline <= $#snmpttconf) && !($line2 =~ /^EVENT/) && 
                                          !($line2 =~ /^EDESC/)) {
                                          print "DESC line: $line2\n";
                                          push (@desc,$line2);
                                          $currentline++; # Increment to the next line
                                          $line2 = $snmpttconf[$currentline]; # Get next line
                                        }
				}

                                $currentline++; # Increment to the next line
				$line2 = $snmpttconf[$currentline]; # Get next line
			}
			if ($lineformat ne '' || $exec[0] ne '')
			{			
				# At least the mandatory FORMAT line was defined, so we'll keep it
				# Stuff all elements into a one hash
				# $event{@temp[2]} = [@temp[1],@temp[3],@temp[4],$lineformat,$lineexec,$linenodes];

				if (!defined(@{ $event{$temp[2]} }[0])) 
				{  
					@{ $event{$temp[2]} }[0] = 0;
				}

				#@regex = qw/one two/;
								
				$countx = @{ $event{$temp[2]} }[0];
				#print "countx $countx\n";
				@{ $event{$temp[2]} }[1+$countx]= $temp[1];
				@{ $event{$temp[2]} }[2+$countx]= $temp[3];
				@{ $event{$temp[2]} }[3+$countx]= $temp[4];
				@{ $event{$temp[2]} }[4+$countx]= $lineformat;
				#@{ $event{$temp[2]} }[5+$countx]= $lineexec;
				@{ $event{$temp[2]} }[5+$countx]= [@exec];
				
				# If dynamic_nodes is disabled, process the nodes list to load any nodes
				# files first so they are not loaded each time a trap is processed
				if ($dynamic_nodes == 0)
				{
				  my @nodes2 = &process_nodes($linenodes);
				  @{ $event{$temp[2]} }[6+$countx]= "@nodes2";
				}
				else
				{
				  @{ $event{$temp[2]} }[6+$countx]= $linenodes;
				}

				@{ $event{$temp[2]} }[7+$countx]= [@regex];
				@{ $event{$temp[2]} }[8+$countx]= [@match];
				@{ $event{$temp[2]} }[9+$countx]= [@desc];
				@{ $event{$temp[2]} }[0]= 10+ @{ $event{$temp[2]} }[0];
				
				$tempcount2++;		# Temporary counter of the number of EVENTS found with at
							# least a FORMAT line specified
			}
	
			# Change currentline back so the next increment will re-evalutate the last line which
			# would have been an EVENT (to get out of the WHILE loop above), or the end of the file
			$currentline--;
		}
		$currentline++;		# Increment current line for next loop through snmpttconf array	
	}
	if ($DEBUGGING >= 1)
	{
		print "$tempcount EVENTs found\n";
		print "$tempcount2 EVENTs found that contain at least the mandatory FORMAT line\n";
		if($tempcount2<$tempcount)
		{
			print "  Warning:  EVENT entries were found that did not contain the mandatory FORMAT line!\n";
			print "  These EVENTS will be ignored!\n";
		}
		print "Finished processing memory copy of configuration files\n\n";
	}
	
	if ($DEBUGGING >= 2)
	{
		print "Printing out all the events in hash table:\n\n";
		foreach $key (sort keys %event)
		{		
			$l=0;
			while (defined($event{$key}[$l+1]))
			{	
				print  "hash: $key => $event{$key}[1+$l],$event{$key}[2+$l],$event{$key}[3+$l],$event{$key}[4+$l],$event{$key}[7+$l][0]\n";
			$l+=9;
			}

		}
		print "Finished printing out all events in hash table\n";
	}

	close SNMPTTCONF;
}

sub readtrap
{
	# Flush out @tempvar, @var and @entvar
	@tempvar = ();
	@var = ();
	@entvar = ();
	@entvarname = ();

        # Statistics
        $g_total_traps_received++;

	if ($DEBUGGING >= 2) 
	{	  
	  print "Reading trap.  Current time: " . scalar(localtime()). "\n";
	}
	
	if ( $daemon == 1)
	{
	  chomp($trap_date_time_epoch = (<$input>));	# Pull time trap was spooled
	  $trap_date_time_epoch =~ s(`)(')g;	#` Replace any back ticks with regular single quote
	}
	else
	{
	  $trap_date_time_epoch = time();		# Use current time as time trap was received
	}
	
	if ( $daemon == 1 && $use_trap_time == 1 )	# Daemon mode only
	{
	  $trap_date_time = localtime($trap_date_time_epoch);
	  $trap_date = strftime "%a %b %e %Y", localtime($trap_date_time_epoch);
	  $trap_time = strftime "%H:%M:%S", localtime($trap_date_time_epoch);
	}
	else
	{
	  $trap_date_time = localtime();
	  $trap_date = strftime "%a %b %e %Y", localtime();
	  $trap_time = strftime "%H:%M:%S", localtime();
	}
	
	chomp($tempvar[0]=<$input>);	# hostname
	$tempvar[0] =~ s(`)(')g;	#` Replace any back ticks with regular single quote
	
	chomp($tempvar[1]=<$input>);	# ip address
	$tempvar[1] =~ s(`)(')g;	#` Replace any back ticks with regular single quote
	
	# Some systems pass the IP address as udp:ipaddress:portnumber.  This will pull
	# out just the IP address
	$tempvar[1] =~ /(\d+\.\d+\.\d+\.\d+)/;
	$tempvar[1] = $1;
	
	#Process varbinds
	#Separate everything out, keeping both the variable name and the value
	$linenum = 1;
	while (defined($line = <$input>))
	{
	  $line =~ s(`)(')g;	#` Replace any back ticks with regular single quote
	  
	  if ($remove_backslash_from_quotes == 1)  # Remove escape from quotes if enabled
	  {
	    $line =~ s/\\\"/"/g;
	  }
	  
	  my $temp1;
	  my $temp2;

	  ($temp1, $temp2) = split (/ /, $line, 2);
  
	  chomp ($temp1);       # Variable NAME
	  chomp ($temp2);       # Variable VALUE
	  chomp ($line);
	
	  if ($linenum == 1)
	  {
	    if (defined($temp2) )	# Check if line 1 contains 'variable value' or just 'value'  
	    {
	      $variable_fix = 0;
	    }
	    else
	    {
	      $variable_fix = 1;
	    }
	  }

	  if ($variable_fix == 0 )
	  {
            # Make sure variable names are numerical
            $temp1 = translate_symbolic_to_oid($temp1);

            # If line begins with a double quote (") but does not END in a double quote then we need to merge
	    # the following lines together into one until we find the closing double quote.
	    # Net-SNMP sometimes divides long lines into multiple lines..
	    if ( ($temp2 =~ /^\"/) && ( ! ($temp2 =~ /\"$/)) )
	    {
	      if ($DEBUGGING >= 2) {
		print "  Multi-line value detected - merging onto one line...\n";
	      }
	      chomp $temp2;				# Remove the newline character
	      while (defined($line2 = <$input>))
	      {
		chomp $line2;
		$temp2.=" ".$line2;
		if ($line2 =~ /\"$/)
		{
		  last;
		}
	      }
	    }

	    # If the value is blank, set it to (null)
	    if ($temp2 eq "")
	    {
	      $temp2 = "(null)";
	    }
	    
	    if ($temp2 =~ /^\"/ && $temp2 =~ /$\"/)			# Have quotes around it?
	    {
	      $temp2 = substr($temp2,1,(length($temp2)-2));		# Remove quotes
	      push(@tempvar, $temp1);
	      push(@tempvar, $temp2);
	    }
	    else
	    {
	      push(@tempvar, $temp1);
	      push(@tempvar, $temp2);
	    }
	  }
	  else
	  {
	    # Should have been variable value, but only value found.  Workaround
	    # 
	    # Normally it is expected that a line contains a variable name
	    # followed by a space followed by the value (except for the 
	    # first line which is the hostname and the second which is the
	    # IP address).  If there is no variable name on the line (only
	    # one string), then add a variable string called 'variable' so 
	    # it is handled correctly in the next section.
	    # This happens with ucd-snmp v4.2.3 but not v4.2.1 or v4.2.5.
	    # This appears to be a bug in ucd-snmp v4.2.3.  This works around
	    # the problem by using 'variable' for the variable name, although 
	    # v4.2.3 should NOT be used with SNMPTT as it prevents SNMP V2 traps 
	    # from being handled correctly.

	    if ($DEBUGGING >= 2)
	    {
	      print "Data passed from snmptrapd is incorrect.  UCD-SNMP v4.2.3 is known to cause this\n";
	    }

	    # If line begins with a double quote (") but does not END in a double quote then we need to merge
	    # the following lines together into one until we find the closing double quote.
	    # Net-SNMP sometimes divides long lines into multiple lines..
	    if ( ($line =~ /^\"/) && ( ! ($line =~ /\"$/)) )
	    {
	      if ($DEBUGGING >= 2) {
		print "  Multi-line value detected - merging onto one line...\n";
	      }
	      chomp $line;				# Remove the newline character
	      while (defined($line2 = <$input>))
	      {
		chomp $line2;
		$line.=" ".$line2;
		if ($line2 =~ /\"$/)
		{
		  last;
		}
	      }
	    }

	    # If the value is blank, set it to (null)
	    if ($line eq "")
	    {
	      $line = "(null)";
	    }
	    
	    if ($line =~ /^\"/ && $line =~ /$\"/)			# Have quotes around it?
	    {
	      $line = substr($line,1,(length($line)-2));		# Remove quotes
	      push(@tempvar, "variable");
	      push(@tempvar, $line);
	    }
	    else
	    {
	      push(@tempvar, "variable");
	      push(@tempvar, $line);
	    }
	  }
	
	  $linenum++;
	}
	
	if ($DEBUGGING >= 2)
	{
		# Print out all items passed from snmptrapd
		print "\nItems passed from snmptrapd:\n";
		for ($i=0;$i <= $#tempvar;$i++)
		{
			print "value $i: $tempvar[$i]\n\n";
		}
	}
	
	# Copy what I need to new variables to make it easier to manipulate later
	
	# Standard variables
	$var[0] = $tempvar[0];		# hostname
	$var[1] = $tempvar[1];		# ip address
	$var[2] = $tempvar[3];		# uptime
	$var[3] = $tempvar[5];		# trapname / OID - assume first value after uptime is
					# the trap OID (value for .1.3.6.1.6.3.1.1.4.1.0)
	
	$var[4] = "";	# Clear ip address from trap agent
	$var[5] = "";	# Clear trap community string
	$var[6] = "";	# Clear enterprise

        # Make sure trap OID is numerical as event lookups are done using numerical OIDs only
        $var[3] = translate_symbolic_to_oid($var[3]);
        
	# Cycle through remaining variables searching for for agent IP (.1.3.6.1.6.3.18.1.3.0),
	# community name (.1.3.6.1.6.3.18.1.4.0) and enterpise (.1.3.6.1.6.3.1.1.4.3.0)
	# All others found are regular passed variables
	$j=0;
	for ($i=6;$i <= $#tempvar; $i+=2)
        {
          if ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.3.0$/)		# ip address from trap agent
          {
            $var[4] = $tempvar[$i+1];
          }
          elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.4.0$/)	# trap community string
          {
            $var[5] = $tempvar[$i+1];
          }
          elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.1.1.4.3.0$/)	# enterprise
          {
            # $var[6] = $tempvar[$i+1];
            # Make sure enterprise value is numerical
            $var[6] = translate_symbolic_to_oid($tempvar[$i+1]);
          }
          else	# application specific variables
          {
            $entvarname[$j] = $tempvar[$i];
            $entvar[$j] = $tempvar[$i+1];
            $j++;
          }
	}
              
     	if ($dns_enable == 1 && $var[0] =~  /^\d+\.\d+\.\d+\.\d+$/) # Only if it's not already resolved
	{
	  my $temp = gethostbyaddr(Socket::inet_aton($var[0]),Socket::AF_INET());
	  if (defined ($temp))
	  {
	    if ($DEBUGGING => 1)
	    {
	      print "Host IP address ($var[0]) resolved to: $temp\n\n";
	    }
	    $var[0] = $temp;
	  }
	  else
	  {
	    if ($DEBUGGING => 1)
	    {
	      print "Host IP address ($var[0]) could not be resolved by DNS.  Variable \$r / \$R etc will use the IP address\n\n";
	    }
	  }
	}

        # If the agent IP is blank, copy the IP from the host IP.
        # var[4] would only be blank if it wasn't passed from snmptrapd, which
        # should only happen with ucd-snmp 4.2.3, which you should be using anyway!
        if ($var[4] eq '')
        {
          $var[4] = $var[1];
          if ($DEBUGGING => 1)
          {
            print "Agent IP address was blank, so setting to the same as the host IP address of $var[1]\n\n";
          }
        }

        # If the agent IP is the same as the host IP, then just use the host DNS name, no need
        # to look up, as it's obviously the same..
        if ($var[4] eq $var[1])
        {
          if ($DEBUGGING => 1)
          {
            print "Agent IP address ($var[4]) is the same as the host IP, so copying the host name: $var[0]\n\n";
          }
          $agent_dns_name = $var[0];
        }
        else
        {
          $agent_dns_name = $var[4];     # Default to IP address
	  if ($dns_enable == 1 && $var[4] ne '')
	  {
	    my $temp = gethostbyaddr(Socket::inet_aton($var[4]),Socket::AF_INET());
	    if (defined ($temp))
	    {
	      if ($DEBUGGING => 1)
	      {
	        print "Agent IP address ($var[4]) resolved to: $temp\n\n";
	      }
	      $agent_dns_name = $temp;
	    }
	    else
	    {
	      if ($DEBUGGING => 1)
	      {
	        print "Agent IP address ($var[4]) could not be resolved by DNS.  Variable \$A etc will use the IP address\n\n";
	      }
            }
	  }
	}

	if ($strip_domain)
	{
	  $var[0] = strip_domain_name($var[0], $strip_domain);
	  $agent_dns_name = strip_domain_name($agent_dns_name, $strip_domain);
	}
	
	if ($DEBUGGING >= 1)
	{
		print "Trap received from $tempvar[0]: $tempvar[5]\n";
	}
	
	if ($DEBUGGING >= 2)
	{
		print "0:		hostname\n";
		print "1:		ip address\n";
		print "2:		uptime\n";
	 	print "3:		trapname / OID\n";
		print "4:		ip address from trap agent\n";
		print "5:		trap community string\n";
		print "6:		enterprise\n";
		print "0+:		passed variables\n\n";	
		
		#print out all standard variables
		for ($i=0;$i <= $#var;$i++)
		{
			print "Value $i: $var[$i]\n\n";
		}
               
                print "Agent dns name: $agent_dns_name\n\n";

		#print out all enterprise specific variables
		for ($i=0;$i <= $#entvar;$i++)
		{
			print "Ent Value $i (\$" . ($i+1) . "): $entvarname[$i]=$entvar[$i]\n\n";
		}
	}
		
	# Variables of trap received by SNMPTRAPD:
	# 
	# $var[0]		hostname
	# $var[1] 		ip address
	# $var[2] 		uptime
	# $var[3] 		trapname / OID
	# $var[4] 		ip address from trap agent
	# $var[5] 		trap community string
	# $var[6] 		enterprise
	#
	# $entvarname[0]	passed variable name 1
	# $entvarname[1]	passed variable name 2
	#
	# $entvar[0]		passed variable 1
	# $entvar[1]		passed variable 2
	# .
	# .
	# etc..
	#
	##############################################################################
	
}
	
sub searchfortrap
{
		
	# Variables of trap received by SNMPTRAPD:
	# 
	# $var[0]		hostname
	# $var[1] 		ip address
	# $var[2] 		uptime
	# $var[3] 		trapname / OID
	# $var[4] 		ip address from trap agent
	# $var[5] 		trap community string
	# $var[6] 		enterprise
	#
	# $entvarname[0]	passed variable name 1
	# $entvarname[1]	passed variable name 2
	#
	# $entvar[0]		passed variable 1
	# $entvar[1]		passed variable 2
	# .
	# .
	# etc..
	# 
	# $event hash for each trapped defined in snmptt.conf with following values:
	# 
	# 0: name of trap
	# 1: category
	# 2: severity
	# 3: FORMAT string
	# 4: EXEC string
	# 5: NODES string
	# 6: REGEX array
	# 7: MATCH array
	# 9: DESC array
	#
	# Example: 	To access FORMAT string for received trap:
	# 		$event{"$var[3]"}[3]
	#
	#		or
	# 		
	# 		$event{"$receivedtrap"}[3]	
	###############################################################################
	# Check to see if received trap is defined in the snmptt.conf, and perform variable
	# substitution, logging etc if found
	
	# Look for matching OID
	
	$processed = 0;
	$receivedtrap = $var[3];
	$receivedtrap_entry = $receivedtrap; 	# Will be used to processtrap to lookup EVENT info
						# Changed to actual EVENT entry if using a wildcard 

	$trap_attempted_to_log = 0;
	$trap_successfully_logged = 0;

	# Check for exact match
	if (exists $event{"$receivedtrap"})
	{
		if ($DEBUGGING >= 1)
		{
			print "Exact match of trap found in EVENT hash table\n\n";
		}
		
		&processtrap;
	}
	else
	{
		if ($DEBUGGING >= 1)
		{
			print "Exact match of trap NOT found in EVENT hash table\n\n";
		}
	}
	
	# Check for wildcard match in hash table if not already processed
	if ($processed == 0)
	{
		$receivedtraptemp = $receivedtrap;
		
		$counter = 0;	# Drill down only 40 times.  Should never need this, but
				# it's here to prevent an infinite loop in this while statement
				# just in case
		
		if ($DEBUGGING >= 1)
		{
		  print "Looking for wildcards in the EVENT hash table\n";
		}			
				
		while ($counter <= 40 && $receivedtraptemp ne '')
		{
			# Remove last digit of recevied trap, add a * and look for match
			$receivedtraptemp =~ s/(.*)\.\d+$/$1/;
	
			if ($DEBUGGING >= 2)
			{
				print "Drilling down looking for wildcards in the EVENT hash table\n";
				print $receivedtraptemp.".\*\n\n";
			}
			
			if (exists $event{$receivedtraptemp.'.*'})
			{
				$receivedtrap_entry = $receivedtraptemp.'.*';	# Set $receivedtrap to matching trap with wildcard
				&processtrap;
				last;	# found, so stop this while loop
			}
			$counter++;
		}
	}
	
	# Log to unknowntraplog etc if no match was found
	if ($processed == 0)
	{
		if ($DEBUGGING >= 1)
		{
			print "\n\nTrap not defined.  Nothing to do...\n\n";
		}

                # Statistics
                $g_total_traps_unknown++;
		
		if ($unknown_trap_log_enable == 1)
		{
			if ($unknown_trap_log_file ne "") {
				if (open LOGFILE, ">>$unknown_trap_log_file")
				{	
					print LOGFILE scalar($trap_date_time),": Unknown trap ($var[3]) received from $var[0] at: \n";
					#print out all standard variables
					for ($i=0;$i <= $#var;$i++)
					{
						print 	LOGFILE "Value $i: $var[$i]\n";
					}
					#print out all enterprise specific variables
					for ($i=0;$i <= $#entvar;$i++)
					{
						print LOGFILE "Ent Value $i: $entvarname[$i]=$entvar[$i]\n";
					}
					print LOGFILE "\n\n";
					close LOGFILE;
				}
				else
				{
					warn "Can not open log_file: $!";
				}
			}

			if ( ($mysql_dbi_enable == 1) ||
		   	     ($postgresql_dbi_enable == 1) ||
     			     ($dbd_odbc_enable == 1) ||
     			     ($sql_win32_odbc_enable == 1) ) {

				if ($DEBUGGING >= 1)
				{
					print "Logging unknown trap to SQL\n";
				}

				# Translate if enabled, and if we can
				$receivedtrap_trans = &translate_log_trap_oid_sub($receivedtrap);
			
				$enterprise_trans = &translate_enterprise_oid_format_sub($var[6]);

				$db_enterprise;
				if ($net_snmp_perl_enable == 1 && $db_translate_enterprise == 1)
				{
	      				$db_enterprise = $enterprise_trans;
				}
				else
				{
	      				$db_enterprise = $var[6];
				}

				$_ = $db_unknown_trap_format;
				
				&substitute;
				
				$message_short = $_;
			}
			
			if ($mysql_dbi_enable == 1 && defined ($dbh_mysql) && $mysql_dbi_table_unknown ne "")
			{
				
				# Backslash any quotes
				my $message_short2 = $message_short;
				$message_short2 =~ s(\')(\\\')g;   #'
				$message_short2 =~ s(\")(\\\")g;   #"

				my $community = $var[5];
				$community =~ s(\')(\\\')g;   #'
				$community =~ s(\")(\\\")g;   #"				
				
				unless (defined ($dbh_mysql->do(
					"INSERT INTO $mysql_dbi_table_unknown (trapoid, enterprise, 
					community, hostname, agentip, uptime, traptime, 
					formatline) VALUES (
					\'$receivedtrap_trans'\,\'$db_enterprise\',
					\'$community\',\'$agent_dns_name\',\'$var[4]\',
					\'$var[2]\',\'".$trap_date_time."\',
					\'$message_short2\')" ) ) )
					{
					  warn "MySQL error: Unable to perform INSERT INTO: ".$dbh_mysql->errstr."\n";

					  if ($syslog_system_enable == 1)
					  {
					    syslog_system("MySQL error: Unable to perform INSERT INTO: ".$dbh_mysql->errstr);
					  }
					  
					  if ($eventlog_system_enable == 1)
					  {
					    eventlog_system("MySQL error: Unable to perform INSERT INTO: ".$dbh_mysql->errstr,
					    		15,$eventlog_error);
					  }
				 }
			}

			if ($postgresql_dbi_enable == 1 && defined ($dbh_postgresql) && $postgresql_dbi_table_unknown ne "")
			{
				# Backslash any quotes
				my $message_short2 = $message_short;
				$message_short2 =~ s(\')(\\\')g;   #'
				$message_short2 =~ s(\")(\\\")g;   #"

				my $community = $var[5];
				$community =~ s(\')(\\\')g;   #'
				$community =~ s(\")(\\\")g;   #"
				
				unless (defined ($dbh_postgresql->do(
					"INSERT INTO $postgresql_dbi_table_unknown (trapoid, enterprise, 
					community, hostname, agentip, uptime, traptime, 
					formatline) VALUES (
					\'$receivedtrap_trans'\,
					\'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
					\'$var[2]\',\'".$trap_date_time."\',
					\'$message_short2\')" ) ) )
					{
					  warn "Postgres error: Unable to perform INSERT INTO: ".$dbh_postgresql->errstr."\n";

					  if ($syslog_system_enable == 1)
					  {
					    syslog_system("Postgres error: Unable to perform INSERT INTO: ".$dbh_postgresql->errstr);
					  }
					  
					  if ($eventlog_system_enable == 1)
					  {
					    eventlog_system("Postgres error: Unable to perform INSERT INTO: ".$dbh_postgresql->errstr,
					    		19,$eventlog_error);
					  }
				 }
			}
			
			if ($dbd_odbc_enable == 1 && defined ($dbh_odbc) && $dbd_odbc_table_unknown ne "")
			{
				# Double any single quotes
				my $message_short2 = $message_short;
				$message_short2 =~ s(\')('')g;     #'

				my $community = $var[5];
				$community =~ s(\')('')g;   #'
				
				unless (defined ($dbh_odbc->do(
					"INSERT INTO $dbd_odbc_table_unknown (trapoid, enterprise, 
					community, hostname, agentip, uptime, traptime, 
					formatline) VALUES (
					\'$receivedtrap_trans'\,
					\'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
					\'$var[2]\',\'".$trap_date_time."\',
					\'$message_short2\')" ) ) )
					{
					  warn "DBI DBD::ODBC error: Unable to perform INSERT INTO: ".$dbh_odbc->errstr."\n";

					  if ($syslog_system_enable == 1)
					  {
					    syslog_system("DBI DBD::ODBC error: Unable to perform INSERT INTO: ".$dbh_odbc->errstr);
					  }
					  
					  if ($eventlog_system_enable == 1)
					  {
					    eventlog_system("DBI DBD::ODBC error: Unable to perform INSERT INTO: ".$dbh_odbc->errstr,
					    		16,$eventlog_error);
					  }

					}
			}				 
			
			if ($sql_win32_odbc_enable == 1 && defined ($dbh_win32_odbc) && $sql_win32_odbc_table_unknown ne "")
			{
				# Double any single quotes
				my $message_short2 = $message_short;
				$message_short2 =~ s(\')('')g;     #'

				my $community = $var[5];
				$community =~ s(\')('')g;   #'
				
				# if defined, there's an error
				if (defined ($dbh_win32_odbc->Sql
				        ("INSERT INTO $sql_win32_odbc_table_unknown (trapoid, enterprise, 
					community, hostname, agentip, uptime, traptime, 
					formatline) VALUES (
					\'$receivedtrap_entry'\,
					\'$db_enterprise\',\'$community\',\'$agent_dns_name\',\'$var[4]\',
					\'$var[2]\',\'".$trap_date_time."\',
					\'$message_short2\')" ) ) )
				{					
				  warn "Unable to perform INSERT INTO: ".Win32::ODBC::Error()."\n";
				  
				  if ($eventlog_system_enable == 1)
				  {
				    eventlog_system("Win32::ODBC error: Unable to perform INSERT INTO: ".Win32::ODBC::Error(),
				    17,$eventlog_error);
				  }
				}
			}	
		}
	}
	

	# If keep_unlogged_traps is set to 0, or if we didn't even try to log, set it as a successful log 
	# so the trap file gets deleted from the spool dir (the trap wasn't found, or a node didn't match)
	if ($trap_attempted_to_log == 0 || $keep_unlogged_traps == 0)	
	{					
	  $trap_successfully_logged = 1;	
	}
	elsif ($keep_unlogged_traps == 1)	# Delete spool if ONE successfully logged
	{
	  if ($trap_successfully_logged >= 1)
	  {
	    $trap_successfully_logged = 1;
	  }
	  else
	  {
	    $trap_successfully_logged = 0;
	  }
	}
	elsif ($keep_unlogged_traps == 2)	# Delete spool if ALL successfully logged
	{
	  if ($trap_successfully_logged == $trap_attempted_to_log)
	  {
	    $trap_successfully_logged = 1;
	  }
	  else
	  {
	    $trap_successfully_logged = 0;
	  }
	}
}

sub findsnmpttini
{
	##############################################################################
	# Load config file start
	# 
	# For Linux / Unix, try in order:
        #   /etc/snmp/snmptt.ini
        #   /etc/snmptt.ini
        #   /usr/local/etc/snmp/snmptt.ini
        #   /usr/local/etc/snmptt.ini
        #   
	#
	# For Windows, try %SystemRoot%\snmptt.ini only.
	# 
	if ($ini ne '')
	{
	  $configfile = $ini;
	}
	else
	{	
	 	if ($^O ne "MSWin32")
		{
		  $configfile = '/etc/snmp/snmptt.ini';
		  if( open( CONFIG, '/etc/snmp/snmptt.ini' ) )
		  {
		    $configfile = '/etc/snmp/snmptt.ini';
		    close CONFIG;
		  }
		  elsif ( open( CONFIG, '/etc/snmptt.ini' ) )
		  {
		    $configfile = '/etc/snmptt.ini';
		    close CONFIG;
		  }
		  elsif ( open( CONFIG, '/usr/local/etc/snmp/snmptt.ini' ) )
		  {
		    $configfile = '/usr/local/etc/snmp/snmptt.ini';
		    close CONFIG;
		  }
		  elsif ( open( CONFIG, '/usr/local/etc/snmptt.ini' ) )
		  {
		    $configfile = '/usr/local/etc/snmptt.ini';
		    close CONFIG;
		  }
		}
		else {
		  $configfile = $ENV{'SystemRoot'}."\\snmptt.ini";
		}
	}
	##############################################################################
}
      
sub loadsnmpttini {

	##############################################################################
	# 
	# Load config file start
	# 
	use Config::IniFiles;
	my $cfg;

	#
	##############################################################################

	if( open( CONFIG, $configfile ) ) {
	  close CONFIG;
	  $cfg = new Config::IniFiles( -file => $configfile);
	  if ($DEBUGGING >= 0) {
	  	print "Config file $configfile loaded\n";
	  }
	}
	else
	{
	  if ($DEBUGGING >= 0) {
	  	print "Config file could not be loaded\n";
	  }
	  exit(1);
	}
	
	if (! $cfg)
	{
	  if ($DEBUGGING >= 0) {
		  print "Error in config file - please check the syntax in the config file\n";
	  }
	  exit(1);
	}

	@snmptt_conf_files = '';
	
	# General
	if ( ($cfg->val('General', 'mode') eq "daemon") || ($daemoncmdline == 1) )
	{
	  $daemon = 1;
	}
	else {
	  $daemon = 0;
	}
	
	$multiple_event = $cfg->val('General', 'multiple_event');
	$dns_enable = $cfg->val('General', 'dns_enable');
	$strip_domain = $cfg->val('General', 'strip_domain');
	@strip_domain_list = $cfg->val('General', 'strip_domain_list');
	$net_snmp_perl_enable = $cfg->val('General', 'net_snmp_perl_enable');
	$net_snmp_perl_best_guess = $cfg->val('General', 'net_snmp_perl_best_guess');
	$translate_log_trap_oid = $cfg->val('General', 'translate_log_trap_oid');
	$translate_value_oids = $cfg->val('General', 'translate_value_oids');
	$resolve_value_ip_addresses = $cfg->val('General', 'resolve_value_ip_addresses');
	$translate_enterprise_oid_format = $cfg->val('General', 'translate_enterprise_oid_format');
	$translate_trap_oid_format = $cfg->val('General', 'translate_trap_oid_format');
	$translate_varname_oid_format = $cfg->val('General', 'translate_varname_oid_format');
	$translate_integers = $cfg->val('General', 'translate_integers');
	$wildcard_expansion_separator = $cfg->val('General', 'wildcard_expansion_separator');
	$mibs_environment = $cfg->val('General', 'mibs_environment');
	$allow_unsafe_regex = $cfg->val('General', 'allow_unsafe_regex');
	$remove_backslash_from_quotes = $cfg->val('General', 'remove_backslash_from_quotes');
	$dynamic_nodes = $cfg->val('General', 'dynamic_nodes');
	$description_mode = $cfg->val('General', 'description_mode');
	$description_clean = $cfg->val('General', 'description_clean');
        
	# DaemonMode
	$daemon_fork = $cfg->val('DaemonMode', 'daemon_fork');
	$daemon_uid = $cfg->val('DaemonMode', 'daemon_uid');
	$spool_directory = $cfg->val('DaemonMode', 'spool_directory');
	$sleep = $cfg->val('DaemonMode', 'sleep');
	$use_trap_time = $cfg->val('DaemonMode', 'use_trap_time');
	$keep_unlogged_traps = $cfg->val('DaemonMode', 'keep_unlogged_traps');	
	
	# Logging
	$stdout_enable = $cfg->val('Logging', 'stdout_enable');
	$log_enable = $cfg->val('Logging', 'log_enable');
	$log_file = $cfg->val('Logging', 'log_file');
	$unknown_trap_log_enable = $cfg->val('Logging', 'unknown_trap_log_enable');
	$unknown_trap_log_file = $cfg->val('Logging', 'unknown_trap_log_file');
	$statistics_interval = $cfg->val('Logging', 'statistics_interval');
	$syslog_enable = $cfg->val('Logging', 'syslog_enable');
	$syslog_facility = $cfg->val('Logging', 'syslog_facility');
	@syslog_level_alert = $cfg->val('Logging', 'syslog_level_alert');
	@syslog_level_crit = $cfg->val('Logging', 'syslog_level_crit');
	@syslog_level_err = $cfg->val('Logging', 'syslog_level_err');	
	@syslog_level_warning = $cfg->val('Logging', 'syslog_level_warning');
	@syslog_level_notice = $cfg->val('Logging', 'syslog_level_notice');
	@syslog_level_info = $cfg->val('Logging', 'syslog_level_info');	
	@syslog_level_debug = $cfg->val('Logging', 'syslog_level_debug');
	$syslog_level = $cfg->val('Logging', 'syslog_level');
	$syslog_system_enable = $cfg->val('Logging', 'syslog_system_enable');
	$syslog_system_facility = $cfg->val('Logging', 'syslog_system_facility');
	$syslog_system_level = $cfg->val('Logging', 'syslog_system_level');
	$eventlog_enable = $cfg->val('Logging', 'eventlog_enable');
	@eventlog_type_information = $cfg->val('Logging', 'eventlog_type_information');
	@eventlog_type_warning = $cfg->val('Logging', 'eventlog_type_warning');
	@eventlog_type_error = $cfg->val('Logging', 'eventlog_type_error');	
	$eventlog_type = $cfg->val('Logging', 'eventlog_type');
	$eventlog_system_enable = $cfg->val('Logging', 'eventlog_system_enable');
	
	# Exec
	$exec_enable = $cfg->val('Exec', 'exec_enable');
	
	# SQL
	$db_translate_enterprise = $cfg->val('SQL', 'db_translate_enterprise');
	$db_unknown_trap_format = $cfg->val('SQL', 'db_unknown_trap_format');
	$mysql_dbi_enable = $cfg->val('SQL', 'mysql_dbi_enable');
	$mysql_dbi_host = $cfg->val('SQL', 'mysql_dbi_host');
	$mysql_dbi_port = $cfg->val('SQL', 'mysql_dbi_port');
	$mysql_dbi_database = $cfg->val('SQL', 'mysql_dbi_database');
	$mysql_dbi_table = $cfg->val('SQL', 'mysql_dbi_table');
	$mysql_dbi_table_unknown = $cfg->val('SQL', 'mysql_dbi_table_unknown');
	$mysql_dbi_username = $cfg->val('SQL', 'mysql_dbi_username');
	$mysql_dbi_password = $cfg->val('SQL', 'mysql_dbi_password');
	$mysql_ping_on_insert = $cfg->val('SQL', 'mysql_ping_on_insert');
        $mysql_ping_interval = $cfg->val('SQL', 'mysql_ping_interval');

	$postgresql_dbi_enable = $cfg->val('SQL', 'postgresql_dbi_enable');
	$postgresql_dbi_module = $cfg->val('SQL', 'postgresql_dbi_module');
	$postgresql_dbi_hostport_enable = $cfg->val('SQL', 'postgresql_dbi_hostport_enable');
	$postgresql_dbi_host = $cfg->val('SQL', 'postgresql_dbi_host');
	$postgresql_dbi_port = $cfg->val('SQL', 'postgresql_dbi_port');
	$postgresql_dbi_database = $cfg->val('SQL', 'postgresql_dbi_database');
	$postgresql_dbi_table = $cfg->val('SQL', 'postgresql_dbi_table');
	$postgresql_dbi_table_unknown = $cfg->val('SQL', 'postgresql_dbi_table_unknown');
	$postgresql_dbi_username = $cfg->val('SQL', 'postgresql_dbi_username');
	$postgresql_dbi_password = $cfg->val('SQL', 'postgresql_dbi_password');
	$postgresql_ping_on_insert = $cfg->val('SQL', 'postgresql_ping_on_insert');
        $postgresql_ping_interval = $cfg->val('SQL', 'postgresql_ping_interval');
	
	$dbd_odbc_enable = $cfg->val('SQL', 'dbd_odbc_enable');
	$dbd_odbc_dsn = $cfg->val('SQL', 'dbd_odbc_dsn');
	$dbd_odbc_table = $cfg->val('SQL', 'dbd_odbc_table');
	$dbd_odbc_table_unknown = $cfg->val('SQL', 'dbd_odbc_table_unknown');
	$dbd_odbc_username = $cfg->val('SQL', 'dbd_odbc_username');
	$dbd_odbc_password = $cfg->val('SQL', 'dbd_odbc_password');
	$dbd_odbc_ping_on_insert = $cfg->val('SQL', 'dbd_odbc_ping_on_insert');
        $dbd_odbc_ping_interval = $cfg->val('SQL', 'dbd_odbc_ping_interval');
	
	$sql_win32_odbc_enable = $cfg->val('SQL', 'sql_win32_odbc_enable');
	$sql_win32_odbc_dsn = $cfg->val('SQL', 'sql_win32_odbc_dsn');
	$sql_win32_odbc_table = $cfg->val('SQL', 'sql_win32_odbc_table');
	$sql_win32_odbc_table_unknown = $cfg->val('SQL', 'sql_win32_odbc_table_unknown');
	$sql_win32_odbc_username = $cfg->val('SQL', 'sql_win32_odbc_username');
	$sql_win32_odbc_password = $cfg->val('SQL', 'sql_win32_odbc_password');
	
	# Debugging
	if ($debugcmdline == 0) {
	  $DEBUGGING = $cfg->val('Debugging', 'DEBUGGING');
	}
	if ($debugfilecmdline == 0) {
	  $DEBUGGING_FILE = $cfg->val('Debugging', 'DEBUGGING_FILE');
	}
	
	# TrapFiles
	@snmptt_conf_files = $cfg->val('TrapFiles', 'snmptt_conf_files');
	
	$cfg->Delete;

	# 
	# Defaults Start
	# 
	if (! defined ($multiple_event)) { $multiple_event = 0} ;
	if (! defined ($dns_enable)) { $dns_enable = 0} ;
	if (! defined ($strip_domain)) { $strip_domain = 0} ;
	if (! defined ($net_snmp_perl_enable)) { $net_snmp_perl_enable = 0} ;
	if (! defined ($net_snmp_perl_best_guess)) { $net_snmp_perl_best_guess = 0} ;
	if (! defined ($translate_log_trap_oid)) { $translate_log_trap_oid = 0} ;
	if (! defined ($translate_value_oids)) { $translate_value_oids = 0} ;
	if (! defined ($resolve_value_ip_addresses)) { $resolve_value_ip_addresses = 0} ;
	if (! defined ($translate_enterprise_oid_format)) { $translate_enterprise_oid_format = 1} ;
	if (! defined ($translate_trap_oid_format)) { $translate_trap_oid_format = 1} ;
	if (! defined ($translate_varname_oid_format)) { $translate_varname_oid_format = 1} ;
	if (! defined ($translate_integers)) { $translate_integers = 0} ;
	if (! defined ($wildcard_expansion_separator)) { $wildcard_expansion_separator = " "} ;
	if (! defined ($allow_unsafe_regex)) { $allow_unsafe_regex = 0} ;
	if (! defined ($remove_backslash_from_quotes)) { $remove_backslash_from_quotes = 0} ;
	if (! defined ($dynamic_nodes)) { $dynamic_nodes = 0} ;
	if (! defined ($description_mode)) { $description_mode = 0} ;
	if (! defined ($description_clean)) { $description_clean = 1} ;

        if (! defined ($daemon_fork)) { $daemon_fork = 1} ;
	if (! defined ($daemon_uid)) { $daemon_uid = ''} ;
	if (! defined ($spool_directory)) { $spool_directory = ''} ;
	if (! defined ($sleep)) { $sleep = 5} ;
	if (! defined ($use_trap_time)) { $use_trap_time = 1} ;

	if (! defined ($keep_unlogged_traps)) { $keep_unlogged_traps = 1} ;
	if (! defined ($stdout_enable)) { $stdout_enable = 0} ;
	if (! defined ($log_enable)) { $log_enable = 1} ;
	if (! defined ($log_file)) { $log_file = ''} ;
	if (! defined ($unknown_trap_log_enable)) { $unknown_trap_log_enable = 0} ;
	if (! defined ($unknown_trap_log_file)) { $unknown_trap_log_file = ''} ;
	if (! defined ($syslog_enable)) { $syslog_enable = 0} ;
	if (! defined ($syslog_facility)) { $syslog_facility = 'local0'} ;
	if (! defined ($statistics_interval)) { $statistics_interval = 0} ;
	if (! defined ($syslog_level)) { $syslog_level = 'warning'} ;
	if (! defined ($syslog_system_enable)) { $syslog_system_enable = 0} ;
	if (! defined ($syslog_system_facility)) { $syslog_system_facility = 'local0'} ;
	if (! defined ($syslog_system_level)) { $syslog_system_level = 'warning'} ;
	if (! defined ($eventlog_enable)) { $eventlog_enable = 0} ;
	if (! defined ($eventlog_enable)) { $eventlog_enable = 'WARNING'} ;
	if (! defined ($eventlog_system_enable)) { $eventlog_system_enable = 0} ;	
	if (! defined ($exec_enable)) { $exec_enable = 1} ;
	if (! defined ($db_translate_enterprise)) { $db_translate_enterprise = 0} ;
	if (! defined ($db_unknown_trap_format)) { $db_unknown_trap_format = '$-*'} ;
	if (! defined ($mysql_dbi_enable)) { $mysql_dbi_enable = 0} ;
	if (! defined ($mysql_dbi_host)) { $mysql_dbi_host = 'localhost'} ;
	if (! defined ($mysql_dbi_port)) { $mysql_dbi_port = '3306'} ;
	if (! defined ($mysql_dbi_database)) { $mysql_dbi_database = ''} ;
	if (! defined ($mysql_dbi_table)) { $mysql_dbi_table = ''} ;
	if (! defined ($mysql_dbi_table_unknown)) { $mysql_dbi_table_unknown = ''} ;
	if (! defined ($mysql_dbi_username)) { $mysql_dbi_username = ''} ;
	if (! defined ($mysql_dbi_password)) { $mysql_dbi_password = ''} ;
	if (! defined ($mysql_ping_on_insert)) { $mysql_ping_on_insert = 1} ;
	if (! defined ($mysql_ping_interval)) { $mysql_ping_interval = 500} ;
	if (! defined ($postgresql_dbi_enable)) { $postgresql_dbi_enable = 0} ;
	if (! defined ($postgresql_dbi_module)) { $postgresql_dbi_module = 0} ;
	if (! defined ($postgresql_dbi_hostport_enable)) { $postgresql_dbi_hostport_enable = 0} ;
	if (! defined ($postgresql_dbi_host)) { $postgresql_dbi_host = 'localhost'} ;
	if (! defined ($postgresql_dbi_port)) { $postgresql_dbi_port = '5432'} ;
	if (! defined ($postgresql_dbi_database)) { $postgresql_dbi_database = ''} ;
	if (! defined ($postgresql_dbi_table)) { $postgresql_dbi_table = ''} ;
	if (! defined ($postgresql_dbi_table_unknown)) { $postgresql_dbi_table_unknown = ''} ;
	if (! defined ($postgresql_dbi_username)) { $postgresql_dbi_username = ''} ;
	if (! defined ($postgresql_dbi_password)) { $postgresql_dbi_password = ''} ;
	if (! defined ($postgresql_ping_on_insert)) { $postgresql_ping_on_insert = 1} ;
	if (! defined ($postgresql_ping_interval)) { $postgresql_ping_interval = 500} ;
	if (! defined ($dbd_odbc_enable)) { $dbd_odbc_enable = 0} ;
	if (! defined ($dbd_odbc_dsn)) { $dbd_odbc_dsn = ''} ;
	if (! defined ($dbd_odbc_table)) { $dbd_odbc_table = ''} ;
	if (! defined ($dbd_odbc_table_unknown)) { $dbd_odbc_table_unknown = ''} ;
	if (! defined ($dbd_odbc_username)) { $dbd_odbc_username = ''} ;
	if (! defined ($dbd_odbc_password)) { $dbd_odbc_password = ''} ;
	if (! defined ($dbd_odbc_ping_on_insert)) { $dbd_odbc_ping_on_insert = 1} ;
	if (! defined ($dbd_odbc_ping_interval)) { $dbd_odbc_ping_interval = 500} ;
	if (! defined ($sql_win32_odbc_enable)) { $sql_win32_odbc_enable = 0} ;
	if (! defined ($sql_win32_odbc_dsn)) { $sql_win32_odbc_dsn = ''} ;
	if (! defined ($sql_win32_odbc_table)) { $sql_win32_odbc_table = ''} ;
	if (! defined ($sql_win32_odbc_table_unknown)) { $sql_win32_odbc_table_unknown = ''} ;
	if (! defined ($sql_win32_odbc_username)) { $sql_win32_odbc_username = ''} ;
	if (! defined ($sql_win32_odbc_password)) { $sql_win32_odbc_password = ''} ;
	if (! defined ($DEBUGGING)) { $DEBUGGING = 0} ;
	if (! defined ($DEBUGGING_FILE)) { $DEBUGGING_FILE = ''} ;

	# Make sure it's at least 3 characters, and remove quotes around wildcard_expansion_separator
	if (length ($wildcard_expansion_separator) < 3)
	{
	  $wildcard_expansion_separator = "x x";
	}
	$wildcard_expansion_separator = substr($wildcard_expansion_separator,1,(length($wildcard_expansion_separator)-2));	
	#
	# Defaults End
	# 
	
	# print "Config file loaded\n";
	
	# 
	# Load config file end
	# 
	##############################################################################
}

sub signal_handler_reload {
	#
	# Daemon reload
	#
	$timetoreload = 1;
}

sub signal_handler_die {
	#
	# Daemon die
	#
	$timetodie = 1;
}

sub syslog_system {
  my $message = $_[0];
  if (Sys::Syslog::openlog('snmptt-sys[' . $> . ']', '',$syslog_system_facility) )
  {
    Sys::Syslog::syslog ($syslog_system_level, $message,1);
    Sys::Syslog::closelog();
  }
  else
  {
    warn "Can not open log_file: $!";
    print "Can not open syslog.  Message to be logged: $message\n" if ($DEBUGGING >= 1);
  }
  
}

sub eventlog_system {
  my $message = $_[0];
  my $eventid = $_[1];
  my $type = $_[2];
  my $trap_log = $_[3];		# Should be 1 if this is a trap log.  Used
  				# to set $trap_successfully_logged variable below.

  my %event_entry = ('Source' => "SNMPTT",
  		     'EventType' => $type,
		     'Category' => NULL,
		     'EventID' => $eventid,
		     'Strings' => $message);
  
  if (my $eventlog=Win32::EventLog->new('Application') )
  {
    unless ($eventlog->Report(\%event_entry) )
    {
      warn "Can not create event log entry: $!";
      $eventlog->Close();
      return 1;
    }
    $eventlog->Close();
    return 0;
  }
  else
  {
    warn "Can not open log_file: $!";
    return 1;
  }
}  
  
sub checkip
{
  my $node1 = $_[0];		# Should be a plain IP address
  my $node2 = $_[1];		# Can be a plain IP address, CIDR or range

  # Remove all white space
  $node1 =~ s/\s*//g;
  $node2 =~ s/\s*//g;
  
  # Check for an exact match
  if ($node1 eq $node2)
  {
    return 1;
  }

  #print "node1:$node1\n";
  
  # First is an IP address, and second IP address is a CIDR address
  if ($node1 =~ /^\d+\.\d+\.\d+\.\d+$/ && $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/)
  {   
    # Get IP address in binary
    $node1 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
    my $node1_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4);
    
    # Get CIDR network address in binary
    $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/;
    my $cidr_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4);
    my $net_bits = $5;
    my $host_bits = 32 - $net_bits;
    $cidr_binary = substr($cidr_binary, 0, $net_bits);
    
    # Get network number for ip_address
    my $node1_binary_net = substr($node1_binary, 0, $net_bits);
    
    if ($node1_binary_net eq $cidr_binary)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  # First is an IP address, and second IP address is a network range
  elsif ($node1 =~ /^\d+\.\d+\.\d+\.\d+$/ && $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)/)
  {
    # Get IP address in binary
    $node1 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
    my $node1_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4);
    
    # Get left network address in binary
    $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)/;
    my $left_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4);
    my $right_binary = dec2bin($5) . dec2bin($6) .dec2bin($7) .dec2bin($8);
    
    if (bin2dec($node1_binary) >= bin2dec($left_binary) &&
    	bin2dec($node1_binary) <= bin2dec($right_binary))
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  # Didn't match at all, so return 0.
  else
  {
	  return 0;
  }

}

sub dec2bin {
    my $str = unpack("B8", pack("C", shift));
    #$str =~ s/^0+(?=\d)//;   # otherwise you'll get leading zeros
    return $str;
}

sub bin2dec {
    return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}

sub translate_value_oids_sub
{
  my $string = $_[0];

  if ($string =~ /\.\d+/)	# If it appears to contain an OID
  {
    if ($DEBUGGING >= 2)
    {
      print "  Value appears to contain an OID or IP address.\n";
    }
    my $string_temp = $string;
    my @oids = ();
    my $done = 1;
    my $noinf = 0;

    # Find any IP addresses and remove from $string_temp
    
    $noinf = 0;
    while ($done)
    {
      if ($string_temp =~ /(?<!\.)(?<!\d)(\d+\.\d+\.\d+\.\d+)(?!\.\d)(?!\d)/ ) { $found = 1; }
      #if ($string_temp =~ /(?<!\.)(\d+\.\d+\.\d+\.\d+)(?!\.)/ ) { $found = 1; }
      else { $found = 0; }

      if ($found)
      {
	#print "ip:$&\n";
	$string_temp =~ s/(?<!\.)$1(?!\.)//g;	# Remove IP from string_temp
      }
      else
      {
	$done = 0;
      }
      $noinf++;
      if ($noinf >= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an
                         # infinit loop in the while.  Shouldn't, but just in case..
      {
	$done = 0;
        if ($DEBUGGING >= 2)
        {
          print "  Possible infinit loop (1) in translate_value_oids_sub averted.\n";
        }
      }
    }

    # Find all the OIDs and put them in a @oids array, and remove from $string_temp
    $done = 1;
    my $oid_found = 0;
    $noinf = 0;
    while ($done)
    {   
      if ($string_temp =~ /(?<!\d)(\.\d+)+/) {	$found = 1; }  # don't match n.n.n but do match .n.n.n
      else { $found = 0; }

      if ($found)
      {
	my $oid = $&;
	push (@oids, $oid);
	#print "oid:$oid\n";
	#print "\$string_temp before: $string_temp\n";
	$string_temp =~ s/(?<!\d)$oid(?!\.\d)//g;	# Remove OID from string_temp
	#print "\$string_temp after : $string_temp\n";
	$oid_found = 1;
      }
      else
      {
	$done = 0;
      }
      $noinf++;
      if ($noinf >= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an
                         # infinit loop in the while.  Shouldn't, but just in case..
      {
	$done = 0;
        if ($DEBUGGING >= 2)
        {
          print "  Possible infinit loop (2) in translate_value_oids_sub averted.\n";
        }
      }
    }

    if ($oid_found == 1 && $DEBUGGING >= 2)
    {
      print "  Value contains an OID.\n";
    }
    
    # Reverse sort the list to make sure large OIDs are done first, so we don't get half
    # converted OIDs.  Probably not needed due to look ahead and behind expression below
    @oids = reverse sort(@oids);
 
    #print "oids: @oids\n";
     
    # Replace all OIDs in $string with textual names if possible
    foreach my $oid (@oids)
    {
      my $temp = my_translateObj($oid,$translate_value_oids);
      if (defined ($temp))
      {
	$string =~ s/(?<!\.|\d)$oid(?!\.\d)/$temp/g;
	if ($oid_found == 1 && $DEBUGGING >= 2)
	{
	  print "    OID: $oid=$temp\n";
	}
      }
    }
  }
  else
  {
    if ($DEBUGGING >= 2)
    {
      print "  Value does not appear to contain an OID\n";
    }
  }
  # Return the new string
  return $string;    
}

sub resolve_value_ip_addresses_sub
{
  my $string = $_[0];

  if ($string =~ /(?<!\.)(\d+\.\d+\.\d+\.\d+)(?!\.)/)	# If it appears to contain an IP address
  {
    if ($DEBUGGING >= 2)
    {
      print "  Value appears to contain an IP address.\n";
    }
    my $string_temp = $string;
    my @hostnames = ();
    my $done = 1;
    my $noinf = 0;

    # Find any IP addresses and put them in a @oids array, and remove from $string_temp
    
    $done = 1;
    my $hostname_found = 0;
    $noinf = 0;
    while ($done)
    {
      if ($string_temp =~ /(?<!\.)(?<!\d)(\d+\.\d+\.\d+\.\d+)(?!\.\d)(?!\d)/ ) { $found = 1; }
      #if ($string_temp =~ /(?<!\.)(\d+\.\d+\.\d+\.\d+)(?!\.)/ ) { $found = 1; }
      else { $found = 0; }

      if ($found)
      {
	my $hostname = $&;
	push (@hostnames, $hostname);
	#print "ip:$&\n";
	$string_temp =~ s/(?<!\.)$1(?!\.)//g;	# Remove IP from string_temp
	$hostname_found = 1;
      }
      else
      {
	$done = 0;
      }
      $noinf++;
      if ($noinf >= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an
                         # infinit loop in the while.  Shouldn't, but just in case..
      {
	$done = 0;
        if ($DEBUGGING >= 2)
        {
          print "  Possible infinit loop (1) in resolve_value_ip_addresses_sub averted.\n";
        }
      }
    }

    if ($hostname_found == 1 && $DEBUGGING >= 2)
    {
      print "  Value contains an IP address.\n";
    }
    
    #print "hostnames: @hostnames\n";
     
    # Replace all ip addresses in $string with textual names if possible
    foreach my $hostname (@hostnames)
    {
      my $temp = gethostbyaddr(Socket::inet_aton($hostname),Socket::AF_INET());
      if (defined ($temp))
      {
	if ($DEBUGGING => 2)
	{
	  print "    IP address ($hostname) resolved to: $temp\n";
	}
	if ($strip_domain)
	{
	  $temp = strip_domain_name($temp, $strip_domain);
	}
	$string =~ s/(?<!\.|\d)$hostname(?!\.\d)/$temp/g;
      }
      else
      {
	if ($DEBUGGING => 2)
	{
	  print "    IP address ($hostname) could not be resolved by DNS.\n";
	}
      }
    }
  }
  else
  {
    if ($DEBUGGING >= 2)
    {
      print "  Value does not appear to contain an IP address\n";
    }
  }
  # Return the new string
  return $string;    
}

sub match
{
  my $match = shift;
  my $value;
  my $not = 0;
  my $result = 0;

  $match =~ /^\$(\d+):(.*)/;
  $value = $entvar[$1-1];
  $match = $2;

  if ($value eq '' || $match eq '')
  {
    return 0;
  }
  if ($match =~ /^\!(.*)/) 
  {
    $not = 1;
    $match = $1;
  }
  else 
  {  
    $not = 0; 
  }
  
  # Match is a REGEX
  if ($match =~ /\(.*\)/)
  {
    #print "Regex detected\n";
      
    $result = match_regex($value, $match);
    $result = match_result($result, $not);
    if ($DEBUGGING >= 1)
    {
      print "    REGEX: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . 
             ($result == 1 ? "true" : "false") . "\n";
    }
  }
  # Match is an IP address
  elsif ($match =~ /\d+\.\d+\.\d+\.\d+/)
  {
    #print "IP address of some sort\n";
    $result = checkip($value, $match);
    $result = match_result($result, $not);
    if ($DEBUGGING >= 1)
    {
      print "    IP ADDRESS: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . 
             ($result == 1 ? "true" : "false") . "\n";
    }
  }
  # Match is a number
  elsif ($match =~ /\d+/)
  {
    #print "Number detected\n";
    $result = match_number($value, $match);
    $result = match_result($result, $not);
    if ($DEBUGGING >= 1)
    {
      print "    NUMBER: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . 
             ($result == 1 ? "true" : "false") . "\n";
    }
  }
  # Unknown entry - ignore it
  else
  {
    if ($DEBUGGING >= 1)
    {
      print "    IGNORED: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . "\n";
      $result = 0;
    }
  }
  
  if ($result == 1)
  {
    #print "true\n";
    return 1;
  }
  else
  {
    #print "false\n";
    return 0;
  }
}

sub match_result
{
  my $result = shift;
  my $not = shift;

  if ($not == 0)
  {
    return $result;
  }
  else
  {
    if ($result >= 1)
    {
      return 0;
    }
    else
    {
      return 1;
    }
  }
  return 0;
}

sub match_number
{
  my $value = shift;
  my $match = shift;

  my $result;

  if ($match == $value)
  {
    return 1;
  }
  elsif ($match =~ /,/)
  {
    my @temp = split (",",$match);
    foreach my $a (@temp)
    {
      if ($a == $value)
      {
	return 1;
      }
    }
  }
  elsif ($match =~ /^\>(.*)/)
  {
    if ($value > $1)
    {
      return 1;
    }
  }
  elsif ($match =~ /^\<(.*)/)
  {
    if ($value < $1)
    {
      return 1;
    }
  }
  elsif ($match =~/(\d+)-(\d+)/)
  {
    if ($value => $1 && $value <= $2)
    {
      return 1;
    }
  }
  return 0;
}

sub match_regex
{
  my $value = shift;
  my $regex_temp = shift;

  my $result;
  my $modifier_i = 0;

  #print "value: $value\n";
  #print "regex: $regex_temp\n";

  if ($regex_temp =~ /i$/)
  {
    #print "! i modifier\n";
    $modifier_i = 1;
  }
  
  $regex_temp =~ /\((.*)\)/;
  $regex_temp = $1;

  #print ":$regex_temp:\n";
       			  
  if ($regex_temp ne '')
  {
    if ($DEBUGGING >= 1)
    {
      print "  Doing REGEX MATCH: ($regex_temp)\n";
    }
    if ($modifier_i == 1)
    {
      if ($value =~ /$regex_temp/i)
      {
	#print "Match!\n";
	return 1;
      }
    }
    else
    {
      if ($value =~ /$regex_temp/)
      {
	#print "Match!\n";
	return 1;
      }
    }
  }
  return 0;
}

sub my_translateObj
{
  my $oid = shift;
  my $mode = shift;
  
  my $use_long = 0;
  my $include_module = 0;
  my $temp;
    
  if ($mode == 1) 	# Short text
  {
    $use_long = 0;
    $include_module = 0;
  }
  elsif ($mode == 2)	# Short module::text
  {
    $use_long = 0;
    $include_module = 1;
  }
  elsif ($mode == 3)	# Long text
  {
    $use_long = 1;
    $include_module = 0;
  }
  elsif ($mode == 4)	# Long module::text
  {
    $use_long = 1;
    $include_module = 1;
  }
  
  $temp = &SNMP::translateObj($oid,$use_long,$include_module);
  if (defined ($temp))
  {
    # If it ends in a single ., chop it off
    if ($temp =~ /\.$/)
    {
      chop $temp;
    }
  }
  return $temp;	# Will be either the translated trap, or undef if translateObj failed
}

# Strip domain name from hostname
sub strip_domain_name
{
  my $name = shift;
  my $mode = shift;

  # If mode = 1, strip off all domain names leaving only the host
  if ($mode == 1 && !($name =~ /^\d+\.\d+\.\d+\.\d+$/))
  {
    if ($name =~ /\./)      # Contain a . ?
    {
      $name =~ /^([^\.]+?)\./;
      $name = $1;
    }
  }
  # If mode = 2, strip off the domains as listed in strip_domain_list in .ini file
  elsif ($mode == 2 && !($name =~ /^\d+\.\d+\.\d+\.\d+$/))
  {
    if (defined (@strip_domain_list))
    {
      foreach $strip_domain_list_temp (@strip_domain_list)
      {
	if ($strip_domain_list_temp =~ /^\..*/) # If domain from list starts with a '.' then remove it first
	{
	  ($strip_domain_list_temp) = $strip_domain_list_temp =~ /^\.(.*)/;
	}
	
	if ($name =~ /^.+\.$strip_domain_list_temp/)      # host is something . domain name?	      
	{
	  $name =~ /(.*)\.$strip_domain_list_temp/;	# strip the domain name
	  $name = $1;
	  last;  # Only process once 
	}
      }
    }
  }
  return $name;
}

sub my_getType
{
  my $oid = shift;

  # Problem #1: UCD-SNMP 4.2.3 allows getType and mapEnum to be passed a numerical OID.  
  # Net-SNMP 5.0.8 does not.  Because of this, lets try using the OID first.  If that 
  # fails, then we try the short textual OID instead. 

  my $type = SNMP::getType($oid);
  if (defined($type))
  {
    #print "my_getType: numeric\n";
    return $type;
  }
  else
  {
    my $temp_textOID = &my_translateObj($oid,0);
    #print "temp_textOID:$temp_textOID\n";
    my $type = &SNMP::getType($temp_textOID);
    if (defined($type))
    {
      #print "my_getType: symbolic\n";
      return $type;
    }
    else
    {
      #print "my_getType: failed\n";
      return undef;
    }
  }
}

sub my_mapEnum
{
  my $oid = shift;
  my $value = shift;

  # Problem #1: UCD-SNMP 4.2.3 allows getType and mapEnum to be passed a numerical OID.  
  # Net-SNMP 5.0.8 does not.  Because of this, lets try using the OID first.  If that 
  # fails, then we try the short textual OID instead. 

  my $enum = SNMP::mapEnum($oid, $value);
  if (defined($enum))
  {
    #print "my_mapEnum: numeric\n";
    return $enum;
  }
  else
  {
    my $temp_textOID = &my_translateObj($oid,0);
    #print "temp_textOID:$temp_textOID\n";
    my $enum = &SNMP::mapEnum($temp_textOID, $value);
    if (defined($enum))
    {
      #print "my_mapEnum: symbolic\n";
      return $enum;
    }
    else
    {
      #print "my_mapEnum: failed\n";
      return undef;
    }
  }
}
 
sub process_nodes
{
  my $nodes_list = shift;
  my @nodes = ();
  my @nodes2 = ();

  # print "Processing NODES\n";
  
  # Put all the NODES entries into @nodes, and then go through them all and put
  # them into @nodes2.  This is done so files that are in the NODES list can be
  # read in and merged into @nodes2 to allow a NODES line to contain both host
  # names / ip addresses AND the entries from nodes files.
  @nodes = split /\s/, $nodes_list;
  foreach my $a (@nodes)
  {
    # Contain a \ or /?  Must be a filename and not an IP address / network number
    if ( ($a =~ /\\|\//) && !($a =~ /\d+\.\d+\.\d+\.\d+/))
    {
      if ($dynamic_nodes == 1)		# Dynamic enabled, always load the file
      {	
	#print "!Dynamic enabled\n";
	if (open NODESFILE, $a)
	{
	  while (defined($line = <NODESFILE>))
	  {
	    chomp($line);
	    # Allow comment lines starting with a #
	    if (!($line =~ /((^#)|(\s+#)).*/))
	    {
	      my @temp2 = split /\s/, $line;
	      foreach my $b (@temp2)
	      {
		push (@nodes2, $b);
	      }
	    }
	  }
	  close LOGFILE;
	}
	else
	{
	  warn "Can not open NODES file: $event2[5] $!";
	}
      }
      else				# Not dynamic, so check nodes_file_hash first before loading
      {
	#print "!Dynamic NOT enabled\n";
	if ($nodes_file_hash{$a})
	{
	  push(@nodes2, $nodes_file_hash{$a});
	  #print " !Found in hash\n";
	}
	else
	{
	  #print " !Not found in hash\n";
	  if (open NODESFILE, $a)
	  {
	    while (defined($line = <NODESFILE>))
	    {
	      chomp($line);
	      # Allow comment lines starting with a #
	      if (!($line =~ /((^#)|(\s+#)).*/))
	      {
		my @temp2 = split /\s/, $line;
		foreach my $b (@temp2)
		{
		  push (@nodes2, $b);
		}
	      }
	    }
	    close LOGFILE;
	    $nodes_file_hash{$a} = "@nodes2";
	  }
	  else
	  {
	    warn "Can not open NODES file: $event2[5] $!";
	  }
	}
      }
    }
    else 	# Must be a single node
    {
      push (@nodes2, $a);
    }
  }
  return @nodes2;
}

# Used when reading received traps to symbolic names in variable names and
# values to numerical
sub translate_symbolic_to_oid
{
  my $temp = shift;
  
  # Check to see if OID passed from snmptrapd is fully numeric.  If not, try to translate
  if (! ($temp =~ /^(\.\d+)+$/)) 
  {
    # Not numeric
    # Try to convert to numerical
    if ($DEBUGGING >= 2) {
      print "Symbolic trap variable name detected ($temp).  Will attempt to translate to a numerical OID\n";
    }
    if ($net_snmp_perl_enable == 1)
    {
      my $temp3 = SNMP::translateObj("$temp",0);
      if (defined ($temp3) ) {
        if ($DEBUGGING >= 2) {
          print "  Translated to $temp3\n\n";
        }
        $temp = $temp3;
      }
      else {
        # Could not translate default to numeric
        if ($DEBUGGING >= 2) {
          print "  Could not translate - will leave as-is\n\n";
        }
      }
    }
    else
    {
      if ($DEBUGGING >= 2) {
        print "  Could not translate - Net-SNMP Perl module not enabled - will leave as-is\n\n ";
      }
    }
  }
  return $temp;
}

sub translate_log_trap_oid_sub
{	
	my $trap_oid_temp = shift;
	
	if ($net_snmp_perl_enable == 1 && $translate_log_trap_oid)
	{
		# Try to convert to text
		if ($DEBUGGING >= 2) {
			print "\nOID of trap: $trap_oid_temp.  Will attempt to translate to text\n";
		}
		my $temp2 = my_translateObj("$trap_oid_temp",$translate_log_trap_oid);
		if (defined ($temp2) ) {
			if ($DEBUGGING >= 2) {
  				print "  Translated to $temp2\n";
			}
			$trap_oid_temp = $temp2;
		}
		else {
			# Could not translate default to numeric
			if ($DEBUGGING >= 2) {
  				print "  Could not translate - defaulting to numeric\n";
			}
		}
	}
	return $trap_oid_temp;
}

sub translate_enterprise_oid_format_sub
{
	my $enterprise_oid_temp = shift;

	if ($net_snmp_perl_enable == 1 && $var[6] ne '')
	{
		if ($DEBUGGING >= 2) {
			print "\nOID of enterprise: $var[6].  Will attempt to translate to text\n";
		}
		my $temp = my_translateObj("$var[6]",$translate_enterprise_oid_format);
		if (defined ($temp) ) {
			if ($DEBUGGING >= 2) {
  				print "  Translated to $temp\n";
			}
			$enterprise_oid_temp = $temp;
		}
		else 
		{
			# Could not translate default to numeric
			if ($DEBUGGING >= 2) {
  				print "  Could not translate - defaulting to numeric\n";
			}
		}
	}
	return $enterprise_oid_temp;

}

# Log statistics to syslog, event log etc
sub log_statistics {

  if ($DEBUGGING >= 1)
  {
    print "Total traps received:    $g_total_traps_received\n";
    print "Total traps translated:  $g_total_traps_translated\n";                
    print "Total traps ignored:     $g_total_traps_ignored\n";                
    print "Total unknown traps:     $g_total_traps_unknown\n\n";
  }
  
  if ($syslog_system_enable == 1)  
  { 
    syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown");                
  }
  if ($eventlog_system_enable == 1)
  {
    my $message = "" . \
    "Total traps received:    $g_total_traps_received\n" . \
    "Total traps translated:  $g_total_traps_translated\n" . \
    "Total traps ignored:     $g_total_traps_ignored:\n" . \
    "Total unknown traps:     $g_total_traps_unknown\n";
    
    eventlog_system($message,1,$eventlog_information);
  }

  $g_last_statistics_logged = time();
}

sub signal_log_statistics {
	#
	# Statistics log
	#
	$timetologstatistics = 1;
}
  
# Open connections to MySQL, PostresQL, ODBC etc
sub create_db_connections {
  &dbh_mysql_connect();
  &dbh_postgresql_connect();
  &dbh_odbc_connect();
  &dbh_win32_odbc_connect();
}

sub close_db_connections {
  &dbh_mysql_close();
  &dbh_postgresql_close();
  &dbh_odbc_close();
  &dbh_win32_odbc_close();
}

sub dbh_mysql_close {
  if (defined $dbh_mysql)
  {
    $dbh_mysql->disconnect;
    $dbh_mysql = undef;
  }
}

sub dbh_postgresql_close {
  if (defined $dbh_postgresql)
  {
    $dbh_postgresql->disconnect;
    $dbh_postgresql = undef;
  }
}

sub dbh_odbc_close {
  if (defined $dbh_odbc)
  {
    $dbh_odbc->disconnect;
    $dbh_odbc = undef;
  }
}

sub dbh_win32_odbc_close {
  if (defined $dbh_win32_odbc)
  {
    $dbh_win32_odbc->Close();
    $dbh_win32_odbc = undef;
  }
}

sub dbh_mysql_connect() {
  if ($mysql_dbi_enable == 1)
  {
    &dbh_mysql_close();
    
    unless ($dbh_mysql = DBI->connect("DBI:mysql:database=$mysql_dbi_database;host=$mysql_dbi_host;
        port=$mysql_dbi_port",$mysql_dbi_username,$mysql_dbi_password) )
    {
      my $msg = "MySQL error: Unable to connect to database: $DBI::errstr";
      warn $msg, "\n";
      if ($DEBUGGING >= 1)
      {
        print $msg, "\n";
      }
      if ($syslog_system_enable == 1)
      {
        syslog_system($msg);
      }
      if ($eventlog_system_enable == 1)
      {
        eventlog_system($msg,12,$eventlog_error);
      }
    }
  }
}

sub dbh_postgresql_connect() {
  if ($postgresql_dbi_enable == 1)
  {
    my $connect_string;
    my $dbi_module;

    if ($postgresql_dbi_module == 0)
    {
      $dbi_module = "PgPP";
    }
    else
    { 
      $dbi_module = "Pg";
    }
    
    if ($postgresql_dbi_hostport_enable == 0)
    {
      # No network support
      $connect_string = "DBI:$dbi_module:dbname=$postgresql_dbi_database;";
    }
    else
    {
      # Network support - include host and port
      $connect_string = "DBI:$dbi_module:dbname=$postgresql_dbi_database;host=$postgresql_dbi_host;port=$postgresql_dbi_port";
    }

    &dbh_postgresql_close();

    unless ($dbh_postgresql = DBI->connect($connect_string,$postgresql_dbi_username,$postgresql_dbi_password) )
    {
      my $msg = "Postgres error: Unable to connect to database: $DBI::errstr";
      warn $msg, "\n";
      if ($DEBUGGING >= 1)
      {
        print $msg, "\n";
      }
      if ($syslog_system_enable == 1)
      {
        syslog_system($msg);
      }
      if ($eventlog_system_enable == 1)
      {
        eventlog_system($msg,18,$eventlog_error);
      }
    }
  }
}

sub dbh_odbc_connect() { 
  if ($dbd_odbc_enable == 1)
  {
    &dbh_odbc_close();
    
    unless ($dbh_odbc = DBI->connect("DBI:ODBC:$dbd_odbc_dsn",$dbd_odbc_username,$dbd_odbc_password) )
    {
      my $msg = "DBI DBD:ODBC error: Unable to connect to DSN: $DBI::errstr";
      warn $msg, "\n";
      if ($DEBUGGING >= 1)
      {
        print $msg, "\n";
      }
      if ($syslog_system_enable == 1)
      {
        syslog_system($msg);
      }
      if ($eventlog_system_enable == 1)
      {
        eventlog_system($msg,13,$eventlog_error);
      }
    }
  }
}

sub dbh_win32_odbc_connect() { 
  if ($sql_win32_odbc_enable == 1)
  {
    &dbh_win32_odbc_close();
    
    unless ($dbh_win32_odbc = new Win32::ODBC("DSN=$sql_win32_odbc_dsn","UID=$sql_win32_odbc_username","PWD=$sql_win32_odbc_password") )
    {
      my $msg = "SQL error: Unable to connect to DSN $sql_win32_odbc_dsn:" . Win32::ODBC::Error();
      warn $msg, "\n";
      if ($DEBUGGING >= 1)
      {
        print $msg, "\n";
      }     
      if ($eventlog_system_enable == 1)
      {
        eventlog_system($msg,13,$eventlog_error);
      }
    }
  }
}

sub mysql_ping {
  if ($mysql_dbi_enable == 1) {
    if (defined ($dbh_mysql)) {
      my $rc = $dbh_mysql->ping;
      if ($dbh_mysql->{'errno'} != 0) {
        &dbh_mysql_connect();
      }
    }
    else {
      &dbh_mysql_connect();
    }
  }
  
  $g_last_mysql_ping = time();
  
  if ($DEBUGGING >= 2)
  {
    print "MYSQL Ping\n"
  }
}

sub postgresql_ping {
  if ($postgresql_dbi_enable == 1) {
    if (defined ($dbh_postgresql)) {
      my $rc = $dbh_postgresql->ping;
      if (!$rc) { 
        &dbh_postgresql_connect();
      }
    }
    else {
      &dbh_postgresql_connect();
    }
  }
  
  $g_last_postgresql_ping = time();
  
  if ($DEBUGGING >= 2)
  {
    print "PostgreSQL Ping\n"
  }
}

sub dbd_odbc_ping {
  if ($dbd_odbc_enable == 1) {
    if (defined ($dbh_odbc)) {
      my $rc = $dbh_odbc->ping;
      if (!$rc) { 
        &dbd_odbc_connect();
      }
    }
    else {
      &dbd_odbc_connect();
    }
  }
  
  $g_last_dbd_odbc_ping = time();
  
  if ($DEBUGGING >= 2)
  {
    print "DBD_ODBC Ping\n"
  }
}

