~ K A L I ~
UNAME : Linux web63.extendcp.co.uk 4.18.0-553.56.1.el8_10.x86_64 #1 SMP Tue Jun 10 05:00:59 EDT 2025 x86_64SERVER IP : 10.0.187.63 -________-
CLIENT IP : 216.73.216.210 MINI SHELL D ZAB '
Current File : //bin/glpi-injector |
#!/usr/bin/perl
use strict;
use warnings;
use lib "/usr/share/glpi-agent/lib";
use setup;
use Compress::Zlib;
use English qw(-no_match_vars);
use Fcntl qw(:flock);
use Getopt::Long;
use LWP::UserAgent;
use Pod::Usage;
use Cpanel::JSON::XS;
use Data::UUID;
use GLPI::Agent::XML;
my $options = {
useragent => 'GLPI-Injector'
};
my @failedFiles;
# Set bundling to support aggregated options. It also make single char options case sensitive.
Getopt::Long::Configure("bundling");
GetOptions(
$options,
'help|h',
'directory|d=s',
'recursive|R',
'file|f=s',
'no-ssl-check',
'ssl-cert-file=s',
'proxy|P=s',
'url|u=s',
'useragent=s',
'remove|r',
'verbose|v',
'debug',
'stdin',
'xml-ua|x',
'json-ua',
'no-compression|C',
'oauth-client-id=s',
'oauth-client-secret=s',
);
$OUTPUT_AUTOFLUSH = 1;
pod2usage(-verbose => 0, -exitstatus => 0) if $options->{help};
$options->{verbose} = 1 if $options->{debug};
if ($options->{stdin}) {
loadstdin();
} elsif ($options->{file}) {
loadfile($options->{file});
} elsif ($options->{directory}) {
loaddirectory($options->{directory});
} else {
pod2usage();
}
if (@failedFiles) {
warn "These elements were not sent:\n";
map { warn "$_\n" } @failedFiles;
exit(1);
}
exit(0);
sub loadfile {
my ($file) = @_;
die "file $file does not exist\n" unless -f $file;
die "file $file is not readable\n" unless -r $file;
print "Loading $file..." if $options->{verbose};
open (my $handle, '<', $file) or die "can't open file $file: $ERRNO\n";
## no critic (ProhibitBitwise)
flock ($handle, LOCK_EX | LOCK_NB) or die "can't lock file $file: $ERRNO\n";
local $RS;
my $content = <$handle>;
close $handle or die "Can't close file $file: $ERRNO\n";
my $agentid;
my ($uuid_match) = $file =~ m{([0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12})\.(?:json|data)$}i;
if ($uuid_match) {
$agentid = $uuid_match;
} elsif ($file =~ /\.(?:json|data)$/i) {
$agentid = newagentid();
}
my $success = sendContent($content, $agentid);
if ($success && $options->{remove}) {
unlink $file or warn "Can't remove $file: $ERRNO\n";
}
push @failedFiles, $file unless $success;
}
sub newagentid {
my $uuid = Data::UUID->new();
return lc($uuid->to_string($uuid->create()));
}
sub loaddirectory {
my ($directory) = @_;
die "directory $directory does not exist\n" unless -d $directory;
die "directory $directory is not readable\n" unless -r $directory;
opendir (my $handle, $directory)
or die "can't open directory $directory: $ERRNO\n";
foreach my $file (sort readdir($handle)) {
next if $file =~ /^\.\.?$/ ;
if (-d "$directory/$file") {
loaddirectory("$directory/$file") if ($options->{recursive});
} else {
loadfile("$directory/$file") if $file =~ /\.(?:data|json|ocs|xml)$/;
}
}
closedir $handle;
}
sub loadstdin {
my $content;
undef $RS;
$content = <STDIN>;
push @failedFiles, 'STDIN DATA' unless sendContent($content);
}
sub sendContent {
my $content = shift;
my $agentid = shift;
my $useragent = $options->{useragent};
if (uncompress($content)) {
$content = uncompress($content);
}
if ($options->{"xml-ua"} || $options->{"json-ua"}) {
if ($content =~ /^<\?xml/) {
undef $agentid;
my $xml = GLPI::Agent::XML->new(string => $content);
my $tree = $xml->dump_as_hash();
$useragent = $tree->{REQUEST}->{CONTENT}->{VERSIONCLIENT}
if $tree && $tree->{REQUEST} && $tree->{REQUEST}->{CONTENT} &&
$tree->{REQUEST}->{CONTENT}->{VERSIONCLIENT};
} elsif ($agentid || $content =~ /^{/) {
$agentid = newagentid() unless $agentid;
my $json = decode_json($content);
$useragent = $json->{content}->{versionclient}
if $json && $json->{content} && $json->{content}->{versionclient};
}
}
my $ua = LWP::UserAgent->new(
agent => $useragent,
parse_head => 0, # No need to parse HTML
keep_alive => 1,
requests_redirectable => ['POST', 'GET', 'HEAD']
);
# Support proxy setup
if ($options->{proxy}) {
$ua->proxy(['http', 'https'], $options->{proxy});
} else {
$ua->env_proxy();
}
my $request = HTTP::Request->new( POST => $options->{url} );
my $info = "";
if ($options->{"no-ssl-check"} || $options->{"ssl-cert-file"}) {
my $url = $request->uri();
if ($url->scheme() eq 'https') {
if ($ua->can('ssl_opts')) {
IO::Socket::SSL->require();
if ($options->{"no-ssl-check"}) {
$ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0);
$info = " (ssl check disabled)";
} elsif ($options->{"ssl-cert-file"}) {
$ua->ssl_opts(SSL_cert_file => $options->{"ssl-cert-file"});
$info = " (ssl cert file)";
}
} else {
$info = " (unsupported ssl options)";
}
}
}
# Support Oauth access token request
my $bearer_token;
if ($options->{"oauth-client-id"} && $options->{"oauth-client-secret"}) {
# Guess access token api path from url
my $url = $request->uri()->clone();
my $path = $url->path();
$path = $1 if $path =~ /^(.*)(marketplace|plugins).*$/;
$path =~ s{/+$}{};
$path .= '/' if length($path);
$path .= 'api.php/token';
$url->path($path);
my $oauth_request = HTTP::Request->new(POST => $url);
my $content = encode_json(
{
grant_type => "client_credentials",
client_id => $options->{"oauth-client-id"},
client_secret => $options->{"oauth-client-secret"},
scope => "inventory",
}
);
$oauth_request->header('Content-Type' => 'application/json');
$oauth_request->header('Content-Length' => length($content));
$oauth_request->content($content);
print "requesting oauth access token\n"
if $options->{debug};
my $res = $ua->request($oauth_request);
if ($res->is_success()) {
my $content = $res->content;
eval {
my $json = decode_json($content);
$bearer_token = $json->{access_token};
};
if ($EVAL_ERROR) {
print "ERROR (oauth-access-request): invalid oauth access token: $content\n";
}
} else {
print "ERROR (oauth-access-request): ".$res->status_line().", failed to request oauth access token\n";
}
}
$request->header(
'Pragma' => 'no-cache',
'Content-type', $options->{"no-compression"} ?
$agentid ? 'Application/json' : 'Application/xml'
: 'Application/x-compress-zlib'
);
if ($agentid) {
$request->header('GLPI-Agent-ID' => $agentid);
}
if ($bearer_token) {
$request->header('Authorization' => "Bearer $bearer_token");
}
if ($options->{debug}) {
my $requestid = join('', map { sprintf("%02X", int(rand(256))) } 1..4);
print "[$requestid] ";
$request->header('GLPI-Request-ID' => $requestid);
print "[agentid:$agentid] " if $agentid;
}
$request->content($options->{"no-compression"} ? $content : compress($content));
my $res = $ua->request($request);
my $error;
eval {
$content = $res->content;
if ($res->header('Content-type') =~ /x-compress-zlib/) {
print "DEBUG: Uncompressing received content\n" if $options->{debug};
$content = uncompress($content);
}
my $xml = GLPI::Agent::XML->new(string => $content);
my $tree = $xml->dump_as_hash()
or die "Not an XML\n";
$error = $tree->{REPLY}->{ERROR}
if ref($tree->{REPLY}) eq 'HASH' && exists($tree->{REPLY}->{ERROR});
};
if ($EVAL_ERROR) {
if (!$content) {
$error = "Unexpected ".(defined($content) ? length($content) ? "'$content'" : "empty" : "undefined")." server response";
} elsif ($content =~ /^{/) {
eval {
my $json = decode_json($content);
if ($json->{status} eq "error") {
$error = $json->{message} // "Server failed to import" .
($options->{debug} ? "" : ", use --debug option to debug");
} elsif ($json->{status} eq "pending" && $res->header('GLPI-Request-ID')) {
print "DEBUG, CONTENT: $content\n" if defined($content) && $options->{debug};
print "waiting for proxy, ";
# Next request should be a GET with expected RequestID and no content
$request->method("GET");
$request->content("");
$request->header("GLPI-Request-ID" => $res->header('GLPI-Request-ID'));
my $max_pending_request = 12;
while ($max_pending_request--) {
my $expiration = $json->{expiration} // "";
if ($expiration =~ /^(\d+)s$/ && $1) {
print "waiting $1s... ";
sleep $1;
}
$res = $ua->request($request);
$content = $res->content;
print "DEBUG, CONTENT: $content\n" if defined($content) && $options->{debug};
$json = decode_json($content);
}
if ($json->{status} eq "pending") {
print "failed to get final proxy answer";
} elsif ($json->{status} ne "ok") {
$error = $json->{message} // "Server failed to import" .
($options->{debug} ? "" : ", use --debug option to debug");
}
undef $content;
} elsif ($json->{status} ne "ok") {
$error = $json->{message} // "Server failed to import" .
($options->{debug} ? "" : ", use --debug option to debug");
}
};
if ($EVAL_ERROR) {
$error = "Failed to parse GLPI JSON answer" .
($options->{debug} ? "" : ", use --debug option to debug");
}
} else {
$error = "Bad content as server response" .
($options->{debug} ? "" : ", use --debug option to debug");
}
}
if ($options->{verbose} || $error) {
if ($res->is_success()) {
print "OK\n";
} elsif ($error) {
# Chomp seems inefficient on multi-lined strings
chop($error) if $error =~ /\n$/m;
print "ERROR$info: ".$res->status_line().", $error\n";
}
}
print "DEBUG: $content\n" if $content && $options->{debug};
return $res->is_success() && ! $error ;
}
__END__
=head1 NAME
glpi-injector - A tool to push inventory in an OCS Inventory or compatible server.
=head1 SYNOPSIS
glpi-injector [-h|--help] [-R|--recursive] [-r|--remove] [-v|--verbose] [--debug]
[--useragent <user-agent>|-x|--xml-ua|--json-ua] [-C|--no-compression]
[--no-ssl-check] [--ssl-cert-file <private certificate file>] [[-P|--proxy] <proxy url>]
[[-f|--file] <file>|[-d|--directory] <directory>|--stdin] [-u|--url] <url>
Options:
-h --help this menu
-d --directory load every inventory files from a directory
-R --recursive recursively load inventory files from <directory>
-f --file load a specific file
-u --url server URL
-r --remove remove succesfuly injected files
-v --verbose verbose mode
--debug debug mode to output server answer
--stdin read data from STDIN
--useragent set used HTTP User-Agent for POST
-x --xml-ua --json-ua
use Client version found in XML or JSON as User-Agent for POST
--no-ssl-check do not check server SSL certificate
--ssl-cert-file client certificate file
-C --no-compression don't compress sent XML inventories
-P --proxy=PROXY proxy address
--oauth-client-id
oauth client id to request oauth access token
--oauth-client-secret
oauth client secret to request oauth access token
Examples:
glpi-injector -v -f /tmp/toto-2010-09-10-11-42-22.json --url https://login:pw@example/
glpi-injector -v -R -d /srv/ftp/fusion --url https://login:pw@example/
=head1 DESCRIPTION
This tool can be used to test your server, do benchmark or push inventory from
off-line machine.
Coded by KALI :v Greetz to DR HARD ../ kali.zbi@hotmail.com