The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package BlueCoat::SGOS;
use strict;
use warnings;
use Data::Dumper;
use Date::Parse;
use LWP::UserAgent;
require HTTP::Request;
use HTTP::Request::Common qw/POST/;

our %_URL = (
	'archconf_expanded'    => '/archconf_expanded.txt',
	'contentfilter_status' => '/ContentFilter/Status',
	'sysinfo'              => '/SYSINFO',
	'send_command'         => '/Secure/Local/console/install_upload_action/cli_post_setup.txt'
);

our %defaults = (
	'appliancehost'        => 'proxy',
	'applianceport'        => 8082,
	'applianceusername'    => 'admin',
	'appliancepassword'    => 'password',
	'applianceconnectmode' => 'https',
	'debuglevel'           => 0,
);

our %map = (
	'HTTP_MAIN_0015' => 'HTTP 0.9 requests received',
	'HTTP_MAIN_0010' => 'HTTP 1.0 requests received',
	'HTTP_MAIN_0011' => 'HTTP 1.1 requests received',
	'HTTP_MAIN_0016' => 'HTTP 0.9 responses received',
	'HTTP_MAIN_0012' => 'HTTP 1.0 responses received',
	'HTTP_MAIN_0013' => 'HTTP 1.1 responses received',
	'HTTP_MAIN_0014' => 'Transparent requests received',
	'HTTP_MAIN_0018' => 'HTTPS requests received',
	'HTTP_MAIN_0019' => 'HTTPS responses received',
	'HTTP_MAIN_0017' => 'NULL requests received',
);

=head1 NAME

BlueCoat::SGOS - A module to interact with Blue Coat SGOS-based devices.

=head1 VERSION

Version 1.02

=cut

our $VERSION = '1.02';

=head1 SYNOPSIS

This module interacts with Blue Coat SGOS-based devices.  Right
now, this is limited to parsing of the 'sysinfo' data from the
device.

	use strict; #always!
	use BlueCoat::SGOS;
	my $bc = BlueCoat::SGOS->new(
		'appliancehost'		=> 'swg.example.com',
		'applianceport'		=> 8082,
		'applianceuser'		=> 'admin',
		'appliancepassword'	=> 'password'
	);
	$bc->get_sysinfo();
	$bc->parse_sysinfo();

	# or from a file
	my $bc = BlueCoat::SGOS->new();
	$bc->get_sysinfo_from_file('/path/to/file.sysinfo');
	$bc->parse_sysinfo();

	# or from a data structure
	# in this case, $sysinfodata already contains sysinfo data
	my $bc = BlueCoat::SGOS->new();
	$bc->get_sysinfo_from_data($sysinfodata);
	$bc->parse_sysinfo();

	my $sysinfodata = $bc->{'sgos_sysinfo'};
	my $sgosversion = $bc->{'sgosversion'};
	my $sgosreleaseid = $bc->{'sgosreleaseid'};
	my $serialnumber = $bc->{'serialnumber'};
	my $modelnumber = $bc->{'modelnumber'};
	my $sysinfotime = $bc->{'sysinfotime'};

	# Hardware section of the sysinfo file
	my $hwinfo = $bc->{'sgos_sysinfo_sect'}{'Hardware Information'};

	# Software configuration (i.e. show configuration)
	my $swconfig = $bc->{'sgos_sysinfo_sect'}{'Software Configuration'};


=head1 SUBROUTINES/METHODS

Below are methods for BlueCoat::SGOS.
=cut

=head2 new

Creates a new BlueCoat::SGOS object.  Can be passed one of the following:

	appliancehost
	applianceport
	applianceusername
	appliancepassword
	applianceconnectmode (one of http or https)
	debuglevel

=cut

sub new {
	my $class = shift;
	my $self  = {};
	bless($self, $class);
	my %args = (%defaults, @_);

	$self->{'_appliancehost'}        = $args{'appliancehost'};
	$self->{'_applianceport'}        = $args{'applianceport'};
	$self->{'_applianceusername'}    = $args{'applianceusername'};
	$self->{'_appliancepassword'}    = $args{'appliancepassword'};
	$self->{'_applianceconnectmode'} = $args{'applianceconnectmode'};
	$self->{'_debuglevel'}           = $args{'debuglevel'};
	if (   $self->{'_appliancehost'}
		&& $self->{'_applianceport'}
		&& $self->{'_applianceconnectmode'}
		&& $self->{'_applianceusername'}
		&& $self->{'_appliancepassword'}) {

		if ($self->{'_applianceconnectmode'} eq 'https') {
			$self->{'_applianceurlbase'} = q#https://# . $self->{'_appliancehost'} . q#:# . $self->{'_applianceport'};
		}
		elsif ($self->{'_applianceconnectmode'} eq 'http') {
			$self->{'_applianceurlbase'} = q#http://# . $self->{'_appliancehost'} . q#:# . $self->{'_applianceport'};
		}
	}

	return $self;
}

