#
# Plugin::Parameter for perl version of Nagios plugins
#
# This module provides the support for the Plugin wrapper.
# It represents a parameter that can be supplied to a plugin
#

package Plugin::Parameter;

require 5.006;
require Exporter;
use strict;

our @ISA = qw(Exporter);
sub _check_hostname();
sub _check_integer();
sub _check_timeout();
sub _check_port();
sub _check_directory();
sub _check_file();
sub _check_user();
sub _check_community();
sub _check_authprotocol();
sub _check_privprotocol();
sub _check_password();
sub _check_snmpversion();
sub _check_snmpv3seclevel();
sub _check_snmpcontext();
sub _check_temperature();
sub _check_time();
sub _check_percent();
sub _check_threshold();
sub _check_smbpath();

our $helpparameter = new Plugin::Parameter(-name => "help", -flags => [ 'h', 'help' ],
					   -mandatory => 0,
					   -binding => \$Plugin::__help,
					   -description => "Print a long help listing");
our $h_opt = $helpparameter;
our $versionparameter = new Plugin::Parameter(-name => "version", -flags => [ 'V', 'version' ],
					      -mandatory => 0,
					      -binding => \$Plugin::__version,
					      -description => "Print the program version");
our $V_opt = $versionparameter;
our $xmlparameter = new Plugin::Parameter(-name => "xml", -flags => [ 'xml' ],
					  -mandatory => 0,
					  -description => "Output data from plugin in XML",
					  -binding => \$Plugin::__xml);
our $helpparameterlist = [ $helpparameter, $xmlparameter ];
our $h_opts = $helpparameterlist;
our $versionparameterlist = [ $versionparameter, $xmlparameter ];
our $V_opts = $versionparameterlist;

our $hostnameparameter = new Plugin::Parameter(-name => "hostname", -flags => [ 'H', 'hostname' ],
					       -type => "HOSTADDRESS", -mandatory => 1, -valueoptional => 0,
					       -checker => \&_check_hostname,
					       -description=> "Host name or address for server");
our $H_opt = $hostnameparameter;
our $warningparameter = new Plugin::Parameter(-name => "warning", -flags => [ 'w', 'warning' ],
					      -type => "INTEGER", -mandatory => 1, -valueoptional => 0,
					      -checker => \&_check_integer);
our $w_opt = $warningparameter;
our $criticalparameter = new Plugin::Parameter(-name => "critical", -flags => [ 'c', 'critical' ],
					       -type => "INTEGER", -mandatory => 1, -valueoptional => 0,
					       -checker => \&_check_integer);
our $c_opt = $criticalparameter;
our $timeoutparameter = new Plugin::Parameter(-name => "timeout", -flags => [ 't', 'timeout' ],
					      -type => "TIMEOUT", -mandatory => 0, -valueoptional => 0,
					      -default => 30,
					      -checker => \&_check_timeout,
					      -description => "number of seconds to wait for response");
our $t_opt = $timeoutparameter;
our $portparameter = new Plugin::Parameter(-name => "port", -flags => [ 'p', 'port' ],
					   -type => "PORT", -mandatory => 0, -valueoptional => 0,
					   -checker => \&_check_port,
					   -description => "port to use to query service");
our $p_opt = $portparameter;
our $verboseparameter = new Plugin::Parameter(-name => "verbose", -flags => [ 'v', 'verbose' ],
					      -mandatory => 0,
					      -description => "verbose output from plugin");
our $v_opt = $verboseparameter;

our $directoryparameter = new Plugin::Parameter(-name => "directory", -flags => [ 'd', 'directory' ],
						-mandatory => 1, -valueoptional => 0, -type => "DIRECTORY",
						-checker => \&_check_directory,
						-description => "Directory to check" );
our $configfileparameter = new Plugin::Parameter(-name => "configfile", -flags => [ 'f', 'config-file' ],
						 -type => "FILENAME", -mandatory => 1, -valueoptional => 0,
						 -checker => \&_check_file,
						 -description => "Configuration file");
