git.lirion.de

Of git, get, and gud

summaryrefslogtreecommitdiffstats
path: root/nagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject
diff options
context:
space:
mode:
Diffstat (limited to 'nagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject')
-rwxr-xr-xnagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject1979
1 files changed, 1979 insertions, 0 deletions
diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject b/nagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject
new file mode 100755
index 0000000..d8a377e
--- /dev/null
+++ b/nagios-plugins-contrib-24.20190301~bpo9+1/check_webinject/check_webinject
@@ -0,0 +1,1979 @@
+#!/usr/bin/perl
+# nagios: +epn
+
+package Webinject;
+
+# Copyright 2010-2012 Sven Nierlein (nierlein@cpan.org)
+# Copyright 2004-2006 Corey Goldberg (corey@goldb.org)
+#
+# This file is part of WebInject.
+#
+# WebInject is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# WebInject is distributed in the hope that it will be useful,
+# but without any warranty; without even the implied warranty of
+# merchantability or fitness for a particular purpose. See the
+# GNU General Public License for more details.
+
+use 5.006;
+use strict;
+use warnings;
+use Carp;
+use LWP;
+use HTTP::Request::Common;
+use HTTP::Cookies;
+use XML::Simple;
+use Time::HiRes 'time', 'sleep';
+use Getopt::Long;
+use Crypt::SSLeay; # for SSL/HTTPS (you may comment this out if you don't need it)
+use XML::Parser; # for web services verification (you may comment this out if aren't doing XML verifications for web services)
+use Error qw(:try); # for web services verification (you may comment this out if aren't doing XML verifications for web services)
+use Data::Dumper; # dump hashes for debugging
+use File::Temp qw/ tempfile /; # create temp files
+
+our $VERSION = '1.80';
+
+=head1 NAME
+
+Webinject - Perl Module for testing web services
+
+=head1 SYNOPSIS
+
+ use Webinject;
+ my $webinject = Webinject->new(reporttype => "nagios", timeout => 30, break_on_errors => 1);
+ $webinject->engine();
+
+=head1 DESCRIPTION
+
+WebInject is a free tool for automated testing of web applications and web
+services. It can be used to test individual system components that have HTTP
+interfaces (JSP, ASP, CGI, PHP, AJAX, Servlets, HTML Forms, XML/SOAP Web
+Services, REST, etc), and can be used as a test harness to create a suite of
+[HTTP level] automated functional, acceptance, and regression tests. A test
+harness allows you to run many test cases and collect/report your results.
+WebInject offers real-time results display and may also be used for monitoring
+system response times.
+
+=head1 CONSTRUCTOR
+
+=head2 new ( [ARGS] )
+
+Creates an C<Webinject> object.
+
+=over 4
+
+=item reporttype
+
+possible values are 'standard', 'nagios', 'nagios2', 'mrtg' or 'external:'
+
+=item nooutput
+
+suppress all output to STDOUT, create only logilfes
+
+=item break_on_errors
+
+stop after the first testcase fails, otherwise Webinject would go on and
+execute all tests regardless of the previous case.
+
+=item timeout
+
+Default timeout is 180seconds. Timeout starts again for every testcase.
+
+=item useragent
+
+Set the useragent used in HTTP requests. Default is 'Webinject'.
+
+=item max_redirect
+
+Set maximum number of HTTP redirects. Default is 0.
+
+=item proxy
+
+Sets a proxy which is then used for http and https requests.
+
+=item output_dir
+
+Output directory where all logfiles will go to. Defaults to current directory.
+
+=item globalhttplog
+
+Can be 'yes' or 'onfail'. Will log the http request and response to a http.log file.
+
+=item httpauth
+
+Provides credentials for webserver authentications. The format is:
+
+ ['servername', 'portnumber', 'realm-name', 'username', 'password']
+
+=item baseurl
+
+the value can be used as {BASEURL} in the test cases
+
+=item baseurl1
+
+the value can be used as {BASEURL1} in the test cases
+
+=item baseurl2
+
+the value can be used as {BASEURL2} in the test cases
+
+=item standaloneplot
+
+can be "on" or "off". Default is off.
+Create gnuplot graphs when enabled.
+
+=item graphtype
+
+Defaults to 'lines'
+
+=item gnuplot
+
+Defines the path to your gnuplot binary.
+
+=back
+
+=cut
+
+sub new {
+ my $class = shift;
+ my (%options) = @_;
+ $| = 1; # don't buffer output to STDOUT
+
+ my $self = {};
+ bless $self, $class;
+
+ # set default config options
+ $self->_set_defaults();
+
+ for my $opt_key ( keys %options ) {
+ if( exists $self->{'config'}->{$opt_key} ) {
+ if($opt_key eq 'httpauth') {
+ $self->_set_http_auth($options{$opt_key});
+ } else {
+ $self->{'config'}->{$opt_key} = $options{$opt_key};
+ }
+ }
+ else {
+ $self->_usage("ERROR: unknown option: ".$opt_key);
+ }
+ }
+
+ # get command line options
+ $self->_getoptions();
+
+ return $self;
+}
+
+########################################
+
+=head1 METHODS
+
+=head2 engine
+
+start the engine of webinject
+
+=cut
+
+sub engine {
+ #wrap the whole engine in a subroutine so it can be integrated with the gui
+ my $self = shift;
+
+ if($self->{'gui'}) {
+ $self->_gui_initial();
+ }
+ else {
+ # delete files leftover from previous run (do this here so they are whacked each run)
+ $self->_whackoldfiles();
+ }
+
+ $self->_processcasefile();
+
+ my $useragent = $self->_get_useragent();
+
+ # write opening tags for STDOUT.
+ $self->_writeinitialstdout();
+
+ # create the gnuplot config file
+ $self->_plotcfg();
+
+ # timer for entire test run
+ my $startruntimer = time();
+
+ # process test case files named in config
+ for my $currentcasefile ( @{ $self->{'casefilelist'} } ) {
+ #print "\n$currentcasefile\n\n";
+
+ my $resultfile = {
+ 'name' => $currentcasefile,
+ 'cases' => [],
+ };
+
+ if($self->{'gui'}) { $self->_gui_processing_msg($currentcasefile); }
+
+ my $tempfile = $self->_convtestcases($currentcasefile);
+
+ my $xmltestcases;
+ eval {
+ $xmltestcases = XMLin( $tempfile,
+ varattr => 'varname',
+ variables => $self->{'config'} ); # slurp test case file to parse (and specify variables tag)
+ };
+ if($@) {
+ my $error = $@;
+ $error =~ s/^\s*//mx;
+ $self->_usage("ERROR: reading xml test case ".$currentcasefile." failed: ".$error);
+ }
+
+ unless( defined $xmltestcases->{case} ) {
+ $self->_usage("ERROR: no test cases defined!");
+ }
+
+ # fix case if there is only one case
+ if( defined $xmltestcases->{'case'}->{'id'} ) {
+ my $tmpcase = $xmltestcases->{'case'};
+ $xmltestcases->{'case'} = { $tmpcase->{'id'} => $tmpcase };
+ }
+
+ #delete the temp file as soon as we are done reading it
+ if ( -e $tempfile ) { unlink $tempfile; }
+
+ my $repeat = 1;
+ if(defined $xmltestcases->{repeat} and $xmltestcases->{repeat} > 0) {
+ $repeat = $xmltestcases->{repeat};
+ }
+
+ my $useragent = $self->_get_useragent();
+
+ for my $run_nr (1 .. $repeat) {
+
+ # process cases in sorted order
+ for my $testnum ( sort { $a <=> $b } keys %{ $xmltestcases->{case} } ) {
+
+ # if an XPath Node is defined, only process the single Node
+ if( $self->{'xnode'} ) {
+ $testnum = $self->{'xnode'};
+ }
+
+ # create testcase
+ my $case = { 'id' => $testnum };
+
+ # populate variables with values from testcase file, do substitutions, and revert converted values back
+ for my $key (keys %{$xmltestcases->{'case'}->{$testnum}}) {
+ $case->{$key} = $xmltestcases->{'case'}->{$testnum}->{$key};
+ }
+
+ my $label = '';
+ if(defined $case->{'label'}) {
+ $label = $case->{'label'}." - ";
+ }
+ $self->_out(qq|Test: $label$currentcasefile - $testnum \n|);
+
+ $case = $self->_run_test_case($case, $useragent);
+
+ push @{$resultfile->{'cases'}}, $case;
+
+ # break from sub if user presses stop button in gui
+ if( $self->{'switches'}->{'stop'} eq 'yes' ) {
+ my $rc = $self->_finaltasks();
+ $self->{'switches'}->{'stop'} = 'no';
+ return $rc; # break from sub
+ }
+
+ # break here if the last result was an error
+ if($self->{'config'}->{'break_on_errors'} and $self->{'result'}->{'iscritical'}) {
+ last;
+ }
+
+ # if an XPath Node is defined, only process the single Node
+ if( $self->{'xnode'} ) {
+ last;
+ }
+ }
+ }
+
+ push @{$self->{'result'}->{'files'}}, $resultfile;
+ }
+
+ my $endruntimer = time();
+ $self->{'result'}->{'totalruntime'} = ( int( 1000 * ( $endruntimer - $startruntimer ) ) / 1000 ); #elapsed time rounded to thousandths
+
+
+ # do return/cleanup tasks
+ return $self->_finaltasks();
+}
+
+################################################################################
+# runs a single test case
+sub _run_test_case {
+ my($self,$case,$useragent) =@_;
+
+ confess("no testcase!") unless defined $case;
+
+ # set some defaults
+ $case->{'id'} = 1 unless defined $case->{'id'};
+ $case->{'passedcount'} = 0;
+ $case->{'failedcount'} = 0;
+ $case->{'iswarning'} = 0;
+ $case->{'iscritical'} = 0;
+ $case->{'messages'} = [];
+
+ $useragent = $self->_get_useragent() unless defined $useragent;
+
+ # don't do this if monitor is disabled in gui
+ if($self->{'gui'} and $self->{'monitorenabledchkbx'} ne 'monitor_off') {
+ my $curgraphtype = $self->{'config'}->{'graphtype'};
+ }
+
+ # used to replace parsed {timestamp} with real timestamp value
+ my $timestamp = time();
+
+ for my $key (keys %{$case}) {
+ $case->{$key} = $self->_convertbackxml($case->{$key}, $timestamp);
+ next if $key eq 'errormessage';
+ $case->{$key} = $self->_convertbackxmlresult($case->{$key});
+ }
+
+ if( $self->{'gui'} ) { $self->_gui_tc_descript($case); }
+
+ push @{$case->{'messages'}}, { 'html' => "<td>" }; # HTML: open table column
+ for(qw/description1 description2/) {
+ next unless defined $case->{$_};
+ $self->_out(qq|Desc: $case->{$_}\n|);
+ push @{$case->{'messages'}}, {'key' => $_, 'value' => $case->{$_}, 'html' => "<b>$case->{$_}</b><br />" };
+ }
+ my $method;
+ if (defined $case->{method}) {
+ $method = uc($case->{method});
+ } else {
+ $method = "GET";
+ }
+ push @{$case->{'messages'}}, { 'html' => qq|<small>$method <a href="$case->{url}">$case->{url}</a> </small><br />\n| };
+
+ push @{$case->{'messages'}}, { 'html' => "</td><td>" }; # HTML: next column
+
+ my($latency,$request,$response);
+ alarm($self->{'config'}->{'timeout'}+1); # timeout should be handled by LWP, but just in case...
+ eval {
+ local $SIG{ALRM} = sub { die("alarm") };
+ if($case->{method}){
+ if(lc $case->{method} eq "get") {
+ ($latency,$request,$response) = $self->_httpget($useragent, $case);
+ }
+ elsif(lc $case->{method} eq "post") {
+ ($latency,$request,$response) = $self->_httppost($useragent, $case);
+ }
+ else {
+ $self->_usage('ERROR: bad HTTP Request Method Type, you must use "get" or "post"');
+ }
+ }
+ else {
+ ($latency,$request,$response) = $self->_httpget($useragent, $case); # use "get" if no method is specified
+ }
+ };
+ alarm(0);
+ if($@) {
+ $case->{'iscritical'} = 1;
+ } else {
+ $case->{'latency'} = $latency;
+ $case->{'request'} = $request->as_string();
+ $case->{'response'} = $response->as_string();
+
+ # verify result from http response
+ $self->_verify($response, $case);
+
+ if($case->{verifypositivenext}) {
+ $self->{'verifylater'} = $case->{'verifypositivenext'};
+ $self->_out("Verify On Next Case: '".$case->{verifypositivenext}."' \n");
+ push @{$case->{'messages'}}, {'key' => 'verifypositivenext', 'value' => $case->{verifypositivenext}, 'html' => "Verify On Next Case: ".$case->{verifypositivenext}."<br />" };
+ }
+
+ if($case->{verifynegativenext}) {
+ $self->{'verifylaterneg'} = $case->{'verifynegativenext'};
+ $self->_out("Verify Negative On Next Case: '".$case->{verifynegativenext}."' \n");
+ push @{$case->{'messages'}}, {'key' => 'verifynegativenext', 'value' => $case->{verifynegativenext}, 'html' => "Verify Negative On Next Case: ".$case->{verifynegativenext}."<br />" };
+ }
+
+ # write to http.log file
+ $self->_httplog($request, $response, $case);
+
+ # send perf data to log file for plotting
+ $self->_plotlog($latency);
+
+ # call the external plotter to create a graph
+ $self->_plotit();
+
+ if( $self->{'gui'} ) {
+ $self->_gui_updatemontab(); # update monitor with the newly rendered plot graph
+ }
+
+ $self->_parseresponse($response, $case); # grab string from response to send later
+
+ # make parsed results available in the errormessage
+ for my $key (keys %{$case}) {
+ next unless $key eq 'errormessage';
+ $case->{$key} = $self->_convertbackxmlresult($case->{$key});
+ }
+ }
+
+ push @{$case->{'messages'}}, { 'html' => "</td><td>\n" }; # HTML: next column
+ # if any verification fails, test case is considered a failure
+ if($case->{'iscritical'}) {
+ # end result will be also critical
+ $self->{'result'}->{'iscritical'} = 1;
+
+ push @{$case->{'messages'}}, {'key' => 'success', 'value' => 'false' };
+ if( $self->{'result'}->{'returnmessage'} ) { # Add returnmessage to the output
+ my $prefix = "case #".$case->{'id'}.": ";
+ if(defined $case->{'label'}) {
+ $prefix = $case->{'label'}." (case #".$case->{'id'}."): ";
+ }
+ $self->{'result'}->{'returnmessage'} = $prefix.$self->{'result'}->{'returnmessage'};
+ my $message = $self->{'result'}->{'returnmessage'};
+ $message = $message.' - '.$case->{errormessage} if defined $case->{errormessage};
+ push @{$case->{'messages'}}, {
+ 'key' => 'result-message',
+ 'value' => $message,
+ 'html' => "<b><span class=\"fail\">FAILED :</span> ".$message."</b>"
+ };
+ $self->_out("TEST CASE FAILED : ".$message."\n");
+ }
+ # print regular error output
+ elsif ( $case->{errormessage} ) { # Add defined error message to the output
+ push @{$case->{'messages'}}, {
+ 'key' => 'result-message',
+ 'value' => $case->{errormessage},
+ 'html' => "<b><span class=\"fail\">FAILED :</span> ".$case->{errormessage}."</b>"
+ };
+ $self->_out(qq|TEST CASE FAILED : $case->{errormessage}\n|);
+ }
+ else {
+ push @{$case->{'messages'}}, {
+ 'key' => 'result-message',
+ 'value' => 'TEST CASE FAILED',
+ 'html' => "<b><span class=\"fail\">FAILED</span></b>"
+ };
+ $self->_out(qq|TEST CASE FAILED\n|);
+ }
+ unless( $self->{'result'}->{'returnmessage'} ) { #(used for plugin compatibility) if it's the first error message, set it to variable
+ if( $case->{errormessage} ) {
+ $self->{'result'}->{'returnmessage'} = $case->{errormessage};
+ }
+ else {
+ $self->{'result'}->{'returnmessage'} = "Test case number ".$case->{'id'}." failed";
+ if(defined $case->{'label'}) {
+ $self->{'result'}->{'returnmessage'} = "Test case ".$case->{'label'}." (#".$case->{'id'}.") failed";
+ }
+ }
+ }
+ if( $self->{'gui'} ) {
+ $self->_gui_status_failed();
+ }
+ }
+ elsif($case->{'iswarning'}) {
+ # end result will be also warning
+ $self->{'result'}->{'iswarning'} = 1;
+
+ push @{$case->{'messages'}}, {'key' => 'success', 'value' => 'false' };
+ if( $case->{errormessage} ) { # Add defined error message to the output
+ push @{$case->{'messages'}}, {'key' => 'result-message', 'value' => $case->{errormessage}, 'html' => "<b><span class=\"fail\">WARNED :</span> ".$case->{errormessage}."</b>" };
+ $self->_out(qq|TEST CASE WARNED : $case->{errormessage}\n|);
+ }
+ # print regular error output
+ else {
+ # we suppress most logging when running in a plugin mode
+ push @{$case->{'messages'}}, {'key' => 'result-message', 'value' => 'TEST CASE WARNED', 'html' => "<b><span class=\"fail\">WARNED</span></b>" };
+ $self->_out(qq|TEST CASE WARNED\n|);
+ }
+ unless( $self->{'result'}->{'returnmessage'} ) { #(used for plugin compatibility) if it's the first error message, set it to variable
+ if( $case->{errormessage} ) {
+ $self->{'result'}->{'returnmessage'} = $case->{errormessage};
+ }
+ else {
+ $self->{'result'}->{'returnmessage'} = "Test case number ".$case->{'id'}." warned";
+ if(defined $case->{'label'}) {
+ $self->{'result'}->{'returnmessage'} = "Test case ".$case->{'label'}." (#".$case->{'id'}.") warned";
+ }
+ }
+
+ }
+ if( $self->{'gui'} ) {
+ $self->_gui_status_failed();
+ }
+ }
+ else {
+ $self->_out(qq|TEST CASE PASSED\n|);
+ push @{$case->{'messages'}}, {'key' => 'success', 'value' => 'true' };
+ push @{$case->{'messages'}}, {
+ 'key' => 'result-message',
+ 'value' => 'TEST CASE PASSED',
+ 'html' => "<b><span class=\"pass\">PASSED</span></b>"
+ };
+ if( $self->{'gui'} ) {
+ $self->_gui_status_passed();
+ }
+ }
+
+ if( $self->{'gui'} ) { $self->_gui_timer_output($latency); }
+
+ $self->_out(qq|Response Time = $latency sec \n|);
+ $self->_out(qq|------------------------------------------------------- \n|);
+ push @{$case->{'messages'}}, {
+ 'key' => 'responsetime',
+ 'value' => $latency,
+ 'html' => "<br />".$latency." sec </td>\n" };
+
+ $self->{'result'}->{'runcount'}++;
+ $self->{'result'}->{'totalruncount'}++;
+
+ if( $self->{'gui'} ) {
+ # update the statusbar
+ $self->_gui_statusbar();
+ }
+
+ if( $latency > $self->{'result'}->{'maxresponse'} ) {
+ # set max response time
+ $self->{'result'}->{'maxresponse'} = $latency;
+ }
+ if(!defined $self->{'result'}->{'minresponse'} or $latency < $self->{'result'}->{'minresponse'} ) {
+ # set min response time
+ $self->{'result'}->{'minresponse'} = $latency;
+ }
+ # keep total of response times for calculating avg
+ $self->{'result'}->{'totalresponse'} = ( $self->{'result'}->{'totalresponse'} + $latency );
+ # avg response rounded to thousands
+ $self->{'result'}->{'avgresponse'} = ( int( 1000 * ( $self->{'result'}->{'totalresponse'} / $self->{'result'}->{'totalruncount'} ) ) / 1000 );
+
+ if( $self->{'gui'} ) {
+ # update timers and counts in monitor tab
+ $self->_gui_updatemonstats();
+ }
+
+
+ # if a sleep value is set in the test case, sleep that amount
+ if( $case->{sleep} ) {
+ sleep( $case->{sleep} );
+ }
+
+ $self->{'result'}->{'totalpassedcount'} += $case->{'passedcount'};
+ $self->{'result'}->{'totalfailedcount'} += $case->{'failedcount'};
+
+ if($case->{'iscritical'} or $case->{'iswarning'}) {
+ $self->{'result'}->{'totalcasesfailedcount'}++;
+ } else {
+ $self->{'result'}->{'totalcasespassedcount'}++;
+ }
+
+ return $case;
+}
+
+################################################################################
+sub _get_useragent {
+ my $self = shift;
+
+ # construct LWP object
+ my $useragent = LWP::UserAgent->new(keep_alive=>1);
+
+ # store cookies in our LWP object
+ my $fh;
+ our $cookietempfilename;
+ ($fh, $cookietempfilename) = tempfile(undef, UNLINK => 1);
+ unlink ($cookietempfilename);
+ $useragent->cookie_jar(HTTP::Cookies->new(
+ file => $cookietempfilename,
+ autosave => 1,
+ ));
+
+ # http useragent that will show up in webserver logs
+ unless(defined $self->{'config'}->{'useragent'}) {
+ $useragent->agent('WebInject');
+ } else {
+ $useragent->agent($self->{'config'}->{'useragent'});
+ }
+
+ # add proxy support if it is set in config.xml
+ if( $self->{'config'}->{'proxy'} ) {
+ my $proxy = $self->{'config'}->{'proxy'};
+ $proxy =~ s/^http:\/\///mx;
+ $useragent->proxy([qw( http )], "http://".$proxy);
+ $ENV{'HTTPS_PROXY'} = "http://".$proxy;
+ }
+
+ # don't follow redirects unless set by config
+ push @{$useragent->requests_redirectable}, 'POST';
+ $useragent->max_redirect($self->{'config'}->{'max_redirect'});
+
+ # add http basic authentication support
+ # corresponds to:
+ # $useragent->credentials('servername:portnumber', 'realm-name', 'username' => 'password');
+ if(scalar @{$self->{'config'}->{'httpauth'}}) {
+ # add the credentials to the user agent here. The foreach gives the reference to the tuple ($elem), and we
+ # deref $elem to get the array elements.
+ for my $elem ( @{ $self->{'config'}->{'httpauth'} } ) {
+ #print "adding credential: $elem->[0]:$elem->[1], $elem->[2], $elem->[3] => $elem->[4]\n";
+ $useragent->credentials( $elem->[0].":".$elem->[1], $elem->[2], $elem->[3] => $elem->[4] );
+ }
+ }
+
+ # change response delay timeout in seconds if it is set in config.xml
+ if($self->{'config'}->{'timeout'}) {
+ $useragent->timeout($self->{'config'}->{'timeout'}); # default LWP timeout is 180 secs.
+ }
+
+ return $useragent;
+}
+
+################################################################################
+# set defaults
+sub _set_defaults {
+ my $self = shift;
+ $self->{'config'} = {
+ 'currentdatetime' => scalar localtime time, #get current date and time for results report
+ 'standaloneplot' => 'off',
+ 'graphtype' => 'lines',
+ 'httpauth' => [],
+ 'reporttype' => 'standard',
+ 'output_dir' => './',
+ 'nooutput' => undef,
+ 'baseurl' => '',
+ 'baseurl1' => '',
+ 'baseurl2' => '',
+ 'break_on_errors' => 0,
+ 'max_redirect' => 0,
+ 'globalhttplog' => 'no',
+ 'proxy' => '',
+ 'timeout' => 180,
+ };
+ $self->{'exit_codes'} = {
+ 'UNKNOWN' => 3,
+ 'OK' => 0,
+ 'WARNING' => 1,
+ 'CRITICAL' => 2,
+ };
+ $self->{'switches'} = {
+ 'stop' => 'no',
+ 'plotclear' => 'no',
+ };
+ $self->{'out'} = '';
+ $self->_reset_result();
+ return;
+}
+
+################################################################################
+# reset result
+sub _reset_result {
+ my $self = shift;
+ $self->{'result'} = {
+ 'cases' => [],
+ 'returnmessage' => undef,
+ 'totalcasesfailedcount' => 0,
+ 'totalcasespassedcount' => 0,
+ 'totalfailedcount' => 0,
+ 'totalpassedcount' => 0,
+ 'totalresponse' => 0,
+ 'totalruncount' => 0,
+ 'totalruntime' => 0,
+ 'casecount' => 0,
+ 'avgresponse' => 0,
+ 'iscritical' => 0,
+ 'iswarning' => 0,
+ 'maxresponse' => 0,
+ 'minresponse' => undef,
+ 'runcount' => 0,
+ };
+ return;
+}
+
+################################################################################
+# write initial text for STDOUT
+sub _writeinitialstdout {
+ my $self = shift;
+
+ if($self->{'config'}->{'reporttype'} !~ /^nagios/mx) {
+ $self->_out(qq|
+Starting WebInject Engine (v$Webinject::VERSION)...
+|);
+ }
+ $self->_out("-------------------------------------------------------\n");
+ return;
+}
+
+################################################################################
+# write summary and closing tags for results file
+sub _write_result_html {
+ my $self = shift;
+
+ my $file = $self->{'config'}->{'output_dir'}."results.html";
+ open( my $resultshtml, ">", $file )
+ or $self->_usage("ERROR: Failed to write ".$file.": ".$!);
+
+ print $resultshtml
+ qq|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>WebInject Test Results</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <style type="text/css">
+ body {
+ background-color: #F5F5F5;
+ color: #000000;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 10px;
+ }
+ table, td {
+ border: solid #ddd 1px;
+ }
+ .pass {
+ color: green;
+ }
+ .fail {
+ color: red;
+ }
+ </style>
+</head>
+<body>
+<table>
+<tr>
+<th>Test</th>
+<th>Description<br />Request URL</th>
+<th>Results</th>
+<th>Summary<br />Response Time</th>
+</tr>
+|;
+ for my $file (@{$self->{'result'}->{'files'}}) {
+ for my $case (@{$file->{'cases'}}) {
+ print $resultshtml qq|<tr><td>$file->{'name'}<br /><b>$case->{'id'} </b></td>\n|;
+ for my $message (@{$case->{'messages'}}) {
+ next unless defined $message->{'html'};
+ print $resultshtml $message->{'html'} . "\n";
+ }
+ print $resultshtml "</tr>\n";
+ }
+ }
+
+ print $resultshtml qq|
+</table>
+<b>
+Start Time: $self->{'config'}->{'currentdatetime'} <br />
+Total Run Time: $self->{'result'}->{'totalruntime'} seconds <br />
+<br />
+Test Cases Run: $self->{'result'}->{'totalruncount'} <br />
+Test Cases Passed: $self->{'result'}->{'totalcasespassedcount'} <br />
+Test Cases Failed: $self->{'result'}->{'totalcasesfailedcount'} <br />
+Verifications Passed: $self->{'result'}->{'totalpassedcount'} <br />
+Verifications Failed: $self->{'result'}->{'totalfailedcount'} <br />
+<br />
+Average Response Time: $self->{'result'}->{'avgresponse'} seconds <br />
+Max Response Time: $self->{'result'}->{'maxresponse'} seconds <br />
+Min Response Time: $self->{'result'}->{'minresponse'} seconds <br />
+</b>
+<br />
+
+</body>
+</html>
+|;
+ close($resultshtml);
+ return;
+}
+
+################################################################################
+# write summary and closing tags for XML results file
+sub _write_result_xml {
+ my $self = shift;
+
+ my $file = $self->{'config'}->{'output_dir'}."results.xml";
+ open( my $resultsxml, ">", $file )
+ or $self->_usage("ERROR: Failed to write ".$file.": ".$!);
+
+ print $resultsxml "<results>\n\n";
+
+ for my $file (@{$self->{'result'}->{'files'}}) {
+ print $resultsxml " <testcases file=\"".$file->{'name'}."\">\n\n";
+ for my $case (@{$file->{'cases'}}) {
+ print $resultsxml " <testcase id=\"".$case->{'id'}."\">\n";
+ for my $message (@{$case->{'messages'}}) {
+ next unless defined $message->{'key'};
+ print $resultsxml " <".$message->{'key'}.">".$message->{'value'}."</".$message->{'key'}.">\n";
+ }
+ print $resultsxml " </testcase>\n\n";
+ }
+ print $resultsxml " </testcases>\n";
+ }
+
+ print $resultsxml qq|
+ <test-summary>
+ <start-time>$self->{'config'}->{'currentdatetime'}</start-time>
+ <total-run-time>$self->{'result'}->{'totalruntime'}</total-run-time>
+ <test-cases-run>$self->{'result'}->{'totalruncount'}</test-cases-run>
+ <test-cases-passed>$self->{'result'}->{'totalcasespassedcount'}</test-cases-passed>
+ <test-cases-failed>$self->{'result'}->{'totalcasesfailedcount'}</test-cases-failed>
+ <verifications-passed>$self->{'result'}->{'totalpassedcount'}</verifications-passed>
+ <verifications-failed>$self->{'result'}->{'totalfailedcount'}</verifications-failed>
+ <average-response-time>$self->{'result'}->{'avgresponse'}</average-response-time>
+ <max-response-time>$self->{'result'}->{'maxresponse'}</max-response-time>
+ <min-response-time>$self->{'result'}->{'minresponse'}</min-response-time>
+ </test-summary>
+
+</results>
+|;
+ close($resultsxml);
+ return;
+}
+
+################################################################################
+# write summary and closing text for STDOUT
+sub _writefinalstdout {
+ my $self = shift;
+
+ if($self->{'config'}->{'reporttype'} !~ /^nagios/mx) {
+ $self->_out(qq|
+Start Time: $self->{'config'}->{'currentdatetime'}
+Total Run Time: $self->{'result'}->{'totalruntime'} seconds
+
+|);
+ }
+
+ $self->_out(qq|
+Test Cases Run: $self->{'result'}->{'totalruncount'}
+Test Cases Passed: $self->{'result'}->{'totalcasespassedcount'}
+Test Cases Failed: $self->{'result'}->{'totalcasesfailedcount'}
+Verifications Passed: $self->{'result'}->{'totalpassedcount'}
+Verifications Failed: $self->{'result'}->{'totalfailedcount'}
+
+|);
+ return;
+}
+
+################################################################################
+sub _http_defaults {
+ my $self = shift;
+ my $request = shift;
+ my $useragent = shift;
+ my $case = shift;
+
+ # add an additional HTTP Header if specified
+ if($case->{'addheader'}) {
+ # can add multiple headers with a pipe delimiter
+ for my $addheader (split /\|/mx, $case->{'addheader'}) {
+ $addheader =~ m~(.*):\ (.*)~mx;
+ $request->header( $1 => $2 ); # using HTTP::Headers Class
+ }
+ }
+
+ # print $self->{'request'}->as_string; print "\n\n";
+
+ my $starttimer = time();
+ my $response = $useragent->request($request);
+ my $endtimer = time();
+ my $latency = ( int( 1000 * ( $endtimer - $starttimer ) ) / 1000 ); # elapsed time rounded to thousandths
+ # print $response->as_string; print "\n\n";
+
+ return($latency,$request,$response);
+}
+
+################################################################################
+# send http request and read response
+sub _httpget {
+ my $self = shift;
+ my $useragent = shift;
+ my $case = shift;
+
+ $self->_out("GET Request: ".$case->{url}."\n");
+ my $request = new HTTP::Request( 'GET', $case->{url} );
+ return $self->_http_defaults($request, $useragent, $case);
+}
+
+################################################################################
+# post request based on specified encoding
+sub _httppost {
+ my $self = shift;
+ my $useragent = shift;
+ my $case = shift;
+
+ if($case->{posttype} ) {
+ if($case->{posttype} =~ m~application/x\-www\-form\-urlencoded~mx) {
+ return $self->_httppost_form_urlencoded($useragent, $case);
+ }
+ elsif($case->{posttype} =~ m~multipart/form\-data~mx) {
+ return $self->_httppost_form_data($useragent, $case);
+ }
+ elsif( ($case->{posttype} =~ m~text/xml~mx)
+ or ($case->{posttype} =~ m~application/soap\+xml~mx)
+ )
+ {
+ return $self->_httppost_xml($useragent, $case);
+ }
+ else {
+ $self->_usage('ERROR: Bad Form Encoding Type, I only accept "application/x-www-form-urlencoded", "multipart/form-data", "text/xml", "application/soap+xml"');
+ }
+ }
+ else {
+ # use "x-www-form-urlencoded" if no encoding is specified
+ $case->{posttype} = 'application/x-www-form-urlencoded';
+ return $self->_httppost_form_urlencoded($useragent, $case);
+ }
+ return;
+}
+
+################################################################################
+# send application/x-www-form-urlencoded HTTP request and read response
+sub _httppost_form_urlencoded {
+ my $self = shift;
+ my $useragent = shift;
+ my $case = shift;
+
+ $self->_out("POST Request: ".$case->{url}."\n");
+ my $request = new HTTP::Request('POST', $case->{url} );
+ $request->content_type($case->{posttype});
+ $request->content($case->{postbody});
+
+ return $self->_http_defaults($request,$useragent, $case);
+}
+
+################################################################################
+# send text/xml HTTP request and read response
+sub _httppost_xml {
+ my $self = shift;
+ my $useragent = shift;
+ my $case = shift;
+
+ my($latency,$request,$response);
+
+ # read the xml file specified in the testcase
+ $case->{postbody} =~ m~file=>(.*)~imx;
+ open( my $xmlbody, "<", $1 ) or $self->_usage("ERROR: Failed to open text/xml file ".$1.": ".$!); # open file handle
+ my @xmlbody = <$xmlbody>; # read the file into an array
+ close($xmlbody);
+
+ # Get the XML input file to use PARSEDRESULT and substitute the contents
+ my $content = $self->_convertbackxmlresult(join( " ", @xmlbody ));
+
+ $self->_out("POST Request: ".$case->{url}."\n");
+ $request = new HTTP::Request( 'POST', $case->{url} );
+ $request->content_type($case->{posttype});
+ $request->content( $content ); # load the contents of the file into the request body
+
+ ($latency,$request,$response) = $self->_http_defaults($request, $useragent, $case);
+
+ my $xmlparser = new XML::Parser;
+ # see if the XML parses properly
+ try {
+ $xmlparser->parse($response->decoded_content);
+
+ # print "good xml\n";
+ push @{$case->{'messages'}}, {'key' => 'verifyxml-success', 'value' => 'true', 'html' => '<span class="pass">Passed XML Parser (content is well-formed)</span>' };
+ $self->_out("Passed XML Parser (content is well-formed) \n");
+ $case->{'passedcount'}++;
+
+ # exit try block
+ return;
+ }
+ catch Error with {
+ # get the exception object
+ my $ex = shift;
+ # print "bad xml\n";
+ # we suppress most logging when running in a plugin mode
+ if($self->{'config'}->{'reporttype'} eq 'standard') {
+ push @{$case->{'messages'}}, {'key' => 'verifyxml-success', 'value' => 'false', 'html' => "<span class=\"fail\">Failed XML parser on response:</span> ".$ex };
+ }
+ $self->_out("Failed XML parser on response: $ex \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+ }; # <-- remember the semicolon
+
+ return($latency,$request,$response);
+}
+
+################################################################################
+# send multipart/form-data HTTP request and read response
+sub _httppost_form_data {
+ my $self = shift;
+ my $useragent = shift;
+ my $case = shift;
+ my %myContent_;
+ ## no critic
+ eval "\%myContent_ = $case->{postbody}";
+ ## use critic
+
+ $self->_out("POST Request: ".$case->{url}."\n");
+ my $request = POST($case->{url},
+ Content_Type => $case->{posttype},
+ Content => \%myContent_);
+
+ return $self->_http_defaults($request, $useragent, $case);
+}
+
+################################################################################
+# do verification of http response and print status to HTML/XML/STDOUT/UI
+sub _verify {
+ my $self = shift;
+ my $response = shift;
+ my $case = shift;
+
+ confess("no response") unless defined $response;
+ confess("no case") unless defined $case;
+
+ if( $case->{verifyresponsecode} ) {
+ $self->_out(qq|Verify Response Code: "$case->{verifyresponsecode}" \n|);
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode', 'value' => $case->{verifyresponsecode} };
+
+ # verify returned HTTP response code matches verifyresponsecode set in test case
+ if ( $case->{verifyresponsecode} == $response->code() ) {
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-success', 'value' => 'true', 'html' => '<span class="pass">Passed HTTP Response Code:</span> '.$case->{verifyresponsecode} };
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-messages', 'value' => 'Passed HTTP Response Code Verification' };
+ $self->_out(qq|Passed HTTP Response Code Verification \n|);
+ $case->{'passedcount'}++;
+ }
+ else {
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-success', 'value' => 'false', 'html' => '<span class="fail">Failed HTTP Response Code:</span> received '.$response->code().', expecting '.$case->{verifyresponsecode} };
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-messages', 'value' => 'Failed HTTP Response Code Verification (received '.$response->code().', expecting '.$case->{verifyresponsecode}.')' };
+ $self->_out(qq|Failed HTTP Response Code Verification (received |.$response->code().qq|, expecting $case->{verifyresponsecode}) \n|);
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed HTTP Response Code Verification (received '.$response->code().', expecting '.$case->{verifyresponsecode}.')';
+ return;
+ }
+ }
+ }
+ else {
+ # verify http response code is in the 100-399 range
+ if($response->as_string() =~ /HTTP\/1.(0|1)\ (1|2|3)/imx ) { # verify existance of string in response
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-success', 'value' => 'true', 'html' => '<span class="pass">Passed HTTP Response Code Verification (not in error range)</span>' };
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-messages', 'value' => 'Passed HTTP Response Code Verification (not in error range)' };
+ $self->_out(qq|Passed HTTP Response Code Verification (not in error range) \n|);
+
+ # succesful response codes: 100-399
+ $case->{'passedcount'}++;
+ }
+ else {
+ $response->as_string() =~ /(HTTP\/1.)(.*)/mxi;
+ if($1) { #this is true if an HTTP response returned
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-success', 'value' => 'false', 'html' => '<span class="fail">Failed HTTP Response Code Verification ('.$1.$2.')</span>' };
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-messages', 'value' => 'Failed HTTP Response Code Verification ('.$1.$2.')' };
+ $self->_out("Failed HTTP Response Code Verification ($1$2) \n"); #($1$2) is HTTP response code
+
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed HTTP Response Code Verification ('.$1.$2.')';
+ return;
+ }
+ }
+ #no HTTP response returned.. could be error in connection, bad hostname/address, or can not connect to web server
+ else
+ {
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-success', 'value' => 'false', 'html' => '<span class="fail">Failed - No Response</span>' };
+ push @{$case->{'messages'}}, {'key' => 'verifyresponsecode-messages', 'value' => 'Failed - No Response' };
+ $self->_out("Failed - No valid HTTP response:\n".$response->as_string());
+
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed - No valid HTTP response: '.$response->as_string();
+ return;
+ }
+ }
+ }
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+
+ for my $nr ('', 1..1000) {
+ my $key = "verifypositive".$nr;
+ if( $case->{$key} ) {
+ $self->_out("Verify: '".$case->{$key}."' \n");
+ push @{$case->{'messages'}}, {'key' => $key, 'value' => $case->{$key} };
+ my $regex = $self->_fix_regex($case->{$key});
+ # verify existence of string in response
+ if( $response->as_string() =~ m~$regex~simx ) {
+ push @{$case->{'messages'}}, {'key' => $key.'-success', 'value' => 'true', 'html' => "<span class=\"pass\">Passed:</span> ".$case->{$key} };
+ $self->_out("Passed Positive Verification \n");
+ $case->{'passedcount'}++;
+ }
+ else {
+ push @{$case->{'messages'}}, {'key' => $key.'-success', 'value' => 'false', 'html' => "<span class=\"fail\">Failed:</span> ".$case->{$key} };
+ $self->_out("Failed Positive Verification \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed Positive Verification, can not find a string matching regex: '.$regex;
+ return;
+ }
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+ }
+ elsif($nr ne '' and $nr > 5) {
+ last;
+ }
+ }
+
+ for my $nr ('', 1..1000) {
+ my $key = "verifynegative".$nr;
+ if( $case->{$key} ) {
+ $self->_out("Verify Negative: '".$case->{$key}."' \n");
+ push @{$case->{'messages'}}, {'key' => $key, 'value' => $case->{$key} };
+ my $regex = $self->_fix_regex($case->{$key});
+ # verify existence of string in response
+ if( $response->as_string() =~ m~$regex~simx ) {
+ push @{$case->{'messages'}}, {'key' => $key.'-success', 'value' => 'false', 'html' => '<span class="fail">Failed Negative:</span> '.$case->{$key} };
+ $self->_out("Failed Negative Verification \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed Negative Verification, found regex matched string: '.$regex;
+ return;
+ }
+ }
+ else {
+ push @{$case->{'messages'}}, {'key' => $key.'-success', 'value' => 'true', 'html' => '<span class="pass">Passed Negative:</span> '.$case->{$key} };
+ $self->_out("Passed Negative Verification \n");
+ $case->{'passedcount'}++;
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+ }
+ elsif($nr ne '' and $nr > 5) {
+ last;
+ }
+ }
+
+ if($self->{'verifylater'}) {
+ my $regex = $self->_fix_regex($self->{'verifylater'});
+ # verify existence of string in response
+ if($response->as_string() =~ m~$regex~simx ) {
+ push @{$case->{'messages'}}, {'key' => 'verifypositivenext-success', 'value' => 'true', 'html' => '<span class="pass">Passed Positive Verification (verification set in previous test case)</span>' };
+ $self->_out("Passed Positive Verification (verification set in previous test case) \n");
+ $case->{'passedcount'}++;
+ }
+ else {
+ push @{$case->{'messages'}}, {'key' => 'verifypositivenext-success', 'value' => 'false', 'html' => '<span class="fail">Failed Positive Verification (verification set in previous test case)</span>' };
+ $self->_out("Failed Positive Verification (verification set in previous test case) \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed Positive Verification (verification set in previous test case), can not find a string matching regex: '.$regex;
+ return;
+ }
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+ # set to null after verification
+ delete $self->{'verifylater'};
+ }
+
+ if($self->{'verifylaterneg'}) {
+ my $regex = $self->_fix_regex($self->{'verifylaterneg'});
+ # verify existence of string in response
+ if($response->as_string() =~ m~$regex~simx) {
+ push @{$case->{'messages'}}, {'key' => 'verifynegativenext-success', 'value' => 'false', 'html' => '<span class="fail">Failed Negative Verification (negative verification set in previous test case)</span>' };
+ $self->_out("Failed Negative Verification (negative verification set in previous test case) \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+
+ if($self->{'config'}->{'break_on_errors'}) {
+ $self->{'result'}->{'returnmessage'} = 'Failed Negative Verification (negative verification set in previous test case), found regex matched string: '.$regex;
+ return;
+ }
+ }
+ else {
+ push @{$case->{'messages'}}, {'key' => 'verifynegativenext-success', 'value' => 'true', 'html' => '<span class="pass">Passed Negative Verification (negative verification set in previous test case)</span>' };
+ $self->_out("Passed Negative Verification (negative verification set in previous test case) \n");
+ $case->{'passedcount'}++;
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+ # set to null after verification
+ delete $self->{'verifylaterneg'};
+ }
+
+ if($case->{'warning'}) {
+ $self->_out("Verify Warning Threshold: ".$case->{'warning'}."\n");
+ push @{$case->{'messages'}}, {'key' => "Warning Threshold", 'value' => $case->{''} };
+ if($case->{'latency'} > $case->{'warning'}) {
+ push @{$case->{'messages'}}, {'key' => 'warning-success', 'value' => 'false', 'html' => "<span class=\"fail\">Failed Warning Threshold:</span> ".$case->{'warning'} };
+ $self->_out("Failed Warning Threshold \n");
+ $case->{'failedcount'}++;
+ $case->{'iswarning'} = 1;
+ }
+ else {
+ $self->_out("Passed Warning Threshold \n");
+ push @{$case->{'messages'}}, {'key' => 'warning-success', 'value' => 'true', 'html' => "<span class=\"pass\">Passed Warning Threshold:</span> ".$case->{'warning'} };
+ $case->{'passedcount'}++;
+ }
+ push @{$case->{'messages'}}, { 'html' => '<br />' };
+ }
+
+ if($case->{'critical'}) {
+ $self->_out("Verify Critical Threshold: ".$case->{'critical'}."\n");
+ push @{$case->{'messages'}}, {'key' => "Critical Threshold", 'value' => $case->{''} };
+ if($case->{'latency'} > $case->{'critical'}) {
+ push @{$case->{'messages'}}, {'key' => 'critical-success', 'value' => 'false', 'html' => "<span class=\"fail\">Failed Critical Threshold:</span> ".$case->{'critical'} };
+ $self->_out("Failed Critical Threshold \n");
+ $case->{'failedcount'}++;
+ $case->{'iscritical'} = 1;
+ }
+ else {
+ $self->_out("Passed Critical Threshold \n");
+ push @{$case->{'messages'}}, {'key' => 'critical-success', 'value' => 'true', 'html' => "<span class=\"pass\">Passed Critical Threshold:</span> ".$case->{'critical'} };
+ $case->{'passedcount'}++;
+ }
+ }
+
+ return;
+}
+
+################################################################################
+# parse values from responses for use in future request (for session id's, dynamic URL rewriting, etc)
+sub _parseresponse {
+ my $self = shift;
+ my $response = shift;
+ my $case = shift;
+
+ my ( $resptoparse, @parseargs );
+ my ( $leftboundary, $rightboundary, $escape );
+
+ for my $type ( qw/parseresponse parseresponse1 parseresponse2 parseresponse3 parseresponse4 parseresponse5/ ) {
+
+ next unless $case->{$type};
+
+ @parseargs = split( /\|/mx, $case->{$type} );
+
+ $leftboundary = $parseargs[0];
+ $rightboundary = $parseargs[1];
+ $escape = $parseargs[2];
+
+ $resptoparse = $response->as_string;
+ ## no critic
+ if ( $resptoparse =~ m~$leftboundary(.*?)$rightboundary~s ) {
+ $self->{'parsedresult'}->{$type} = $1;
+ }
+ ## use critic
+ elsif(!defined $case->{'parsewarning'} or $case->{'parsewarning'}) {
+ push @{$case->{'messages'}}, {'key' => $type.'-success', 'value' => 'false', 'html' => "<span class=\"fail\">Failed Parseresult, cannot find</span> $leftboundary(.*?)$rightboundary" };
+ $self->_out("Failed Parseresult, cannot find $leftboundary(*)$rightboundary\n");
+ $case->{'iswarning'} = 1;
+ }
+
+ if ($escape) {
+ if ( $escape eq 'escape' ) {
+ $self->{'parsedresult'}->{$type} =
+ $self->_url_escape( $self->{'parsedresult'}->{$type} );
+ }
+ }
+
+ #print "\n\nParsed String: $self->{'parsedresult'}->{$type}\n\n";
+ }
+ return;
+}
+
+################################################################################
+# read config.xml
+sub _read_config_xml {
+ my $self = shift;
+ my $config_file = shift;
+
+ my($config, $comment_mode,@configlines);
+
+ # process the config file
+ # if -c option was set on command line, use specified config file
+ if(defined $config_file) {
+ open( $config, '<', $config_file )
+ or $self->_usage("ERROR: Failed to open ".$config_file." file: ".$!);
+ $self->{'config'}->{'exists'} = 1; # flag we are going to use a config file
+ }
+ # if config.xml exists, read it
+ elsif( -e "config.xml" ) {
+ open( $config, '<', "config.xml" )
+ or $self->_usage("ERROR: Failed to open config.xml file: ".$!);
+ $self->{'config'}->{'exists'} = 1; # flag we are going to use a config file
+ }
+
+ if( $self->{'config'}->{'exists'} ) { #if we have a config file, use it
+
+ my @precomment = <$config>; #read the config file into an array
+
+ #remove any commented blocks from config file
+ foreach (@precomment) {
+ unless (m~<comment>.*</comment>~mx) { # single line comment
+ # multi-line comments
+ if (/<comment>/mx) {
+ $comment_mode = 1;
+ }
+ elsif (m~</comment>~mx) {
+ $comment_mode = 0;
+ }
+ elsif ( !$comment_mode ) {
+ push( @configlines, $_ );
+ }
+ }
+ }
+ close($config);
+ }
+
+ #grab values for constants in config file:
+ foreach (@configlines) {
+
+ for my $key (
+ qw/baseurl baseurl1 baseurl2 gnuplot proxy timeout output_dir
+ globaltimeout globalhttplog standaloneplot max_redirect
+ break_on_errors useragent/
+ )
+ {
+
+ if (/<$key>/mx) {
+ $_ =~ m~<$key>(.*)</$key>~mx;
+ $self->{'config'}->{$key} = $1;
+
+ #print "\n$_ : $self->{'config'}->{$_} \n\n";
+ }
+ }
+
+ if (/<reporttype>/mx) {
+ $_ =~ m~<reporttype>(.*)</reporttype>~mx;
+ if ( $1 ne "standard" ) {
+ $self->{'config'}->{'reporttype'} = $1;
+ $self->{'config'}->{'nooutput'} = "set";
+ }
+
+ #print "\nreporttype : $self->{'config'}->{'reporttype'} \n\n";
+ }
+
+ if (/<httpauth>/mx) {
+
+ $_ =~ m~<httpauth>(.*)</httpauth>~mx;
+ $self->_set_http_auth($1);
+
+ #print "\nhttpauth : @{$self->{'config'}->{'httpauth'}} \n\n";
+ }
+
+ if(/<testcasefile>/mx) {
+ my $firstparse = $'; #print "$' \n\n";
+ $firstparse =~ m~</testcasefile>~mx;
+ my $filename = $`; #string between tags will be in $filename
+ #print "\n$filename \n\n";
+ push @{ $self->{'casefilelist'} }, $filename; #add next filename we grab to end of array
+ }
+ }
+
+ return;
+}
+
+################################################################################
+# parse and set http auth config
+sub _set_http_auth {
+ my $self = shift;
+ my $confstring = shift;
+
+ #each time we see an <httpauth>, we set @authentry to be the
+ #array of values, then we use [] to get a reference to that array
+ #and push that reference onto @httpauth.
+
+ my @authentry = split( /:/mx, $confstring );
+ if( scalar @authentry != 5 ) {
+ $self->_usage("ERROR: httpauth should have 5 fields delimited by colons, got: ".$confstring);
+ }
+ else {
+ push( @{ $self->{'config'}->{'httpauth'} }, [@authentry] );
+ }
+ # basic authentication only works with redirects enabled
+ if($self->{'config'}->{'max_redirect'} == 0) {
+ $self->{'config'}->{'max_redirect'}++;
+ }
+
+ return;
+}
+
+################################################################################
+# get test case files to run (from command line or config file) and evaluate constants
+sub _processcasefile {
+ # parse config file and grab values it sets
+ my $self = shift;
+
+ if( ( $#ARGV + 1 ) < 1 ) { #no command line args were passed
+ unless( $self->{'casefilelist'}->[0] ) {
+ if ( -e "testcases.xml" ) {
+ # if no files are specified in config.xml, default to testcases.xml
+ push @{ $self->{'casefilelist'} }, "testcases.xml";
+ }
+ else {
+ $self->_usage("ERROR: I can't find any test case files to run.\nYou must either use a config file or pass a filename "
+ . "on the command line if you are not using the default testcase file (testcases.xml).");
+ }
+ }
+ }
+
+ elsif( ( $#ARGV + 1 ) == 1 ) { # one command line arg was passed
+ # use testcase filename passed on command line (config.xml is only used for other options)
+ push @{ $self->{'casefilelist'} }, $ARGV[0]; # first commandline argument is the test case file, put this on the array for processing
+ }
+
+ elsif( ( $#ARGV + 1 ) == 2 ) { # two command line args were passed
+ my $xpath = $ARGV[1];
+ if ( $xpath =~ /\/(.*)\[/mx ) { # if the argument contains a "/" and "[", it is really an XPath
+ $xpath =~ /(.*)\/(.*)\[(.*?)\]/mx; #if it contains XPath info, just grab the file name
+ $self->{'xnode'} = $3; # grab the XPath Node value.. (from inside the "[]")
+ # print "\nXPath Node is: $self->{'xnode'} \n";
+ }
+ else {
+ $self->_usage("ERROR: Sorry, $xpath is not in the XPath format I was expecting, I'm ignoring it...");
+ }
+
+ # use testcase filename passed on command line (config.xml is only used for other options)
+ push @{ $self->{'casefilelist'} }, $ARGV[0]; # first command line argument is the test case file, put this on the array for processing
+ }
+
+ elsif ( ( $#ARGV + 1 ) > 2 ) { #too many command line args were passed
+ $self->_usage("ERROR: Too many arguments.");
+ }
+
+ #print "\ntestcase file list: @{$self->{'casefilelist'}}\n\n";
+
+ return;
+}
+
+################################################################################
+# here we do some pre-processing of the test case file and write it out to a temp file.
+# we convert certain chars so xml parser doesn't puke.
+sub _convtestcases {
+ my $self = shift;
+ my $currentcasefile = shift;
+
+ my @xmltoconvert;
+
+ my ( $fh, $tempfilename ) = tempfile();
+ my $filename = $currentcasefile;
+ open( my $xmltoconvert, '<', $filename )
+ or $self->_usage("ERROR: Failed to read test case file: ".$filename.": ".$!);
+ # read the file into an array
+ @xmltoconvert = <$xmltoconvert>;
+ my $ids = {};
+ for my $line (@xmltoconvert) {
+
+ # convert escaped chars and certain reserved chars to temporary values that the parser can handle
+ # these are converted back later in processing
+ $line =~ s/&/{AMPERSAND}/gmx;
+ $line =~ s/\\</{LESSTHAN}/gmx;
+
+ # convert variables to lowercase
+ $line =~ s/(\$\{[\w\.]+\})/\L$1\E/gmx;
+ $line =~ s/(varname=('|").*?('|"))/\L$1\E/gmx;
+
+ # count cases while we are here
+ if ( $line =~ /<case/mx ) {
+ $self->{'result'}->{'casecount'}++;
+ }
+
+ # verify id is only use once per file
+ if ( $line =~ /^\s*id\s*=\s*\"*(\d+)\"*/mx ) {
+ if(defined $ids->{$1}) {
+ $self->{'result'}->{'iswarning'} = 1;
+ $self->_out("Warning: case id $1 is used more than once!\n");
+ }
+ $ids->{$1} = 1;
+ }
+ }
+
+ close($xmltoconvert);
+
+ # open file handle to temp file
+ open( $xmltoconvert, '>', $tempfilename )
+ or $self->_usage("ERROR: Failed to write ".$tempfilename.": ".$!);
+ print $xmltoconvert @xmltoconvert; # overwrite file with converted array
+ close($xmltoconvert);
+ return $tempfilename;
+}
+
+################################################################################
+# converts replaced xml with substitutions
+sub _convertbackxml {
+ my ( $self, $string, $timestamp ) = @_;
+ return unless defined $string;
+ $string =~ s~{AMPERSAND}~&~gmx;
+ $string =~ s~{LESSTHAN}~<~gmx;
+ $string =~ s~{TIMESTAMP}~$timestamp~gmx;
+ $string =~ s~{BASEURL}~$self->{'config'}->{baseurl}~gmx;
+ $string =~ s~{BASEURL1}~$self->{'config'}->{baseurl1}~gmx;
+ $string =~ s~{BASEURL2}~$self->{'config'}->{baseurl2}~gmx;
+ return $string;
+}
+
+################################################################################
+# converts replaced xml with parsed result
+sub _convertbackxmlresult {
+ my ( $self, $string) = @_;
+ return unless defined $string;
+ $string =~ s~\{PARSEDRESULT\}~$self->{'parsedresult'}->{'parseresponse'}~gmx if defined $self->{'parsedresult'}->{'parseresponse'};
+ for my $x (1..5) {
+ $string =~ s~\{PARSEDRESULT$x\}~$self->{'parsedresult'}->{"parseresponse$x"}~gmx if defined $self->{'parsedresult'}->{"parseresponse$x"};
+ }
+ return $string;
+}
+
+################################################################################
+# escapes difficult characters with %hexvalue
+sub _url_escape {
+ my ( $self, @values ) = @_;
+
+ # LWP handles url encoding already, but use this to escape valid chars that LWP won't convert (like +)
+ my @return;
+ for my $val (@values) {
+ $val =~ s/[^-\w.,!~'()\/\ ]/uc sprintf "%%%02x", ord $&/egmx;
+ push @return, $val;
+ }
+ return wantarray ? @return : $return[0];
+}
+
+################################################################################
+# write requests and responses to http.log file
+sub _httplog {
+ my $self = shift;
+ my $request = shift;
+ my $response = shift;
+ my $case = shift;
+ my $output = '';
+
+ # http request - log setting per test case
+ if($case->{'logrequest'} && $case->{'logrequest'} =~ /yes/mxi ) {
+ $output .= $request->as_string."\n\n";
+ }
+
+ # http response - log setting per test case
+ if($case->{'logresponse'} && $case->{'logresponse'} =~ /yes/mxi ) {
+ $output .= $response->as_string."\n\n";
+ }
+
+ # global http log setting
+ if($self->{'config'}->{'globalhttplog'} && $self->{'config'}->{'globalhttplog'} =~ /yes/mxi ) {
+ $output .= $request->as_string."\n\n";
+ $output .= $response->as_string."\n\n";
+ }
+
+ # global http log setting - onfail mode
+ if($self->{'config'}->{'globalhttplog'} && $self->{'config'}->{'globalhttplog'} =~ /onfail/mxi && $case->{'iscritical'}) {
+ $output .= $request->as_string."\n\n";
+ $output .= $response->as_string."\n\n";
+ }
+
+ if($output ne '') {
+ my $file = $self->{'config'}->{'output_dir'}."http.log";
+ open( my $httplogfile, ">>", $file )
+ or $self->_usage("ERROR: Failed to write ".$file.": ".$!);
+ print $httplogfile $output;
+ print $httplogfile "\n************************* LOG SEPARATOR *************************\n\n\n";
+ close($httplogfile);
+ }
+
+ return;
+}
+
+################################################################################
+# write performance results to plot.log in the format gnuplot can use
+sub _plotlog {
+ my ( $self, $value ) = @_;
+
+ my ( %months, $date, $time, $mon, $mday, $hours, $min, $sec, $year );
+
+ # do this unless: monitor is disabled in gui, or running standalone mode without config setting to turn on plotting
+ if( ( $self->{'gui'} and $self->{'monitorenabledchkbx'} ne 'monitor_off')
+ or (!$self->{'gui'} and $self->{'config'}->{'standaloneplot'} eq 'on')
+ ) {
+
+ %months = (
+ "Jan" => 1,
+ "Feb" => 2,
+ "Mar" => 3,
+ "Apr" => 4,
+ "May" => 5,
+ "Jun" => 6,
+ "Jul" => 7,
+ "Aug" => 8,
+ "Sep" => 9,
+ "Oct" => 10,
+ "Nov" => 11,
+ "Dec" => 12
+ );
+
+ $date = scalar localtime;
+ ($mon, $mday, $hours, $min, $sec, $year) = $date =~ /\w+\ (\w+)\ +(\d+)\ (\d\d):(\d\d):(\d\d)\ (\d\d\d\d)/mx;
+ $time = "$months{$mon} $mday $hours $min $sec $year";
+
+ my $plotlog;
+ # used to clear the graph when requested
+ if( $self->{'switches'}->{'plotclear'} eq 'yes' ) {
+ # open in clobber mode so log gets truncated
+ my $file = $self->{'config'}->{'output_dir'}."plot.log";
+ open( $plotlog, '>', $file )
+ or $self->_usage("ERROR: Failed to write ".$file.": ".$!);
+ $self->{'switches'}->{'plotclear'} = 'no'; # reset the value
+ }
+ else {
+ my $file = $self->{'config'}->{'output_dir'}."plot.log";
+ open( $plotlog, '>>', $file )
+ or $self->_usage("ERROR: Failed to write ".$file.": ".$!); #open in append mode
+ }
+
+ printf $plotlog "%s %2.4f\n", $time, $value;
+ close($plotlog);
+ }
+ return;
+}
+
+################################################################################
+# create gnuplot config file
+sub _plotcfg {
+ my $self = shift;
+
+ # do this unless: monitor is disabled in gui, or running standalone mode without config setting to turn on plotting
+ if( ( $self->{'gui'} and $self->{'monitorenabledchkbx'} ne 'monitor_off')
+ or (!$self->{'gui'} and $self->{'config'}->{'standaloneplot'} eq 'on')
+ ) {
+ my $file = $self->{'config'}->{'output_dir'}."plot.plt";
+ open( my $gnuplotplt, ">", $file )
+ or _usage("ERROR: Could not open ".$file.": ".$!);
+ print $gnuplotplt qq|
+set term png
+set output \"$self->{'config'}->{'output_dir'}plot.png\"
+set size 1.1,0.5
+set pointsize .5
+set xdata time
+set ylabel \"Response Time (seconds)\"
+set yrange [0:]
+set bmargin 2
+set tmargin 2
+set timefmt \"%m %d %H %M %S %Y\"
+plot \"$self->{'config'}->{'output_dir'}plot.log\" using 1:7 title \"Response Times" w $self->{'config'}->{'graphtype'}
+|;
+ close($gnuplotplt);
+
+ }
+ return;
+}
+
+################################################################################
+# do ending tasks
+sub _finaltasks {
+ my $self = shift;
+
+ if ( $self->{'gui'} ) { $self->_gui_stop(); }
+
+ # we suppress most logging when running in a plugin mode
+ if($self->{'config'}->{'reporttype'} eq 'standard') {
+ # write summary and closing tags for results file
+ $self->_write_result_html();
+
+ #write summary and closing tags for XML results file
+ $self->_write_result_xml();
+ }
+
+ # write summary and closing tags for STDOUT
+ $self->_writefinalstdout();
+
+ #plugin modes
+ if($self->{'config'}->{'reporttype'} ne 'standard') {
+ # return value is set which corresponds to a monitoring program
+ # Nagios plugin compatibility
+ if($self->{'config'}->{'reporttype'} =~ /^nagios/mx) {
+ # nagios perf data has following format
+ # 'label'=value[UOM];[warn];[crit];[min];[max]
+ my $crit = 0;
+ if(defined $self->{'config'}->{globaltimeout}) {
+ $crit = $self->{'config'}->{globaltimeout};
+ }
+ my $lastid = 0;
+ my $perfdata = '|time='.$self->{'result'}->{'totalruntime'}.'s;0;'.$crit.';0;0';
+ for my $file (@{$self->{'result'}->{'files'}}) {
+ for my $case (@{$file->{'cases'}}) {
+ my $warn = $case->{'warning'} || 0;
+ my $crit = $case->{'critical'} || 0;
+ my $label = $case->{'label'} || 'case'.$case->{'id'};
+ $perfdata .= ' '.$label.'='.$case->{'latency'}.'s;'.$warn.';'.$crit.';0;0';
+ $lastid = $case->{'id'};
+ }
+ }
+ # report performance data for missed cases too
+ for my $nr (1..($self->{'result'}->{'casecount'} - $self->{'result'}->{'totalruncount'})) {
+ $lastid++;
+ my $label = 'case'.$lastid;
+ $perfdata .= ' '.$label.'=0s;0;0;0;0';
+ }
+
+ my($rc,$message);
+ if($self->{'result'}->{'iscritical'}) {
+ $message = "WebInject CRITICAL - ".$self->{'result'}->{'returnmessage'};
+ $rc = $self->{'exit_codes'}->{'CRITICAL'};
+ }
+ elsif($self->{'result'}->{'iswarning'}) {
+ $message = "WebInject WARNING - ".$self->{'result'}->{'returnmessage'};
+ $rc = $self->{'exit_codes'}->{'WARNING'};
+ }
+ elsif( $self->{'config'}->{globaltimeout} && $self->{'result'}->{'totalruntime'} > $self->{'config'}->{globaltimeout} ) {
+ $message = "WebInject WARNING - All tests passed successfully but global timeout (".$self->{'config'}->{globaltimeout}." seconds) has been reached";
+ $rc = $self->{'exit_codes'}->{'WARNING'};
+ }
+ else {
+ $message = "WebInject OK - All tests passed successfully in ".$self->{'result'}->{'totalruntime'}." seconds";
+ $rc = $self->{'exit_codes'}->{'OK'};
+ }
+
+ if($self->{'result'}->{'iscritical'} or $self->{'result'}->{'iswarning'}) {
+ $message .= "\n".$self->{'out'};
+ $message =~ s/^\-+$//mx;
+ }
+ if($self->{'config'}->{'reporttype'} eq 'nagios2') {
+ $message =~ s/\n/<br>/mxg;
+ }
+ print $message.$perfdata."\n";
+
+ $self->{'result'}->{'perfdata'} = $perfdata;
+ return $rc;
+ }
+
+ #MRTG plugin compatibility
+ elsif( $self->{'config'}->{'reporttype'} eq 'mrtg' )
+ { #report results in MRTG format
+ if( $self->{'result'}->{'totalcasesfailedcount'} > 0 ) {
+ print "$self->{'result'}->{'totalruntime'}\n$self->{'result'}->{'totalruntime'}\n\nWebInject CRITICAL - $self->{'result'}->{'returnmessage'} \n";
+ }
+ else {
+ print "$self->{'result'}->{'totalruntime'}\n$self->{'result'}->{'totalruntime'}\n\nWebInject OK - All tests passed successfully in $self->{'result'}->{'totalruntime'} seconds \n";
+ }
+ }
+
+ #External plugin. To use it, add something like that in the config file:
+ # <reporttype>external:/home/webinject/Plugin.pm</reporttype>
+ elsif ( $self->{'config'}->{'reporttype'} =~ /^external:(.*)/mx ) {
+ our $webinject = $self; # set scope of $self to global, so it can be access in the external module
+ unless( my $return = do $1 ) {
+ croak "couldn't parse $1: $@\n" if $@;
+ croak "couldn't do $1: $!\n" unless defined $return;
+ croak "couldn't run $1\n" unless $return;
+ }
+ }
+
+ else {
+ $self->_usage("ERROR: only 'nagios', 'nagios2', 'mrtg', 'external', or 'standard' are supported reporttype values");
+ }
+
+ }
+
+ return 1 if $self->{'result'}->{'totalcasesfailedcount'} > 0;
+ return 0;
+}
+
+################################################################################
+# delete any files leftover from previous run if they exist
+sub _whackoldfiles {
+ my $self = shift;
+
+ for my $file (qw/plot.log plot.plt plot.png/) {
+ unlink $self->{'config'}->{'output_dir'}.$file if -e $self->{'config'}->{'output_dir'}.$file;
+ }
+
+ # verify files are deleted, if not give the filesystem time to delete them before continuing
+ while (-e $self->{'config'}->{'output_dir'}."plot.log"
+ or -e $self->{'config'}->{'output_dir'}."plot.plt"
+ or -e $self->{'config'}->{'output_dir'}."plot.png"
+ ) {
+ sleep .5;
+ }
+ return;
+}
+
+################################################################################
+# call the external plotter to create a graph (if we are in the appropriate mode)
+sub _plotit {
+ my $self = shift;
+
+ # do this unless: monitor is disabled in gui, or running standalone mode without config setting to turn on plotting
+ if( ( $self->{'gui'} and $self->{'monitorenabledchkbx'} ne 'monitor_off')
+ or (!$self->{'gui'} and $self->{'config'}->{'standaloneplot'} eq 'on')
+ ) {
+ # do this unless its being called from the gui with No Graph set
+ unless ( $self->{'config'}->{'graphtype'} eq 'nograph' )
+ {
+ my $gnuplot;
+ if(defined $self->{'config'}->{gnuplot}) {
+ $gnuplot = $self->{'config'}->{gnuplot}
+ }
+ elsif($^O eq 'MSWin32') {
+ $gnuplot = "./wgnupl32.exe";
+ } else {
+ $gnuplot = "/usr/bin/gnuplot";
+ }
+
+ # if gnuplot exists
+ if( -e $gnuplot ) {
+ system $gnuplot, $self->{'config'}->{output_dir}."plot.plt"; # plot it
+ }
+ elsif( $self->{'gui'} ) {
+ # if gnuplot not specified, notify on gui
+ $self->_gui_no_plotter_found();
+ }
+ }
+ }
+ return;
+}
+
+################################################################################
+# fix a user supplied regex to make it compliant with mx options
+sub _fix_regex {
+ my $self = shift;
+ my $regex = shift;
+
+ $regex =~ s/\\\ / /mx;
+ $regex =~ s/\ /\\ /gmx;
+
+ return $regex;
+}
+
+################################################################################
+# command line options
+sub _getoptions {
+ my $self = shift;
+
+ my( @sets, $opt_version, $opt_help, $opt_configfile );
+ Getopt::Long::Configure('bundling');
+ my $opt_rc = GetOptions(
+ 'h|help' => \$opt_help,
+ 'v|V|version' => \$opt_version,
+ 'c|config=s' => \$opt_configfile,
+ 'o|output=s' => \$self->{'config'}->{'output_dir'},
+ 'n|no-output' => \$self->{'config'}->{'nooutput'},
+ 'r|report-type=s' => \$self->{'config'}->{'reporttype'},
+ 't|timeout=i' => \$self->{'config'}->{'timeout'},
+ 's=s' => \@sets,
+ );
+ if(!$opt_rc or $opt_help) {
+ $self->_usage();
+ }
+ if($opt_version) {
+ print "WebInject version $Webinject::VERSION\nFor more info: http://www.webinject.org\n";
+ exit 3;
+ }
+ $self->_read_config_xml($opt_configfile);
+ for my $set (@sets) {
+ my ( $key, $val ) = split /=/mx, $set, 2;
+ if($key eq 'httpauth') {
+ $self->_set_http_auth($val);
+ } else {
+ $self->{'config'}->{ lc $key } = $val;
+ }
+ }
+ return;
+}
+
+################################################################################
+# _out - print text to STDOUT and save it for later retrieval
+sub _out {
+ my $self = shift;
+ my $text = shift;
+ if($self->{'config'}->{'reporttype'} !~ /^nagios/mx and !$self->{'config'}->{'nooutput'}) {
+ print $text;
+ }
+ $self->{'out'} .= $text;
+ return;
+}
+
+################################################################################
+# print usage
+sub _usage {
+ my $self = shift;
+ my $text = shift;
+
+ print $text."\n\n" if defined $text;
+
+ print <<EOB;
+ Usage:
+ $0
+ [-c|--config config_file]
+ [-o|--output output_location]
+ [-n|--no-output]
+ [-t|--timeout]
+ [-r|--report-type]
+ [-s key=value]
+ [testcase_file [XPath]]
+ $0 --version|-v
+EOB
+ exit 3;
+}
+
+################################################################################
+# make sure we don't keep the cookie temp file
+END {
+ our $cookietempfilename;
+ unlink($cookietempfilename) if $cookietempfilename;
+}
+
+=head1 EXAMPLES
+
+=head2 example test case
+
+ <testcases>
+ <case
+ id = "1"
+ description1 = "Sample Test Case"
+ method = "get"
+ url = "{BASEURL}/test.jsp"
+ verifypositive = "All tests succeded"
+ warning = "5"
+ critical = "15"
+ label = "testpage"
+ errormessage = "got error: {PARSERESPONSE}"
+ />
+ </testcases>
+
+detailed description about the syntax of testcases can be found on the Webinject homepage.
+
+
+=head1 SEE ALSO
+
+For more information about webinject visit http://www.webinject.org
+
+=head1 AUTHOR
+
+Corey Goldberg, E<lt>corey@goldb.orgE<gt>
+
+Sven Nierlein, E<lt>nierlein@cpan.orgE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2010 by Sven Nierlein
+
+Copyright (C) 2004-2006 by Corey Goldberg
+
+This library is free software; you can redistribute it under the GPL2 license.
+
+=cut
+
+1;
+#!/usr/bin/env perl
+
+# Copyright 2010 Sven Nierlein (nierlein@cpan.org)
+# Copyright 2004-2006 Corey Goldberg (corey@goldb.org)
+#
+# This file is part of WebInject.
+#
+# WebInject is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# WebInject is distributed in the hope that it will be useful,
+# but without any warranty; without even the implied warranty of
+# merchantability or fitness for a particular purpose. See the
+# GNU General Public License for more details.
+
+
+use warnings;
+use strict;
+
+my $webinject = Webinject->new(reporttype => "nagios", timeout => 30, break_on_errors => 1);
+my $rc = $webinject->engine();
+exit $rc;