shell bypass 403
package Ocsinventory::Agent::XML::Inventory;
# TODO: resort the functions
use strict;
use warnings;
=head1 NAME
Ocsinventory::Agent::XML::Inventory - the XML abstraction layer
=head1 DESCRIPTION
OCS Inventory uses XML for the data transmission. The module is the
abstraction layer. It's mostly used in the backend module where it
called $inventory in general.
=cut
use XML::Simple;
use Digest::MD5 qw(md5_base64);
use Config;
use Ocsinventory::Agent::Backend;
=over 4
=item new()
The usual constructor.
=cut
sub new {
my (undef, $params) = @_;
my $self = {};
$self->{accountinfo} = $params->{context}->{accountinfo};
$self->{accountconfig} = $params->{context}->{accountconfig};
$self->{backend} = $params->{backend};
$self->{common} = $params->{context}->{common};
my $logger = $self->{logger} = $params->{context}->{logger};
$self->{config} = $params->{context}->{config};
if (!($self->{config}{deviceid})) {
$logger->fault ('deviceid unititalised!');
}
$self->{xmlroot}{QUERY} = ['INVENTORY'];
$self->{xmlroot}{DEVICEID} = [$self->{config}->{deviceid}];
#$self->{xmlroot}{CONTENT}{HARDWARE} = {
# TODO move that in a backend module
# ARCHNAME => [$Config{archname}]
#};
# Is the XML centent initialised?
$self->{isInitialised} = undef;
bless $self;
}
=item initialise()
Runs the backend modules to initialise the data.
=cut
sub initialise {
my ($self) = @_;
return if $self->{isInitialised};
$self->{backend}->feedInventory ({inventory => $self});
$self->{isInitialised} = 1;
}
=item getContent()
Return the inventory as a XML string.
=cut
sub getContent {
my ($self, $args) = @_;
my $logger = $self->{logger};
my $common = $self->{common};
if ($self->{isInitialised}) {
$self->processChecksum();
# checks for MAC, NAME and SSN presence
my $macaddr = $self->{xmlroot}->{CONTENT}->{NETWORKS}->[0]->{MACADDR}->[0];
my $ssn = $self->{xmlroot}->{CONTENT}->{BIOS}->{SSN}->[0];
my $name = $self->{xmlroot}->{CONTENT}->{HARDWARE}->{NAME}->[0];
my $osname = $self->{xmlroot}->{CONTENT}->{HARDWARE}->{OSNAME}->[0];
my $missing;
$missing .= "MAC-address " unless $macaddr;
$missing .= "SSN " unless $ssn;
$missing .= "HOSTNAME " unless $name;
if ($missing) {
$logger->debug('Missing value(s): '.$missing.'. I will send this inventory to the server BUT important value(s) to identify the computer are missing');
}
$self->{accountinfo}->setAccountInfo($self);
my $content = XMLout( $self->{xmlroot}, RootName => 'REQUEST', XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>', SuppressEmpty => undef );
# Cleaning XML to delete unprintable characters
my $clean_content = $common->cleanXml($content);
# Cleaning xmltags content after adding it o inventory
$common->flushXMLTags();
if(defined($osname) && lc($osname) eq 'macos') {
return $content;
}
return $clean_content;
}
}
=item printXML()
Only for debugging purpose. Print the inventory on STDOUT.
=cut
sub printXML {
my ($self, $args) = @_;
if ($self->{isInitialised}) {
print $self->getContent();
}
}
=item writeXML()
Save the generated inventory as an XML file. The 'local' key of the config
is used to know where the file as to be saved.
=cut
sub writeXML {
my ($self, $args) = @_;
my $logger = $self->{logger};
if ($self->{config}{local} =~ /^$/) {
$logger->fault ('local path unititalised!');
}
if ($self->{isInitialised}) {
my $localfile = $self->{config}{local}."/".$self->{config}{deviceid}.'.ocs';
$localfile =~ s!(//){1,}!/!;
# Convert perl data structure into xml strings
if (open OUT, ">$localfile") {
print OUT $self->getContent();
close OUT or warn;
$logger->info("Inventory saved in $localfile");
} else {
warn "Can't open `$localfile': $!"
}
}
}
=item processChecksum()
Compute the <CHECKSUM/> field. This information is used by the server to
know which parts of the XML have changed since the last inventory.
The is done thank to the last_file file. It has MD5 prints of the previous
inventory.
=cut
sub processChecksum {
my $self = shift;
my $logger = $self->{logger};
my $common = $self->{common};
# To apply to $checksum with an OR
my %mask = (
'HARDWARE' => 1,
'BIOS' => 2,
'MEMORIES' => 4,
'SLOTS' => 8,
'REGISTRY' => 16,
'CONTROLLERS' => 32,
'MONITORS' => 64,
'PORTS' => 128,
'STORAGES' => 256,
'DRIVES' => 512,
'INPUTS' => 1024,
'MODEMS' => 2048,
'NETWORKS' => 4096,
'PRINTERS' => 8192,
'SOUNDS' => 16384,
'VIDEOS' => 32768,
'SOFTWARES' => 65536,
'VIRTUALMACHINES' => 131072,
'CPUS' => 262144,
'BATTERIES' => 1048576,
);
if (!$self->{config}->{vardir}) {
$logger->fault ("vardir uninitialised!");
}
my $checksum = 0;
if (!$self->{config}{local} && $self->{config}->{last_statefile}) {
if (-f $self->{config}->{last_statefile}) {
# TODO: avoid a violant death in case of problem with XML
$self->{last_state_content} = XML::Simple::XMLin(
$self->{config}->{last_statefile},
SuppressEmpty => undef,
ForceArray => 1
);
} else {
$logger->debug ('last_state file: `'.
$self->{config}->{last_statefile}.
"' doesn't exist (yet).");
}
}
foreach my $section (keys %mask) {
# If the checksum has changed...
my $hash = md5_base64(XML::Simple::XMLout($self->{xmlroot}{'CONTENT'}{$section}));
if (!$self->{last_state_content}->{$section}[0] || $self->{last_state_content}->{$section}[0] ne $hash ) {
$logger->debug ("Section $section has changed since last inventory");
# We make OR on $checksum with the mask of the current section
$checksum |= $mask{$section};
# Finally I store the new value.
$self->{last_state_content}->{$section}[0] = $hash;
}
}
$common->setHardware({CHECKSUM => $checksum});
}
=item saveLastState()
At the end of the process IF the inventory was saved
correctly, the last_state is saved.
=cut
sub saveLastState {
my ($self, $args) = @_;
my $logger = $self->{logger};
if (!defined($self->{last_state_content})) {
$self->processChecksum();
}
if (!defined ($self->{config}->{last_statefile})) {
$logger->debug ("Can't save the last_state file. File path is not initialised.");
return;
}
if (open LAST_STATE, ">".$self->{config}->{last_statefile}) {
print LAST_STATE my $string = XML::Simple::XMLout( $self->{last_state_content}, RootName => 'LAST_STATE' );;
close LAST_STATE or warn;
} else {
$logger->debug ("Cannot save the checksum values in ".$self->{config}->{last_statefile}.":$!");
}
}
=item addSection()
A generic way to save a section in the inventory. Please avoid this
solution.
=cut
sub addSection {
my ($self, $args) = @_;
my $logger = $self->{logger};
my $multi = $args->{multi};
my $tagname = $args->{tagname};
for ( keys %{$self->{xmlroot}{CONTENT}} ){
if ( $tagname eq $_ ){
$logger->debug("Tag name `$tagname` already exists - Don't add it");
return 0;
}
}
if ($multi){
$self->{xmlroot}{CONTENT}{$tagname} = [];
} else {
$self->{xmlroot}{CONTENT}{$tagname} = {};
}
return 1;
}
=item feedSection()
Add information in inventory.
=back
=cut
# Q: is that really useful()? Can't we merge with addSection()?
sub feedSection{
my ($self, $args) = @_;
my $tagname = $args->{tagname};
my $values = $args->{data};
my $logger = $self->{logger};
my $found=0;
for ( keys %{$self->{xmlroot}{CONTENT}} ){
$found = 1 if $tagname eq $_;
}
if (!$found){
$logger->debug("Tag name `$tagname` doesn't exist - Cannot feed it");
return 0;
}
if ( $self->{xmlroot}{CONTENT}{$tagname} =~ /ARRAY/ ){
push @{$self->{xmlroot}{CONTENT}{$tagname}}, $args->{data};
} else {
$self->{xmlroot}{CONTENT}{$tagname} = $values;
}
return 1;
}
1;