our $filenameparameter = new Plugin::Parameter(-name => "filename", -flags => [ 'f', 'filename' ],
					       -type => "FILENAME", -mandatory => 1, -valueoptional => 0,
					       -checker => \&_check_file,
					       -description => "File to check");
our $commandfileparameter = new Plugin::Parameter(-name => "commandfile", -flags => [ 'F', 'command-file' ],
						  -type => "FILENAME", -mandatory => 1, -valueoptional => 0,
						  -checker => \&_check_file,
						  -description => "Command file to run");
our $filesizeparameter = new Plugin::Parameter(-name => "filesize", -flags => [ 's', 'filesize' ],
					       -type => "INTEGER", -mandatory => 1, -valueoptional => 0,
					       -checker => \&_check_integer,
					       -description => "Size of file to check");
our $userparameter = new Plugin::Parameter(-name => "user", -flags => [ 'u', 'user' ],
					   -mandatory => 0, -valueoptional => 0, -type => "USER",
					   -checker => \&_check_user,
					   -description => "Username to log in to server");
our $u_opt = $userparameter;
our $passwordparameter = new Plugin::Parameter(-name => "password", -flags => ['p', 'password' ],
					       -mandatory => 0, -valueoptional => 0, -type => "PASSWORD",
					       -checker => \&_check_password,
					       -description => "Password to log in to server");
our $P_opt = $passwordparameter;
our $communityparameter = new Plugin::Parameter(-name => "community", -flags => [ 'C', 'community' ],
						-type => "COMMUNITY", -mandatory => 0, -valueoptional => 0,
						-default => "public",
						-checker => \&_check_community,
						-description=> "Community string to use in SNMP operations");
our $C_opt = $communityparameter;
our $authprotocolparameter = new Plugin::Parameter(-name => "authprotocol", -flags => [ 'a', 'authprotocol' ],
						   -type => "AUTHPROTOCOL", -mandatory => 0, -valueoptional => 0,
						   -checker => \&_check_authprotocol,
						   -description => "SNMP authentication protocol - MD5, SHA");
our $authpasswordparameter = new Plugin::Parameter(-name => "authpassword", -flags => [ 'A', 'authpassword' ],
						   -type => "PASSWORD", -mandatory => 0, -valueoptional => 0,
						   -checker => \&_check_password,
						   -description => "SNMP authentication password - pre-encryption");
our $authkeyparameter = new Plugin::Parameter(-name => "authkey", -flags => [ 'k', 'authkey' ],
					      -type => "PASSWORD", -mandatory => 0, -valueoptional => 0,
					      -checker => \&_check_password,
					      -description => "SNMP authentication password as a key - localized with authoritativeEngineID");
our $privprotocolparameter = new Plugin::Parameter(-name => "privprotocol", -flags => [ 'x', 'privprotocol' ],
						   -type => "PRIVPROTOCOL", -mandatory => 0, -valueoptional => 0,
						   -checker => \&_check_privprotocol,
						   -description => "SNMP privacy protocol - DES, 3DESEDE, AESCBC128, AESCBC192, AESCBC256");
our $privpasswordparameter = new Plugin::Parameter(-name => "privpassword", -flags => [ 'P', 'privpassword' ],
						   -type => "PASSWORD", -mandatory => 0, -valueoptional => 0,
						   -checker => \&_check_password,
						   -description => "SNMP privacy password - pre-encryption");
our $privkeyparameter = new Plugin::Parameter(-name => "privkey", -flags => [ 'K', 'privkey' ],
					      -type => "PASSWORD", -mandatory => 0, -valueoptional => 0,
					      -checker => \&_check_password,
					      -description => "SNMP privacy password as a key - localized with authoritativeEngineID");
our $snmpversionparameter = new Plugin::Parameter(-name => "snmpversion", -flags => [ 'S', 'snmpversion' ],
						  -type => "SNMPVERSION", -mandatory => 0, -valueoptional => 0,
						  -default => "snmpv1",
						  -checker => \&_check_snmpversion,
						  -description => "SNMP version - 1, 2, 3; snmpv1, snmpv2c, snmpv3");