sub _create_ua {
	my $self = shift;
	$self->{'_lwpua'} = LWP::UserAgent->new();
	$self->{'_lwpua'}->agent("BlueCoat-SGOS/$VERSION");
	$self->{'_lwpua'}->ssl_opts(
		'SSL_verify_mode' => 0,
		'verify_hostname' => 0,
	);

}

=head2 get_sysinfo

Takes no parameters, but instead fetches the sysinfo from the
appliance specified in the constructor.

	$bc->get_sysinfo();

=cut

sub get_sysinfo {
	my $self = shift;

	if ($self->{'_debuglevel'} > 0) {
		print 'URLBASE=' . $self->{'_applianceurlbase'} . "\n";
		print 'Getting ' . $self->{'_applianceurlbase'} . $_URL{'sysinfo'} . "\n";
	}
	if (!defined($self->{'_lwpua'})) {
		$self->_create_ua();
	}
	my $request = HTTP::Request->new('GET', $self->{'_applianceurlbase'} . $_URL{'sysinfo'});
	$request->authorization_basic($self->{'_applianceusername'}, $self->{'_appliancepassword'});
	my $response = $self->{'_lwpua'}->request($request);

	if ($response->is_error) {
		return undef;
	}
	else {
		$self->{'sgos_sysinfo'} = $response->content;
		$self->{'sgos_sysinfo'} =~ s/\r\n/\n/gi;
		if ($self->{'_debuglevel'} > 0) {
			print 'status=' . $response->status_line . "\n";
		}
	}
	if ($self->{'sgos_sysinfo'}) {
		return 1;
	}
	else {
		return undef;
	}
}

=head2 get_sysinfo_from_file

Takes one parameter: the filename of a sysinfo file on the disk.  Use this
instead of logging in over the network.

	$bc->get_sysinfo_from_file('sysinfo.filename.here');

=cut

sub get_sysinfo_from_file {
	my $self     = shift;
	my $filename = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "sub:get_sysinfo_from_file, filename=$filename\n";
	}
	if (-f $filename) {
		open(FSDFLKFJ, '<' . $filename);

		# slurp
		{
			local $/ = undef;
			$self->{'sgos_sysinfo'} = <FSDFLKFJ>;
			$self->{'sgos_sysinfo'} =~ s/\r\n/\n/gi;
		}
		close FSDFLKFJ;

		if ($self->{'sgos_sysinfo'}) {
			return 1;
		}
		else {
			return undef;
		}
	}
	else {

		# no filename specified
		return undef;
	}
}

=head2 get_sysinfo_from_data

Takes one parameter: a scalar that contains sysinfo data.
Use this instead of logging in over the network.

	$bc->get_sysinfo_from_data($sysinfodata);

=cut

sub get_sysinfo_from_data {
	my $self = shift;
	my $data = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "sub:get_sysinfo_from_data\n";
	}
	$self->{'sgos_sysinfo'} = $data;
	$self->{'sgos_sysinfo'} =~ s/\r\n/\n/gi;
	if ($self->{'sgos_sysinfo'}) {
		return 1;
	}
	else {
		return undef;
	}
}

=head2 parse_sysinfo

Takes no parameters.  Tells the object to parse the sysinfo
data and populate the object variables.

=cut

