#!/usr/bin/perl 
#===============================================================================
#
#         FILE: html_form.pl
#
#        USAGE: 
#
#  DESCRIPTION:  
#
#      OPTIONS:  ---
# REQUIREMENTS:  ---
#         BUGS:  ---
#        NOTES:  ---
#       AUTHOR:  Tim Pretlove
#      COMPANY:  ---
#      VERSION:  0.3
#      CREATED:  24-05-2011 15:32
#     MODIFIED:  27-07-2011 13:42
#===============================================================================


use strict;
use warnings;

use Pod::Usage;
use Time::HiRes qw(gettimeofday tv_interval);
use Getopt::Long;
#use WWW::HtmlUnit DIRECTORY => '/tmp';
use WWW::HtmlUnit::Sweet DIRECTORY => '/tmp';


sub htmlunit {
    my ($url,$username,$userform,$passwdform,$passwd,$formname,$submit,$expect,$ffield,$ufield,$pfield,$sfield,$debug,$verbose,$crit) = @_;
    if (!defined $verbose) { $verbose = 0 }
    #my $webClient = WWW::HtmlUnit->new("INTERNET_EXPLORER_8");
    my $webClient = WWW::HtmlUnit->new("FIREFOX_3_6");
    
    my $startsec = [gettimeofday()];
    $webClient->setJavaScriptEnabled(1);
    $webClient->setCssEnabled(0);
    $webClient->setThrowExceptionOnScriptError(0);
    $webClient->setThrowExceptionOnFailingStatusCode(0);
    $webClient->setUseInsecureSSL(1);
    $webClient->setRedirectEnabled(1);
    my $page = $webClient->getPage($$url);
    $webClient->waitForBackgroundJavaScript(1000);
    
    my ($form,$button,$usrfrm,$passfrm);
    if ($$formname =~ /^\d+?$/) {
        if ($verbose == 1) { print "Looking for form by number ($$formname)\n" }
        $form = $page->getForms()->get($$formname);
    } elsif (defined $$ffield) {
        if ($ffield eq "value") {
            if ($verbose == 1) { print "Looking for form by value ($$formname)\n" }
            $form = $page->getFormByValue($$formname);
        }
    } else {
        if ($verbose == 1) { print "Looking for form by name ($$formname)\n" }
        $form = $page->getFormByName($$formname);
    }
    if (defined $$sfield) {
        if ($$sfield eq "value") {
            if ($verbose == 1) { print "Getting sumbit button by value ($$submit)\n" }
            $button = $form->getInputByValue($$submit);
        }
    } else {
        if ($verbose == 1) { print "Getting sumbit button by name ($$submit)\n" }
        $button = $form->getInputByName($$submit);
    }
    if (defined $$ufield) {        
        if ($$ufield eq "value") {
            if ($verbose == 1) { print "Setting user by value ($$userform)\n" }
            $usrfrm = $form->getInputByValue($$userform);
        }
    } else {
        $usrfrm = $form->getInputByName($$userform);
        if ($verbose == 1) { print "Setting user by name ($$userform)\n" }
    }
    $usrfrm->setValueAttribute($$username);
    if (defined $$pfield) {
        if ($$pfield eq "value") {
            $passfrm = $form->getInputByValue($$passwdform);
            if ($verbose == 1) { print "Setting password by value ($$passwdform)\n" }
        }
    } else {
        if ($verbose == 1) { print "Setting password by name ($$passwdform)\n" }
        $passfrm = $form->getInputByName($$passwdform);
    }
    $passfrm->setValueAttribute($$passwd);
    if ($verbose == 1) { print "Clicking Button\n" }
    my $res = $button->click();
    $webClient->waitForBackgroundJavaScript(1000);
    my $status = $res->getWebResponse->getStatusCode();
    my $statusmess = $res->getWebResponse->getStatusMessage();
    my $resheader = $res->getWebResponse->getResponseHeaders();
    my $content = $res->asXml;
    my $elapsed = tv_interval ($startsec, [gettimeofday]);
    if ($$debug) {
        print "$content";
        print "status: $statusmess\n";
    }
    if ($content =~ /$$expect/) {
        $content = 1;
    } else {
        $content = 0;
    }
    $webClient->closeAllWindows();
    return ($content,$status,$elapsed);
}

sub ender {
    my ($retcode,$elapsed,$warn,$crit) = @_;
    print "|time=$$elapsed" . "s;$$warn;$$crit;0;$$crit";
    exit $retcode;
}

my %opt;
GetOptions(\%opt,'man','help|?','warning=s','critical=s','debug=s','expect=s','form=s','url=s','page','verbose+');

if (defined $opt{debug}) { 
	open (STDERR,">>",$opt{debug}) or die "$!";
    print STDERR "$opt{url}\n";
    foreach (keys %opt) {
        print STDERR "$_ $opt{$_}\n";
    }
}
pod2usage(-exitstatus => 3,2) if $opt{help};
pod2usage(-exitstatus => 0, -verbose => 2) if $opt{man};
if((!defined $opt{form}) || (!defined $opt{url}) || (!defined $opt{warning}) || (!defined $opt{expect})) {
    pod2usage(2);
    exit(3);
}

    
my ($fsplit,$usplit,$psplit,$ssplit) = split('&',$opt{form});
my ($userform,$username,$ufield) = split('=',$usplit);
my ($passwdform,$passwd,$pfield) = split('=',$psplit);
my ($formname,$ffield) = split('=',$fsplit);
my ($submit,$sfield) = split('=',$ssplit);
my $rc = 0;