our $snmpv3seclevelparameter = new Plugin::Parameter(-name => "seclevel", -flags => [ 'L', 'seclevel' ],
						     -type => "SNMPV3SECLEVEL", -mandatory => 0, -valueoptional => 0,
						     -default => "noAuthNoPriv",
						     -checker => \&_check_snmpv3seclevel,
						     -description => "SNMP version 3 security level - noAuthNoPriv, authNoPriv, authPriv");
our $snmpcontextparameter = new Plugin::Parameter(-name => "snmpcontext", -flags => [ 'c', 'context' ],
						  -type => "SNMPCONTEXT", -mandatory => 0, -valueoptional => 0,
						  -default => "",
						  -checker => \&_check_snmpcontext,
						  -description => "SNMP version 3 context name");

our @standardparameters = ( $hostnameparameter, $warningparameter, $criticalparameter, $timeoutparameter, $verboseparameter );
our @stdopts = ( @standardparameters );

our @snmpparameters = ( $communityparameter, $snmpversionparameter, $snmpv3seclevelparameter, $userparameter, $snmpcontextparameter,
			$authpasswordparameter, $authkeyparameter, $authprotocolparameter,
			$privpasswordparameter, $privkeyparameter, $privprotocolparameter,
			$portparameter);

our @defaultparameterlists = ( $h_opts, $V_opts );
our @defoptslists = ( @defaultparameterlists );

our @EXPORT = qw($helpparameterlist          $h_opts
		 $versionparameterlist       $V_opts
		 $helpparameter              $h_opt
		 $versionparameter           $V_opt
		 $xmlparameter               $xml_opt

		 @standardparameters         @stdopts
		 @defaultparameterlists      @defoptslists

		 $hostnameparameter          $H_opt
		 $timeoutparameter           $t_opt
		 $verboseparameter           $v_opt
		);

our @EXPORT_OK = qw($warningparameter        $w_opt
		    $criticalparameter       $c_opt
		    $portparameter           $p_opt
		    $directoryparameter
		    $configfileparameter
		    $filenameparameter
		    $commandfileparameter
		    $filesizeparameter
		    $userparameter           $u_opt
		    $passwordparameter       $P_opt

		    @snmpparameters

		    $communityparameter      $C_opt
		    $authprotocolparameter
		    $authpasswordparameter
		    $authkeyparameter
		    $privprotocolparameter
		    $privpasswordparameter
		    $privkeyparameter
		    $snmpversionparameter
		    $snmpv3seclevelparameter
		    $snmpcontextparameter

		    _check_hostname
		    _check_integer
		    _check_timeout
		    _check_port
		    _check_directory
		    _check_file
		    _check_user
		    _check_community
		    _check_authprotocol
		    _check_privprotocol
		    _check_password
		    _check_snmpversion
		    _check_snmpv3seclevel
		    _check_snmpcontext

		    _check_temperature
		    _check_time
		    _check_percent
		    _check_threshold
		    _check_smbpath
		   );

our %EXPORT_TAGS = (snmp => [ qw($communityparameter $C_opt
				 $authprotocolparameter
				 $authpasswordparameter
				 $authkeyparameter
				 $privprotocolparameter
				 $privpasswordparameter
				 $privkeyparameter
				 $snmpversionparameter
				 $snmpv3seclevelparameter
				 $snmpcontextparameter

				 $userparameter $u_opt
				 $portparameter $p_opt

				 $hostnameparameter $H_opt

				 @snmpparameters
				) ],
		    thresholds => [ qw($warningparameter $w_opt
				       $criticalparameter $c_opt
				      ) ],
		    checkers => [ qw(_check_hostname
				     _check_integer
				     _check_timeout
				     _check_port
				     _check_directory
				     _check_file
				     _check_user
				     _check_community
				     _check_authprotocol
				     _check_privprotocol
				     _check_password
				     _check_snmpversion
				     _check_snmpv3seclevel
				     _check_snmpcontext

				     _check_temperature
				     _check_time
				     _check_percent
				     _check_threshold
				     _check_smbpath
				    ) ],
		    standard => [ qw($hostnameparameter  $H_opt
				     $warningparameter   $w_opt
				     $criticalparameter  $c_opt
				     $timeoutparameter   $t_opt
				     $verboseparameter   $v_opt
				     @standardparameters
				    ) ],
		   );