sub parse_sysinfo {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "parse_sysinfo\n";
	}
	my @split_sysinfo =
	  split(/__________________________________________________________________________/, $self->{'sgos_sysinfo'});
	$self->{'_sgos_sysinfo_split_count'} = $#split_sysinfo;

	# init the % var
	$self->{'sgos_sysinfo_sect'}{'_ReportInfo'} = $split_sysinfo[0];

	# Populate the sysinfo version
	# As of 6.2.2011, these are the known versions:
	# Version 4.6
	# Version 5.0
	# Version 6.0
	# Version 6.1
	# Version 7.0
	($self->{'_sysinfoversion'}) = $self->{'sgos_sysinfo_sect'}{'_ReportInfo'} =~ m/Version (\d+\.\d+)/;

	# Loop through each section of the split sysinfo
	foreach (1 .. $#split_sysinfo) {
		my $chunk = $split_sysinfo[$_];
		my @section = split(/\n/, $chunk);
		chomp @section;

		# the first 2 lines are junk
		shift @section;
		shift @section;
		my $sectionname = shift @section;

		if ($sectionname eq 'Software Configuration') {

			# get rid of 3 lines from top and 1 from bottom
			shift @section;
			shift @section;
			shift @section;
			pop @section;
		}
		if ($sectionname eq 'TCP/IP Routing Table') {
			shift @section;
			shift @section;
			shift @section;
			shift @section;
			shift @section;
		}

		# throw away the next line, it contains the URL for the source data
		shift @section;
		my $data = join("\n", @section);
		$self->{'sgos_sysinfo_sect'}{$sectionname} = $data;
	}

	# parse version
	$self->_parse_sgos_version();

	# parse releaseid
	$self->_parse_sgos_releaseid();

	# parse serial number
	$self->_parse_serial_number();

	# parse sysinfo time
	$self->_parse_sysinfo_time();

	# parse last reboot time
	$self->_parse_reboot_time();

	# parse model
	$self->_parse_model_number();

	# parse the configuration
	if ($self->{'sgos_sysinfo_sect'}{'Software Configuration'}) {
		$self->_parse_swconfig;
		$self->{'sysinfo_type'} = 'sysinfo';
	}
	else {
		$self->{'sysinfo_type'} = 'sysinfo_snapshot';
	}

	# parse VPM-CPL and VPM-XML
	$self->_parse_vpm();

	# parse the static bypass list
	$self->_parse_static_bypass();

	# parse the appliance name
	$self->_parse_appliance_name();

	# parse the network information
	$self->_parse_network();

	# parse the ssl accelerator info
	$self->_parse_ssl_accelerator();

	# parse the default gateway
	$self->_parse_default_gateway();

	# parse the route table
	$self->_parse_route_table();

	return 1;
}

# Find appliance-name
# located in the Software Configuration
# looks like:
# appliance-name "ProxySG 210 4609077777"
# limited to 127 characters
# e.g.: % String exceeds allowed length (127)
#
sub _parse_appliance_name {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_swconfig\n";
	}
	if (defined($self->{'sgos_sysinfo_sect'}{'Software Configuration'})) {
		(undef, $self->{'appliance-name'}) =
		  $self->{'sgos_sysinfo_sect'}{'Software Configuration'} =~ m/(appliance-name|hostname) (.+)$/im;
		$self->{'appliance-name'}                                =~ s/^\"//;
		$self->{'appliance-name'}                                =~ s/\"$//;

		if ($self->{'_debuglevel'} > 0) {
			print "appliancename=$self->{'appliance-name'}\n";
		}
	}
}

# model
# Model: 200-B
sub _parse_model_number {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_model_number\n";
	}

	#210-5 (unsupported configuration)
	#
	if (defined($self->{'sgos_sysinfo_sect'}{'Hardware Information'})) {
		($self->{'modelnumber'}) = $self->{'sgos_sysinfo_sect'}{'Hardware Information'} =~ m/Model:\s(.+)/im;
		if ($self->{'modelnumber'} =~ m/unsupported configuration/i) {
			$self->{'modelnumber'} =~ s/\s*\(unsupported configuration\)\s*//ig;
			$self->{'supported_configuration'} = 0;
		}
		else {
			$self->{'supported_configuration'} = 1;
		}
	}
}