if ((!defined $formname) || (!defined $submit) || (!defined $userform) || (!defined $username) || (!defined $passwdform) || (!defined $passwd) || (!defined $opt{url}) || (!defined $opt{warning}) || (!defined $opt{critical})) {
    if (!defined $formname) { print "Missing formname\n" }
    if (!defined $submit) { print "Missing submit\n" }
    if (!defined $userform) { print "Missing user form\n" }
    if (!defined $username) { print "Missing username\n" }
    if (!defined $passwd) { print "Missing password\n" }
    if (!defined $passwdform) { print "Missing password form\n" }
    if (!defined $opt{url}) { print "Missing url\n" }
    if (!defined $opt{warning}) { print "Missing warning\n" }
    if (!defined $opt{critical}) { print "Missing critical\n" }
    print "formname: $formname Submit: $submit userform: $userform\n";
    print "username: $username passwordform: $passwdform passwd: $passwd\n";
    print "url: $opt{url} warn: $opt{warning} critical: $opt{critical}\n";
    print "Undefined variable\n";
    pod2usage(2);
    exit(3);
}
    
my ($content,$status,$elapsed) = htmlunit(\$opt{url},\$username,\$userform,\$passwdform,\$passwd,\$formname,\$submit,\$opt{expect},\$ffield,\$ufield,\$pfield,\$sfield,\$opt{page},$opt{verbose},$opt{critical});

print "HTTPFORM ";
if ($elapsed > $opt{critical}) {
    print "CRITICAL: response time too slow";
    ender(2,\$elapsed,\$opt{warning},\$opt{critical});
} elsif ($elapsed > $opt{warning}) {
    print "WARNING: slow response time";
    ender(1,\$elapsed,\$opt{warning},\$opt{critical});
} 
if ($status != 200) {
    print "CRITICAL: Bad status code";
    ender(2,\$elapsed,\$opt{warning},\$opt{critical});
}
if ($content == 0) {
    print "CRITICAL: Expect string not found";
    ender(2,\$elapsed,\$opt{warning},\$opt{critical});
}
        
print "OK: Expect string recieved";
ender(0,\$elapsed,\$opt{warning},\$opt{critical});

__END__

=head1 NAME

html_form.pl - Authenticate a system by filling in a form using browser emulation

This script fills in the username and password boxes within a form and then searches
the output for an expected string.

Browser emulation allows for javascript and cookies to be processed.

This script relies on WWW::HTML unit being installed

http://search.cpan.org/~awwaiid/WWW-HtmlUnit-0.15/lib/WWW/HtmlUnit.pm

=head1 SYNOPSIS

html_form.pl -w <sec> -c <sec> -u <url> -f <form> -e <expect> (-p) (-d)

Options:

 --help or -h               This brief help message
 --man or -m                Full documentation
 --warning or -w <sec>      Time before a warning in seconds
 --critical or -c <sec>     Time before going critical in seconds
 --url or -u <url>          The url of server (https://someserver.com)
 --form or -f <FormString>  The form string Formname&UserBoxName=username&PassBoxName=password&SubmitButtonName
 --expect or -e <string>    String to find in returned page
 --page or -p               Show returned page
 --debug or -d <file>       File to redirect STDERR output to

=head1 OPTIONS

=over 8

=item B<--man or -m>

Prints this manual page and exits

=item B<--help or -h>

Prints the help page and exits

=item B<--form or -f>

# The fields are seperated by '\&' and the keys and the values are seperated by '='
 The first field is the value or number of the form so in html terms <form name=login> would mean the
 first field would be "login" or if you count down the forms in the html can use a number i.e 3 being the third form on the page. 
 The second field is the name of the username input box followed by the username you wish to input
 The third field is the the name of the password input box followed by the password you wish to input
 Lastly comes the the name of the submit button which is pressed to complete the transaction.
So take a simple form:

<form name=test1>
    <input name="user"/>
    <input name="password"/>
    <input type=submit name="login"/></form>

This would result in form string like:
    "test1&user=youruser&password=yourpass&login"

It is also possible to add to the end of the username, password and submit fields the =value option
this allow the program to search for the field by the value element rather than by the name element
e.g test1&user=youruser&password=yourpass&login=value

=item B<--url or -u>

The url of the system e.g https://testsys1.co.uk/index.html

=item B<--warning or -w>

The time in seconds before a warning would occure

=item B<--critical or -c>

The time in seconds before going critical

=item B<--expect or -e>

The string to search for to verify that the returned page has successfully logged in

=item B<--debug or -d>

File to redirect STDERR and options to

=item B<--page or -p>

Display the returned page (not for inside nagios!)

=back

=head1 RETURNS

0 = Okay
1 = Warning
2 = Critical
3 = Unknown (most likely due to misconfigure of command line options)

=head1 AUTHOR

Tim Pretlove, spaceshippie at gmail dot com

=cut
