# ----------------------------------------------------------------------------------------------------------
# © Copyright 2000-2006 by Alex Peeters [alex.peeters@citap.be]
# ----------------------------------------------------------------------------------------------------------
# 2006/01/29, v3.000.003, package ASNMTAP::Asnmtap::Plugins Object-Oriented Perl
# ----------------------------------------------------------------------------------------------------------

package ASNMTAP::Asnmtap::Plugins;

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

use strict;
use warnings;           # Must be used in test mode only. This reduce a little process speed
#use diagnostics;       # Must be used in test mode only. This reduce a lot of process speed

no warnings 'deprecated';

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

use Carp qw(carp);
use Time::HiRes qw(gettimeofday tv_interval);

# include the class files - - - - - - - - - - - - - - - - - - - - - - - -

use ASNMTAP::Asnmtap qw(:ASNMTAP :COMMANDS :_HIDDEN :PLUGINS);

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

BEGIN {
  use Exporter ();

  @ASNMTAP::Asnmtap::Plugins::ISA         = qw(Exporter ASNMTAP::Asnmtap);

  %ASNMTAP::Asnmtap::Plugins::EXPORT_TAGS = (ALL      => [ qw($APPLICATION $BUSINESS $DEPARTMENT $COPYRIGHT $SENDEMAILTO
                                                              $CAPTUREOUTPUT
                                                              $PREFIXPATH
                                                              %ERRORS %STATE %TYPE

                                                              $PERLCOMMAND $RSYNCCOMMAND $SCPCOMMAND $SSHCOMMAND

                                                              &_checkAccObjRef
                                                              &_checkSubArgs0 &_checkSubArgs1 &_checkSubArgs2
                                                              &_checkReadOnly0 &_checkReadOnly1 &_checkReadOnly2
                                                              &_dumpValue

                                                              $PLUGINPATH) ],

                                             PLUGINS  => [ qw($APPLICATION $BUSINESS $DEPARTMENT $COPYRIGHT $SENDEMAILTO
                                                              $CAPTUREOUTPUT
                                                              $PREFIXPATH $PLUGINPATH
                                                              %ERRORS %STATE %TYPE) ],

                                             COMMANDS => [ qw($PERLCOMMAND $RSYNCCOMMAND $SCPCOMMAND $SSHCOMMAND) ],

                                            _HIDDEN   => [ qw(&_checkAccObjRef
                                                              &_checkSubArgs0 &_checkSubArgs1 &_checkSubArgs2
                                                              &_checkReadOnly0 &_checkReadOnly1 &_checkReadOnly2
                                                              &_dumpValue) ] );

  @ASNMTAP::Asnmtap::Plugins::EXPORT_OK   = ( @{ $ASNMTAP::Asnmtap::Plugins::EXPORT_TAGS{ALL} } );

  $ASNMTAP::Asnmtap::Plugins::VERSION     = 3.000.003;
}

# Constructor & initialisation  - - - - - - - - - - - - - - - - - - - - -