# get network
# Network:
#   Interface 0:0: Bypass 10/100     with no link  (MAC 00:d0:83:04:ae:fc)
#   Interface 0:1: Bypass 10/100     running at 100 Mbps full duplex (MAC 00:d0:83:04:ae:fd)
sub _parse_network {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_network\n";
	}
	if (defined($self->{'sgos_sysinfo_sect'}{'Hardware Information'})) {
		my ($netinfo) = $self->{'sgos_sysinfo_sect'}{'Hardware Information'} =~ m/Network:(.+)Accelerators/ism;
		my @s = split(/\n/, $netinfo);
		chomp @s;
		foreach (@s) {
			my $line = $_;
			my ($interface) = $line =~ m/Interface\s+(.+)\:\s/im;
			my ($mac)       = $line =~ m/\(MAC\s(.+)\)/im;
			my ($running)   = $line =~ m/running\sat\s(.+)\s\(MAC/im;
			my $capabilities;

			#Interface 0:0: Intel Gigabit     running at 1 Gbps full duplex (MAC 00:e0:81:79:a5:1a)
			#Interface 2:0: Bypass 10/100/1000 with no link  (MAC 00:e0:ed:0b:67:e6)
			if ($line =~ m/running at/) {
				($capabilities) = $line =~ m/Interface\s$interface\:\s\w+(.+)\s+running at/;
			}
			if ($line =~ m/with no link/) {
				($capabilities) = $line =~ m/Interface\s$interface\:\s\w+(.+)\s+with no link/;
			}
			if ($capabilities) {
				$capabilities =~ s/\s+//ig;
			}
			if ($interface && $capabilities) {
				$self->{'interface'}{$interface}{'capabilities'} = $capabilities;
			}

			#print "Running=$running\n";
			if ($interface && $mac) {
				$self->{'interface'}{$interface}{'mac'} = $mac;
			}
			if ($interface && $running) {
				$self->{'interface'}{$interface}{'linkstatus'} = $running;
			}
			if ($interface && !$running) {
				$self->{'interface'}{$interface}{'linkstatus'} = 'no link';
			}

			#print "interface=$interface, mac=$mac\n";
		}
	}

	# supplement from swconfig/networking
	#print "getting supplemental networking info\n";
	my @t;

	#if (defined($self->{'sgos_swconfig_section'}{'networking'} ) ) {
	if (defined($self->{'sgos_swconfig_section'}{'networking'})) {
		@t = split(/\n/, $self->{'sgos_swconfig_section'}{'networking'});
		chomp @t;
	}

	if (defined($self->{'sgos_sysinfo_sect'}{'Software Configuration'})) {
		if ($#t < 2) {
			@t =
			  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});
		}
	}

	#}

	my $interface;
	my ($ip, $netmask);
	foreach (@t) {
		my $line = $_;

		if ($line =~ m/interface (.+)\;/i) {
			($interface) = $line =~ m/^interface (\d+\:?\d*\.*\d*)/i;
		}

		# sgos5, ip address and subnet mask are on SAME line
		if ($line =~ m/ip-address/) {

			($ip, $netmask) =
			  $line =~ m/^ip-address\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) *(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*/i;
			if (defined($ip)) {
				$ip =~ s/\s+//gi;
			}
			if (defined($netmask)) {
				$netmask =~ s/\s+//gi;
			}
		}
		if ($line =~ m/subnet-mask/) {
			($netmask) = $line =~ m/^subnet-mask *(.{1,3}\..{1,3}\..{1,3}\..{1,3})/i;
			$netmask =~ s/\s+//gi;
		}

		if (length($interface) > 1 && $ip && $netmask) {
			$self->{'interface'}{$interface}{'ip'}      = $ip;
			$self->{'interface'}{$interface}{'netmask'} = $netmask;
			$interface                                  = undef;
			$ip                                         = undef;
		}

	}
}

sub _parse_swconfig {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_swconfig\n";
	}
	my @split_swconfig =
	  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});

	# only applies to SGOS >5
	my $sectionname = '';
	foreach (1 .. $#split_swconfig) {
		my $line = $split_swconfig[$_];
		chomp $line;
		if ($line =~ m/!- BEGIN/) {
			($sectionname) = $line =~ m/!- BEGIN (.+)/;
		}
		elsif ($line =~ m/!- END/) {
			next;
		}
		else {
			if (defined($self->{'sgos_swconfig_section'}{$sectionname})) {
				$self->{'sgos_swconfig_section'}{$sectionname} = $self->{'sgos_swconfig_section'}{$sectionname} . $line . "\n";
			}
			else {
				$self->{'sgos_swconfig_section'}{$sectionname} = $line . "\n";
			}
		}

	}

}

sub _parse_static_bypass {
	my $self = shift;
	if (defined($self->{'sgos_sysinfo_sect'}{'Software Configuration'})) {
		my @lines =
		  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});
		my $have_static_bypass;
		foreach my $line (@lines) {
			if ($line =~ m/static-bypass/) {
				$have_static_bypass = 1;
			}
			elsif ($have_static_bypass) {
				if ($line =~ m/exit/) {
					last;
				}
				else {
					$line =~ s/^add //i;
					if (defined($self->{'static-bypass'})) {
						$self->{'static-bypass'} = $self->{'static-bypass'} . $line . "\n";
					}
					else {
						$self->{'static-bypass'} = $line . "\n";
					}
				}
			}
		}
	}
}