{
  my %seen = ();
  push @{$EXPORT_TAGS{all}},
    grep {!$seen{$_}++} @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS;
}

my $PROGNAME = $main::PROGNAME;

sub new {
  my ($class, %argv) = @_;
  my $this = bless { _name            => undef,
		     _flags           => undef,
		     _type            => undef,
		     _mandatory       => 0,
		     _valueoptional   => 0,
		     _default         => undef,
		     _description     => undef,
		     _errormessage    => undef,
		     _checker         => undef,
		     _binding         => undef }, $class;
  foreach my $key (keys %argv) {
    if ($key =~ m/^-?name$/i) {
      $this->name($argv{$key});
    } elsif ($key =~ m/^-?flags/i) {
      $this->flags($argv{$key});
    } elsif ($key =~ m/^-?flag$/i) {
      $this->add_flag($argv{$key})
    } elsif ($key =~ m/^-?type$/i) {
      $this->type($argv{$key});
    } elsif ($key =~ m/^-?mandatory$/i
	     || $key =~ m/^-?required$/i) {
      $this->mandatory($argv{$key});
    } elsif ($key =~ m/^-?optional$/i) {
      $this->optional($argv{$key});
    } elsif ($key =~ m/^-?valueoptional$/i) {
      $this->valueoptional($argv{$key})
    } elsif ($key =~ m/^-?default$/i) {
      $this->default($argv{$key});
    } elsif ($key =~ m/^-?description$/i) {
      $this->description($argv{$key});
    } elsif ($key =~ m/^-?binding$/i) {
      $this->binding($argv{$key});
    } elsif ($key =~ m/^-?message$/i) {
      $this->message($argv{$key});
    } elsif ($key =~ m/^-?checker$/i) {
      $this->checker($argv{$key});
    } else {
      # Structural calling error - report and exit
      utils::usage("$PROGNAME UNKNOWN: arg $key unknown in Plugin::Parameter->new\n");
    }
  }

  return $this;
}

sub name {
  my ($this, $name) = @_;
  return $this->{_name} = $name if (defined $name);
  return $this->{_name};
}

sub flags {
  my ($this, $flags) = @_;
  if (defined $flags) {
    if (ref($flags) ne 'ARRAY') {
      utils::usage("$PROGNAME UNKNOWN: flags supplied must be reference to an ARRAY\n");
    }
    return $this->{_flags} = $flags;
  }
  return $this->{_flags};
}

sub add_flag {
  my ($this, $flag) = @_;
  push @{$this->{_flags}}, $flag;
}

sub type {
  my ($this, $type) = @_;
  $this->{_type} = $type if (defined $type);
  return $this->{_type};
}

sub mandatory {
  my ($this, $mandatory) = @_;
  if (defined $mandatory) {
    $mandatory = ($mandatory =~ /^(yes|true)$/i)?1
      :($mandatory =~ /^(no|false)$/i)?0
	:$mandatory;
    return $this->{_mandatory} = $mandatory;
  }
  return $this->{_mandatory};
}

sub required {
  my ($this, $required) = @_;
  return $this->mandatory($required);
}

sub optional {
  my ($this, $optional) = @_;
  if (defined $optional) {
    my $optional = ($optional =~ /^(yes|true)$/i)?1
      :($optional =~ /^(no|false)$/i)?0
	:$optional;
    return $this->mandatory(!$optional);
  }
  return !$this->mandatory();
}

sub valueoptional {
  my ($this, $valueoptional) = @_;
  if (defined $valueoptional) {
    my $valueoptional = ($valueoptional =~ /^(yes|true)$/i)?1
      :($valueoptional =~ /^(no|false)$/i)?0
	:$valueoptional;
    return $this->{_valueoptional} = $valueoptional;
  }
  return $this->{_valueoptional};
}

sub default {
  my ($this, $default) = @_;
  return $this->{_default} = $default if (defined $default);
  return $this->{_default};
}

sub description {
  my ($this, $description) = @_;
  return $this->{_description} = $description if (defined $description);
  return $this->{_description};
}

