Subversion Repositories vnag

Rev

Rev 2 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php /* <ViaThinkSoftSignature>
  2. i1Ox4pM0A5UeIZrXsnfEQNOTzk0ajnmpk57FeGqfm6NfPzJn2WB6PgTV1mt61S0De
  3. Xbj4756UEGFYmRASqM9SC4r4GR4PLgZ7+gZ99D2kezXEqu1BpXozrw65BRRgls9u1
  4. 8g4i1oU+V5+/zqCFlDRSXS3UCoeReHtYdZG6Ve9JhI0xB2eG4LL+n1mAsC810LhCn
  5. gHIqP6W5LDgufSiWT8cmJIATcVnVF8AgAVCe6qDrkzFL1/KlHUsjdWYdExkERM2mi
  6. Wt25wdDk5NGUvqs/L6PUVS/0AHZLoNnFmZtUpobIfMXboSF4GxSbC/j2UVrvTnS1g
  7. NLSPEe26U7gbCTXD+3ixpEWUiTVFsLN9KlS1KpvZO/FHafYqiV7WvdjD1wLeXvPQ7
  8. /mcZAzRW837FR6ouoZwJnOv5iFAIQFn9Ci0UGtvu6HJV1jnctkkkrfdaK+tf+w2Fe
  9. gLvisDHWMm2NFX5soFokVkPKPqu+b4HjJhsjv8FWo9a4INiQBSQ60IaU5mIeLC4Gl
  10. YSf6otP6r74O2TUUZzWEd/vhe0ZoQK/HuAnnibT7ftBHrTfSmdQ258/1mvMB7+sG1
  11. OT8YpCSM8RqdN3PtKt/qFK6DBXiCkrL45NuarVhu0v0gbN+Wsq4SHiEkK2iDsFZPb
  12. HLNGqh1qrV23AUKYdxNpsypVboe74DcV3Rg51maiYavMbs4Zk9sy+0b0VjHI3MorK
  13. K/FV3nr6HeCJEGyV2pWu45wGGfFQ9vZntZxeZ/V6LNd0Dcbp4HgLix4mTA3bz1YMD
  14. trCRKUt7oY7rKAdTCC+iJzGUUBTquf5HsCEbG+ztY1oHk4uN9IJ1r2eu/38XfUJm+
  15. 4wUyX8mXNOzSPA2SqKrz3gvJRkDZ+E1ZN6KbaEIV17ae7exSWoYyZm3ML0fFrwQG+
  16. 9GiWBmyEqQ8Ad5YxtWOnvCTHvFbEYl0TqpcnPDEhcEoU7Cv/78G+wezq3aUo4ij3n
  17. 7yjyDOW6BphHpA6yMFyx2AAj4u8c/LNpssVBFUwAkh1SAochIN0hzo3Kfx/csJSze
  18. OSTVUbS1NdAR0Xb/POii7tM/Pka9yUZ6b0Y+5goKBKIpEPxJfb0rLelmLB2DtL0sK
  19. qHg9BDpGjyHeSffXLCHs9bDP93F/7SFONrDSXoAaVfJuh8duW31lfBoiIEN0X8X4a
  20. 0ZqRPCbMcPgOq5VF3NoDe26SFhnR1VE78u1xdJorNc1yW16fcYDW1mDBLmsZRbTSh
  21. lACvUoW58SdHPFM5aoZFwhNtqmqzjqte5qdDvz2EWkS8KB+8Z+Ve2UvfQ5yVkyjLp
  22. 3HLo1XJeDphQGdHooYyZBQUtvTDJ1F2ZwfC+a6iimM5SGB9eJfXEHH5uPtJW0Tsgs
  23. Q==
  24. </ViaThinkSoftSignature> */ ?>
  25. <?php
  26.  
  27. /*
  28.  
  29.       VNag - Nagios Framework for PHP                  (C) 2014-2018
  30.       __     ___      _____ _     _       _     ____         __ _
  31.       \ \   / (_) __ |_   _| |__ (_)_ __ | | __/ ___|  ___  / _| |_
  32.        \ \ / /| |/ _` || | | '_ \| | '_ \| |/ /\___ \ / _ \| |_| __|
  33.         \ V / | | (_| || | | | | | | | | |   <  ___) | (_) |  _| |_
  34.          \_/  |_|\__,_||_| |_| |_|_|_| |_|_|\_\|____/ \___/|_|  \__|
  35.  
  36.       Developed by Daniel Marschall             www.viathinksoft.com
  37.       Licensed under the terms of the Apache 2.0 license
  38.       Revision 2018-11-04
  39.  
  40. */
  41.  
  42. /****************************************************************************************************
  43.  
  44. Introduction:
  45.  
  46.         VNag is a small framework for Nagios Plugin Developers who use PHP CLI scripts.
  47.         The main purpose of VNag is to make the development of plugins as easy as possible, so that
  48.         the developer can concentrate on the actual work. VNag will try to automate as much
  49.         as possible.
  50.  
  51.         Please note that your script should include the +x chmod flag:
  52.                 chmod +x myscript.php
  53.  
  54.         Please see the the demo/ folder for a few examples how to use this framework.
  55.  
  56. Arguments:
  57.  
  58.         Example:
  59.                 $this->addExpectedArgument($argSilent = new VNagArgument('s', 'silent', VNagArgument::VALUE_FORBIDDEN, null,       'Description for the --silent output', $defaultValue));
  60.                 $this->addExpectedArgument($argHost   = new VNagArgument('H', 'host',   VNagArgument::VALUE_REQUIRED,  'hostname', 'Description for the --host output',   $defaultValue));
  61.  
  62.         In the example above, the two argument objects $argSilent and $argHost were created.
  63.         With these objects of the type VNagArgument, you can query the argument's value,
  64.         how often the argument was passed and if it is set:
  65.  
  66.                 $argSilent->count();      // 1 if "-s" is passed, 2 if "-s -s" is passed etc.
  67.                 $argSilent->available();  // true if "-s" is passed, false otherwise
  68.                 $argHost->getValue();     // "example.com" if "-h example.com" is passed
  69.  
  70.         It is recommended that you pass every argument to $this->addExpectedArgument() .
  71.         Using this way, VNag can generate a --help page for you, which lists all your arguments.
  72.         Future version of VNag may also require to have a complete list of all valid arguments,
  73.         since the Nagios Development Guidelines recommend to output the usage information if an illegal
  74.         argument is passed. Due to PHP's horrible bad implementation of GNU's getopt(), this check for
  75.         unknown arguments is currently not possible, and the developer of VNag does not want to use
  76.         dirty hacks/workarounds, which would not match to all argument notation variations/styles.
  77.         See: https://bugs.php.net/bug.php?id=68806
  78.              https://bugs.php.net/bug.php?id=65673
  79.              https://bugs.php.net/bug.php?id=26818
  80.  
  81. Setting the status:
  82.  
  83.         You can set the status with:
  84.                 $this->setStatus(VNag::STATUS_OK);
  85.         If you don't set a status, the script will return Unknown instead.
  86.         setStatus($status) will keep the most severe status, e.g.
  87.                 $this->setStatus(VNag::STATUS_CRITICAL);
  88.                 $this->setStatus(VNag::STATUS_OK);
  89.         will result in a status "Critical".
  90.         If you want to completely overwrite the status, use $force=true:
  91.                 $this->setStatus(VNag::STATUS_CRITICAL);
  92.                 $this->setStatus(VNag::STATUS_OK, true);
  93.         The status will now be "OK".
  94.  
  95.         Possible status codes are:
  96.                 (For service plugins:)
  97.                 VNag::STATUS_OK       = 0;
  98.                 VNag::STATUS_WARNING  = 1;
  99.                 VNag::STATUS_CRITICAL = 2;
  100.                 VNag::STATUS_UNKNOWN  = 3;
  101.  
  102.                 (For host plugins:)
  103.                 VNag::STATUS_UP       = 0;
  104.                 VNag::STATUS_DOWN     = 1;
  105.  
  106. Output:
  107.  
  108.         After the callback function cbRun() of your job has finished,
  109.         the framework will automatically output the results in the Nagios console output format,
  110.         the visual HTML output and/or the invisible HTML output.
  111.  
  112.         In case of CLI invokation, the Shell exit code will be remembered and
  113.         automatically returned by the shutdown handler once the script normally
  114.         terminates. (In case you run different jobs, which is not recommended, the
  115.         shutdown handler will output the baddest exit code).
  116.  
  117.         The Shell output format will be:
  118.                 <Service status text>: <Comma separates messages> | <whitespace separated primary performance data>
  119.                 "Verbose information:"
  120.                 <Multiline verbose output> | <Multiline secondary performance data>
  121.  
  122.         <Service status text> will be automatically created by VNag.
  123.  
  124.         Verbose information are printed below the first line. Most Nagios clients will only print the first line.
  125.         If you have important output, use $this->setHeadline() instead.
  126.         You can add verbose information with following method:
  127.                 $this->addVerboseMessage('foobar', $verbosity);
  128.  
  129.         Following verbosity levels are defined:
  130.                 VNag::VERBOSITY_SUMMARY                = 0; // always printed
  131.                 VNag::VERBOSITY_ADDITIONAL_INFORMATION = 1; // requires at least -v
  132.                 VNag::VERBOSITY_CONFIGURATION_DEBUG    = 2; // requiers at least -vv
  133.                 VNag::VERBOSITY_PLUGIN_DEBUG           = 3; // requiers at least -vvv
  134.  
  135.         All STDOUT outputs of your script (e.g. by echo) will be interpreted as "verbose" output
  136.         and is automatically collected, so
  137.                 echo "foobar";
  138.         has the same functionality as
  139.                 $this->addVerboseMessage('foobar', VNag::VERBOSITY_SUMMARY);
  140.  
  141.         You can set messages (which will be added into the first line, which is preferred for plugin outputs)
  142.         using
  143.                 $this->setHeadline($msg, $append, $verbosity);
  144.         Using the flag $append, you can choose if you want to append or replace the message.
  145.  
  146.         VNag will catch Exceptions of your script and will automatically end the plugin,
  147.         returning a valid Nagios output.
  148.  
  149. Automatic handling of basic arguments:
  150.  
  151.         VNag will automatic handle of following CLI arguments:
  152.                 -?
  153.                 -V --version
  154.                 -h --help
  155.                 -v --verbose
  156.                 -t --timeout   (only works if you set declare(ticks=1) at the beginning of each of your scripts)
  157.                 -w --warning
  158.                 -c --critical
  159.  
  160.         You can performe range checking by using:
  161.                 $example_value = '10MB';
  162.                 $this->checkAgainstWarningRange($example_value);
  163.         this is more or less the same as:
  164.                 $example_value = '10MB';
  165.                 $wr = $this->getWarningRange();
  166.                 if (isset($wr) && $wr->checkAlert($example_value)) {
  167.                         $this->setStatus(VNag::STATUS_WARNING);
  168.                 }
  169.  
  170.         In case that your script allows ranges which can be relative and absolute, you can provide multiple arguments;
  171.         $wr->checkAlert() will be true, as soon as one of the arguments is in the warning range.
  172.         The check will be done in this way:
  173.                 $example_values = array('10MB', '5%');
  174.                 $this->checkAgainstWarningRange($example_values);
  175.         this is more or less the same as:
  176.                 $example_values = array('10MB', '5%');
  177.                 $wr = $this->getWarningRange();
  178.                 if (isset($wr) && $wr->checkAlert($example_values)) {
  179.                         $this->setStatus(VNag::STATUS_WARNING);
  180.                 }
  181.  
  182.         Note that VNag will automatically detect the UOM (Unit of Measurement) and is also able to convert them,
  183.         e.g. if you use the range "-w 20MB:40MB", your script will be able to use $wr->checkAlert('3000KB')
  184.  
  185.         Please note that only following UOMs are accepted (as defined in the Plugin Development Guidelines):
  186.         - no unit specified: assume a number (int or float) of things (eg, users, processes, load averages)
  187.         - s, ms, us: seconds
  188.         - %: percentage
  189.         - B, KB, MB, TB: bytes  // NOTE: GB is not in the official development guidelines,probably due to an error, so I've added them anyway
  190.         - c: a continous counter (such as bytes transmitted on an interface)
  191.  
  192. Multiple warning/critical ranges:
  193.  
  194.         The arguments -w and -c can have many different values, separated by comma.
  195.         We can see this feature e.g. with the official plugin /usr/lib/nagios/plugins/check_ping:
  196.         It has following syntax for the arguments -w and -c: <latency>,<packetloss>%
  197.  
  198.         When you are using checkAgainstWarningRange, you can set the fourth argument to the range number
  199.         you would like to check (beginning with 0).
  200.  
  201.         Example:
  202.                 // -w 1MB:5MB,5%:10%
  203.                 $this->checkAgainstWarningRange('4MB', true, true, 0); // check value 4MB against range "1MB:5MB" (no warning)
  204.                 $this->checkAgainstWarningRange('15%', true, true, 1); // check value 15% gainst range "5%:10%" (gives warning)
  205.  
  206. Visual HTTP output:
  207.  
  208.         Can be enabled/disabled with $this->http_visual_output
  209.  
  210.         Valid values:
  211.  
  212.         VNag::OUTPUT_SPECIAL   = 1; // illegal usage / help page, version page
  213.         VNag::OUTPUT_NORMAL    = 2;
  214.         VNag::OUTPUT_EXCEPTION = 4;
  215.         VNag::OUTPUT_ALWAYS    = 7;
  216.         VNag::OUTPUT_NEVER     = 0;
  217.  
  218. Encryption and Decryption:
  219.  
  220.         In case you are emitting machine readable code in your HTTP output
  221.         (can be enabled/disabled by $this->http_invisible_output),
  222.         you can encrypt the machine readable part of your HTTP output by
  223.         setting $this->password_out . If you want to read the information,
  224.         you need to set $this->password_in at the web-reader plugin.
  225.  
  226.         You can sign the output by setting $this->privkey with a filename containing
  227.         a private key created by OpenSSL. If it is encrypted, please also set
  228.         $this->privkey_password .
  229.         To check the signature, set $this->pubkey at your web-reader plugin with
  230.         the filename of the public key file.
  231.  
  232.         Attention!
  233.         - An empty string is also considered as password. If you don't want to encrypt the
  234.           machine readable output, please set $this->password_out to null.
  235.         - Both features (encryption and signatures) require the OpenSSL plugin in PHP.
  236.  
  237. Performance data:
  238.  
  239.         You can add performance data using
  240.                 $this->addPerformanceData(new VNagPerformanceData($label, $value, $warn, $crit, $min, $max));
  241.         or by the alternative constructor
  242.                 $this->addPerformanceData(VNagPerformanceData::createByString("'XYZ'=100;120;130;0;500"));
  243.         $value may contain an UOM, e.g. "10MB". All other parameters may not contain an UOM.
  244.  
  245. Guidelines:
  246.  
  247.         This framework currently supports meets following guidelines:
  248.         - https://nagios-plugins.org/doc/guidelines.html#PLUGOUTPUT (Plugin Output for Nagios)
  249.         - https://nagios-plugins.org/doc/guidelines.html#AEN33 (Print only one line of text)
  250.         - https://nagios-plugins.org/doc/guidelines.html#AEN41 (Verbose output)
  251.         - https://nagios-plugins.org/doc/guidelines.html#AEN78 (Plugin Return Codes)
  252.         - https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT (Threshold and ranges)
  253.         - https://nagios-plugins.org/doc/guidelines.html#AEN200 (Performance data)
  254.         - https://nagios-plugins.org/doc/guidelines.html#PLUGOPTIONS (Plugin Options)
  255.         - https://nagios-plugins.org/doc/guidelines.html#AEN302 (Option Processing)
  256.           Note: The screen output of the help page will (mostly) be limited to 80 characters width; but the max recommended length of 23 lines cannot be guaranteed.
  257.  
  258.         This framework does currently NOT support following guidelines:
  259.         - https://nagios-plugins.org/doc/guidelines.html#AEN74 (Screen Output)
  260.         - https://nagios-plugins.org/doc/guidelines.html#AEN239 (Translations)
  261.         - https://nagios-plugins.org/doc/guidelines.html#AEN293 (Use DEFAULT_SOCKET_TIMEOUT)
  262.         - https://nagios-plugins.org/doc/guidelines.html#AEN296 (Add alarms to network plugins)
  263.         - https://nagios-plugins.org/doc/guidelines.html#AEN245 (Don't execute system commands without specifying their full path)
  264.         - https://nagios-plugins.org/doc/guidelines.html#AEN249 (Use spopen() if external commands must be executed)
  265.         - https://nagios-plugins.org/doc/guidelines.html#AEN253 (Don't make temp files unless absolutely required)
  266.         - https://nagios-plugins.org/doc/guidelines.html#AEN259 (Validate all input)
  267.         - https://nagios-plugins.org/doc/guidelines.html#AEN317 (Plugins with more than one type of threshold, or with threshold ranges)
  268.  
  269.         We will intentionally NOT follow the following guidelines:
  270.         - https://nagios-plugins.org/doc/guidelines.html#AEN256 (Don't be tricked into following symlinks)
  271.           Reason: We believe that this guideline is contraproductive.
  272.                   Nagios plugins usually run as user 'nagios'. It is the task of the system administrator
  273.                   to ensure that the user 'nagios' must not read/write to files which are not intended
  274.                   for access by the Nagios service. Instead, symlinks are useful for several tasks.
  275.                   See also http://stackoverflow.com/questions/27112949/nagios-plugins-why-not-following-symlinks
  276.  
  277. VNag over HTTP:
  278.  
  279.         A script that uses the VNag framework can run as CLI script (normal Nagios plugin) or as web site (or both).
  280.         Having the script run as website, you can include a Nagios information combined with a human friendly HTML output which can
  281.         include colors, graphics (like charts) etc.
  282.  
  283.         For example:
  284.         A script that measures traffic can have a website which shows graphs,
  285.         and has a hidden Nagios output included, which can be read by a Nagios plugin that
  286.         converts the hidden information on that website into an output that Nagios can evaluate.
  287.  
  288.         Here is a comparison of the usage and behavior of VNag in regards to CLI and HTTP calls:
  289.  
  290.         ------------------------------------------------------------------------------------------
  291.           CLI script                                 HTTP script
  292.         ------------------------------------------------------------------------------------------
  293.         * "echo" will be discarded.                  * "echo" output will be discarded.
  294.  
  295.         * Exceptions will be handled.                * Exceptions will be handled.
  296.  
  297.         * outputHTML() will be ignored.              * outputHTML() will be handled.
  298.           (This allows you to have the same script
  299.           running as CLI and HTML)
  300.  
  301.         * Arguments are passed via CLI.              * Arguments are passed via $_REQUEST
  302.                                                        (i.e. GET or POST)
  303.  
  304.         * Arguments: "-vvv"                          * Arguments: GET ?v[]=&v[]=&v[]= or POST
  305.  
  306.         * When run() has finished, the program       * When run() has finished, the program
  307.           flow continues, although it is not           flow continues.
  308.           recommended that you do anything after it.
  309.           (The exit code is remembered for the
  310.            shutdown handler)
  311.  
  312.         * Exactly 1 job must be called, resulting     * You can call as many jobs as you want.
  313.           in a single output of that job.               A website can include more than one
  314.                                                         Nagios output which are enumerated with
  315.                                                         a serial number (0,1,2,3,...) or manual ID.
  316.         ------------------------------------------------------------------------------------------
  317.  
  318. ****************************************************************************************************/
  319.  
  320. error_reporting(-1);
  321.  
  322. # If you want to use -t/--timeout with your module, you must add following line in your module code:
  323. // WONTFIX: declare(ticks=1) is deprecated? http://www.hackingwithphp.com/4/21/0/the-declare-function-and-ticks
  324. // WONTFIX: check is the main script used declare(ticks=1). (Not possible in PHP)
  325. declare(ticks=1);
  326.  
  327. # Attention: The -t/--timeout parameter does not respect the built-in set_time_limit() of PHP.
  328. # PHP should set this time limit to infinite.
  329. set_time_limit(0);
  330.  
  331. define('VNAG_JSONDATA_V1', 'oid:1.3.6.1.4.1.37476.2.3.1.1');
  332.  
  333. // Set this to an array to overwrite getopt() and $_REQUEST[], respectively.
  334. // Useful for mock tests.
  335. $OVERWRITE_ARGUMENTS = null;
  336.  
  337. function _empty($x) {
  338. if (is_array($x)) debug_print_stacktrace();
  339.         // Returns true for '' or null. Does not return true for value 0 or '0' (like empty() does)
  340.         return trim($x) == '';
  341. }
  342.  
  343. abstract class VNag {
  344.         const VNAG_VERSION = '2018-10-02';
  345.  
  346.         // Status 0..3 for STATUSMODEL_SERVICE (the default status model):
  347.         # The guideline states: "Higher-level errors (such as name resolution errors, socket timeouts, etc) are outside of the control of plugins and should generally NOT be reported as UNKNOWN states."
  348.         # We choose 4 as exitcode. The plugin developer is free to return any other status.
  349.         const STATUS_OK       = 0;
  350.         const STATUS_WARNING  = 1;
  351.         const STATUS_CRITICAL = 2;
  352.         const STATUS_UNKNOWN  = 3;
  353.         const STATUS_ERROR    = 4; // and upwards
  354.  
  355.         // Status 0..1 for STATUSMODEL_HOST:
  356.         // The page https://blog.centreon.com/good-practices-how-to-develop-monitoring-plugin-nagios/
  357.         // states that host plugins may return following status codes:
  358.         // 0=UP, 1=DOWN, Other=Maintains last known state
  359.         const STATUS_UP       = 0;
  360.         const STATUS_DOWN     = 1;
  361.         const STATUS_MAINTAIN = 2; // and upwards
  362.  
  363.         const VERBOSITY_SUMMARY                = 0;
  364.         const VERBOSITY_ADDITIONAL_INFORMATION = 1;
  365.         const VERBOSITY_CONFIGURATION_DEBUG    = 2;
  366.         const VERBOSITY_PLUGIN_DEBUG           = 3;
  367.         const MAX_VERBOSITY = self::VERBOSITY_PLUGIN_DEBUG;
  368.  
  369.         const STATUSMODEL_SERVICE = 0;
  370.         const STATUSMODEL_HOST    = 1;
  371.  
  372.         private $initialized = false;
  373.  
  374.         private $status = null;
  375.         private $messages = array(); // array of messages which are all put together into the headline, comma separated
  376.         private $verbose_info = ''; // all other lines
  377.         private $warningRanges = array();
  378.         private $criticalRanges = array();
  379.         private $performanceDataObjects = array();
  380.         private static $exitcode = 0;
  381.  
  382.         private $helpObj = null;
  383.         private $argHandler = null;
  384.  
  385.         const OUTPUT_SPECIAL   = 1; // illegal usage / help page, version page
  386.         const OUTPUT_NORMAL    = 2;
  387.         const OUTPUT_EXCEPTION = 4;
  388.         const OUTPUT_ALWAYS    = 7;
  389.         const OUTPUT_NEVER     = 0;
  390.  
  391.         // set to true if you want the output being shown in the HTML beside of the invisible tag
  392.         // set to false if you just want to send the invisible tag
  393.         public $http_visual_output    = self::OUTPUT_ALWAYS;
  394.         public $http_invisible_output = self::OUTPUT_ALWAYS;
  395.  
  396.         // $html_before and $html_after contain the output HTML which were sent by the user
  397.  
  398.         // before and after the visual output
  399.         protected $html_before = '';
  400.         protected $html_after = '';
  401.  
  402.         protected $statusmodel = self::STATUSMODEL_SERVICE;
  403.  
  404.         protected $show_status_in_headline = true;
  405.  
  406.         protected $default_status = self::STATUS_UNKNOWN;
  407.         protected $default_warning_range = null;
  408.         protected $default_critical_range = null;
  409.  
  410.         protected $argWarning;
  411.         protected $argCritical;
  412.         protected $argVersion;
  413.         protected $argVerbosity;
  414.         protected $argTimeout;
  415.         protected $argHelp;
  416.         protected $argUsage;
  417.  
  418.         // -----------------------------------------------------------
  419.  
  420.         // The ID will be used for writing AND reading of the machine readable
  421.         // Nagios output embedded in a website. (A web-reader acts as proxy, so the
  422.         // input and output ID will be equal)
  423.         // Attention: Once you run run(), $id will be "used" and resetted to null.
  424.         // The ID can be any string, e.g. a GUID, an OID, a package name or something else.
  425.         // It should be unique. If you don't set an ID, a serial number (0, 1, 2, 3, ...) will be
  426.         // used for your outputs.
  427.         public $id = null;
  428.         protected static $http_serial_number = 0;
  429.  
  430.         // -----------------------------------------------------------
  431.  
  432.         // Private key: Optional feature used in writeInvisibleHTML (called by run in HTTP mode)
  433.         public $privkey = null;
  434.         public $privkey_password = null;
  435.         public $sign_algo = OPENSSL_ALGO_SHA256;
  436.  
  437.         // Public key: Used in readInvisibleHTML
  438.         public $pubkey = null;
  439.  
  440.         // -----------------------------------------------------------
  441.  
  442.         // These settings should be set by derivated classes where the user intuitively expects the
  443.         // warning (w) or critical (c) parameter to mean something else than defined in the development guidelines.
  444.         // Usually, the single value "-w X" means the same like "-w X:X", which means everything except X is bad.
  445.         // This behavior is VNag::SINGLEVALUE_RANGE_DEFAULT.
  446.         // But for plugins e.g. for checking disk space, the user expects the argument "-w X" to mean
  447.         // "everything below X is bad" (if X is defined as free disk space).
  448.         // So we would choose the setting VNag::SINGLEVALUE_RANGE_VAL_LT_X_BAD.
  449.         // Note: This setting is implemented as array, so that each range number (in case you want to have more
  450.         //       than one range, like in the PING plugin that checks latency and package loss)
  451.         //       can have its individual behavior for single values.
  452.         protected $warningSingleValueRangeBehaviors  = array(self::SINGLEVALUE_RANGE_DEFAULT);
  453.         protected $criticalSingleValueRangeBehaviors = array(self::SINGLEVALUE_RANGE_DEFAULT);
  454.  
  455.         // Default behavior according to the development guidelines:
  456.         //  x means  x:x, which means, everything except x% is bad.
  457.         // @x means @x:x, which means, x is bad and everything else is good.
  458.         const SINGLEVALUE_RANGE_DEFAULT = 0;
  459.  
  460.         // The single value x means, everything > x is bad. @x is not defined.
  461.         const SINGLEVALUE_RANGE_VAL_GT_X_BAD = 1;
  462.  
  463.         // The single value x means, everything >= x is bad. @x is not defined.
  464.         const SINGLEVALUE_RANGE_VAL_GE_X_BAD = 2;
  465.  
  466.         // The single value x means, everything < x is bad. @x is not defined.
  467.         const SINGLEVALUE_RANGE_VAL_LT_X_BAD = 3;
  468.  
  469.         // The single value x means, everything <= x is bad. @x is not defined.
  470.         const SINGLEVALUE_RANGE_VAL_LE_X_BAD = 4;
  471.  
  472.         // -----------------------------------------------------------
  473.  
  474.         // Encryption password: Optional feature used in writeInvisibleHTML (called by run in HTTP mode)
  475.         public $password_out = null;
  476.  
  477.         // Decryption password: Used in readInvisibleHTML to decrypt an encrypted machine readable info
  478.         public $password_in = null;
  479.  
  480.         // -----------------------------------------------------------
  481.  
  482.         public static function is_http_mode() {
  483.                 return php_sapi_name() !== 'cli';
  484.         }
  485.  
  486.         public function getHelpManager() {
  487.                 return $this->helpObj;
  488.         }
  489.  
  490.         public function getArgumentHandler() {
  491.                 return $this->argHandler;
  492.         }
  493.  
  494.         public function outputHTML($text, $after_visual_output=true) {
  495.                 if ($this->is_http_mode()) {
  496.                         if ($this->initialized) {
  497.                                 if ($after_visual_output) {
  498.                                         $this->html_after .= $text;
  499.                                 } else {
  500.                                         $this->html_before .= $text;
  501.                                 }
  502.                         } else {
  503.                                 echo $text;
  504.                         }
  505.                 }
  506.         }
  507.  
  508.         protected function resetArguments() {
  509.                 $this->argWarning   = null;
  510.                 $this->argCritical  = null;
  511.                 $this->argVersion   = null;
  512.                 $this->argVerbosity = null;
  513.                 $this->argTimeout   = null;
  514.                 $this->argHelp      = null;
  515.                 // $this->argUsage  = null;
  516.  
  517.                 // Also remove cache
  518.                 $this->warning      = null;
  519.                 $this->critical     = null;
  520.         }
  521.  
  522.         // e.g. $args = "wcVvht"
  523.         public function registerExpectedStandardArguments($args) {
  524.                 $this->resetArguments();
  525.  
  526.                 for ($i=0; $i<strlen($args); $i++) {
  527.                         switch ($args[$i]) {
  528.                                 case 'w':
  529.                                         $this->addExpectedArgument($this->argWarning   = new VNagArgument('w', 'warning',  VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_value, VNagLang::$warning_range));
  530.                                         break;
  531.                                 case 'c':
  532.                                         $this->addExpectedArgument($this->argCritical  = new VNagArgument('c', 'critical', VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_value, VNagLang::$critical_range));
  533.                                         break;
  534.                                 case 'V':
  535.                                         $this->addExpectedArgument($this->argVersion   = new VNagArgument('V', 'version',  VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$prints_version));
  536.                                         break;
  537.                                 case 'v':
  538.                                         // In HTTP: -vvv is &v[]=&v[]=&v[]=
  539.                                         $this->addExpectedArgument($this->argVerbosity = new VNagArgument('v', 'verbose',  VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$verbosity_helptext));
  540.                                         break;
  541.                                 case 't':
  542.                                         // Attention: not every plugin supports it because of declare(ticks=1) needs to be written in the main script
  543.                                         $this->addExpectedArgument($this->argTimeout   = new VNagArgument('t', 'timeout',  VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_seconds, VNagLang::$timeout_helptext));
  544.                                         break;
  545.                                 // case '?':
  546.                                 case 'h':
  547.                                         $this->addExpectedArgument($this->argHelp      = new VNagArgument('h', 'help',     VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$help_helptext));
  548.                                         break;
  549.                                 default:
  550.                                         $letter = $args[$i];
  551.                                         throw new VNagInvalidStandardArgument(sprintf(VNagLang::$no_standard_arguments_with_letter, $letter));
  552.                                         break;
  553.                         }
  554.                 }
  555.         }
  556.  
  557.         public function addExpectedArgument($argObj) {
  558.                 // Emulate C++ "friend" access to hidden functions
  559.  
  560.                 // $this->helpObj->_addOption($argObj);
  561.                 $helpObjAddEntryMethod = new ReflectionMethod($this->helpObj, '_addOption');
  562.                 $helpObjAddEntryMethod->setAccessible(true);
  563.                 $helpObjAddEntryMethod->invoke($this->helpObj, $argObj);
  564.  
  565.                 // $this->argHandler->_addExpectedArgument($argObj);
  566.                 $argHandlerAddEntryMethod = new ReflectionMethod($this->argHandler, '_addExpectedArgument');
  567.                 $argHandlerAddEntryMethod->setAccessible(true);
  568.                 $argHandlerAddEntryMethod->invoke($this->argHandler, $argObj);
  569.         }
  570.  
  571.         protected function createArgumentHandler() {
  572.                 $this->argHandler = new VNagArgumentHandler();
  573.         }
  574.  
  575.         protected function createHelpObject() {
  576.                 $this->helpObj = new VNagHelp();
  577.         }
  578.  
  579.         protected function checkInitialized() {
  580.                 if (!$this->initialized) throw new VNagFunctionCallOutsideSession();
  581.         }
  582.  
  583.         protected function getVerbosityLevel() {
  584.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  585.  
  586.                 if (!isset($this->argVerbosity)) {
  587.                         //The verbose argument is always optional
  588.                         //throw new VNagRequiredArgumentNotRegistered('-v');
  589.                         return self::VERBOSITY_SUMMARY;
  590.                 } else {
  591.                         $level = $this->argVerbosity->count();
  592.                         if ($level > self::MAX_VERBOSITY) $level = self::MAX_VERBOSITY;
  593.                         return $level;
  594.                 }
  595.         }
  596.  
  597.         public function getWarningRange($argumentNumber=0) {
  598.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  599.  
  600.                 if (!isset($this->warningRanges[$argumentNumber])) {
  601.                         if (!is_null($this->argWarning)) {
  602.                                 $warning = $this->argWarning->getValue();
  603.                                 if (!is_null($warning)) {
  604.                                         $vals = explode(',',$warning);
  605.                                         foreach ($vals as $number => $val) {
  606.                                                 if (_empty($val)) {
  607.                                                         $this->warningRanges[$number] = null;
  608.                                                 } else {
  609.                                                         $singleValueBehavior = isset($this->warningSingleValueRangeBehaviors[$number]) ? $this->warningSingleValueRangeBehaviors[$number] : VNag::SINGLEVALUE_RANGE_DEFAULT;
  610.                                                         $this->warningRanges[$number] = new VNagRange($val, $singleValueBehavior);
  611.                                                 }
  612.                                         }
  613.                                 } else {
  614.                                         $this->warningRanges[0] = $this->default_warning_range;
  615.                                 }
  616.                         } else {
  617.                                 return null;
  618.                         }
  619.                 }
  620.  
  621.                 if (isset($this->warningRanges[$argumentNumber])) {
  622.                         return $this->warningRanges[$argumentNumber];
  623.                 } else {
  624.                         return null;
  625.                 }
  626.         }
  627.  
  628.         public function getCriticalRange($argumentNumber=0) {
  629.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  630.  
  631.                 if (!isset($this->criticalRanges[$argumentNumber])) {
  632.                         if (!is_null($this->argCritical)) {
  633.                                 $critical = $this->argCritical->getValue();
  634.                                 if (!is_null($critical)) {
  635.                                         $vals = explode(',',$critical);
  636.                                         foreach ($vals as $number => $val) {
  637.                                                 $singleValueBehavior = isset($this->criticalSingleValueRangeBehaviors[$number]) ? $this->criticalSingleValueRangeBehaviors[$number] : VNag::SINGLEVALUE_RANGE_DEFAULT;
  638.                                                 $this->criticalRanges[$number] = new VNagRange($val, $singleValueBehavior);
  639.                                         }
  640.                                 } else {
  641.                                         $this->criticalRanges[0] = $this->default_critical_range;
  642.                                 }
  643.                         } else {
  644.                                 return null;
  645.                         }
  646.                 }
  647.  
  648.                 if (isset($this->criticalRanges[$argumentNumber])) {
  649.                         return $this->criticalRanges[$argumentNumber];
  650.                 } else {
  651.                         return null;
  652.                 }
  653.         }
  654.  
  655.         public function checkAgainstWarningRange($values, $force=true, $autostatus=true, $argumentNumber=0) {
  656.                 $this->checkInitialized(); // if (!$this->initialized) return;
  657.  
  658.                 if (!$this->getArgumentHandler()->isArgRegistered('w')) {
  659.                         // Developer's mistake: The argument is not in the list of expected arguments
  660.                         throw new VNagRequiredArgumentNotRegistered('-w');
  661.                 }
  662.  
  663.                 $wr = $this->getWarningRange($argumentNumber);
  664.                 if (isset($wr)) {
  665.                         if ($wr->checkAlert($values)) {
  666.                                 if ($autostatus) $this->setStatus(VNag::STATUS_WARNING);
  667.                                 return true;
  668.                         } else {
  669.                                 if ($autostatus) $this->setStatus(VNag::STATUS_OK);
  670.                                 return false;
  671.                         }
  672.                 } else {
  673.                         if ($force) {
  674.                                 // User's mistake: They did not pass the argument to the plugin
  675.                                 if (($argumentNumber > 0) && (count($this->warningRanges) > 0)) {
  676.                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$too_few_warning_ranges, $argumentNumber+1));
  677.                                 } else {
  678.                                         throw new VNagRequiredArgumentMissing('-w');
  679.                                 }
  680.                         }
  681.                 }
  682.         }
  683.  
  684.         public function checkAgainstCriticalRange($values, $force=true, $autostatus=true, $argumentNumber=0) {
  685.                 $this->checkInitialized(); // if (!$this->initialized) return;
  686.  
  687.                 if (!$this->getArgumentHandler()->isArgRegistered('c')) {
  688.                         // Developer's mistake: The argument is not in the list of expected arguments
  689.                         throw new VNagRequiredArgumentNotRegistered('-c');
  690.                 }
  691.  
  692.                 $cr = $this->getCriticalRange($argumentNumber);
  693.                 if (isset($cr)) {
  694.                         if ($cr->checkAlert($values)) {
  695.                                 if ($autostatus) $this->setStatus(VNag::STATUS_CRITICAL);
  696.                                 return true;
  697.                         } else {
  698.                                 if ($autostatus) $this->setStatus(VNag::STATUS_OK);
  699.                                 return false;
  700.                         }
  701.                 } else {
  702.                         if ($force) {
  703.                                 // User's mistake: They did not pass the argument to the plugin
  704.                                 if (($argumentNumber > 0) && (count($this->warningRanges) > 0)) {
  705.                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$too_few_critical_ranges, $argumentNumber+1));
  706.                                 } else {
  707.                                         throw new VNagRequiredArgumentMissing('-c');
  708.                                 }
  709.                         }
  710.                 }
  711.         }
  712.  
  713.         protected static function getBaddestExitcode($code1, $code2) {
  714.                 return max($code1, $code2);
  715.         }
  716.  
  717.         # DO NOT CALL MANUALLY
  718.         # Unfortunately, this function has to be public, otherwise register_shutdown_function() wouldn't work
  719.         public static function _shutdownHandler() {
  720.                 if (!self::is_http_mode()) {
  721.                         exit((int)self::$exitcode);
  722.                 }
  723.         }
  724.  
  725.         protected function _exit($code) {
  726.                 self::$exitcode = $this->getBaddestExitcode($code, self::$exitcode);
  727.         }
  728.  
  729.         private $constructed = false;
  730.         function __construct() {
  731.                 $this->createHelpObject();
  732.                 $this->createArgumentHandler();
  733.  
  734.                 $this->addExpectedArgument($this->argUsage = new VNagArgument('?', '', VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$prints_usage));
  735.  
  736.                 $this->constructed = true;
  737.         }
  738.  
  739.         function __destruct() {
  740.                 if (Timeouter::started()) {
  741.                         Timeouter::end();
  742.                 }
  743.         }
  744.  
  745.         // $optional_args will be forwarded to the callback function cbRun()
  746.         public function run($optional_args=array()) {
  747.                 if (!$this->constructed) {
  748.                         throw new VNagNotConstructed(VNagLang::$notConstructed);
  749.                 }
  750.  
  751.                 try {
  752.                         $this->initialized = true;
  753.                         $this->html_before = '';
  754.                         $this->html_after = '';
  755.                         $this->setStatus(null, true);
  756.                         $this->messages = array();
  757.  
  758.                         register_shutdown_function(array($this, '_shutdownHandler'));
  759.  
  760.                         if ($this->argHandler->illegalUsage()) {
  761.                                 $content = $this->helpObj->printUsagePage();
  762.                                 $this->setStatus(VNag::STATUS_UNKNOWN);
  763.  
  764.                                 if ($this->is_http_mode()) {
  765.                                         echo $this->html_before;
  766.                                         if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  767.                                         if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  768.                                         echo $this->html_after;
  769.                                         return; // cancel
  770.                                 } else {
  771.                                         echo $content;
  772.                                         return $this->_exit($this->status);
  773.                                 }
  774.                         }
  775.  
  776.                         if (!is_null($this->argVersion) && ($this->argVersion->available())) {
  777.                                 $content = $this->helpObj->printVersionPage();
  778.                                 $this->setStatus(VNag::STATUS_UNKNOWN);
  779.  
  780.                                 if ($this->is_http_mode()) {
  781.                                         echo $this->html_before;
  782.                                         if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  783.                                         if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  784.                                         echo $this->html_after;
  785.                                         return; // cancel
  786.                                 } else {
  787.                                         echo $content;
  788.                                         return $this->_exit($this->status);
  789.                                 }
  790.                         }
  791.  
  792.                         if (!is_null($this->argHelp) && ($this->argHelp->available())) {
  793.                                 $content = $this->helpObj->printHelpPage();
  794.                                 $this->setStatus(VNag::STATUS_UNKNOWN);
  795.  
  796.                                 if ($this->is_http_mode()) {
  797.                                         echo $this->html_before;
  798.                                         if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  799.                                         if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  800.                                         echo $this->html_after;
  801.                                         return; // cancel
  802.                                 } else {
  803.                                         echo $content;
  804.                                         return $this->_exit($this->status);
  805.                                 }
  806.                         }
  807.  
  808.                         // Initialize ranges (and check their validity)
  809.                         $this->getWarningRange();
  810.                         $this->getCriticalRange();
  811.  
  812.                         if (!is_null($this->argTimeout)) {
  813.                                 $timeout = $this->argTimeout->getValue();
  814.                                 if (!is_null($timeout)) {
  815.                                         Timeouter::start($timeout);
  816.                                 }
  817.                         }
  818.  
  819.                         ob_start();
  820.                         $init_ob_level = ob_get_level();
  821.                         try {
  822.                                 $this->cbRun($optional_args);
  823.  
  824.                                 // This will NOT be put in the 'finally' block, because otherwise it would trigger if an Exception happened (Which clears the OB)
  825.                                 if (ob_get_level() < $init_ob_level) throw new VNagImplementationErrorException(VNagLang::$output_level_lowered);
  826.                         } finally {
  827.                                 while (ob_get_level() > $init_ob_level) @ob_end_clean();
  828.                         }
  829.  
  830.                         if (is_null($this->status)) $this->setStatus($this->default_status,true);
  831.  
  832.                         $outputType = VNag::OUTPUT_NORMAL;
  833.                 } catch (Exception $e) {
  834.                         $this->handleException($e);
  835.                         $outputType = VNag::OUTPUT_EXCEPTION;
  836.                 }
  837.  
  838.                 if ($this->is_http_mode()) {
  839.                         echo $this->html_before;
  840.                         if ($this->http_invisible_output & $outputType) {
  841.                                 echo $this->writeInvisibleHTML();
  842.                         }
  843.                         if ($this->http_visual_output & $outputType) {
  844.                                 echo $this->writeVisualHTML();
  845.                         }
  846.                         echo $this->html_after;
  847.                 } else {
  848.                         echo $this->getNagiosConsoleText();
  849.                         return $this->_exit($this->status);
  850.                 }
  851.  
  852.                 Timeouter::end();
  853.         }
  854.  
  855.         private function getNagiosConsoleText() {
  856.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  857.                 // 1. space separated list of label/value pairs
  858.                 $ary_perfdata = $this->getPerformanceData();
  859.                 $performancedata_first = array_shift($ary_perfdata);
  860.                 $performancedata_rest  = implode(' ', $ary_perfdata);
  861.  
  862.                 $status_text = VNagLang::status($this->status, $this->statusmodel);
  863.                 if (_empty($this->getHeadline())) {
  864.                         $content = $status_text;
  865.                 } else {
  866.                         if ($this->show_status_in_headline) {
  867.                                 $content = $status_text.': '.$this->getHeadline();
  868.                         } else {
  869.                                 $content = $this->getHeadline();
  870.                         }
  871.                 }
  872.  
  873.                 if (!_empty($performancedata_first)) $content .= '|'.trim($performancedata_first);
  874.                 $content .= "\n";
  875.                 if (!_empty($this->verbose_info)) {
  876.                         //$content .= "\n".VNagLang::$verbose_info.":\n\n";
  877.                         $content .= trim($this->verbose_info);
  878.                 }
  879.                 if (!_empty($performancedata_rest)) $content .= '|'.trim($performancedata_rest);
  880.                 $content .= "\n";
  881.  
  882.                 return trim($content)."\n";
  883.         }
  884.  
  885.         abstract protected function cbRun();
  886.  
  887.         public function addPerformanceData($prefDataObj, $move_to_font=false, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  888.                 $this->checkInitialized(); // if (!$this->initialized) return;
  889.  
  890.                 if ((!isset($this->argVerbosity)) && ($verbosityLevel > VNag::VERBOSITY_SUMMARY)) throw new VNagRequiredArgumentNotRegistered('-v');
  891.                 if (self::getVerbosityLevel() < $verbosityLevel) return false;
  892.  
  893.                 if ($move_to_font) {
  894.                         array_unshift($this->performanceDataObjects, $prefDataObj);
  895.                 } else {
  896.                         $this->performanceDataObjects[] = $prefDataObj;
  897.                 }
  898.  
  899.                 return true;
  900.         }
  901.  
  902.         public function getPerformanceData() {
  903.                 $this->checkInitialized(); // if (!$this->initialized) return null;
  904.  
  905.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  906.                 // 1. space separated list of label/value pairs
  907.                 return $this->performanceDataObjects;
  908.         }
  909.  
  910.         public function removePerformanceData($prefDataObj) {
  911.                 if (($key = array_search($prefDataObj, $this->performanceDataObjects, true)) !== false) {
  912.                         unset($this->performanceDataObjects[$key]);
  913.                         return true;
  914.                 } else {
  915.                         return false;
  916.                 }
  917.         }
  918.  
  919.         public function clearPerformanceData() {
  920.                 $this->performanceDataObjects = array();
  921.         }
  922.  
  923.         public function getVerboseInfo() {
  924.                 return $this->verbose_info;
  925.         }
  926.  
  927.         public function clearVerboseInfo() {
  928.                 $this->verbose_info = '';
  929.         }
  930.  
  931.         private function writeVisualHTML($special_content=null) {
  932.                 if (!_empty($special_content)) {
  933.                         $content = $special_content;
  934.                 } else {
  935.                         $content = strtoupper(VNagLang::$status.': '.VNagLang::status($this->status, $this->statusmodel))."\n\n";
  936.  
  937.                         $content .= strtoupper(VNagLang::$message).":\n";
  938.                         $status_text = VNagLang::status($this->status, $this->statusmodel);
  939.                         if (_empty($this->getHeadline())) {
  940.                                 $content .= $status_text;
  941.                         } else {
  942.                                 if ($this->show_status_in_headline) {
  943.                                         $content .= $status_text.': '.trim($this->getHeadline());
  944.                                 } else {
  945.                                         $content .= trim($this->getHeadline());
  946.                                 }
  947.                         }
  948.                         $content .= "\n\n";
  949.  
  950.                         if (!_empty($this->verbose_info)) {
  951.                                 $content .= strtoupper(VNagLang::$verbose_info).":\n".trim($this->verbose_info)."\n\n";
  952.                         }
  953.  
  954.                         $perfdata = $this->getPerformanceData();
  955.                         if (count($perfdata) > 0) {
  956.                                 $content .= strtoupper(VNagLang::$performance_data).":\n";
  957.                                 foreach ($perfdata as $pd) {
  958.                                         $content .= trim($pd)."\n";
  959.                                 }
  960.                                 $content .= "\n";
  961.                         }
  962.                 }
  963.  
  964.                 $colorinfo = '';
  965.                 $status = $this->getStatus();
  966.  
  967.                 if ($status == VNag::STATUS_OK)                 $colorinfo = ' style="background-color:green;color:white;font-weight:bold"';
  968.                 else if ($status == VNag::STATUS_WARNING)       $colorinfo = ' style="background-color:yellow;color:black;font-weight:bold"';
  969.                 else if ($status == VNag::STATUS_CRITICAL)      $colorinfo = ' style="background-color:red;color:white;font-weight:bold"';
  970.                 else if ($status == VNag::STATUS_ERROR)         $colorinfo = ' style="background-color:purple;color:white;font-weight:bold"';
  971.                 else /* if ($status == VNag::STATUS_UNKNOWN) */ $colorinfo = ' style="background-color:lightgray;color:black;font-weight:bold"';
  972.  
  973.                 $html_content = trim($content);
  974.                 $html_content = htmlentities($html_content);
  975.                 $html_content = str_replace(' ', '&nbsp;', $html_content);
  976.                 $html_content = nl2br($html_content);
  977.  
  978.                 // FUT: Allow individual design via CSS
  979.                 return '<table border="1" cellspacing="2" cellpadding="2" style="width:100%" class="vnag_table">'.
  980.                         '<tr'.$colorinfo.' class="vnag_title_row">'.
  981.                         '<td>'.VNagLang::$nagios_output.'</td>'.
  982.                         '</tr>'.
  983.                         '<tr class="vnag_message_row">'.
  984.                         '<td><code>'.
  985.                         $html_content.
  986.                         '</code></td>'.
  987.                         '</tr>'.
  988.                         '</table>';
  989.         }
  990.  
  991.         protected function readInvisibleHTML($html) {
  992.                 $this->checkInitialized(); // if (!$this->initialized) return;
  993.  
  994.                 $doc = new DOMDocument(); // Requires: aptitude install php-dom
  995.                 @$doc->loadHTML($html);   // added '@' because we don't want a warning for the non-standard <vnag> tag
  996.  
  997.                 $tags = $doc->getElementsByTagName('script');
  998.                 foreach ($tags as $tag) {
  999.                         $type = $tag->getAttribute('type');
  1000.                         if ($type !== 'application/json') continue;
  1001.  
  1002.                         $json = $tag->nodeValue;
  1003.                         if (!$json) continue;
  1004.  
  1005.                         $data = @json_decode($json,true);
  1006.                         if (!is_array($data)) continue;
  1007.  
  1008.                         if (!isset($data['type'])) continue;
  1009.                         if ($data['type'] === VNAG_JSONDATA_V1) {
  1010.                                 if (!isset($data['datasets'])) throw new VNagWebInfoException(VNagLang::$dataset_missing);
  1011.                                 foreach ($data['datasets'] as $dataset) {
  1012.                                         $payload = base64_decode($dataset['payload']);
  1013.                                         if (!$payload) {
  1014.                                                 throw new VNagWebInfoException(VNagLang::$payload_not_base64);
  1015.                                         }
  1016.  
  1017.                                         if (isset($dataset['encryption'])) {
  1018.                                                 // The dataset is encrypted. We need to decrypt it first.
  1019.  
  1020.                                                 $cryptInfo = $dataset['encryption'];
  1021.                                                 if (!is_array($cryptInfo)) {
  1022.                                                         throw new VNagWebInfoException(VNagLang::$dataset_encryption_no_array);
  1023.                                                 }
  1024.  
  1025.                                                 $password = is_null($this->password_in) ? '' : $this->password_in;
  1026.  
  1027.                                                 $salt = base64_decode($cryptInfo['salt']);
  1028.  
  1029.                                                 if ($cryptInfo['hash'] != hash('sha256',$salt.$password)) {
  1030.                                                         if ($password == '') {
  1031.                                                                 throw new VNagWebInfoException(VNagLang::$require_password);
  1032.                                                         } else {
  1033.                                                                 throw new VNagWebInfoException(VNagLang::$wrong_password);
  1034.                                                         }
  1035.                                                 }
  1036.  
  1037.                                                 $payload = openssl_decrypt($payload, $cryptInfo['method'], $password, 0, $cryptInfo['iv']);
  1038.                                         }
  1039.  
  1040.                                         if (!is_null($this->pubkey)) {
  1041.                                                 if (!file_exists($this->pubkey)) {
  1042.                                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$pubkey_file_not_found, $this->pubkey));
  1043.                                                 }
  1044.  
  1045.                                                 $public_key = @file_get_contents($this->pubkey);
  1046.                                                 if (!$public_key) {
  1047.                                                         throw new VNagPublicKeyException(sprintf(VNagLang::$pubkey_file_not_readable, $this->pubkey));
  1048.                                                 }
  1049.  
  1050.                                                 if (!isset($dataset['signature'])) {
  1051.                                                         throw new VNagSignatureException(VNagLang::$signature_missing);
  1052.                                                 }
  1053.  
  1054.                                                 $signature = base64_decode($dataset['signature']);
  1055.                                                 if (!$signature) {
  1056.                                                         throw new VNagSignatureException(VNagLang::$signature_not_bas64);
  1057.                                                 }
  1058.  
  1059.                                                 if (!openssl_verify($payload, $signature, $public_key, $this->sign_algo)) {
  1060.                                                         throw new VNagSignatureException(VNagLang::$signature_invalid);
  1061.                                                 }
  1062.                                         }
  1063.  
  1064.                                         $payload = @json_decode($payload,true);
  1065.                                         if (!$payload) {
  1066.                                                 throw new VNagWebInfoException(VNagLang::$payload_not_json);
  1067.                                         }
  1068.  
  1069.                                         if ($payload['id'] == $this->id) {
  1070.                                                 return $payload;
  1071.                                         }
  1072.                                 }
  1073.                         }
  1074.                 }
  1075.  
  1076.                 return null;
  1077.         }
  1078.  
  1079.         private function getNextMonitorID($peek=false) {
  1080.                 $result = is_null($this->id) ? self::$http_serial_number : $this->id;
  1081.  
  1082.                 if (!$peek) {
  1083.                         $this->id = null; // use manual ID only once
  1084.                         self::$http_serial_number++;
  1085.                 }
  1086.  
  1087.                 return $result;
  1088.         }
  1089.  
  1090.         private function writeInvisibleHTML($special_content=null) {
  1091.                 // 1. Create the payload
  1092.  
  1093.                 $payload['id'] = $this->getNextMonitorID();
  1094.  
  1095.                 $payload['status'] = $this->getStatus();
  1096.  
  1097.                 if (!_empty($special_content)) {
  1098.                         $payload['text'] = $special_content;
  1099.                 } else {
  1100.                         $payload['headline'] = $this->getHeadline();
  1101.                         $payload['verbose_info'] = $this->verbose_info;
  1102.  
  1103.                         $payload['performance_data'] = array();
  1104.                         foreach ($this->performanceDataObjects as $perfdata) {
  1105.                                 $payload['performance_data'][] = (string)$perfdata;
  1106.                         }
  1107.                 }
  1108.  
  1109.                 $payload = json_encode($payload);
  1110.  
  1111.                 // 2. Encode the payload as JSON and optionally sign and/or encrypt it
  1112.  
  1113.                 $dataset = array();
  1114.  
  1115.                 if (!is_null($this->privkey)) {
  1116.                         if (!file_exists($this->privkey)) {
  1117.                                 throw new VNagInvalidArgumentException(sprintf(VNagLang::$privkey_file_not_found, $this->privkey));
  1118.                         }
  1119.                         $pkeyid = @openssl_pkey_get_private('file://'.$this->privkey, $privkey_password);
  1120.                         if (!$pkeyid) {
  1121.                                 throw new VNagPrivateKeyException(sprintf(VNagLang::$privkey_file_not_readable, $this->privkey));
  1122.                         }
  1123.  
  1124.                         openssl_sign($payload, $signature, $pkeyid, $this->sign_algo);
  1125.                         openssl_free_key($pkeyid);
  1126.  
  1127.                         $dataset['signature'] = base64_encode($signature);
  1128.                 }
  1129.  
  1130.                 if (!is_null($this->password_out)) {
  1131.                         $password = $this->password_out;
  1132.  
  1133.                         $method = 'aes-256-ofb';
  1134.                         $iv = substr(hash('sha256', openssl_random_pseudo_bytes(32)), 0, 16);
  1135.                         $salt = openssl_random_pseudo_bytes(32);
  1136.  
  1137.                         $cryptInfo = array();
  1138.                         $cryptInfo['method'] = $method;
  1139.                         $cryptInfo['iv'] = $iv;
  1140.                         $cryptInfo['salt'] = base64_encode($salt);
  1141.                         $cryptInfo['hash'] = hash('sha256', $salt.$password);
  1142.  
  1143.                         $payload = openssl_encrypt($payload, $method, $password, 0, $iv);
  1144.                         $dataset['encryption'] = $cryptInfo;
  1145.                 }
  1146.  
  1147.                 $dataset['payload'] = base64_encode($payload);
  1148.  
  1149.                 // 3. Encode everything as JSON+Base64 (again) and put it into the data block
  1150.  
  1151.                 $json = array();
  1152.                 $json['type'] = VNAG_JSONDATA_V1;
  1153.                 $json['datasets'] = array($dataset); // we only output 1 dataset. We could technically output more than one into this data block.
  1154.  
  1155.                 // Include the machine readable information as data block
  1156.                 // This method was chosen to support HTML 4.01, XHTML and HTML5 as well without breaking the standards
  1157.                 // see https://stackoverflow.com/questions/51222713/using-an-individual-tag-without-breaking-the-standards/51223609#51223609
  1158.                 return '<script type="application/json">'.
  1159.                        json_encode($json).
  1160.                        '</script>';
  1161.         }
  1162.  
  1163.         protected function appendHeadline($msg) {
  1164.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1165.  
  1166.                 if (_empty($msg)) return false;
  1167.                 $this->messages[] = $msg;
  1168.  
  1169.                 return true;
  1170.         }
  1171.  
  1172.         protected function changeHeadline($msg) {
  1173.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1174.  
  1175.                 if (_empty($msg)) {
  1176.                         $this->messages = array();
  1177.                 } else {
  1178.                         $this->messages = array($msg);
  1179.                 }
  1180.  
  1181.                 return true;
  1182.         }
  1183.  
  1184.         public function setHeadline($msg, $append=false, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  1185.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1186.  
  1187.                 if ((!isset($this->argVerbosity)) && ($verbosityLevel > VNag::VERBOSITY_SUMMARY)) throw new VNagRequiredArgumentNotRegistered('-v');
  1188.                 if (self::getVerbosityLevel() < $verbosityLevel) $msg = '';
  1189.  
  1190.                 if ($append) {
  1191.                         return $this->appendHeadline($msg);
  1192.                 } else {
  1193.                         return $this->changeHeadline($msg);
  1194.                 }
  1195.         }
  1196.  
  1197.         public function getHeadline() {
  1198.                 $this->checkInitialized(); // if (!$this->initialized) return '';
  1199.  
  1200.                 return implode(', ', $this->messages);
  1201.         }
  1202.  
  1203.         public function addVerboseMessage($msg, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  1204.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1205.  
  1206.                 if (self::getVerbosityLevel() >= $verbosityLevel) {
  1207.                         $this->verbose_info .= $msg."\n";
  1208.                 }
  1209.         }
  1210.  
  1211.         public function setStatus($status, $force=false) {
  1212.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1213.  
  1214.                 if (($force) || is_null($this->status) || ($status > $this->status)) {
  1215.                         $this->status = $status;
  1216.                 }
  1217.         }
  1218.  
  1219.         public function getStatus() {
  1220.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1221.  
  1222.                 return $this->status;
  1223.         }
  1224.  
  1225.         protected static function exceptionText($exception) {
  1226.                 // $this->checkInitialized(); // if (!$this->initialized) return false;
  1227.  
  1228.                 $class = get_class($exception);
  1229.                 $msg = $exception->getMessage();
  1230.  
  1231.                 if (!_empty($msg)) {
  1232.                         return sprintf(VNagLang::$exception_x, $msg, $class);
  1233.                 } else {
  1234.                         return sprintf(VNagLang::$unhandled_exception_without_msg, $class);
  1235.                 }
  1236.         }
  1237.  
  1238.         protected function handleException($exception) {
  1239.                 $this->checkInitialized(); // if (!$this->initialized) return;
  1240.  
  1241.                 while (ob_get_level() > 0) @ob_end_clean();
  1242.                 $this->clearVerboseInfo();
  1243.                 $this->clearPerformanceData();
  1244.  
  1245.                 if ($exception instanceof VNagException) {
  1246.                         $this->setStatus($exception->getStatus());
  1247.                 } else {
  1248.                         $this->setStatus(self::STATUS_ERROR);
  1249.                 }
  1250.  
  1251.                 $this->setHeadline($this->exceptionText($exception), false);
  1252.  
  1253.                 if ($exception instanceof VNagImplementationException) {
  1254.                         $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_SUMMARY);
  1255.                 } else {
  1256.                         if (isset($this->argVerbosity)) {
  1257.                                 $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_ADDITIONAL_INFORMATION);
  1258.                         } else {
  1259.                                 // $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_SUMMARY);
  1260.                         }
  1261.                 }
  1262.         }
  1263. }
  1264.  
  1265.  
  1266. class VNagException extends Exception {
  1267.         public function getStatus() {
  1268.                 return VNag::STATUS_ERROR;
  1269.         }
  1270. }
  1271.  
  1272. class VNagTimeoutException extends VNagException {}
  1273.  
  1274. class VNagWebInfoException extends VNagException {}
  1275. class VNagSignatureException extends VNagException {}
  1276. class VNagPublicKeyException extends VNagException {}
  1277. class VNagPrivateKeyException extends VNagException {}
  1278.  
  1279. // VNagInvalidArgumentException are exceptions which result from a wrong use
  1280. // of arguments by the USER (CLI arguments or HTTP parameters)
  1281. class VNagInvalidArgumentException extends VNagException {
  1282.         public function getStatus() {
  1283.                 return VNag::STATUS_UNKNOWN;
  1284.         }
  1285. }
  1286.  
  1287. class VNagValueUomPairSyntaxException extends VNagInvalidArgumentException {
  1288.         public function __construct($str) {
  1289.                 $e_msg = sprintf(VNagLang::$valueUomPairSyntaxError, $str);
  1290.                 parent::__construct($e_msg);
  1291.         }
  1292. }
  1293.  
  1294. class VNagInvalidTimeoutException extends VNagInvalidArgumentException {}
  1295.  
  1296. class VNagInvalidRangeException extends VNagInvalidArgumentException {
  1297.         public function __construct($msg) {
  1298.                 $e_msg = VNagLang::$range_is_invalid;
  1299.                 if (!_empty($msg)) $e_msg .= ': '.trim($msg);
  1300.                 parent::__construct($e_msg);
  1301.         }
  1302. }
  1303.  
  1304. class VNagInvalidShortOpt extends VNagImplementationErrorException {}
  1305. class VNagInvalidLongOpt extends VNagImplementationErrorException {}
  1306. class VNagInvalidValuePolicy extends VNagImplementationErrorException {}
  1307. class VNagIllegalStatusModel extends VNagImplementationErrorException {}
  1308. class VNagNotConstructed extends VNagImplementationErrorException {}
  1309.  
  1310. // To enforce that people use the API correctly, we report flaws in the implementation
  1311. // as Exception.
  1312. class VNagImplementationErrorException extends VNagException {}
  1313.  
  1314. class VNagInvalidStandardArgument extends VNagImplementationErrorException  {}
  1315. class VNagFunctionCallOutsideSession extends VNagImplementationErrorException {}
  1316. class VNagIllegalArgumentValuesException extends VNagImplementationErrorException {}
  1317.  
  1318. class VNagRequiredArgumentNotRegistered extends VNagImplementationErrorException {
  1319.         // Developer's mistake: The argument is not in the list of expected arguments
  1320.         public function __construct($required_argument) {
  1321.                 $e_msg = sprintf(VNagLang::$query_without_expected_argument, $required_argument);
  1322.                 parent::__construct($e_msg);
  1323.         }
  1324. }
  1325.  
  1326. class VNagRequiredArgumentMissing extends VNagInvalidArgumentException {
  1327.         // User's mistake: They did not pass the argument to the plugin
  1328.         public function __construct($required_argument) {
  1329.                 $e_msg = sprintf(VNagLang::$required_argument_missing, $required_argument);
  1330.                 parent::__construct($e_msg);
  1331.         }
  1332. }
  1333.  
  1334. class VNagUnknownUomException extends VNagInvalidArgumentException {
  1335.         public function __construct($uom) {
  1336.                 $e_msg = sprintf(VNagLang::$perfdata_uom_not_recognized, $uom);
  1337.                 parent::__construct($e_msg);
  1338.         }
  1339. }
  1340.  
  1341. class VNagNoCompatibleRangeUomFoundException extends VNagException {}
  1342.  
  1343. class VNagMixedUomsNotImplemented extends VNagInvalidArgumentException {
  1344.         public function __construct($uom1, $uom2) {
  1345.                 if (_empty($uom1)) $uom1 = VNagLang::$none;
  1346.                 if (_empty($uom2)) $uom2 = VNagLang::$none;
  1347.                 $e_msg = sprintf(VNagLang::$perfdata_mixed_uom_not_implemented, $uom1, $uom2);
  1348.                 parent::__construct($e_msg);
  1349.         }
  1350. }
  1351.  
  1352. class VNagUomConvertException extends VNagInvalidArgumentException {
  1353.         // It is unknown where the invalid UOM that was passed to the normalize() function came from,
  1354.         // so it is not clear what parent this Exception class should have...
  1355.         // If the value comes from the developer: VNagImplementationErrorException
  1356.         // If the value came from the user: VNagInvalidArgumentException
  1357.  
  1358.         public function __construct($uom1, $uom2) {
  1359.                 if (_empty($uom1)) $uom1 = VNagLang::$none;
  1360.                 if (_empty($uom2)) $uom2 = VNagLang::$none;
  1361.                 $e_msg = sprintf(VNagLang::$convert_x_y_error, $uom1, $uom2);
  1362.                 parent::__construct($e_msg);
  1363.         }
  1364. }
  1365.  
  1366. class VNagInvalidPerformanceDataException extends VNagInvalidArgumentException {
  1367.         public function __construct($msg) {
  1368.                 $e_msg = VNagLang::$performance_data_invalid;
  1369.                 if (!_empty($msg)) $e_msg .= ': '.trim($msg);
  1370.                 parent::__construct($e_msg);
  1371.         }
  1372. }
  1373.  
  1374. class Timeouter {
  1375.         // based on http://stackoverflow.com/questions/7493676/detecting-a-timeout-for-a-block-of-code-in-php
  1376.  
  1377.         private static $start_time = false;
  1378.         private static $timeout;
  1379.         private static $fired      = false;
  1380.         private static $registered = false;
  1381.  
  1382.         private function __construct() {
  1383.         }
  1384.  
  1385.         public static function start($timeout) {
  1386.                 if (!is_numeric($timeout) || ($timeout <= 0)) {
  1387.                         throw new VNagInvalidTimeoutException(sprintf(VNagLang::$timeout_value_invalid, $timeout));
  1388.                 }
  1389.  
  1390.                 self::$start_time = microtime(true);
  1391.                 self::$timeout    = (float) $timeout;
  1392.                 self::$fired      = false;
  1393.                 if (!self::$registered) {
  1394.                         self::$registered = true;
  1395.                         register_tick_function(array('Timeouter', 'tick'));
  1396.                 }
  1397.         }
  1398.  
  1399.         public static function started() {
  1400.                 return self::$registered;
  1401.         }
  1402.  
  1403.         public static function end() {
  1404.                 if (self::$registered) {
  1405.                         unregister_tick_function(array('Timeouter', 'tick'));
  1406.                         self::$registered = false;
  1407.                 }
  1408.         }
  1409.  
  1410.         public static function tick() {
  1411.                 if ((!self::$fired) && ((microtime(true) - self::$start_time) > self::$timeout)) {
  1412.                         self::$fired = true; // do not fire again
  1413.                         throw new VNagTimeoutException(VNagLang::$timeout_exception);
  1414.                 }
  1415.         }
  1416. }
  1417.  
  1418. class VNagArgument {
  1419.         const VALUE_FORBIDDEN = 0;
  1420.         const VALUE_REQUIRED  = 1;
  1421.         const VALUE_OPTIONAL  = 2;
  1422.  
  1423.         protected $shortopt;
  1424.         protected $longopts;
  1425.         protected $valuePolicy;
  1426.         protected $valueName;
  1427.         protected $helpText;
  1428.         protected $defaultValue = null;
  1429.  
  1430.         protected static $all_short = '';
  1431.         protected static $all_long = array();
  1432.  
  1433.         public function getShortOpt() {
  1434.                 return $this->shortopt;
  1435.         }
  1436.  
  1437.         public function getLongOpts() {
  1438.                 return $this->longopts;
  1439.         }
  1440.  
  1441.         public function getValuePolicy() {
  1442.                 return $this->valuePolicy;
  1443.         }
  1444.  
  1445.         public function getValueName() {
  1446.                 return $this->valueName;
  1447.         }
  1448.  
  1449.         public function getHelpText() {
  1450.                 return $this->helpText;
  1451.         }
  1452.  
  1453.         static private function validateShortOpt($shortopt) {
  1454.                 return preg_match('@^[a-zA-Z0-9\\+\\-\\?]$@', $shortopt, $m);
  1455.         }
  1456.  
  1457.         static private function validateLongOpt($longopt) {
  1458.                 // FUT: Check if this is accurate
  1459.                 return preg_match('@^[a-zA-Z0-9\\+\\-\\?]+$@', $longopt, $m);
  1460.         }
  1461.  
  1462.         // Note: Currently, we do not support following:
  1463.         // 1. How many times may a value be defined (it needs to be manually described in $helpText)
  1464.         // 2. Is this argument mandatory? (No exception will be thrown if the plugin will be started without this argument)
  1465.         public function __construct($shortopt, $longopts, $valuePolicy, $valueName, $helpText, $defaultValue=null) {
  1466.                 // Check if $valueName is defined correctly in regards to the policy $valuePolicy
  1467.                 switch ($valuePolicy) {
  1468.                         case VNagArgument::VALUE_FORBIDDEN:
  1469.                                 if (!_empty($valueName)) {
  1470.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_forbidden));
  1471.                                 }
  1472.                                 break;
  1473.                         case VNagArgument::VALUE_REQUIRED:
  1474.                                 if (_empty($valueName)) {
  1475.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_required));
  1476.                                 }
  1477.                                 break;
  1478.                         case VNagArgument::VALUE_OPTIONAL:
  1479.                                 if (_empty($valueName)) {
  1480.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_required));
  1481.                                 }
  1482.                                 break;
  1483.                         default:
  1484.                                 throw new VNagInvalidValuePolicy(sprintf(VNagLang::$illegal_valuepolicy, $valuePolicy));
  1485.                 }
  1486.  
  1487.                 // We'll check: Does the shortopt contain illegal characters?
  1488.                 // http://stackoverflow.com/questions/28522387/which-chars-are-valid-shortopts-for-gnu-getopt
  1489.                 // We do not filter +, - and ?, since we might need it for other methods, e.g. VNagArgumentHandler::_addExpectedArgument
  1490.                 if (!_empty($shortopt)) {
  1491.                         if (!self::validateShortOpt($shortopt)) {
  1492.                                 throw new VNagInvalidShortOpt(sprintf(VNagLang::$illegal_shortopt, $shortopt));
  1493.                         }
  1494.                 }
  1495.  
  1496.                 if (is_array($longopts)) { // $longopts is an array
  1497.                         if (!is_null($longopts)) {
  1498.                                 foreach ($longopts as $longopt) {
  1499.                                         if (!self::validateLongOpt($longopt)) {
  1500.                                                 throw new VNagInvalidLongOpt(sprintf(VNagLang::$illegal_longopt, $longopt));
  1501.                                         }
  1502.                                 }
  1503.                         }
  1504.                 } else if (!_empty($longopts)) { // $longopts is a string
  1505.                         if (!self::validateLongOpt($longopts)) {
  1506.                                 throw new VNagInvalidLongOpt(sprintf(VNagLang::$illegal_longopt, $longopts));
  1507.                         }
  1508.                         $longopts = array($longopts);
  1509.                 } else {
  1510.                         $longopts = array();
  1511.                 }
  1512.  
  1513.                 # valuePolicy must be between 0..2 and being int
  1514.                 switch ($valuePolicy) {
  1515.                         case VNagArgument::VALUE_FORBIDDEN:
  1516.                                 $policyApdx = '';
  1517.                                 break;
  1518.                         case VNagArgument::VALUE_REQUIRED:
  1519.                                 $policyApdx = ':';
  1520.                                 break;
  1521.                         case VNagArgument::VALUE_OPTIONAL:
  1522.                                 $policyApdx = '::';
  1523.                                 break;
  1524.                         default:
  1525.                                 throw new VNagInvalidValuePolicy(sprintf(VNagLang::$illegal_valuepolicy, $valuePolicy));
  1526.                 }
  1527.  
  1528.                 if ((!is_null($shortopt)) && ($shortopt != '?')) self::$all_short .= $shortopt.$policyApdx;
  1529.                 if (!is_null($longopts)) {
  1530.                         foreach ($longopts as $longopt) {
  1531.                                 self::$all_long[] = $longopt.$policyApdx;
  1532.                         }
  1533.                 }
  1534.  
  1535.                 $this->shortopt     = $shortopt;
  1536.                 $this->longopts     = $longopts;
  1537.                 $this->valuePolicy  = $valuePolicy;
  1538.                 $this->valueName    = $valueName;
  1539.                 $this->helpText     = $helpText;
  1540.                 $this->defaultValue = $defaultValue;
  1541.         }
  1542.  
  1543.         protected static function getOptions() {
  1544.                 // Attention: In PHP 5.6.19-0+deb8u1 (cli), $_REQUEST is always set, so we need is_http_mode() instead of isset($_REQUEST)!
  1545.                 global $OVERWRITE_ARGUMENTS;
  1546.  
  1547.                 if (!is_null($OVERWRITE_ARGUMENTS)) {
  1548.                         return $OVERWRITE_ARGUMENTS;
  1549.                 } else if (VNag::is_http_mode()) {
  1550.                         return $_REQUEST;
  1551.                 } else {
  1552.                         return getopt(self::$all_short, self::$all_long);
  1553.                 }
  1554.         }
  1555.  
  1556.         public function count() {
  1557.                 $options = self::getOptions();
  1558.  
  1559.                 $count = 0;
  1560.  
  1561.                 if (isset($options[$this->shortopt])) {
  1562.                         if (is_array($options[$this->shortopt])) {
  1563.                                 // e.g. -vvv
  1564.                                 $count += count($options[$this->shortopt]);
  1565.                         } else {
  1566.                                 // e.g. -v
  1567.                                 $count += 1;
  1568.                         }
  1569.                 }
  1570.  
  1571.                 if (!is_null($this->longopts)) {
  1572.                         foreach ($this->longopts as $longopt) {
  1573.                                 if (isset($options[$longopt])) {
  1574.                                         if (is_array($options[$longopt])) {
  1575.                                                 // e.g. --verbose --verbose --verbose
  1576.                                                 $count += count($options[$longopt]);
  1577.                                         } else {
  1578.                                                 // e.g. --verbose
  1579.                                                 $count += 1;
  1580.                                         }
  1581.                                 }
  1582.                         }
  1583.                 }
  1584.  
  1585.                 return $count;
  1586.         }
  1587.  
  1588.         public function available() {
  1589.                 $options = self::getOptions();
  1590.  
  1591.                 if (isset($options[$this->shortopt])) return true;
  1592.                 if (!is_null($this->longopts)) {
  1593.                         foreach ($this->longopts as $longopt) {
  1594.                                 if (isset($options[$longopt])) return true;
  1595.                         }
  1596.                 }
  1597.                 return false;
  1598.         }
  1599.  
  1600.         public function require() {
  1601.                 if (!$this->available() && is_null($this->defaultValue)) {
  1602.                         $opt = $this->shortopt;
  1603.                         $opt = !_empty($opt) ? '-'.$opt : (isset($this->longopts[0]) ? '--'.$this->longopts[0] : '?');
  1604.                         throw new VNagRequiredArgumentMissing($opt);
  1605.                 }
  1606.         }
  1607.  
  1608.         public function getValue() {
  1609.                 $options = self::getOptions();
  1610.  
  1611.                 if (isset($options[$this->shortopt])) {
  1612.                         $x = $options[$this->shortopt];
  1613.                         if (is_array($x) && (count($x) <= 1)) $options[$this->shortopt] = $options[$this->shortopt][0];
  1614.                         return $options[$this->shortopt];
  1615.                 }
  1616.  
  1617.                 if (!is_null($this->longopts)) {
  1618.                         foreach ($this->longopts as $longopt) {
  1619.                                 if (isset($options[$longopt])) {
  1620.                                         $x = $options[$longopt];
  1621.                                         if (is_array($x) && (count($x) <= 1)) $options[$longopt] = $options[$longopt][0];
  1622.                                         return $options[$longopt];
  1623.                                 }
  1624.                         }
  1625.                 }
  1626.  
  1627.                 return $this->defaultValue;
  1628.         }
  1629. }
  1630.  
  1631. class VNagArgumentHandler {
  1632.         protected $expectedArgs = array();
  1633.  
  1634.         // Will be called by VNag via ReflectionMethod (like C++ style friends), because it should not be called manually.
  1635.         // Use VNag's function instead (since it adds to the helpObj too)
  1636.         protected function _addExpectedArgument($argObj) {
  1637.                 // -? is always illegal, so it will trigger illegalUsage(). So we don't add it to the list of
  1638.                 // expected arguments, otherwise illegalUsage() would be true.
  1639.                 if ($argObj->getShortOpt() == '?') return false;
  1640.  
  1641.                 // GNU extensions with a special meaning
  1642.                 if ($argObj->getShortOpt() == '-') return false; // cancel parsing
  1643.                 if ($argObj->getShortOpt() == '+') return false; // enable POSIXLY_CORRECT
  1644.  
  1645.                 $this->expectedArgs[] = $argObj;
  1646.                 return true;
  1647.         }
  1648.  
  1649.         public function getArgumentObj($shortopt) {
  1650.                 foreach ($this->expectedArgs as $argObj) {
  1651.                         if ($argObj->getShortOpt() == $shortopt) return $argObj;
  1652.                 }
  1653.                 return null;
  1654.         }
  1655.  
  1656.         public function isArgRegistered($shortopt) {
  1657.                 return !is_null($this->getArgumentObj($shortopt));
  1658.         }
  1659.  
  1660.         public function illegalUsage() {
  1661.                 // In this function, we should check if $argv (resp. getopts) contains stuff which is not expected or illegal,
  1662.                 // so the script can show an usage information and quit the program.
  1663.  
  1664.                 // WONTFIX: PHP's horrible implementation of GNU's getopt does not allow following intended tasks:
  1665.                 // - check for illegal values/arguments (e.g. the argument -? which is always illegal)
  1666.                 // - check for missing values (e.g. -H instead of -H localhost )
  1667.                 // - check for unexpected arguments (e.g. -x if only -a -b -c are defined in $expectedArgs as expected arguments)
  1668.                 // - Of course, everything behind "--" may not be evaluated
  1669.                 // see also http://stackoverflow.com/questions/25388130/catch-unexpected-options-with-getopt
  1670.  
  1671.                 // So the only way is to do this stupid hard coded check for '-?'
  1672.                 // PHP sucks...
  1673.                 global $argv;
  1674.                 return (isset($argv[1])) && (($argv[1] == '-?') || ($argv[1] == '/?'));
  1675.         }
  1676. }
  1677.  
  1678. class VNagRange {
  1679.         // see https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
  1680.         // We allow UOMs inside the range definition, e.g. "-w @10M:50M"
  1681.  
  1682.         public /*VNagValueUomPair|'inf'*/ $start;
  1683.         public /*VNagValueUomPair|'inf'*/ $end;
  1684.         public /*boolean*/ $warnInsideRange;
  1685.  
  1686.         public function __construct($rangeDef, $singleValueBehavior=VNag::SINGLEVALUE_RANGE_DEFAULT) {
  1687.                 //if (!preg_match('|(@){0,1}(\d+)(:){0,1}(\d+){0,1}|', $rangeDef, $m)) {
  1688.                 if (!preg_match('|^(@){0,1}([^:]+)(:){0,1}(.*)$|', $rangeDef, $m)) {
  1689.                         throw new VNagInvalidRangeException(sprintf(VNagLang::$range_invalid_syntax, $rangeDef));
  1690.                 }
  1691.  
  1692.                 $this->warnInsideRange = $m[1] === '@';
  1693.  
  1694.                 $this->start = null;
  1695.                 $this->end   = null;
  1696.  
  1697.                 if ($m[3] === ':') {
  1698.                         if ($m[2] === '~') {
  1699.                                 $this->start = 'inf';
  1700.                         } else {
  1701.                                 $this->start = new VNagValueUomPair($m[2]);
  1702.                         }
  1703.  
  1704.                         if (_empty($m[4])) {
  1705.                                 $this->end = 'inf';
  1706.                         } else {
  1707.                                 $this->end = new VNagValueUomPair($m[4]);
  1708.                         }
  1709.                 } else {
  1710.                         assert(_empty($m[4]));
  1711.                         assert(!_empty($m[2]));
  1712.  
  1713.                         $x = $m[2];
  1714.  
  1715.                         if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_DEFAULT) {
  1716.                                 // Default behavior according to the development guidelines:
  1717.                                 //  x means  x:x, which means, everything except x% is bad.
  1718.                                 // @x means @x:x, which means, x is bad and everything else is good.
  1719.                                 $this->start = new VNagValueUomPair($x);
  1720.                                 $this->end   = new VNagValueUomPair($x);
  1721.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_GT_X_BAD) {
  1722.                                 // The single value x means, everything > x is bad. @x is not defined.
  1723.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1724.                                 $this->warnInsideRange = 0;
  1725.                                 $this->start = 'inf';
  1726.                                 $this->end   = new VNagValueUomPair($x);
  1727.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_GE_X_BAD) {
  1728.                                 // The single value x means, everything >= x is bad. @x is not defined.
  1729.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1730.                                 $this->warnInsideRange = 1;
  1731.                                 $this->start = new VNagValueUomPair($x);
  1732.                                 $this->end   = 'inf';
  1733.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_LT_X_BAD) {
  1734.                                 // The single value x means, everything < x is bad. @x is not defined.
  1735.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1736.                                 $this->warnInsideRange = 0;
  1737.                                 $this->start = new VNagValueUomPair($x);
  1738.                                 $this->end   = 'inf';
  1739.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_LE_X_BAD) {
  1740.                                 // The single value x means, everything <= x is bad. @x is not defined.
  1741.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1742.                                 $this->warnInsideRange = 1;
  1743.                                 $this->start = 'inf';
  1744.                                 $this->end   = new VNagValueUomPair($x);
  1745.                         } else {
  1746.                                 throw new VNagException(VNagLang::$illegalSingleValueBehavior);
  1747.                         }
  1748.                 }
  1749.  
  1750.                 // Check if range is valid
  1751.                 if (is_null($this->start)) {
  1752.                         throw new VNagInvalidRangeException(VNagLang::$invalid_start_value);
  1753.                 }
  1754.                 if (is_null($this->end)) {
  1755.                         throw new VNagInvalidRangeException(VNagLang::$invalid_end_value);
  1756.                 }
  1757.                 if (($this->start instanceof VNagValueUomPair) && ($this->end instanceof VNagValueUomPair) &&
  1758.                     (VNagValueUomPair::compare($this->start,$this->end) > 0)) {
  1759.                         throw new VNagInvalidRangeException(VNagLang::$start_is_greater_than_end);
  1760.                 }
  1761.         }
  1762.  
  1763.         public function __toString() {
  1764.                 // Attention:
  1765.                 // - this function assumes that $start and $end are valid.
  1766.                 // - not the shortest result will be chosen
  1767.  
  1768.                 $ret = '';
  1769.                 if ($this->warnInsideRange) {
  1770.                         $ret = '@';
  1771.                 }
  1772.  
  1773.                 if ($this->start === 'inf') {
  1774.                         $ret .= '~';
  1775.                 } else {
  1776.                         $ret .= $this->start;
  1777.                 }
  1778.  
  1779.                 $ret .= ':';
  1780.  
  1781.                 if ($this->end !== 'inf') {
  1782.                         $ret .= $this->end;
  1783.                 }
  1784.  
  1785.                 return $ret;
  1786.         }
  1787.  
  1788.         public function checkAlert($values) {
  1789.                 $compatibleCount = 0;
  1790.  
  1791.                 if (!is_array($values)) $values = array($values);
  1792.                 foreach ($values as $value) {
  1793.                         if (!($value instanceof VNagValueUomPair)) $value = new VNagValueUomPair($value);
  1794.  
  1795.                         assert($this->start instanceof VNagValueUomPair);
  1796.                         assert($this->end   instanceof VNagValueUomPair);
  1797.  
  1798.                         if (($this->start !== 'inf') && (!$this->start->compatibleWith($value))) continue;
  1799.                         if (($this->end   !== 'inf') && (!$this->end->compatibleWith($value)))   continue;
  1800.                         $compatibleCount++;
  1801.  
  1802.                         if ($this->warnInsideRange) {
  1803.                                 return (($this->start === 'inf') || (VNagValueUomPair::compare($value,$this->start) >= 0)) &&
  1804.                                        (($this->end   === 'inf') || (VNagValueUomPair::compare($value,$this->end)   <= 0));
  1805.                         } else {
  1806.                                 return (($this->start !== 'inf') && (VNagValueUomPair::compare($value,$this->start) <  0)) ||
  1807.                                        (($this->end   !== 'inf') && (VNagValueUomPair::compare($value,$this->end)   >  0));
  1808.                         }
  1809.                 }
  1810.  
  1811.                 if ((count($values) > 0) and ($compatibleCount == 0)) {
  1812.                         throw new VNagNoCompatibleRangeUomFoundException(VNagLang::$no_compatible_range_uom_found);
  1813.                 }
  1814.  
  1815.                 return false;
  1816.         }
  1817. }
  1818.  
  1819. class VNagValueUomPair {
  1820.         protected $value;
  1821.         protected $uom;
  1822.         public $roundTo = -1;
  1823.  
  1824.         public function isRelative() {
  1825.                 return $this->uom === '%';
  1826.         }
  1827.  
  1828.         public function getValue() {
  1829.                 return $this->value;
  1830.         }
  1831.  
  1832.         public function getUom() {
  1833.                 return $this->uom;
  1834.         }
  1835.  
  1836.         public function __toString() {
  1837.                 if ($this->roundTo == -1) {
  1838.                         return $this->value.$this->uom;
  1839.                 } else {
  1840.                         return round($this->value,$this->roundTo).$this->uom;
  1841.                 }
  1842.         }
  1843.  
  1844.         public function __construct($str) {
  1845.                 if (!preg_match('/^([\d\.]+)(.*)$/ism', $str, $m)) {
  1846.                         throw new VNagValueUomPairSyntaxException($str);
  1847.                 }
  1848.                 $this->value = $m[1];
  1849.                 $this->uom = isset($m[2]) ? $m[2] : '';
  1850.  
  1851.                 if (!self::isKnownUOM($this->uom)) {
  1852.                         throw new VNagUnknownUomException($this->uom);
  1853.                 }
  1854.         }
  1855.  
  1856.         public static function isKnownUOM($uom) {
  1857.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  1858.                 // 10. UOM (unit of measurement) is one of:
  1859.                 return (
  1860.                                 // no unit specified - assume a number (int or float) of things (eg, users, processes, load averages)
  1861.                                 ($uom === '') ||
  1862.                                 // s - seconds (also us, ms)
  1863.                                 ($uom === 's') || ($uom === 'ms') || ($uom === 'us') ||
  1864.                                 // % - percentage
  1865.                                 ($uom === '%') ||
  1866.                                 // B - bytes (also KB, MB, TB)
  1867.                                 ($uom === 'B') || ($uom === 'KB') || ($uom === 'MB') || ($uom === 'GB') || ($uom === 'TB') || // NOTE: GB is not in the official development guidelines,probably due to an error, so I've added them anyway
  1868.                                 // c - a continous counter (such as bytes transmitted on an interface)
  1869.                                 ($uom === 'c')
  1870.                         );
  1871.         }
  1872.  
  1873.         public function normalize($target=null) {
  1874.                 $res = clone $this;
  1875.  
  1876.                 // The value is normalized to seconds or megabytes
  1877.                 if ($res->uom === 'ms') {
  1878.                         $res->uom = 's';
  1879.                         $res->value /= 1000;
  1880.                 }
  1881.                 if ($res->uom === 'us') {
  1882.                         $res->uom = 's';
  1883.                         $res->value /= 1000 * 1000;
  1884.                 }
  1885.                 if ($res->uom === 'B') {
  1886.                         $res->uom = 'MB';
  1887.                         $res->value /= 1024 * 1024;
  1888.                 }
  1889.                 if ($res->uom === 'KB') {
  1890.                         $res->uom = 'MB';
  1891.                         $res->value /= 1024;
  1892.                 }
  1893.                 if ($res->uom === 'GB') {
  1894.                         $res->uom = 'MB';
  1895.                         $res->value *= 1024;
  1896.                 }
  1897.                 if ($res->uom === 'TB') {
  1898.                         $res->uom = 'MB';
  1899.                         $res->value *= 1024 * 1024;
  1900.                 }
  1901.                 if ($res->uom === 'c') {
  1902.                         $res->uom = '';
  1903.                 }
  1904.  
  1905.                 // Now, if the user wishes, convert to another unit
  1906.                 if (!is_null($target)) {
  1907.                         if ($res->uom == 'MB') {
  1908.                                 if ($target == 'B') {
  1909.                                         $res->uom = 'B';
  1910.                                         $res->value *= 1024 * 1024;
  1911.                                 } else if ($target == 'KB') {
  1912.                                         $res->uom = 'KB';
  1913.                                         $res->value *= 1024;
  1914.                                 } else if ($target == 'GB') {
  1915.                                         $res->uom = 'GB';
  1916.                                         $res->value /= 1024;
  1917.                                 } else if ($target == 'TB') {
  1918.                                         $res->uom = 'TB';
  1919.                                         $res->value /= 1024 * 1024;
  1920.                                 } else {
  1921.                                         throw new VNagUomConvertException($res->uom, $target);
  1922.                                 }
  1923.                         } else if ($res->uom == 's') {
  1924.                                 if ($target == 'ms') {
  1925.                                         $res->uom = 'ms';
  1926.                                         $res->value /= 1000;
  1927.                                 } else if ($target == 'us') {
  1928.                                         $res->uom = 'us';
  1929.                                         $res->value /= 1000 * 1000;
  1930.                                 } else {
  1931.                                         throw new VNagUomConvertException($res->uom, $target);
  1932.                                 }
  1933.                         } else {
  1934.                                 throw new VNagUomConvertException($res->uom, $target);
  1935.                         }
  1936.                 }
  1937.  
  1938.                 return $res;
  1939.         }
  1940.  
  1941.         public function compatibleWith(VNagValueUomPair $other) {
  1942.                 $a = $this->normalize();
  1943.                 $b = $other->normalize();
  1944.  
  1945.                 return ($a->uom == $b->uom);
  1946.         }
  1947.  
  1948.         public static function compare(VNagValueUomPair $left, VNagValueUomPair $right) {
  1949.                 $a = $left->normalize();
  1950.                 $b = $right->normalize();
  1951.  
  1952.                 // FUT: Also accept mixed UOMs, e.g. MB and %
  1953.                 //      To translate between an absolute and a relative value, the
  1954.                 //      reference value (100%=?) needs to be passed through this comparison
  1955.                 //      function somehow.
  1956.                 if ($a->uom != $b->uom) throw new VNagMixedUomsNotImplemented($a->uom, $b->uom);
  1957.  
  1958.                 if ($a->value  > $b->value) return  1;
  1959.                 if ($a->value == $b->value) return  0;
  1960.                 if ($a->value  < $b->value) return -1;
  1961.         }
  1962. }
  1963.  
  1964. class VNagPerformanceData {
  1965.         // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  1966.         //     https://www.icinga.com/docs/icinga1/latest/en/perfdata.html#formatperfdata
  1967.  
  1968.         protected $label;
  1969.         protected /*VNagValueUomPair*/ $value;
  1970.         protected $warn = null;
  1971.         protected $crit = null;
  1972.         protected $min = null;
  1973.         protected $max = null;
  1974.  
  1975.         public static function createByString($perfdata) {
  1976.                 $perfdata = trim($perfdata);
  1977.  
  1978.                 $ary = explode('=',$perfdata);
  1979.                 if (count($ary) != 2) {
  1980.                         throw new VNagInvalidPerformanceDataException(sprintf(VNagLang::$perfdata_line_invalid, $perfdata));
  1981.                 }
  1982.                 $label = $ary[0];
  1983.                 $bry = explode(';',$ary[1]);
  1984.                 if (substr($label,0,1) === "'") $label = substr($label, 1, strlen($label)-2);
  1985.                 $value = $bry[0];
  1986.                 $warn  = isset($bry[1]) ? $bry[1] : null;
  1987.                 $crit  = isset($bry[2]) ? $bry[2] : null;
  1988.                 $min   = isset($bry[3]) ? $bry[3] : null;
  1989.                 $max   = isset($bry[4]) ? $bry[4] : null;
  1990.  
  1991.                 // Guideline "7. min and max are not required if UOM=%" makes no sense, because
  1992.                 // actually, all fields (except label and value) are optional.
  1993.  
  1994.                 return new self($label, $value, $warn, $crit, $min, $max);
  1995.         }
  1996.  
  1997.         public function __construct($label, $value/*may include UOM*/, $warn=null, $crit=null, $min=null, $max=null) {
  1998.                 // Not checked / Nothing to check:
  1999.                 // - 4. label length is arbitrary, but ideally the first 19 characters are unique (due to a limitation in RRD). Be aware of a limitation in the amount of data that NRPE returns to Nagios
  2000.                 // - 6. warn, crit, min or max may be null (for example, if the threshold is not defined or min and max do not apply). Trailing unfilled semicolons can be dropped
  2001.                 // - 9. warn and crit are in the range format (see the Section called Threshold and ranges). Must be the same UOM
  2002.                 // - 7. min and max are not required if UOM=%
  2003.  
  2004.                 // 2. label can contain any characters except the equals sign or single quote (')
  2005.                 if (strpos($label, '=') !== false) throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_label_equal_sign_forbidden);
  2006.  
  2007.                 // 5. to specify a quote character, use two single quotes
  2008.                 $label = str_replace("'", "''", $label);
  2009.  
  2010.                 // 8. value, min and max in class [-0-9.]. Must all be the same UOM.
  2011.                 //    value may be a literal "U" instead, this would indicate that the actual value couldn't be determined
  2012.                 /*
  2013.                 if (($value != 'U') && (!preg_match('|^[-0-9\\.]+$|', $value, $m))) {
  2014.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_value_must_be_in_class);
  2015.                 }
  2016.                 */
  2017.                 if ((!_empty($min)) && (!preg_match('|^[-0-9\\.]+$|', $min, $m))) {
  2018.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_min_must_be_in_class);
  2019.                 }
  2020.                 if ((!_empty($max)) && (!preg_match('|^[-0-9\\.]+$|', $max, $m))) {
  2021.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_max_must_be_in_class);
  2022.                 }
  2023.  
  2024.                 // 10. UOM (unit of measurement) is one of ....
  2025.                 //     => This rule is checked in the VNagValueUomPair constructor.
  2026.  
  2027.                 $this->label = $label;
  2028.                 $this->value = ($value == 'U') ? 'U' : new VNagValueUomPair($value);
  2029.                 $this->warn  = $warn;
  2030.                 $this->crit  = $crit;
  2031.                 $this->min   = $min;
  2032.                 $this->max   = $max;
  2033.         }
  2034.  
  2035.         public function __toString() {
  2036.                 $label = $this->label;
  2037.                 $value = $this->value;
  2038.                 $warn  = $this->warn;
  2039.                 $crit  = $this->crit;
  2040.                 $min   = $this->min;
  2041.                 $max   = $this->max;
  2042.  
  2043.                 // 5. to specify a quote character, use two single quotes
  2044.                 $label = str_replace("''", "'", $label);
  2045.  
  2046.                 // 'label'=value[UOM];[warn];[crit];[min];[max]
  2047.                 // 3. the single quotes for the label are optional. Required if spaces are in the label
  2048.                 return "'$label'=$value".
  2049.                        ';'.(is_null($warn) ? '' : $warn).
  2050.                        ';'.(is_null($crit) ? '' : $crit).
  2051.                        ';'.(is_null($min)  ? '' : $min).
  2052.                        ';'.(is_null($max)  ? '' : $max);
  2053.         }
  2054. }
  2055.  
  2056. class VNagHelp {
  2057.         public $word_wrap_width = 80; // -1 = disable
  2058.         public $argument_indent = 7;
  2059.  
  2060.         public function printUsagePage() {
  2061.                 $usage = $this->getUsage();
  2062.  
  2063.                 if (_empty($usage)) {
  2064.                         $usage = VNagLang::$no_syntax_defined;
  2065.                 }
  2066.  
  2067.                 return trim($usage)."\n";
  2068.         }
  2069.  
  2070.         public function printVersionPage() {
  2071.                 $out = trim($this->getNameAndVersion())."\n";
  2072.  
  2073.                 if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
  2074.  
  2075.                 return $out;
  2076.         }
  2077.  
  2078.         static private function _conditionalLine($line, $terminator='', $prefix='') {
  2079.                 if (!_empty($line)) {
  2080.                         return trim($line).$terminator;
  2081.                 }
  2082.                 return '';
  2083.         }
  2084.  
  2085.         public function printHelpPage() {
  2086.                 $out  = '';
  2087.                 $out .= self::_conditionalLine($this->getNameAndVersion(), "\n");
  2088.                 $out .= self::_conditionalLine($this->getCopyright(), "\n");
  2089.                 $out .= ($out != '') ? "\n" : '';
  2090.                 $out .= self::_conditionalLine($this->getShortDescription(), "\n\n\n");
  2091.                 $out .= self::_conditionalLine($this->getUsage(), "\n\n");
  2092.  
  2093.                 $out .= VNagLang::$options."\n";
  2094.                 foreach ($this->options as $argObj) {
  2095.                         $out .= $this->printArgumentHelp($argObj);
  2096.                 }
  2097.  
  2098.                 $out .= self::_conditionalLine($this->getFootNotes(), "\n\n", "\n");
  2099.  
  2100.                 if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
  2101.  
  2102.                 return $out;
  2103.         }
  2104.  
  2105.         protected /* VNagArgument[] */ $options = array();
  2106.  
  2107.         // Will be called by VNag via ReflectionMethod (like C++ style friends), because it should not be called manually.
  2108.         // Use VNag's function instead (since it adds to the argHandler too)
  2109.         protected function _addOption($argObj) {
  2110.                 $this->options[] = $argObj;
  2111.         }
  2112.  
  2113.         # FUT: Automatic creation of usage page. Which arguments are necessary?
  2114.         protected function printArgumentHelp($argObj) {
  2115.                 $identifiers = array();
  2116.  
  2117.                 $shortopt = $argObj->getShortopt();
  2118.                 if (!_empty($shortopt)) $identifiers[] = '-'.$shortopt;
  2119.  
  2120.                 $longopts = $argObj->getLongopts();
  2121.                 if (!is_null($longopts)) {
  2122.                         foreach ($longopts as $longopt) {
  2123.                                 if (!_empty($longopt)) $identifiers[] = '--'.$longopt;
  2124.                         }
  2125.                 }
  2126.  
  2127.                 if (count($identifiers) == 0) return;
  2128.  
  2129.                 $valueName = $argObj->getValueName();
  2130.  
  2131.                 $arginfo = '';
  2132.                 switch ($argObj->getValuePolicy()) {
  2133.                         case VNagArgument::VALUE_FORBIDDEN:
  2134.                                 $arginfo = '';
  2135.                                 break;
  2136.                         case VNagArgument::VALUE_REQUIRED:
  2137.                                 $arginfo = '='.$valueName;
  2138.                                 break;
  2139.                         case VNagArgument::VALUE_OPTIONAL:
  2140.                                 $arginfo = '[='.$valueName.']';
  2141.                                 break;
  2142.                 }
  2143.  
  2144.                 $out = '';
  2145.                 $out .= implode(', ', $identifiers).$arginfo."\n";
  2146.  
  2147.                 // https://nagios-plugins.org/doc/guidelines.html#AEN302 recommends supporting a 80x23 screen resolution.
  2148.                 // While we cannot guarantee the vertical height, we can limit the width at least...
  2149.  
  2150.                 $content = trim($argObj->getHelpText());
  2151.                 if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width-$this->argument_indent, "\n", false);
  2152.                 $lines = explode("\n", $content);
  2153.  
  2154.                 foreach ($lines as $line) {
  2155.                         $out .= str_repeat(' ', $this->argument_indent).$line."\n";
  2156.                 }
  2157.                 $out .= "\n";
  2158.  
  2159.                 return $out;
  2160.         }
  2161.  
  2162.         // $pluginName should contain the name of the plugin, without version.
  2163.         protected $pluginName;
  2164.         public function setPluginName($pluginName) {
  2165.                 $this->pluginName = $this->replaceStuff($pluginName);
  2166.         }
  2167.         public function getPluginName() {
  2168.                 if (_empty($this->pluginName)) {
  2169.                         global $argv;
  2170.                         return basename($argv[0]);
  2171.                 } else {
  2172.                         return $this->pluginName;
  2173.                 }
  2174.         }
  2175.  
  2176.         private static function isCertified($file) {
  2177.                 $cont = file_get_contents($file);
  2178.  
  2179.                 $regex = '@<\?php /\* <ViaThinkSoftSignature>(.+)</ViaThinkSoftSignature> \*/ \?>\n@ismU';
  2180.  
  2181.                 if (!preg_match($regex, $cont, $m)) {
  2182.                         return false;
  2183.                 }
  2184.                 $signature = base64_decode($m[1]);
  2185.  
  2186.                 $naked = preg_replace($regex, '', $cont);
  2187.                 $hash = hash("sha256", $naked.basename($file));
  2188.  
  2189.                 $public_key = <<<'VTSKEY'
  2190. -----BEGIN PUBLIC KEY-----
  2191. MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEA4UEmad2KHWzfGLcAzbOD
  2192. IhqWyoPA1Cg4zN5YK/CWUiE7sh2CNinIwYqGnIOhZLp54/Iyv3H05QeWJU7kD+jQ
  2193. 5JwR8+pqk8ZGBfqlxXUBJ2bZhYIBJZYfilSROa7jgPPrrw0CjdGLmM3wmc8ztQRv
  2194. 4GpP7MaKVyVOsRz5xEcpzghWZ+Cl8Nxq1Vo02RkMYOOPA16abxZ65lVM8Vv2EKGf
  2195. /VAVViTFvLWPxggvt1fbJJniC0cwt8gjzFXt6IJJSRlqc1lOO9ZIa/EWDKuHKQ1n
  2196. ENQCqnuVPFDZU3lU20Z+6+EA0YngcvNYi3ucdIvgBd4Yv5FetzuxiOZUoDRfh/3R
  2197. 6dCJ8CvRiq0BSZcynTIWNmF3AVsH7vjxZe8kMDbwZNnR0suZ5MfBh6L/s1lCEWlS
  2198. GwmCLc3MnOLxq3JLnfmbVa509YxlUamdSswnvzes28AjnzQ3LQchspP2a8bSXH6/
  2199. qpbwvmV5WiNgwJck04VhaXrRRy3XFSwuk7KU/L4aqadXP26kgDqIYNvPXSa9JyGc
  2200. 14zwdmAtn36o8vpXM/A7GhdWqgPLlJbdTdK6IfwpBs8P/JB6y3t6RzAGiEOITdj9
  2201. QUhW+sAoKno0j4WT7s80vWNWz37WoFJcvVLnVEYitnW6DqM+GOt2od3g6WgI6dOa
  2202. MESA4J44Y4x1gXBw/M6F/ZngP4EJoAUG0GbzsaZ6HKLt4pDTZmw8PnNcXrOMYkr/
  2203. N5EliTXil45DCaLkgNJmpdXjNpIvShW4ogq2osw+SQUalnAbW8ddiaOVCdgXkDFq
  2204. gvnl5QSeUrKPF5v+vlnwWar6Rp7iInQpnA+PTSbAlO3Dd9WqbWx+uNoI/kXUlN0O
  2205. a/vi5Uwat2Bz3N+jIpnBqg4+O+SG0z3UCVmT6Leg+kqO/rXbzoVv/DV7E30vTqdo
  2206. wsswdJEM1BI7Wyid6HPwBek+rdv77gUg3W37vUcdfKxsYRcoHriXLHpmENznJcEx
  2207. /nvilw6To1zx2LKmM/p56MQriKkXnqoOBpkpn3PaWyXZKY9xJNTAbcSP3haE7z9p
  2208. PzJw88KI8dnYuFg4yS/AgmVGAUtu3bhDG4qF9URu2ck868zViH996lraYkmFIWJG
  2209. r7h1LImhrwDEJvb/rOW8QvOZBX9H6pcSKs/LQbeoy6HMIOTlny+S15xtiS4t6Ayv
  2210. 3m0ry5c0qkl/mgKvGpeRnNlrcr6mb2fzxxGvcuBzi25wgIbRLPgJoqsmeBvW1OLU
  2211. +9DpkNvitEJnPRo86v0VF86aou12Sm8Wb4mtrQ7h3qLIYvw2LN2mYh4WlgrSwPpx
  2212. YvE2+vWapilnnDWoiu2ZmDWa7WW/ihqvX9fmp/qzxQvJmBYIN8dFpgcNLqSx526N
  2213. bwIDAQAB
  2214. -----END PUBLIC KEY-----
  2215. VTSKEY;
  2216.  
  2217.                 if (!openssl_verify($hash, $signature, $public_key, OPENSSL_ALGO_SHA256)) {
  2218.                         return false;
  2219.                 }
  2220.  
  2221.                 return true;
  2222.         }
  2223.  
  2224.         // $version should contain the version, not the program name or copyright.
  2225.         protected $version;
  2226.         public function setVersion($version) {
  2227.                 $this->version = $this->replaceStuff($version);
  2228.         }
  2229.         public function getVersion() {
  2230.                 return $this->version;
  2231.         }
  2232.         public function getNameAndVersion() {
  2233.                 $ret = $this->getPluginName();
  2234.                 if (_empty($ret)) return null;
  2235.  
  2236.                 $ver = $this->getVersion();
  2237.                 if (!_empty($ver)) {
  2238.                         $ret = sprintf(VNagLang::$x_version_x, $ret, $ver);
  2239.                 }
  2240.                 $ret = trim($ret);
  2241.  
  2242.                 $certified = true;
  2243.                 foreach (get_included_files() as $file) {
  2244.                         $certified &= self::isCertified($file);
  2245.                 }
  2246.                 if ($certified) {
  2247.                         $ret .= ' (' . VNagLang::$certified . ')';
  2248.                 }
  2249.  
  2250.                 return $ret;
  2251.         }
  2252.  
  2253.         // $copyright should contain the copyright only, no program name or version.
  2254.         // $CURYEAR$ will be replaced by the current year
  2255.         protected $copyright;
  2256.         public function setCopyright($copyright) {
  2257.                 $this->copyright = $this->replaceStuff($copyright);
  2258.         }
  2259.  
  2260.         private function getVNagCopyright() {
  2261.                 if (VNag::is_http_mode()) {
  2262.                         $vts_email = 'www.viathinksoft.com'; // don't publish email address at web services because of spam bots
  2263.                 } else {
  2264.                         $vts_email = base64_decode('aW5mb0B2aWF0aGlua3NvZnQuZGU='); // protect email address from spambots which might parse this code
  2265.                 }
  2266.                 return "VNag Framework ".VNag::VNAG_VERSION." (C) 2014-".date('Y')." ViaThinkSoft <$vts_email>";
  2267.         }
  2268.  
  2269.         public function getCopyright() {
  2270.                 if (_empty($this->copyright)) {
  2271.                         return sprintf(VNagLang::$plugin_uses, $this->getVNagCopyright());
  2272.                 } else {
  2273.                         return trim($this->copyright)."\n".sprintf(VNagLang::$uses, $this->getVNagCopyright());
  2274.                 }
  2275.         }
  2276.  
  2277.         // $shortDescription should describe what this plugin does.
  2278.         protected $shortDescription;
  2279.         public function setShortDescription($shortDescription) {
  2280.                 $this->shortDescription = $this->replaceStuff($shortDescription);
  2281.         }
  2282.         public function getShortDescription() {
  2283.                 if (_empty($this->shortDescription)) {
  2284.                         return null;
  2285.                 } else {
  2286.                         $content = $this->shortDescription;
  2287.                         if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width, "\n", false);
  2288.                         return $content;
  2289.                 }
  2290.         }
  2291.  
  2292.         protected function replaceStuff($text) {
  2293.                 global $argv;
  2294.                 $text = str_replace('$SCRIPTNAME$', $argv[0], $text);
  2295.                 $text = str_replace('$CURYEAR$', date('Y'), $text);
  2296.                 return $text;
  2297.         }
  2298.  
  2299.         // $syntax should contain the option syntax only, no explanations.
  2300.         // $SCRIPTNAME$ will be replaced by the actual script name
  2301.         // $CURYEAR$ will be replaced by the current year
  2302.         # FUT: Automatically generate syntax?
  2303.         protected $syntax;
  2304.         public function setSyntax($syntax) {
  2305.                 $syntax = $this->replaceStuff($syntax);
  2306.                 $this->syntax = $syntax;
  2307.         }
  2308.         public function getUsage() {
  2309.                 if (_empty($this->syntax)) {
  2310.                         return null;
  2311.                 } else {
  2312.                         return sprintf(VNagLang::$usage_x, $this->syntax);
  2313.                 }
  2314.         }
  2315.  
  2316.         // $footNotes can be contact information or other notes which should appear in --help
  2317.         protected $footNotes;
  2318.         public function setFootNotes($footNotes) {
  2319.                 $this->footNotes = $this->replaceStuff($footNotes);
  2320.         }
  2321.         public function getFootNotes() {
  2322.                 return $this->footNotes;
  2323.         }
  2324. }
  2325.  
  2326. class VNagLang {
  2327.         public static function status($code, $statusmodel) {
  2328.                 switch ($statusmodel) {
  2329.                         case VNag::STATUSMODEL_SERVICE:
  2330.                                 switch ($code) {
  2331.                                         case VNag::STATUS_OK:
  2332.                                                 return 'OK';
  2333.                                                 break;
  2334.                                         case VNag::STATUS_WARNING:
  2335.                                                 return 'Warning';
  2336.                                                 break;
  2337.                                         case VNag::STATUS_CRITICAL:
  2338.                                                 return 'Critical';
  2339.                                                 break;
  2340.                                         case VNag::STATUS_UNKNOWN:
  2341.                                                 return 'Unknown';
  2342.                                                 break;
  2343.                                         default:
  2344.                                                 return sprintf('Error (%d)', $code);
  2345.                                                 break;
  2346.                                 }
  2347.                                 break;
  2348.                         case VNag::STATUSMODEL_HOST:
  2349.                                 switch ($code) {
  2350.                                         case VNag::STATUS_UP:
  2351.                                                 return 'Up';
  2352.                                                 break;
  2353.                                         case VNag::STATUS_DOWN:
  2354.                                                 return 'Down';
  2355.                                                 break;
  2356.                                         default:
  2357.                                                 return sprintf('Maintain last state (%d)', $code);
  2358.                                                 break;
  2359.                                 }
  2360.                                 break;
  2361.                         default:
  2362.                                 throw new VNagIllegalStatusModel(sprintf(self::$illegal_statusmodel, $statusmodel));
  2363.                                 break;
  2364.                 }
  2365.         }
  2366.  
  2367.         static $nagios_output = 'VNag-Output';
  2368.         static $verbose_info = 'Verbose information';
  2369.         static $status = 'Status';
  2370.         static $message = 'Message';
  2371.         static $performance_data = 'Performance data';
  2372.         static $status_ok = 'OK';
  2373.         static $status_warn = 'Warning';
  2374.         static $status_critical = 'Critical';
  2375.         static $status_unknown = 'Unknown';
  2376.         static $status_error = 'Error';
  2377.         static $unhandled_exception_without_msg = "Unhandled exception of type %s";
  2378.         static $plugin_uses = 'This plugin uses %s';
  2379.         static $uses = 'uses %s';
  2380.         static $x_version_x = '%s, version %s';
  2381.  
  2382.         // Argument names (help page)
  2383.         static $argname_value = 'value';
  2384.         static $argname_seconds = 'seconds';
  2385.         static $certified = 'Certified by ViaThinkSoft';
  2386.  
  2387.         // Exceptions
  2388.         static $query_without_expected_argument = "The argument '%s' is queried, but was not added to the list of expected arguments. Please contact the plugin author.";
  2389.         static $required_argument_missing = "The argument '%s' is required.";
  2390.         static $performance_data_invalid = 'Performance data invalid.';
  2391.         static $no_standard_arguments_with_letter = "No standard argument with letter '%s' exists.";
  2392.         static $invalid_start_value = 'Invalid start value.';
  2393.         static $invalid_end_value = 'Invalid end value.';
  2394.         static $start_is_greater_than_end = 'Start is greater than end value.';
  2395.         static $value_name_forbidden = "Implementation error: You may not define a value name for the argument, because the value policy is VALUE_FORBIDDEN.";
  2396.         static $value_name_required = "Implementation error: Please define a name for the argument (so it can be shown in the help page).";
  2397.         static $illegal_shortopt = "Illegal shortopt '-%s'.";
  2398.         static $illegal_longopt = "Illegal longopt '--%s'.";
  2399.         static $illegal_valuepolicy = "valuePolicy has illegal value '%s'.";
  2400.         static $range_invalid_syntax = "Syntax error in range '%s'.";
  2401.         static $timeout_value_invalid = "Timeout value '%s' is invalid.";
  2402.         static $range_is_invalid = 'Range is invalid.';
  2403.         static $timeout_exception = 'Timeout!';
  2404.         static $perfdata_label_equal_sign_forbidden = 'Label may not contain an equal sign.';
  2405.         static $perfdata_value_must_be_in_class = 'Value must be in class [-0-9.] or be \'U\' if the actual value can\'t be determined.';
  2406.         static $perfdata_min_must_be_in_class = 'Min must be in class [-0-9.] or empty.';
  2407.         static $perfdata_max_must_be_in_class = 'Max must be in class [-0-9.] or empty.';
  2408.         static $perfdata_uom_not_recognized = 'UOM (unit of measurement) "%s" is not recognized.';
  2409.         static $perfdata_mixed_uom_not_implemented = 'Mixed UOMs (%s and %s) are currently not supported.';
  2410.         static $no_compatible_range_uom_found = 'Measured values are not compatible with the provided warning/critical parameter. Most likely, the UOM is incompatible.';
  2411.         static $exception_x = '%s (%s)';
  2412.         static $no_syntax_defined = 'The author of this plugin has not defined a syntax for this plugin.';
  2413.         static $usage_x = "Usage:\n%s";
  2414.         static $options = "Options:";
  2415.         static $illegal_statusmodel = "Invalid statusmodel %d.";
  2416.         static $none = '[none]';
  2417.         static $valueUomPairSyntaxError = 'Syntax error at "%s". Syntax must be Value[UOM].';
  2418.         static $too_few_warning_ranges = "You have too few warning ranges (currently trying to get element %d).";
  2419.         static $too_few_critical_ranges = "You have too few critical ranges (currently trying to get element %d).";
  2420.         static $dataset_missing = 'Dataset missing.';
  2421.         static $payload_not_base64 = 'The payload is not valid Base64.';
  2422.         static $payload_not_json = 'The payload is not valid JSON.';
  2423.         static $signature_missing = 'The signature is missing.';
  2424.         static $signature_not_bas64 = 'The signature is not valid Base64.';
  2425.         static $signature_invalid = 'The signature is invalid. The connection might have been tampered, or a different key is used.';
  2426.         static $pubkey_file_not_found = "Public key file %s was not found.";
  2427.         static $pubkey_file_not_readable = "Public key file %s is not readable.";
  2428.         static $privkey_file_not_found = "Private key file %s was not found.";
  2429.         static $privkey_file_not_readable = "Private key file %s is not readable.";
  2430.         static $perfdata_line_invalid = "Performance data line %s is invalid.";
  2431.         static $singlevalue_unexpected_at_symbol = 'This plugin does not allow the @-symbol at ranges for single values.';
  2432.         static $illegalSingleValueBehavior = "Illegal value for 'singleValueBehavior'. Please contact the creator of the plugin.";
  2433.         static $dataset_encryption_no_array = 'Dataset encryption information invalid.';
  2434.         static $require_password = 'This resource is protected with a password. Please provide a password.';
  2435.         static $wrong_password = 'This resource is protected with a password. You have provided the wrong password, or it was changed.';
  2436.         static $convert_x_y_error = 'Cannot convert from UOM %s to UOM %s.';
  2437.         static $php_error = 'PHP has detected an error in the plugin. Please contact the plugin author.';
  2438.         static $output_level_lowered = "Output Buffer level lowered during cbRun(). Please contact the plugin author.";
  2439.  
  2440.         // Help texts
  2441.         static $warning_range = 'Warning range';
  2442.         static $critical_range = 'Critical range';
  2443.         static $prints_version = 'Prints version';
  2444.         static $verbosity_helptext = 'Verbosity -v, -vv or -vvv';
  2445.         static $timeout_helptext = 'Sets timeout in seconds';
  2446.         static $help_helptext = 'Prints help page';
  2447.         static $prints_usage = 'Prints usage';
  2448.  
  2449.         static $notConstructed = 'Parent constructor not called with parent::__construct().';
  2450. }
  2451.  
  2452. function vnagErrorHandler($errorkind, $errortext, $file, $line) {
  2453.         if (!(error_reporting() & $errorkind)) {
  2454.                 // Code is not included in error_reporting. Don't do anything.
  2455.                 return;
  2456.         }
  2457.  
  2458.         while (ob_get_level() > 0) @ob_end_clean();
  2459.  
  2460.         // We want 100% clean scripts, so any error, warning or notice will shutdown the script
  2461.         // This also fixes the issue that PHP will end with result code 0, showing an error.
  2462.  
  2463.         // Error kinds see http://php.net/manual/en/errorfunc.constants.php
  2464.         if (defined('E_ERROR') && ($errorkind == E_ERROR)) $errorkind = 'Error';
  2465.         if (defined('E_WARNING') && ($errorkind == E_WARNING)) $errorkind = 'Warning';
  2466.         if (defined('E_PARSE') && ($errorkind == E_PARSE)) $errorkind = 'Parse';
  2467.         if (defined('E_NOTICE') && ($errorkind == E_NOTICE)) $errorkind = 'Notice';
  2468.         if (defined('E_CORE_ERROR') && ($errorkind == E_CORE_ERROR)) $errorkind = 'Core Error';
  2469.         if (defined('E_CORE_WARNING') && ($errorkind == E_CORE_WARNING)) $errorkind = 'Core Warning';
  2470.         if (defined('E_COMPILE_ERROR') && ($errorkind == E_COMPILE_ERROR)) $errorkind = 'Compile Error';
  2471.         if (defined('E_COMPILE_WARNING') && ($errorkind == E_COMPILE_WARNING)) $errorkind = 'Compile Warning';
  2472.         if (defined('E_USER_ERROR') && ($errorkind == E_USER_ERROR)) $errorkind = 'User Error';
  2473.         if (defined('E_USER_WARNING') && ($errorkind == E_USER_WARNING)) $errorkind = 'User Warning';
  2474.         if (defined('E_USER_NOTICE') && ($errorkind == E_USER_NOTICE)) $errorkind = 'User Notice';
  2475.         if (defined('E_STRICT') && ($errorkind == E_STRICT)) $errorkind = 'Strict';
  2476.         if (defined('E_RECOVERABLE_ERROR') && ($errorkind == E_RECOVERABLE_ERROR)) $errorkind = 'Recoverable Error';
  2477.         if (defined('E_DEPRECATED') && ($errorkind == E_DEPRECATED)) $errorkind = 'Deprecated';
  2478.         if (defined('E_USER_DEPRECATED') && ($errorkind == E_USER_DEPRECATED)) $errorkind = 'User Deprecated';
  2479.         throw new VNagException(VNagLang::$php_error . " $errortext at $file:$line (kind $errorkind)");
  2480.  
  2481.         // true = the PHP internal error handling will not be called.
  2482.         return true;
  2483. }
  2484.  
  2485. $old_error_handler = set_error_handler("vnagErrorHandler");
  2486.  
  2487. // === End of document ===
  2488.