sub _init {
  $_[0]->SUPER::_init($_[1]);
  carp ('ASNMTAP::Asnmtap::Plugins: _init') if ( $_[0]->{_debug} );

  $_[0]->{_programUsageSuffix} = ' [-S|--status N] [-A|asnmtapEnv [F|T]|[F|T]|[F|T]] [-O|onDemand F|T|N|Y] [-L|--logging <LOGGING>] [-D|--debugfile <DEBUGFILE>] [-d|--debug F|T|L|M|A|S] ' . $_[0]->{_programUsageSuffix};

  $_[0]->{_programHelpSuffix}  = "
-S, --status=N
   N(agios)      : Nagios custom plugin output (default)
-A, --asnmtapEnv=[F|T]|[F|T]|[F|T]
   F(alse)       : all screendebugging off (default)
   T(true)       : all screendebugging on
   |
   F(alse)       : all file debugging off (default)
   T(true)       : all file debugging on
   |
   F(alse)       : nok file debugging off (default)
   T(true)       : nok file debugging on
-O, --onDemand=F|T|N|Y
   F(alse)/N(o)  : normal plugin execution (default)
   T(true)/Y(es) : plugin launched on demand
-L, --logging=LOGGING
   write logging to file LOGGING
-D, --debugfile=DEBUGFILE
   write debug to file DEBUGFILE
-d, --debug=F|T|L|M|A|S
   F(alse)       : screendebugging off (default)
   T(true)       : normal screendebugging on
   L(ong)        : long screendebugging on
   M(oderator)   : long screendebugging on for Moderators
   A(dmin)       : long screendebugging on for Admins
   S(erver Admin): long screendebugging on for Server Admins
" . $_[0]->{_programHelpSuffix};

  push ( @{ $_[0]->{_programGetOptions} }, 'status|S:s', 'asnmtapEnv|A:s', 'onDemand|O:s', 'logging|L:s', 'debugfile|D:s', 'debug|d:s' );

  my ($_programUsageSuffix, $_programHelpSuffix);

  foreach ( @{ $_[0]->{_programGetOptions} } ) {
  foreach ( @{ $_[0]->{_programGetOptions} } ) {
    for ($_) {
      /^timeout\|t([:=])i$/             && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-t|--timeout <TIMEOUT>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-t, --timeout=<TIMEOUT>\n   timeout threshold (seconds) from which a UNKNOWN status will result for the plugin execution time\n"; last; };
      /^trendline\|T([:=])i$/           && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-T|--trendline <TRENDLINE>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-T, --trendline <TRENDLINE>\n   trendline threshold (seconds) from which a TRENDLINE status will result for the plugin response time\n"; last; };
      /^environment\|e([:=])s$/         && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-e|--environment <ENVIRONMENT>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-e, --environment=<ENVIRONMENT>\n P(roduction)\n   S(imulation)\n   A(cceptation)\n   T(est)\n D(evelopment)\n   L(ocal)\n"; last; };
      /^proxy([:=])s$/                  && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'--proxy <username:password@proxy>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "--proxy=<username:password\@proxy>\n"; last; };
      /^host\|H([:=])s$/                && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-H|--host <HOST>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-H, --host=<HOST>\n   hostname or ip address\n"; last; };
      /^url\|U([:=])s$/                 && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-U|--url <URL>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-U, --url=<URL>\n"; last; };
      /^port\|P([:=])i$/                && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-P|--port <PORT>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-P, --port=<PORT>\n"; last; };
      /^community\|C([:=])s$/           && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-C|--community <SNMP COMMUNITY>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-C, --community=<SNMP COMMUNITY>\n"; last; };      /^password\|passwd\|p([:=])s$/    && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-p|--password|--passwd <PASSWORD>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-p, --password/--passwd=<PASSWORD>\n"; last; };
      /^username\|u\|loginname([:=])s$/ && do { $_programUsageSuffix .= ($1 eq ':' ? ' [' : ' ') .'-u|--username|--loginname <USERNAME>'. ($1 eq ':' ? ']' : ''); $_programHelpSuffix .= "-u, --username/--loginname=<USERNAME>\n"; last; };
    }
  }
  }

  $_[0]->{_programUsageSuffix} = $_programUsageSuffix .' '. $_[0]->{_programUsageSuffix} if (defined $_programUsageSuffix);

  $_[0]->{_programHelpSuffix} = "\n". $_programHelpSuffix . $_[0]->{_programHelpSuffix} if (defined $_programHelpSuffix);
  
  $_[0]->[ $_[0]->[0]{_timeout} = @{$_[0]} ] = (defined $_[1]->{_timeout}) ? $_[1]->{_timeout} : 10;

  $_[0]->[ $_[0]->[0]{_browseragent} = @{$_[0]} ] = (defined $_[1]->{_browseragent}) ? $_[1]->{_browseragent} : "Mozilla/5.0 (compatible; MSIE 6.0; ASNMTAP; U; ASNMTAP-3.000.003 postfix; nl-BE; rv:3.000.003) Gecko/20060115 libwww-perl/5.803";

  $_[0]->[ $_[0]->[0]{_clientCertificate} = @{$_[0]} ] = $_[1]->{_clientCertificate} if (defined $_[1]->{_clientCertificate});
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub _getOptions {
  $_[0]->SUPER::_getOptions();
  carp ('ASNMTAP::Asnmtap::Plugins: _getOptions') if ( $_[0]->{_debug} );

  # Default command line options  - - - - - - - - - - - - - - - - - - - -

  my $status = (exists $_[0]->{_getOptionsArgv}->{status}) ? $_[0]->{_getOptionsArgv}->{status} : 'N';
  $_[0]->printUsage ('Invalid status option: ' . $status) unless ($status =~ /^[N]$/);

  if (exists $_[0]->{_getOptionsArgv}->{asnmtapEnv}) {
    my $asnmtapEnv = $_[0]->{_getOptionsArgv}->{asnmtapEnv};

    my ($boolean_screenDebug, $boolean_debug_all, $boolean_debug_NOK) = split (/\|/, $asnmtapEnv);
    $_[0]->printUsage ('Wrong ASNMTAP environment options: ' . $asnmtapEnv) unless (defined $boolean_screenDebug and defined $boolean_debug_all and defined $boolean_debug_NOK);
    $_[0]->printUsage ('Invalid ASNMTAP environment options: ' . $asnmtapEnv) unless ($boolean_screenDebug =~ /^[TF]$/ and $boolean_debug_all =~ /^[TF]$/ and $boolean_debug_NOK =~ /^[TF]$/);

    $_[0]->{_getOptionsValues}->{boolean_screenDebug} = ($boolean_screenDebug eq 'T') ? 1 : 0;
    $_[0]->{_getOptionsValues}->{boolean_debug_all}   = ($boolean_debug_all eq 'T')   ? 1 : 0;
    $_[0]->{_getOptionsValues}->{boolean_debug_NOK}   = ($boolean_debug_NOK eq 'T')   ? 1 : 0;
  } else {
    $_[0]->{_getOptionsValues}->{boolean_screenDebug} = 0;
    $_[0]->{_getOptionsValues}->{boolean_debug_all}   = 0;
    $_[0]->{_getOptionsValues}->{boolean_debug_NOK}   = 0;
  }

  my $onDemand = (exists $_[0]->{_getOptionsArgv}->{onDemand}) ? $_[0]->{_getOptionsArgv}->{onDemand} : 'F';
  $_[0]->printUsage ('Invalid on demand option: ' . $onDemand) unless ($onDemand =~ /^[FTNY]$/);
  $_[0]->{_getOptionsValues}->{onDemand} = ($onDemand =~ /^[TY]$/) ? 1 : 0;

  # exists $_[0]->{_getOptionsArgv}->{logging}

  if ( defined $_[0]->{_getOptionsArgv}->{debugfile} ) {
    unlink $_[0]->{_getOptionsArgv}->{debugfile} if ( -s $_[0]->{_getOptionsArgv}->{debugfile} );
  }
  
  my $debug = (exists $_[0]->{_getOptionsArgv}->{debug}) ? $_[0]->{_getOptionsArgv}->{debug} : 'F';
  $_[0]->printUsage ('Invalid debug option: ' . $debug) unless ($debug =~ /^[FTLMAS]$/);
  $_[0]->{_getOptionsValues}->{debug} = ($debug eq 'T') ? 1 : (($debug eq 'L') ? 2 : (($debug eq 'M') ? 3 : (($debug eq 'A') ? 4 : ((($debug eq 'S') ? 5 : 0)))));

  # Reserved command line options - - - - - - - - - - - - - - - - - - - -

  # exists $_[0]->{_getOptionsArgv}->{timeout}

  # exists $_[0]->{_getOptionsArgv}->{trendline}

  if ( exists $_[0]->{_getOptionsArgv}->{environment} ) {
    $_[0]->printUsage ('Invalid environment option: ' . $_[0]->{_getOptionsArgv}->{environment}) unless ($_[0]->{_getOptionsArgv}->{environment} =~ /^[PSATDL]$/);

    for ($_[0]->{_getOptionsArgv}->{environment}) {
      /P/ && do { $_[0]->{_getOptionsValues}->{environment} = "Production";  last; };
      /S/ && do { $_[0]->{_getOptionsValues}->{environment} = "Simulation";  last; };
      /A/ && do { $_[0]->{_getOptionsValues}->{environment} = "Acceptation"; last; };
      /T/ && do { $_[0]->{_getOptionsValues}->{environment} = "Test";        last; };
      /D/ && do { $_[0]->{_getOptionsValues}->{environment} = "Development"; last; };
      /L/ && do { $_[0]->{_getOptionsValues}->{environment} = "Local";       last; };
      $_[0]->{_getOptionsValues}->{environment} = undef;
    }
  }

  $_[0]->_init_proxy_and_client_certificate ();

  if (exists $_[0]->{_getOptionsArgv}->{host}) {
    my $host = $1 if ( $_[0]->{_getOptionsArgv}->{host} =~ m/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z][-a-zA-Z0-9]+(\.[a-zA-Z][-a-zA-Z0-9]+)*)$/ );
    $_[0]->printUsage ('Invalid hostname or ip address: ' . $_[0]->{_getOptionsArgv}->{host}) unless (defined $host);
  }

  # exists $_[0]->{_getOptionsArgv}->{url}

  # exists $_[0]->{_getOptionsArgv}->{port}

  # exists $_[0]->{_getOptionsArgv}->{community}

  # exists $_[0]->{_getOptionsArgv}->{password}

  # exists $_[0]->{_getOptionsArgv}->{username}

  if (exists $_[0]->{_getOptionsArgv}->{warning}) {
    my $warning = $1 if ( $_[0]->{_getOptionsArgv}->{warning} =~ /^(\d+)$/ );
  # my $warning = $1 if ( $_[0]->{_getOptionsArgv}->{warning} =~ /^(\d(?:[:,]\d+)*)*$/ );
    $_[0]->printUsage ('Invalid warning threshold ranges ' . $_[0]->{_getOptionsArgv}->{warning}) unless (defined $warning);
  }

  if (exists $_[0]->{_getOptionsArgv}->{critical}) {
    my $critical = $1 if ($_[0]->{_getOptionsArgv}->{critical} =~ /^(\d+)$/ );
  # my $critical = $1 if ($_[0]->{_getOptionsArgv}->{critical} =~ /^(\d(?:[:,]\d+)*)*$/ );
    $_[0]->printUsage ('Invalid critical threshold range: ' . $_[0]->{_getOptionsArgv}->{critical}) unless (defined $critical);
  }

  # Default _pluginValues - - - - - - - - - - - - - - - - - - - - - - - -

  $_[0]->[ $_[0]->[0]{_pluginValues} = @{$_[0]} ] = {};

  $_[0]->{_pluginValues}->{stateValue} = $ERRORS{DEPENDENT};
  $_[0]->{_pluginValues}->{stateError} = $STATE{$_[0]->{_pluginValues}->{stateValue}};

  $_[0]->{_pluginValues}->{message}    = $_[0]->{_programDescription};
  $_[0]->{_pluginValues}->{message}   .= ' ('. $_[0]->{_getOptionsValues}->{environment} .')' if ( defined $_[0]->{_getOptionsValues}->{environment} );

  $_[0]->{_pluginValues}->{alert}      = undef;
  $_[0]->{_pluginValues}->{error}      = undef;
  $_[0]->{_pluginValues}->{result}     = undef;

  $_[0]->{_pluginValues}->{performanceData} = undef;

  my ($startTimeSeconds, $startTimeMicroseconds) = gettimeofday();
  $_[0]->{_pluginValues}->{startTime} = $startTimeSeconds .'.'. $startTimeMicroseconds;

  $_[0]->{_pluginValues}->{endTime} = $_[0]->{_pluginValues}->{startTime};
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub _init_proxy_and_client_certificate {
  my $proxy = ( exists $ENV{ASNMTAP_PROXY} ? $ENV{ASNMTAP_PROXY} : ( exists $_[0]->{_getOptionsArgv}->{proxy} and defined $_[0]->{_getOptionsArgv}->{proxy} ? $_[0]->{_getOptionsArgv}->{proxy} : undef ) );

  if ( defined $proxy and $proxy ne '' ) {
    my ($proxyServer, $proxyUsername, $proxyPassword, $proxyUsernamePassword);
    ($proxyUsernamePassword, $proxyServer) = split(/\@/, $proxy );

	if (defined $proxyServer) {
      ($proxyUsername, $proxyPassword) = split(/\:/, $proxyUsernamePassword, 2);
      $_[0]->printUsage ('Username and/or Password missing: : ' . $proxy) unless (defined $proxyUsername and defined $proxyPassword);
    } else {
      $proxyServer = $proxy;
    }

    $_[0]->[ $_[0]->[0]{_proxy} = @{$_[0]} ] = {};
    $_[0]->{_proxy}->{server}   = "http://" . $proxyServer;
    $_[0]->{_proxy}->{username} = $proxyUsername;
    $_[0]->{_proxy}->{password} = $proxyPassword;
  }

  # HTTPS: DEFAULT SSL VERSION
  $ENV{HTTPS_VERSION} = '3';

  # HTTPS: DEBUGGING SWITCH / LOW LEVEL SSL DIAGNOSTICS
  $ENV{HTTPS_DEBUG} = $_[0]->{_getOptionsValues}->{debug};

  if ( defined $proxy and $proxy ne '' ) {
    # HTTPS: PROXY SUPPORT
    $ENV{HTTPS_PROXY} = $_[0]->{_proxy}->{server};

    # HTTPS: PROXY BASIC_AUTH
    $ENV{HTTPS_PROXY_USERNAME} = $_[0]->{_proxy}->{username} if (defined $_[0]->{_proxy}->{username});
    $ENV{HTTPS_PROXY_PASSWORD} = $_[0]->{_proxy}->{password} if (defined $_[0]->{_proxy}->{password});
  }

  if (exists $_[0]->{_clientCertificate}) {
    if (defined $_[0]->{_clientCertificate}->{certFile} and defined $_[0]->{_clientCertificate}->{keyFile}) {
      # CLIENT CERT SUPPORT, PEM encoded certificate and private key files.
      $ENV{HTTPS_CERT_FILE} = $_[0]->{_clientCertificate}->{certFile};
      $ENV{HTTPS_KEY_FILE}  = $_[0]->{_clientCertificate}->{keyFile};

      if (defined $_[0]->{_clientCertificate}->{caFile} and defined $_[0]->{_clientCertificate}->{caDir}) {
        # CA CERT PEER VERIFICATION, Additionally, if you would like to tell the client where the CA file is.
        $ENV{HTTPS_CA_FILE} = $_[0]->{_clientCertificate}->{caFile};
        $ENV{HTTPS_CA_DIR}  = $_[0]->{_clientCertificate}->{caDir};
      }
    }

    if (defined $_[0]->{_clientCertificate}->{pkcs12File} and defined $_[0]->{_clientCertificate}->{pkcs12Password}) {
      # CLIENT PKCS12 CERT SUPPORT, Use of this type of certificate will take precedence over previous certificate settings described.
      $ENV{HTTPS_PKCS12_FILE}     = $_[0]->{_clientCertificate}->{pkcs12File};
      $ENV{HTTPS_PKCS12_PASSWORD} = $_[0]->{_clientCertificate}->{pkcs12Password};
    }
  }
}