sub binding {
  my ($this, $binding) = @_;
  return $this->{_binding} = $binding if (defined $binding);
  return $this->{_binding};
}

sub message {
  my ($this, $message) = @_;
  return $this->{_errormessage} = $message if (defined $message);

  $message = $this->{_errormessage};

  if (!defined $message) {
    $message = "Option " . $this->name()
      . " is " . (($this->required())?"mandatory":"optional");
  }

  return $message;
}

sub checker {
  my ($this, $checker) = @_;
  return $this->{_checker} = $checker if (defined $checker);
  return $this->{_checker};
}

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

sub _check_hostname () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_hostname called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^([A-Za-z0-9][-.A-Za-z0-9]*)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid host: (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_integer () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_integer called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^([-+]?[0-9]+)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid integer (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_timeout () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_timeout called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^([0-9]+)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid timeout (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_port () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_port called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^([A-Za-z][-A-Za-z0-9]*)$/
      && $$opt !~ /^([0-9]+)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid port (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_directory () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_directory called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  # Could check for existence and is a directory but leave that for higher up!
  $$opt =~ /^(.*)$/;

  $$opt = $1; # Untaint option
}

sub _check_file () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_file called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  # Could check for existence and is a file but leave that for higher up!
  $$opt =~ /^(.*)$/;

  $$opt = $1; # Untaint option
}

sub _check_user () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_user called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^([A-Za-z][-_.A-Za-z0-9]*)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid username (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_password () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_passwor called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  # Allow anything as a password - strength checking is an upper layer facility
  $$opt =~ /^(.*)$/;

  $$opt = $1; # Untaint option
}

sub _check_community () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_community called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  $$opt =~ /^(.*)$/;

  $$opt = $1; # Untaint option
}

sub _check_authprotocol () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_authprotocol called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(MD5)$/i && $$opt !~ /^(SHA)$/i) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid authentication protocol (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_privprotocol () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_privprotocol called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(DES)$/i && $$opt !~ /^(3DESEDE)$/i && $$opt !~ /^(AESCBC(128|192|256))$/i) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid privacy protocol (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_snmpversion () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_snmpversion called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(snmpv(1|2c|3))$/i && $$opt !~ /^([123])$/ && $$opt !~ /^(2c)$/i ) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid snmpversion (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
  if ($$opt =~ /^2c$/i) { $$opt = 'snmpv2c'; } # Fake the extra interface
}