sub _parse_vpm {
	my $self = shift;
	if (defined($self->{'sgos_sysinfo_sect'}{'Software Configuration'})) {
		my @lines =
		  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});
		my $have_vpm_cpl;
		my $have_vpm_xml;

		foreach my $line (@lines) {
			if ($line =~ m/^inline policy vpm-cpl \"*end-(\d+)-inline\"*/) {
				($have_vpm_cpl) = $line =~ m/^inline policy vpm-cpl \"*end-(\d+)-inline\"*/;
			}
			elsif ($have_vpm_cpl) {
				if ($line =~ m/end-$have_vpm_cpl-inline/i) {
					last;
				}
				else {
					if (defined($self->{'vpm-cpl'})) {
						$self->{'vpm-cpl'} = $self->{'vpm-cpl'} . $line . "\n";
					}
					else {
						$self->{'vpm-cpl'} = $line . "\n";
					}
				}
			}

		}

		foreach my $line (@lines) {
			if ($line =~ m/^inline policy vpm-xml \"*end-(\d+)-inline\"*/) {
				($have_vpm_xml) = $line =~ m/^inline policy vpm-xml \"*end-(\d+)-inline\"*/;
			}
			elsif ($have_vpm_xml) {
				if ($line =~ m/end-$have_vpm_xml-inline/i) {
					last;
				}
				else {
					if (defined($self->{'vpm-xml'})) {
						$self->{'vpm-xml'} = $self->{'vpm-xml'} . $line . "\n";
					}
					else {
						$self->{'vpm-xml'} = $line . "\n";
					}
				}
			}

		}
	}

	return 1 if ($self->{'vpm-cpl'} && $self->{'vpm-xml'});
}

=head2 vpmcpl

Displays the VPM-CPL data.  Note that this does not currently return the
local, central, or forwarding policies.

=cut

sub vpmcpl {
	my $self = shift;
	return $self->{'vpm-cpl'};
}

=head2 vpmxml

Displays the VPM-XML data.

=cut

sub vpmxml {
	my $self = shift;
	return $self->{'vpm-xml'};
}

sub _parse_default_gateway {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
	}

	my @s;
	if (defined($self->{'sgos_swconfig_section'}{'networking'})) {
		my @s = split(/\n/, $self->{'sgos_swconfig_section'}{'networking'});
		chomp @s;
	}
	if (defined($self->{'sgos_sysinfo_sect'}{'Software Configuration'})) {
		if ($#s < 2) {
			@s =
			  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});
		}
	}
	if ($#s > 0) {
		foreach my $line (@s) {
			if ($line =~ m/ip-default-gateway/) {
				($self->{'ip-default-gateway'}) = $line =~ m/^ip-default-gateway +(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
			}
		}
	}

}

sub _parse_route_table {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_route_table: begin\n";
	}

	#inline static-route-table "end-398382495-inline"
	#; IP-Address Subnet Mask Gateway
	#172.16.0.0 255.240.0.0 172.20.144.1
	#end-398382495-inline
	if (defined($self->{'sgos_sysinfo_sect'}{'TCP/IP Routing Table'})) {
		my @r;
		if ($self->{'sgos_sysinfo_sect'}{'TCP/IP Routing Table'}) {
			$self->{'routetable'} = $self->{'sgos_sysinfo_sect'}{'TCP/IP Routing Table'};
		}
		else {
			@r =
			  split(/\n/, $self->{'sgos_sysinfo_sect'}{'Software Configuration'});
		}
		my $marker;
		foreach my $line (@r) {
			if ($line =~ m/inline static-route-table \"end-\d+-inline\"/i) {
				($marker) = $line =~ m/inline static-route-table \"end-(\d+)-inline\"/i;
			}
			if ($self->{'_debuglevel'} > 0) {
				print "_parse_route_table: marker=$marker\n";
			}
			if (defined($marker)) {
				if ($line =~ m/end-$marker-inline/o) {
					$marker = undef;
				}
			}
			if ($marker && $line !~ /$marker/i) {
				if ($line =~ m/^\s*?\;/) {
					next;
				}
				if ($line =~
m/\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
				  ) {
					if (defined($self->{'static-route-table'})) {
						$self->{'static-route-table'} = $self->{'static-route-table'} . $line . "\n";
					}
					else {
						$self->{'static-route-table'} = $line . "\n";
					}

				}
			}
		}
	}

}

sub _parse_serial_number {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_sgos_serial_number\n";
	}
	if (defined($self->{'sgos_sysinfo_sect'}{'Version Information'})) {
		($self->{'serialnumber'}) = $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/Serial\snumber\sis\s(\d+)/isx;
	}
}