# Object accessor methods - - - - - - - - - - - - - - - - - - - - - - - -

sub appendPerformanceData { &_checkAccObjRef ( $_[0] ); &_checkSubArgs1; $_[0]->{_pluginValues}->{performanceData} .= ' ' . $_[1] if ( defined $_[1] ); }

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub browseragent {&_checkAccObjRef ( $_[0] ); &_checkSubArgs1; $_[0]->{_browseragent} = $_[1] if ( defined $_[1] ); $_[0]->{_browseragent}; }

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub clientCertificate {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs2;

  if ( defined $_[1] ) {
    if ( defined $_[2] ) {
      $_[0]->{_clientCertificate}->{$_[1]} = $_[2] if ( exists $_[0]->{_clientCertificate}->{$_[1]} );
    }

    ( defined $_[0]->{_clientCertificate}->{$_[1]} ) ? $_[0]->{_clientCertificate}->{$_[1]} : undef;
  }
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub pluginValue {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs2;

  if ( defined $_[1] and exists $_[0]->{_pluginValues}->{$_[1]}) {
    if ( exists $_[2] ) {
      if ( $_[1] eq 'stateValue' ) {
        &_dumpValue ( $_[2], 'Wrong value!' ) unless ( defined $_[2] && $_[2] =~ /^[0-3]$/ );
        my $stateValue = $_[0]->{_pluginValues}->{stateValue} == $ERRORS{DEPENDENT} ? $_[2] : ( $_[0]->{_pluginValues}->{stateValue} > $_[2] ? $_[0]->{_pluginValues}->{stateValue} : $_[2] );
        $_[0]->{_pluginValues}->{stateValue} = $stateValue;
        $_[0]->{_pluginValues}->{stateError} = $STATE{$stateValue};
      } elsif ( $_[1] eq 'stateError' ) {
        &_dumpValue ( $_[2], 'Wrong value!' ) unless ( defined $_[2] && $_[2] =~ /^(?:OK|WARNING|CRITICAL|UNKNOWN)$/ );
        my $stateValue = $_[0]->{_pluginValues}->{stateValue} == $ERRORS{DEPENDENT} ? $ERRORS{$_[2]} : ( $_[0]->{_pluginValues}->{stateValue} > $ERRORS{$_[2]} ? $_[0]->{_pluginValues}->{stateValue} : $ERRORS{$_[2]} );
        $_[0]->{_pluginValues}->{stateValue} = $stateValue;
        $_[0]->{_pluginValues}->{stateError} = $STATE{$stateValue};
      } elsif ( $_[1] eq 'message' ) {
        if ( defined $_[2] ) {
          $_[0]->{_pluginValues}->{message} = $_[2];
          $_[0]->{_pluginValues}->{message} .= ' ('. $_[0]->{_getOptionsValues}->{environment} .')' if ( defined $_[0]->{_getOptionsValues}->{environment} );
        }
      } else {
        $_[0]->{_pluginValues}->{$_[1]} = $_[2] if ( defined $_[2] );
      }
    }

    ( defined $_[0]->{_pluginValues}->{$_[1]} ) ? $_[0]->{_pluginValues}->{$_[1]} : undef;
  }
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub pluginValues {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs2;
  &_dumpValue ( $_[1], 'Request parameter is not a hash.' ) if ( ref $_[1] ne 'HASH' );
  &_dumpValue ( $_[1], 'Replace/Append value missing!' ) unless (defined $_[2]);

  if ( exists $_[1]->{stateValue} ) {
    &_dumpValue ( $_[1]->{stateValue}, 'Wrong value!' ) unless ( defined $_[1]->{stateValue} && $_[1]->{stateValue} =~ /^[0-3]$/ );
    my $stateValue = $_[0]->{_pluginValues}->{stateValue} == $ERRORS{DEPENDENT} ? $_[1]->{stateValue} : ( $_[0]->{_pluginValues}->{stateValue} > $_[1]->{stateValue} ? $_[0]->{_pluginValues}->{stateValue} : $_[1]->{stateValue} );
    $_[0]->{_pluginValues}->{stateValue} = $stateValue;
    $_[0]->{_pluginValues}->{stateError} = $STATE{$stateValue};
  } elsif ( exists $_[1]->{stateError} ) {
    &_dumpValue ( $_[1]->{stateError}, 'Wrong value!' ) unless ( defined $_[1]->{stateError} && $_[1]->{stateError} =~ /^(?:OK|WARNING|CRITICAL|UNKNOWN)$/ );
    my $stateValue = $_[0]->{_pluginValues}->{stateValue} == $ERRORS{DEPENDENT} ? $ERRORS{$_[1]->{stateError}} : ( $_[0]->{_pluginValues}->{stateValue} > $ERRORS{$_[1]->{stateError}} ? $_[0]->{_pluginValues}->{stateValue} : $ERRORS{$_[1]->{stateError}} );
    $_[0]->{_pluginValues}->{stateValue} = $stateValue;
    $_[0]->{_pluginValues}->{stateError} = $STATE{$stateValue};
  }

  if ( $_[2] == $TYPE{REPLACE} ) {
    $_[0]->{_pluginValues}->{alert} = $_[1]->{alert} if ( defined $_[1]->{alert} );
    $_[0]->{_pluginValues}->{error} = $_[1]->{error} if ( defined $_[1]->{error} );
  } elsif ( $_[2] == $TYPE{APPEND} ) {
    $_[0]->{_pluginValues}->{alert} .= ( ( $_[0]->{_pluginValues}->{alert} ? ' - ' : '' ) . $_[1]->{alert} ) if ( defined $_[1]->{alert} );
    $_[0]->{_pluginValues}->{error} .= ( ( $_[0]->{_pluginValues}->{error} ? ' - ' : '' ) . $_[1]->{error} ) if ( defined $_[1]->{error} );
  } elsif ( $_[2] == $TYPE{INSERT} ) {
    $_[0]->{_pluginValues}->{alert} = ( $_[1]->{alert} .' - '. $_[0]->{_pluginValues}->{alert} ) if ( defined $_[1]->{alert} );
    $_[0]->{_pluginValues}->{error} = ( $_[1]->{error} .' - '. $_[0]->{_pluginValues}->{error} ) if ( defined $_[1]->{error} );
  }

  $_[0]->{_pluginValues}->{result} = $_[1]->{result} if ( defined $_[1]->{result} );
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub proxy {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs2;

  if ( defined $_[1] ) {
    if ( exists $_[0]->{_proxy} ) {
      if ( defined $_[2] ) {
        $_[0]->{_proxy}->{$_[1]} = $_[2] if ( exists $_[0]->{_proxy}->{$_[1]} );
      }

      ( defined $_[0]->{_proxy}->{$_[1]} ) ? $_[0]->{_proxy}->{$_[1]} : undef;
    } else {
      undef;
    }
  }
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub timeout { &_checkAccObjRef ( $_[0] ); &_checkSubArgs1; $_[0]->{_timeout} = $_[1] if ( defined $_[1] ); $_[0]->{_timeout}; }

# Class accessor methods  - - - - - - - - - - - - - - - - - - - - - - - -

sub setEndTime_and_getResponsTime {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs1;

  my ($endTimeSeconds, $endTimeMicroseconds) = gettimeofday();
  $_[0]->{_pluginValues}->{endTime} = "$endTimeSeconds.$endTimeMicroseconds";

  my ($startTimeSeconds, $startTimeMicroseconds) = split (/\./, $_[1]);
  my $responseTime = ( $endTimeSeconds >= $startTimeSeconds ) ? tv_interval ( [$startTimeSeconds, $startTimeMicroseconds], [$endTimeSeconds, $endTimeMicroseconds]) : tv_interval ( [$endTimeSeconds, $endTimeMicroseconds], [$startTimeSeconds, $startTimeMicroseconds]);

  return ( int ($responseTime * 1000) );
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub write_debugfile {
  &_checkAccObjRef ( $_[0] ); &_checkReadOnly2;

  my $debugfile = $_[0]->{_getOptionsArgv}->{debugfile};
  my $openAppend = ( defined $_[2] and $_[2] =~ /^1$/ ) ? 1 : 0;

  if ( defined $debugfile ) {
    my $rvOpen = open (DEBUGFILE, ($openAppend ? '>>' : '>') . $debugfile);

    if ($rvOpen) {
      print DEBUGFILE ${$_[1]}, "\n";
      close(DEBUGFILE);
    } else {
      print ref ($_[0]) .": Cannot open $debugfile to print debug information\n";
    }
  }
}

# Utility methods - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub call_system {
  &_checkAccObjRef ( $_[0] ); &_checkReadOnly2;

  return ( $ERRORS{DEPENDENT} ) unless ( defined $_[1] );
  my ($status, $stdout, $stderr) = $_[0]->SUPER::call_system ( $_[1] );

  if ( $status ) {
    $_[0]->pluginValues ( { stateValue => $ERRORS{OK}, alert => $_[1] .': '. 'OK', result => ( defined $stdout ? $stdout : undef ) }, $TYPE{APPEND} );
    return ( $ERRORS{OK} );
  } else {
    $_[0]->pluginValues ( { stateValue => $ERRORS{UNKNOWN}, error => $_[1] .': '. $stderr, result => ( defined $stderr ? $stderr : undef ) }, $TYPE{APPEND} );
    return ( $ERRORS{UNKNOWN} );
  }
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

sub exit {
  &_checkAccObjRef ( $_[0] ); &_checkSubArgs1;

  my $duration = $_[0]->setEndTime_and_getResponsTime ($_[0]->{_pluginValues}->{startTime});

  if ( $_[0]->{_getOptionsArgv}->{trendline} ) {
    my $responseTimeSeconds = ( $duration / 1000 );  # convert to seconds

    if ( $_[0]->{_getOptionsValues}->{debug} ) {
      my ($startTimeEpoch, undef) = split (/\./, $_[0]->{_pluginValues}->{startTime});
      my ($endTimeEpoch, undef)   = split (/\./, $_[0]->{_pluginValues}->{endTime});
      print "\nStart time   : ", scalar(localtime($startTimeEpoch)), "\n";
      print "End time     : ", scalar(localtime($endTimeEpoch)), "\n";
      print "Trendline    : ". $_[0]->{_getOptionsArgv}->{trendline} ."\n";
      print "Response time: $responseTimeSeconds\n";
    }

    if ( $_[0]->{_pluginValues}->{stateValue} ) {
      $_[0]->appendPerformanceData('Trendline=' .$responseTimeSeconds. 's;;;;');
    } else {
      $_[0]->appendPerformanceData('Trendline=' .$responseTimeSeconds. 's;' .$_[0]->{_getOptionsArgv}->{trendline}. ';;;');
      $_[0]->{_pluginValues}->{alert} = "Response time $responseTimeSeconds > trendline ". $_[0]->{_getOptionsArgv}->{trendline} if ( $responseTimeSeconds > $_[0]->{_getOptionsArgv}->{trendline} );
    }
  } else {
    $_[0]->appendPerformanceData('Duration=' .$duration. 'ms;;;0;') if ( $_[1] =~ /^[2367]$/ );
  }

  $_[0]->{_pluginValues}->{alert} =~ s/^\s+//g if ( defined $_[0]->{_pluginValues}->{alert} );

  if ( $_[0]->{_getOptionsValues}->{debug} ) {
    print "\nStatus       : ". $_[0]->{_getOptionsArgv}->{status} ."\n" if ( defined $_[0]->{_getOptionsArgv}->{status} );
    print "Debug        : ". $_[0]->{_getOptionsValues}->{debug} ."\n" if ( defined $_[0]->{_getOptionsValues}->{debug} );
    print "Logging      : ". $_[0]->{_getOptionsArgv}->{logging} ."\n" if ( defined $_[0]->{_getOptionsArgv}->{logging} );
    print "Httpdump     : ". $_[0]->{_getOptionsArgv}->{debugfile} ."\n" if ( defined $_[0]->{_getOptionsArgv}->{debugfile} );
    print "State        : ". $_[0]->{_pluginValues}->{stateError} ."\n" if ( defined $_[0]->{_pluginValues}->{stateError} );
    print "Message      : ". $_[0]->{_pluginValues}->{message} ."\n" if ( defined $_[0]->{_pluginValues}->{message} );
    print "Alert        : ". $_[0]->{_pluginValues}->{alert} ."\n" if ( defined $_[0]->{_pluginValues}->{alert} );
    print "Error        : ". $_[0]->{_pluginValues}->{error} ."\n" if ( defined $_[0]->{_pluginValues}->{error} );
	print "\n";
  }

  my $returnMessage = $_[0]->{_pluginValues}->{stateError} .' - '. $_[0]->{_pluginValues}->{message} .':';
  $returnMessage .= ' '. $_[0]->{_pluginValues}->{alert} if ( $_[0]->{_pluginValues}->{alert} );
  $returnMessage .= ' ERROR: '. $_[0]->{_pluginValues}->{error} if (defined $_[0]->{_pluginValues}->{error});

  if (defined $_[1]) {
    $_[0]->appendPerformanceData('Status=' .$_[0]->{_pluginValues}->{stateValue}. ';1;2;0;3') if ( $_[1] =~ /^[1357]$/ );
  }

  $_[0]->appendPerformanceData('Execution=' .$_[0]->setEndTime_and_getResponsTime ($^T). 'ms;;;0;');

  if ( defined $_[0]->{_pluginValues}->{performanceData} ) {
    $_[0]->{_pluginValues}->{performanceData} =~ s/^\s+//g;
    $returnMessage .= '|'. $_[0]->{_pluginValues}->{performanceData};
  }

  if ( $_[0]->{_getOptionsArgv}->{logging} ) {
    if (! $CAPTUREOUTPUT) {
      my $loggedStatus = ( $_[0]->{_getOptionsArgv}->{debugfile} ) ? $_[0]->{_getOptionsArgv}->{debugfile} : $_[0]->{_getOptionsArgv}->{logging};
      $loggedStatus .= "-status.txt";

      my $rvOpen = open( LOGGING, ">$loggedStatus" );

	  if ($rvOpen) {
        print LOGGING "$returnMessage\n";
        close(LOGGING);
      } else {
        print "Cannot open $loggedStatus to print debug information\n";
      }
    }

    if ( $_[0]->{_getOptionsValues}->{boolean_debug_all} ) {
      my $rvOpen = open ( LOGGING, '>>'. $_[0]->{_getOptionsArgv}->{logging} .'-all.txt' );

	  if ($rvOpen) {
        print LOGGING "--> $returnMessage\n";
        print LOGGING ' -> '. $_[0]->{_pluginValues}->{error} ."\n" if ( $_[0]->{_pluginValues}->{error} ne 'SUCCESS' );
        close(LOGGING);
      } else {
        print 'Cannot open '. $_[0]->{_getOptionsArgv}->{logging} ."-all.txt to print debug information\n";
      }
    }

    if ( $_[0]->{_getOptionsValues}->{boolean_debug_NOK} ) {
      if ( $_[0]->{_pluginValues}->{stateValue} ) {
        my $rvOpen = open ( LOGGING, '>'. $_[0]->{_getOptionsArgv}->{logging} .'-nok.txt' );

        if ( $rvOpen ) {
          print LOGGING "--> $returnMessage\n";
          print LOGGING ' -> '. $_[0]->{_pluginValues}->{error} ."\n";
          print LOGGING '  > '. $_[0]->{_pluginValues}->{result} ."\n";
          close(LOGGING);
        } else {
          print "Cannot open ". $_[0]->{_getOptionsArgv}->{logging} ."-nok.txt to print debug information\n";
        }
      }
    }
  }

  $_[0]->dumpData (1) if ( $_[0]->getOptionsArgv ('dumpData') );

  print "$returnMessage\n";
  exit $ERRORS{$_[0]->{_pluginValues}->{stateError}};
}

# Destructor  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub DESTROY { print (ref ($_[0]), "::DESTROY: ()\n") if ( $_[0]->{_debug} ); }

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1;

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__END__

=head1 NAME

ASNMTAP::Asnmtap::Plugin Subclass of ASNMTAP::Asnmtap

=head1 Description

This module that provides a nice object oriented interface for building ASNMTAP (http://asnmtap.citap.be) compatible plugins.

=head1 SEE ALSO

ASNMTAP::Asnmtap, ASNMTAP::Asnmtap::Plugins::Nagios

=head1 AUTHOR

Alex Peeters [alex.peeters@citap.be]

=head1 COPYRIGHT NOTICE

(c) Copyright 2000-2006 by Alex Peeters [alex.peeters@citap.be],
                        All Rights Reserved.

ASNMTAP is based on 'Process System daemons v1.60.17-01', Alex Peeters [alex.peeters@citap.com]

Purpose: CronTab (CT, sysdCT),
         Disk Filesystem monitoring (DF, sysdDF),
         Intrusion Detection for FW-1 (ID, sysdID)
         Process System daemons (PS, sysdPS),
         Reachability of Remote Hosts on a network (RH, sysdRH),
         Rotate Logfiles (system activity files) (RL),
         Remote Socket monitoring (RS, sysdRS),
         System Activity monitoring (SA, sysdSA).

'Process System daemons' is based on 'sysdaemon 1.60' written by Trans-Euro I.T Ltd

=head1 LICENSE

This ASNMTAP CPAN library and Plugin templates are free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The other parts of ASNMTAP may be used and modified free of charge by anyone so long as this copyright notice and the comments above remain intact. By using this code you agree to indemnify Alex Peeters from any liability that might arise from it's use.

Selling the code for this program without prior written consent is expressly forbidden. In other words, please ask first before you try and make money off of my program.

Obtain permission before redistributing this software over the Internet or in any other medium. In all cases copyright and header must remain intact.

=cut