sub _check_snmpv3seclevel () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_snmpv3seclevel called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^((no)?auth(no)?priv)$/i) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid snmpv3seclevel (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_snmpcontext () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_snmpcontext called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(.*)$/i) {
    # Oops!
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid snmpcontext (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_temperature () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_temperature called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(\d+)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid temperature (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_time () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_time called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(\d+)$/) { # What about other formats?
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid time (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_percent () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_percent called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ /^(100(\.0+)?%?)$/ && $$opt !~ /^([1-9][0-9](\.[0-9]+)?%?)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid percentage (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
  if ($$opt =~ /^(.+)%$/) { $$opt = $1; } # Strip the percent sign
}

sub _check_threshold () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_threshold called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ m/^(([1-9][0-9]*)[,:]([1-9][0-9]*)%?)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid threshold (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

sub _check_smbpath () {
  my ($opt, $parameter, $plugin) = @_;

  if (ref($opt) ne 'SCALAR') {
    # Inappropriate checker usage
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: _check_smbpath called with non-scaler option\n");
  }
  if (!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      &utils::usage("$main::PROGRAM UNKNOWN: Option (--" . $parameter->name() . ") required but not set\n");
    }
    return;
  }
  if ($$opt !~ m/^([-_.A-Za-z0-9]+\$?)$/) {
    $plugin->usage();
    &utils::usage("$main::PROGNAME UNKNOWN: Invalid smbpath (--" . $parameter->name() . "): $$opt\n");
  } else {
    $$opt = $1; # Untaint option
  }
}

1;

=head1 NAME

Plugin::Parameter - Parameter object definitions and methods for the Nagios Plugin Framework

=head1 SYNOPSIS

  use Plugin::Parameter qw(:DEFAULT);

$H_opt = new Plugin::Parameter(-name         => "hostname",
			       -flags        => [ 'H', 'hostname' ],
			       -mandatory    => "yes",
			       -binding      => \$opt_H,
			       -checker      => \&Plugin::Parameter::_check_hostname,
			       -description  => "Host name or address for service");

=head1 DESCRIPTION

This package defines the object support for the Parameter objects that are used to construct
the parameterlists used by the Plugin object to implement command line parsing.

The major functionality of the package is to support pre-defined parameters and option
checking routinges.

=head1 METHODS

When named argument are expected by the methods,  two different styles are
supported.  All examples in this documentation use the dashed-option style:

	$object->method(-argument => $value);

However, the IO:: style is also allowed:

	$object->method(Argument => $value);

=head2 new() - create a new Plugin::Parameter object

    see L<SYNOPSIS>

This is the constructor for the Parameter objects.  A reference to the Parameter
object is returned if the creation of the object is successful.  If the creation
fails then the constructor currently does not return.  However, it will in the
future return 'undef' in such a circumstance and if called in list context will
return an error message as the second result.

On failure the current implementation calls the utils::usage function reporting
an unknown option.

The arguments passed to the constructor set up attributes of the object, each of
these can be later changed by the accessor methods.

=over

=item -name Argument

The name of the parameter can be set on the object. Currently this is used in
error reporting functions and to name bound options where an empty flag list
exists. Future use of this attribute may include a has key access interface to
the parameters of a plugin, which could be a partial mechanism for support
of alternative parameterlists (See L<Plugin>)

=item -flags Argument

The flags for a parameter are an ARRAY containing the various flags that can
introduce a parameter.  The convention adopted is to put the short (usually
single character form of the flag as the first or earlier entries), this has
2 consequences: the implicitly bound variable will be a SCALAR $main::opt_<flag>,
thus the shorter flag provides for a shorter variable name; the usage statement
will have short flags reported by default and longer flags will only appear
in the longer help listings.

Flags are accumulated by the B<setup> method of the plugin object, to produce
the input to GetOptions, because of the parameterlists construct a flag may
appear multiple times and this is flattened silently before the call to GetOptions.
A later implementation will address this issue, but plugin developers should be
aware of this and check that they have not repeated a flag setting.

=item -type Argument

The type of the Parameter determines the GetOptions handling set up by the plugin.
It can be any string at present and will be parsed as a string by GetOptions
if not from the inbuilt list of types (See L<Plugin>). The type is also reported
in the usage lines and in the help listings and therefore should be informative.

Later implementations will include a binding from type to default checker function,
allowing automatic binding of basic checking functions to the parameter settings.

A type setting of NONE is treated specially. This indicates that the flag is
self-defining.

=item -mandatory Argument

The mandatory attribute of the Parameter determines whether the parameter must
appear on the command line. Synonyms for mandatory are allow, 'required' can be
used and optional is also allowed with the opposite sense. Mandatory takes a
true or false value - allowing "yes", "no", "true", "false" as recognised strings,
otherwise the value is just set. This allows use of 0 for false and all other values
for true.

=item -required Argument

See L<-mandatory>.

=item -optional Argument

See L<-mandatory>

=item -valueoptional Argument

The valueoptional attribute of the Parameter determines whether a value must be
given for the parameter. Values are optional in 2 cases: where the parameter type
is NONE (See L<-type>); and when the parameter has a defined default.

=item -default Argument

A default value for the parameter can be set and will be used if the parameter is
not set from the command line by use of the flag or by positional substitution.

=item -description Argument

The description of the parameter is used in the help listing output, and should
describe the use to which the parameter will be put.

=item -message Argument

A parameter may have a message sttribute, which can be used as part of error reporting.

The message attribute is used in error reporting from the plugin->init() method. It
generates the message reported when the parameter is mandatory and is not set.

=item -checker Argument

A checker attribute provides a checker method to be called when a parameter has
its final value. This is called from $plugin->init() for all parameters.

A checker subroutine is defined as follows:

=over

sub _check_for_something_strange {
  my ($opt, $parameter, $plugin) = @_;
  if (ref($opt) ne 'SCALAR') {
    # Need SCALAR for this to work.
    $plugin->usage();  # Tell everybody how we should be called
    &utils::usage("$main::PROGNAME UNKNOWN: check failed with wrong typ of option\n");
  }
  if(!defined $$opt) {
    if ($parameter->required()) {
      $plugin->usage();
      .....
    }
    return; # Not required and not set so OK
  }
  if ($$opt !~ /^(Something Strange (.*))$/) {
    # Oops!! not what we expect to a something strange parameter
    $plugin->usage();
    ......
  } else {
    # Options has been checked as OK
    $$opt = $1; # Untaint the option
  }
}

=back

Thus the checker is always called with the reference to the option, the parameter,
and the plugin. It should always check that it can cope with the option (i.e. it
is the right type of object) that it is defined and if not report error if it
should be, and that it has an acceptable value.

The final act of a checker must be to untaint the option so that plugins can run with
Taint checking switched.

Future implementations will return indications of success and thus allow for multiple
checks to be run before exiting the program.

=item -binding Argument

The binding to the parameter is the object that is passed to the GetOptions call,
and is sued by the checker routines. It can be of any type that GetOptions can
deal with, which includes: SCALAR, ARRAY, HASH, and CODE. The checker must also
be expecting and handle the bound type.

The plugin setup code will bind a default option constructed using the initial
flag (or parameter name if not flags) if this attribute is not defined at setup time.

Future implementations may change this behaviour to include a binding table
to allowing more intelligent option binding, when parameterlists are handled
correctly.

=head2 name() accessor

The parameter name can be accessed and changed by using the name() method.

=over

$parameter->name("hostaddress");

my $name = $parameter->name();

=back

=head2 flags() accessor

The parameter flags ARRAY can be accessed and changed by using the flags() method.

=over

$parameter->flags([ 'H', 'hostaddress' ]);

my $flags = $parameter->flags();

=back

=head2 add_flag() method

The add_flag method will append a flag to the current flag set.

=over

$parameter->add_flag( 'hostfqdn' );

=back

=head2 type() accessor

The parameter type can be accessed and changed by using the type() method.

=over

$parameter->type("HOSTADDRESS");

my $type = $parameter->type();

=back

=head2 mandatory() accessor
=head2 required() accessor
=head2 optional() accessor

The parameter's mandatory attribute can be accessed and changed by using one
of the mandatory(), required(), and optional() methods.

=over

$parameter->mandatory("yes");

if($parameter->required()) {
  # Go get a default value from the database....
}

=back

=head2 valueoptional() accessor

The parameter's valueoptional attributes can be accessed and changed by using
the valueoptional() method.

=over

$parameter->valueoptional("no");

if ($parameter-.valueoptional()) { next; }

=back

=head2 default() accessor

The parameter's default attribute can be accessed and changed by using the
default() method.

=over

$parameter->default("Friday is a good day to get better");

my $default = $parameter->default();

=back

=head2 description() accessor

The parameter's description attribute can be accessed and changed by using the
description() method.

=over

$parameter->description("This is a parameter");

my $description = $parameter->description();

=back

=head2 binding() accessor

The parameter's binding attribute can be accessed and changed by using the
binding() method. This will mostly be a SCALAR reference, although ARRAY,
HASH and CODE references are also allowed in the current implementation.

=over

$parameter->binding(\$opt_H);

my $option = ${$parameter->binding()};

=back

=head2 message() accessor

The parameter;s message attribute can be accessed and changed by using the
message() method. This returns the assigned message if one is set, otherwise
a default message is constructed on access.

=over

$parameter->message("Host address options must be given for this plugin to function");

print "$PROGNAME UNKNOWN: " . $parameter->message() . "\n";

=back

=head2 checker accessor

The parameter's checker attribute can be access and changed by using the
checker() method. A checker attribute must be a reference to a subroutine.

THIS IS CURRENTLY NOT CHECKED.

=over

$parameter->checker(sub { my ($opt, $parameter, $plugin) = @_;
                          .......
                        });

$parameter->checker()->(\$opt_H, $parameter, $plugin);

=back

A checker currently returns without any result if everything checks out.
A later implementation will return undef to signal OK and anything
else will be error status information.

=head1 EXPORTED ITEMS

The Plugin::Parameter modules exports a number of pre-defined parameters,
parameterlists and checker functions. Examples can be found in the plugins
of usage of thes facilities.

The list at present is as follows:

our @EXPORT = qw($helpparameterlist          $h_opts
		 $versionparameterlist       $V_opts
		 $helpparameter              $h_opt
		 $versionparameter           $V_opt
		 $xmlparameter               $xml_opt

		 @standardparameters         @stdopts
		 @defaultparameterlists      @defoptslists

		 $hostnameparameter          $H_opt
		 $timeoutparameter           $t_opt
		 $verboseparameter           $v_opt
		);

our @EXPORT_OK = qw($warningparameter        $w_opt
		    $criticalparameter       $c_opt
		    $portparameter           $p_opt
		    $directoryparameter
		    $configfileparameter
		    $filenameparameter
		    $commandfileparameter
		    $filesizeparameter
		    $userparameter           $u_opt
		    $passwordparameter       $P_opt

		    @snmpparameters

		    $communityparameter      $C_opt
		    $authprotocolparameter
		    $authpasswordparameter
		    $authkeyparameter
		    $privprotocolparameter
		    $privpasswordparameter
		    $privkeyparameter
		    $snmpversionparameter
		    $snmpv3seclevelparameter
		    $snmpcontextparameter

		    _check_hostname
		    _check_integer
		    _check_timeout
		    _check_port
		    _check_directory
		    _check_file
		    _check_user
		    _check_community
		    _check_authprotocol
		    _check_privprotocol
		    _check_password
		    _check_snmpversion
		    _check_snmpv3seclevel
		    _check_snmpcontext

		    _check_temperature
		    _check_time
		    _check_percent
		    _check_threshold
		    _check_smbpath
		   );

our %EXPORT_TAGS = (snmp => [ qw($communityparameter $C_opt
				 $authprotocolparameter
				 $authpasswordparameter
				 $authkeyparameter
				 $privprotocolparameter
				 $privpasswordparameter
				 $privkeyparameter
				 $snmpversionparameter
				 $snmpv3seclevelparameter
				 $snmpcontextparameter

				 $userparameter $u_opt
				 $portparameter $p_opt

				 $hostnameparameter $H_opt

				 @snmpparameters
				) ],
		    thresholds => [ qw($warningparameter $w_opt
				       $criticalparameter $c_opt
				      ) ],
		    checkers => [ qw(_check_hostname
				     _check_integer
				     _check_timeout
				     _check_port
				     _check_directory
				     _check_file
				     _check_user
				     _check_community
				     _check_authprotocol
				     _check_privprotocol
				     _check_password
				     _check_snmpversion
				     _check_snmpv3seclevel
				     _check_snmpcontext

				     _check_temperature
				     _check_time
				     _check_percent
				     _check_threshold
				     _check_smbpath
				    ) ],
		    standard => [ qw($hostnameparameter  $H_opt
				     $warningparameter   $w_opt
				     $criticalparameter  $c_opt
				     $timeoutparameter   $t_opt
				     $verboseparameter   $v_opt
				     @standardparameters
				    ) ],
		   );


The $helpparameterlist item is also known as $h_opts for shorthand. See above
for other such statements.

The checkers are used internally by pre-defined parameters and can be used
by plugin code as required.

For further information please see the code of Plugin::Parameter

=head1 Further Information

The Plugin module is a work in progress. It has been integrated with the plugins
available in nagios-plugins-1.4.0alpha1 CVS head as of the 20th March 2004.

Further work will make plugin writing much more about the gathering and
processing of information and less about producing correct output from
properly formatted arguments.

=head1 AUTHOR

Howard E. Wilkinson <howard@cohtech.com>

=head1 Copyright

Copyright (C) 2004 Howard Wilkinson. All rights reserved. This program
is free software; you may redistribute it and/or modify it under the
same terms as Perl itself.

=cut