sub _parse_ssl_accelerator {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_ssl_accelerator\n";
	}

	# SSL Accelerators
	# looks like:
	# Accelerators: none
	# or
	# Accelerators:
	#  Internal: Cavium CN1010 Security Processor
	#  Internal: Cavium CN501 Security Processor
	#  Internal: Broadcom 5825 Security Processor
	#
	if (defined($self->{'sgos_sysinfo_sect'}{'Hardware Information'})) {
		my ($acceleratorinfo) = $self->{'sgos_sysinfo_sect'}{'Hardware Information'} =~ m/(Accelerators\:.+)/ism;
		my @a = split(/\n/, $acceleratorinfo);

		#print "There are $#a lines\n";
		# if 1 line, then no SSL accelerator
		if ($#a == 0) {
			$self->{'ssl-accelerator'} = 'none';
		}
		if ($#a > 0) {
			($self->{'ssl-accelerator'}) = $a[1] =~ m/\s+(.+)/;
		}
	}

	#	print "DEBUG: acceleratorinfo=$acceleratorinfo\n";
	#print "DEBUG: ssl-accelerator=$self->{'ssl-accelerator'}\n";
}

# sysinfo time
# time on this file
# The current time is Mon Nov 23, 2009 18:48:38 GMT (SystemTime 438547718)
# The current time is Sat Mar 7, 2009 16:57:30 GMT (SystemTime 415990650)
sub _parse_sysinfo_time {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_sysinfo_time\n";
	}
	if (defined($self->{'sgos_sysinfo_sect'}{'Version Information'})) {
		($self->{'sysinfotime'}) = $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/^The current time is (.+) \(/im;
		$self->{'sysinfotime_epoch'} = str2time($self->{'sysinfotime'});
	}
}

sub _parse_reboot_time {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_reboot_time\n";
	}

	# Calculate hardware reboot time
	# The ProxySG Appliance was last hardware rebooted 1 days, 5 hours, 55 minutes, and 0 seconds ago.
	if (defined($self->{'sgos_sysinfo_sect'}{'Version Information'})) {
		($self->{'hardware_reboot'}) =
		  $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/The ProxySG Appliance was last hardware rebooted (.*)$/m;
		($self->{'hardware_reboot_day'}) = $self->{'hardware_reboot'} =~ m/(\d+) day/;
		if (!defined($self->{'hardware_reboot_day'})) {
			$self->{'hardware_reboot_day'} = 0;
		}
		($self->{'hardware_reboot_hour'}) = $self->{'hardware_reboot'} =~ m/(\d+) hour/;
		if (!defined($self->{'hardware_reboot_hour'})) {
			$self->{'hardware_reboot_hour'} = 0;
		}
		($self->{'hardware_reboot_minute'}) = $self->{'hardware_reboot'} =~ m/(\d+) minute/;
		if (!defined($self->{'hardware_reboot_minute'})) {
			$self->{'hardware_reboot_minute'} = 0;
		}
		($self->{'hardware_reboot_second'}) = $self->{'hardware_reboot'} =~ m/(\d+) second/;
		if (!defined($self->{'hardware_reboot_second'})) {
			$self->{'hardware_reboot_second'} = 0;
		}
		$self->{'hardware_reboot_seconds_total'} =
		  ($self->{'hardware_reboot_day'} * 24 * 3600) +
		  ($self->{'hardware_reboot_hour'} * 3600) +
		  ($self->{'hardware_reboot_minute'} * 60) +
		  $self->{'hardware_reboot_second'};

		# The ProxySG Appliance was last software rebooted 1 days, 5 hours, 55 minutes, and 1 seconds ago.

		($self->{'software_reboot'}) =
		  $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/The ProxySG Appliance was last software rebooted (.*)$/m;
		($self->{'software_reboot_day'}) = $self->{'software_reboot'} =~ m/(\d+) day/;
		if (!defined($self->{'software_reboot_day'})) {
			$self->{'software_reboot_day'} = 0;
		}
		($self->{'software_reboot_hour'}) = $self->{'software_reboot'} =~ m/(\d+) hour/;
		if (!defined($self->{'software_reboot_hour'})) {
			$self->{'software_reboot_hour'} = 0;
		}
		($self->{'software_reboot_minute'}) = $self->{'software_reboot'} =~ m/(\d+) minute/;
		if (!defined($self->{'software_reboot_minute'})) {
			$self->{'software_reboot_minute'} = 0;
		}
		($self->{'software_reboot_second'}) = $self->{'software_reboot'} =~ m/(\d+) second/;
		if (!defined($self->{'software_reboot_second'})) {
			$self->{'software_reboot_second'} = 0;
		}
		$self->{'software_reboot_seconds_total'} =
		  ($self->{'software_reboot_day'} * 24 * 3600) +
		  ($self->{'software_reboot_hour'} * 3600) +
		  ($self->{'software_reboot_minute'} * 60) +
		  $self->{'software_reboot_second'};
	}
}

sub _parse_sgos_releaseid {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_sgos_releaseid\n";
	}

	# parse  SGOS version, SGOS releaseid, and serial number
	# SGOS release ID
	if (defined($self->{'sgos_sysinfo_sect'}{'Version Information'})) {
		($self->{'sgosreleaseid'}) = $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/Release\sid:\s(\d+)/isx;
	}
}

sub _parse_sgos_version {
	my $self = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "_parse_sgos_version\n";
	}

	# parse  SGOS version, SGOS releaseid, and serial number
	if ($self->{'_debuglevel'} > 0) {
		print "VERSION INFO SECTION:\n";
		print $self->{'sgos_sysinfo_sect'}{'Version Information'} . "\n";
	}

	# SGOS version
	# #Version Information
	# URL_Path /SYSINFO/Version
	# Blue Coat Systems, Inc., ProxySG Appliance Version Information
	# Version: SGOS 4.2.10.1
	#
	if (defined($self->{'sgos_sysinfo_sect'}{'Version Information'})) {
		($self->{'sgosversion'}) = $self->{'sgos_sysinfo_sect'}{'Version Information'} =~ m/Version:\sSGOS\s(\d+\.\d+\.\d+\.\d+)/im;
		if ($self->{'_debuglevel'} > 0) {
			print "SGOS version = $self->{'sgosversion'}\n";
		}
	}
}

=head2 send_command

Takes one parameter: a scalar that contains configuration commands to send to the appliance.
This command is executed in configuration mode.

	my $output = $bc->send_command('show version');
	# or
	my $commands =qq{
		int 0:0
		speed 100
	};
	my $output = $bc->send_command($commands);

=cut

sub send_command {
	my $self    = shift;
	my $command = shift;
	if ($self->{'_debuglevel'} > 0) {
		print "begin sub:send_command\n";
		print "command=$command\n";
	}
	if (!defined($self->{'_lwpua'})) {
		$self->_create_ua();
	}
	my $request = POST $self->{'_applianceurlbase'} . $_URL{'send_command'},
	  Content_Type => 'form-data',
	  'Content'    => ['file' => $command];
	$request->authorization_basic($self->{'_applianceusername'}, $self->{'_appliancepassword'});
	my $response = $self->{'_lwpua'}->request($request);
	my $content;
	if ($response->is_success) {
		$content = $response->content;
	}
	else {
		die 'error';
	}
	$content =~ s/\r//ig;
	return $content;
}

=head2 Other Data

Other data that is directly accessible in the object:

	Appliance Name:   $bc->{'appliance-name'}
	Model Number:     $bc->{'modelnumber'}
	Serial Number:    $bc->{'serialnumber'}
	SGOS Version:     $bc->{'sgosversion'}
	Release ID:       $bc->{'sgosreleaseid'}
	Default Gateway:  $bc->{'ip-default-gateway'}
	Sysinfo Time:     $bc->{'sysinfotime'}
	Accelerator Info: $bc->{'ssl-accelerator'}

	The software configuration can be retrieved as follows:
		$bc->{'sgos_sysinfo_sect'}{'Software Configuration'}

	Other sections that can be retrieved:
		$bc->{'sgos_sysinfo_sect'}{'Software Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Compression Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Node Info'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Sizing Peers'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Sizing Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'ADN Tunnel Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'AOL IM Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Access Log Objects'}
		$bc->{'sgos_sysinfo_sect'}{'Access Log Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Authenticator Memory Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Authenticator Realm Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Authenticator Total Realm Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'CCM Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'CCM Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'CIFS Memory Usage'}
		$bc->{'sgos_sysinfo_sect'}{'CIFS Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'CPU Monitor'}
		$bc->{'sgos_sysinfo_sect'}{'CacheEngine Main'}
		$bc->{'sgos_sysinfo_sect'}{'Configuration Change Events'}
		$bc->{'sgos_sysinfo_sect'}{'Content Filter Status'}
		$bc->{'sgos_sysinfo_sect'}{'Core Image'}
		$bc->{'sgos_sysinfo_sect'}{'Crypto Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'DNS Cache Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'DNS Query Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Disk 1'}
		... and up to Disk 10, in some cases
		$bc->{'sgos_sysinfo_sect'}{'Endpoint Mapper Internal Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Endpoint Mapper Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Endpoint Mapper database contents'}
		$bc->{'sgos_sysinfo_sect'}{'FTP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Forwarding Settings'}
		$bc->{'sgos_sysinfo_sect'}{'Forwarding Statistics Per IP'}
		$bc->{'sgos_sysinfo_sect'}{'Forwarding Summary Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Forwarding health check settings'}
		$bc->{'sgos_sysinfo_sect'}{'Forwarding health check statistics'}
		$bc->{'sgos_sysinfo_sect'}{'HTTP Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'HTTP Main'}
		$bc->{'sgos_sysinfo_sect'}{'HTTP Requests'}
		$bc->{'sgos_sysinfo_sect'}{'HTTP Responses'}
		$bc->{'sgos_sysinfo_sect'}{'Hardware Information'}
		$bc->{'sgos_sysinfo_sect'}{'Hardware sensors'}
		$bc->{'sgos_sysinfo_sect'}{'Health Monitor'}
		$bc->{'sgos_sysinfo_sect'}{'Health check entries'}
		$bc->{'sgos_sysinfo_sect'}{'Health check statistics'}
		$bc->{'sgos_sysinfo_sect'}{'ICP Hosts'}
		$bc->{'sgos_sysinfo_sect'}{'ICP Settings'}
		$bc->{'sgos_sysinfo_sect'}{'ICP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'IM Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'Kernel Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Licensing Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Client Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Conversation Client Errors'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Conversation Other Errors'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Conversation Server Errors'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Errors'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Internal Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Server Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MAPI Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MMS Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'MMS General'}
		$bc->{'sgos_sysinfo_sect'}{'MMS Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MMS Streaming Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'MSN IM Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'OPP Services'}
		$bc->{'sgos_sysinfo_sect'}{'OPP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'P2P Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Persistent Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Policy Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Policy'}
		$bc->{'sgos_sysinfo_sect'}{'Priority 1 Events'}
		$bc->{'sgos_sysinfo_sect'}{'Quicktime Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'Quicktime Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'RIP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Real Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'Real Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Refresh Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'SCSI Disk Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'SOCKS Gateways Settings'}
		$bc->{'sgos_sysinfo_sect'}{'SOCKS Gateways Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'SOCKS Proxy Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'SSL Proxy Certificate Cache'}
		$bc->{'sgos_sysinfo_sect'}{'SSL Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Security processor Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Server Side persistent connections'}
		$bc->{'sgos_sysinfo_sect'}{'Services Management Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Services Per-service Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Services Proxy Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Software Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'System Memory Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP ARP Information'}
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP Listening list'}
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP Malloc Information'}
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP Routing Table'}
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Threshold Monitor Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Version Information'}
		$bc->{'sgos_sysinfo_sect'}{'WCCP Configuration'}
		$bc->{'sgos_sysinfo_sect'}{'WCCP Statistics'}
		$bc->{'sgos_sysinfo_sect'}{'Yahoo IM Statistics'}
		

	The details for interface 0:0 are stored here:
		IP address:   $bc->{'interface'}{'0:0'}{'ip'} 
		Netmask:      $bc->{'interface'}{'0:0'}{'netmask'} 
		MAC address:  $bc->{'interface'}{'0:0'}{'mac'} 
		Link status:  $bc->{'interface'}{'0:0'}{'linkstatus'} 
		Capabilities: $bc->{'interface'}{'0:0'}{'capabilities'} 

	You can retrieve the interface names like this:
		my @interfaces = keys %{$bc->{'interface'}};

	The route table can	be retrieved as follows:
		$bc->{'sgos_sysinfo_sect'}{'TCP/IP Routing Table'}

	The static route table can be retrieved as follows:
		$bc->{'static-route-table'}

	The WCCP configuration can be retrieved as follows:
		$bc->{'sgos_sysinfo_sect'}{'WCCP Configuration'}


=cut

=head1 AUTHOR

Matthew Lange <mmlange@cpan.org>

=head1 BUGS

Please report any bugs or feature requests through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=BlueCoat-SGOS>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc BlueCoat::SGOS


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=BlueCoat-SGOS>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/BlueCoat-SGOS>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/BlueCoat-SGOS>

=item * Search CPAN

L<http://search.cpan.org/dist/BlueCoat-SGOS/>

=back


=head1 LICENSE

Copyright (C) 2008-2012 Matthew Lange

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as 
published by the Free Software Foundation.

=cut

1;    # End of BlueCoat::SGOS

__DATA__