%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/vendor/glpi-project/inventory_format/lib/php/ |
Current File : //var/www/projetos/suporte.iigd.com.br/vendor/glpi-project/inventory_format/lib/php/Converter.php |
<?php /** * --------------------------------------------------------------------- * GLPI - Gestionnaire Libre de Parc Informatique * Copyright (C) 2015-2018 Teclib' and contributors. * * http://glpi-project.org * * based on GLPI - Gestionnaire Libre de Parc Informatique * Copyright (C) 2003-2014 by the INDEPNET Development Team. * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * GLPI 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. * * GLPI 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. * * You should have received a copy of the GNU General Public License * along with GLPI. If not, see <http://www.gnu.org/licenses/>. * --------------------------------------------------------------------- * * PHP version 7 * * @category Inventory * @package Glpi * @author Johan Cwiklinski <jcwiklinski@teclib.com> * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version * @link https://glpi-project.org */ namespace Glpi\Inventory; use DateTime; use Exception; use RuntimeException; use Swaggest\JsonSchema\Context; use Swaggest\JsonSchema\Schema; use UnexpectedValueException; /** * Converts old FusionInventory XML format to new JSON schema * for automatic inventory. * * @category Inventory * @package Glpi * @author Johan Cwiklinski <jcwiklinski@teclib.com> * @copyright 2018-2022 GLPI Team and Contributors * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version * @link https://glpi-project.org */ class Converter { public const LAST_VERSION = 0.1; /** @var ?float */ private ?float $target_version; /** @var bool */ private bool $debug = false; /** * XML a different steps. Used for debug only * @var array<int, mixed> */ private array $steps; /** @var array<string, float> */ private array $mapping = [ '01' => 0.1 ]; /** @var array<string, array<int, string>> */ private array $schema_patterns; /** @var array<string, array<string, string>> */ private array $extra_properties = []; /** @var array<string, array<string, array<string, string>>> */ private array $extra_sub_properties = []; /** * @var array<string, array<int, string>> * * A two dimensions array with types as key, * and nodes names as values. * * Node name is expected to be defined with its parent * separated with a "/": * $convert_types = [ * 'integer' => [ * 'drives/free' * ] * ]; * * With above example, 'drives/free' will replace all * entries found as $drives['free'] and $drives[$i]['free'] * with their value cast to integer. * * @see Converter::getCastedValue() for supported types * @see Converter::convertTypes() for usage */ private array $convert_types; /** * Instantiate converter * * @param ?float $target_version JSON schema based version to target. Use last version if null. */ public function __construct($target_version = null) { if ($target_version === null) { $target_version = self::LAST_VERSION; } if (!is_double($target_version)) { throw new UnexpectedValueException('Version must be a double!'); } $this->target_version = $target_version; } /** * Get target version * * @return float */ public function getTargetVersion(): float { return $this->target_version ?? self::LAST_VERSION; } /** * Set debug on/off * * @param boolean $debug Debug active or not * * @return Converter */ public function setDebug(bool $debug): self { $this->debug = $debug; return $this; } /** * Is debug mode on? * * @return boolean */ public function isDebug(): bool { return $this->debug; } /** * Get path to schema * * @return string */ public function getSchemaPath(): string { $schema_path = realpath(__DIR__ . '/../../inventory.schema.json'); if ($schema_path === false) { throw new RuntimeException('Schema file not found!'); } return $schema_path; } /** * @param array<string, array<string, string>> $properties * @return $this */ public function setExtraProperties(array $properties): self { $this->extra_properties = $properties; return $this; } /** * @param array<string, array<string, array<string, string>>> $properties * @return $this */ public function setExtraSubProperties(array $properties): self { $this->extra_sub_properties = $properties; return $this; } /** * Build (extended) JSON schema * @return mixed */ public function buildSchema() { $string = file_get_contents($this->getSchemaPath()); if ($string === false) { throw new RuntimeException('Unable to read schema file'); } $schema = json_decode($string); $properties = $schema->properties->content->properties; if ($this->extra_properties != null) { foreach ($this->extra_properties as $extra_property => $extra_config) { if (!property_exists($properties, $extra_property)) { $properties->$extra_property = json_decode((string)json_encode($extra_config)); } else { trigger_error( sprintf('Property %1$s already exists in schema.', $extra_property), E_USER_WARNING ); } } } if ($this->extra_sub_properties != null) { foreach ($this->extra_sub_properties as $extra_sub_property => $extra_sub_config) { if (property_exists($properties, $extra_sub_property)) { foreach ($extra_sub_config as $subprop => $subconfig) { $type = $properties->$extra_sub_property->type; switch ($type) { case 'array': if (!property_exists($properties->$extra_sub_property->items->properties, $subprop)) { $properties->$extra_sub_property->items->properties->$subprop = json_decode((string)json_encode($subconfig)); } else { trigger_error( sprintf('Property %1$s already exists in schema.', $subprop), E_USER_WARNING ); } break; case 'object': if (!property_exists($properties->$extra_sub_property->properties, $subprop)) { $properties->$extra_sub_property->properties->$subprop = json_decode((string)json_encode($subconfig)); } else { trigger_error( sprintf( 'Property %1$s/%2$s already exists in schema.', $extra_sub_property, $subprop ), E_USER_WARNING ); } break; default: trigger_error('Unknown type ' . $type, E_USER_WARNING); } } } else { trigger_error( sprintf('Property %1$s does not exists in schema.', $extra_sub_property), E_USER_WARNING ); } } } return $schema; } /** * Do validation (against last schema only!) * * @param mixed $json Converted data to validate * * @return boolean */ public function validate($json): bool { try { $schema = Schema::import($this->buildSchema()); $context = new Context(); $context->tolerateStrings = (!defined('TU_USER')); $schema->in($json, $context); return true; } catch (Exception $e) { $errmsg = "JSON does not validate. Violations:\n"; $errmsg .= $e->getMessage(); $errmsg .= "\n"; throw new RuntimeException($errmsg); } } /** * Do conversion * * @param string $xml Original XML string * * @return string|false */ public function convert(string $xml) { libxml_use_internal_errors(true); $sxml = simplexml_load_string($xml); if ($sxml === false) { $errmsg = 'XML string seems invalid.'; foreach (libxml_get_errors() as $error) { $errmsg .= "\n" . $error->message; } throw new RuntimeException($errmsg); } //remove empty nodes while ($removes = $sxml->xpath('/child::*//*[not(*) and not(text()[normalize-space()])]')) { for ($i = count($removes) - 1; $i >= 0; --$i) { unset($removes[$i][0]); } } //convert SimpleXML object to array, recursively. $data = json_decode( (string)json_encode((array)$sxml), true ); $this->loadSchemaPatterns(); $methods = $this->getMethods(); foreach ($methods as $method) { //reset values to convert for each conversion step if ($this->debug === true) { $this->steps[] = $data; } if (!$data = $this->$method($data)) { throw new RuntimeException('Conversion has failed at ' . $method); } } return json_encode($data, JSON_PRETTY_PRINT); } /** * Get methods names we'll have to call in order to convert * * @return array<int, string> */ public function getMethods(): array { $methods = []; foreach ($this->mapping as $name => $version) { if ($version <= $this->target_version) { $methods[] = 'convertTo' . $name; } } return $methods; } /** * Converts to inventory format 0.1 * * @param array<string, mixed> $data Contents * * @return array<string, mixed> */ private function convertTo01(array $data): array { //all keys are now lowercase $data = $this->arrayChangeKeyCaseRecursive($data); if (!isset($data['action'])) { $query = $data['query'] ?? ($this->isNetworkInventory($data) ? 'snmp' : 'inventory'); switch (strtolower($query)) { case 'snmp': case 'snmpquery': $data['action'] = 'netinventory'; break; case 'netdiscovery': $data['action'] = 'netdiscovery'; break; case 'inventory': default: $data['action'] = 'inventory'; break; } } unset($data['query']); $data = $this->convertNetworkInventory($data); //replace bad typed values... $this->setConvertTypes([ 'boolean' => [ 'antivirus/enabled', 'antivirus/enabled', 'antivirus/uptodate', 'drives/systemdrive', 'networks/virtualdev', 'printers/network', 'printers/shared', 'networks/management', 'softwares/no_remove', 'licenseinfos/trial', 'network_ports/trunk', 'cameras/flashunit', 'powersupplies/hotreplaceable', 'powersupplies/plugged', 'memories/removable' ], 'integer' => [ 'cpus/core', 'cpus/speed', 'cpus/stepping', 'cpus/thread', 'cpus/external_clock', 'cpus/corecount', 'drives/free', 'drives/total', 'hardware/etime', 'storages/disksize', 'physical_volumes/free', 'physical_volumes/pe_size', 'physical_volumes/pv_pe_count', 'physical_volumes/size', 'volume_groups/lv_count', 'volume_groups/pv_count', 'volume_groups/free', 'volume_groups/size', 'logical_volumes/seg_count', 'logical_volumes/size', 'memories/numslots', 'processes/pid', 'processes/virtualmemory', 'networks/mtu', 'softwares/filesize', 'virtualmachines/vcpu', 'network_ports/ifinerrors', 'network_ports/ifinoctets', 'network_ports/ifinbytes', 'network_ports/ifinternalstatus', 'network_ports/ifmtu', 'network_ports/ifnumber', 'network_ports/ifouterrors', 'network_ports/ifoutoctets', 'network_ports/ifoutbytes', 'network_ports/ifspeed', 'network_ports/ifportduplex', 'network_ports/ifstatus', 'network_ports/iftype', 'network_components/fru', 'network_components/index', 'network_device/credentials', 'pagecounters/total', 'pagecounters/black', 'pagecounters/color', 'pagecounters/total', 'pagecounters/rectoverso', 'pagecounters/scanned', 'pagecounters/printtotal', 'pagecounters/printblack', 'pagecounters/printcolor', 'pagecounters/copytotal', 'pagecounters/copyblack', ] ]); $this->convertTypes($data); //replace arrays... $arrays = [ 'cpus', 'local_users', 'local_groups', 'ports', 'sounds', 'usbdevices', 'batteries', 'firewall', 'monitors', 'printers', 'storages', 'slots', 'modems', 'licenseinfos', 'antivirus', 'drives', 'users', 'networks', 'controllers', 'envs', 'inputs', 'logical_volumes', 'physical_volumes', 'volume_groups', 'memories', 'processes', 'softwares', 'virtualmachines', 'firmwares', 'simcards', 'sensors', 'powersupplies', 'videos', 'remote_mgmt', 'cartridges', 'cameras', 'user' ]; foreach ($arrays as $array) { if (isset($data['content'][$array]) && !array_is_list($data['content'][$array])) { $data['content'][$array] = [$data['content'][$array]]; } } $sub_arrays = [ 'local_groups/member', 'versionprovider/comments', 'cameras/resolution', 'cameras/imageformats', 'cameras/resolutionvideo' ]; foreach ($sub_arrays as $array) { $splitted = explode('/', $array); if (isset($data['content'][$splitted[0]])) { foreach ($data['content'][$splitted[0]] as $key => &$parray) { if ($key == $splitted[1] && !is_array($parray)) { $parray = [$parray]; } elseif (isset($parray[$splitted[1]]) && !is_array($parray[$splitted[1]])) { $parray[$splitted[1]] = [$parray[$splitted[1]]]; } } } } //lowercase... if (isset($data['content']['networks'])) { foreach ($data['content']['networks'] as &$network) { if (isset($network['status'])) { $network['status'] = strtolower($network['status']); } if (isset($network['type'])) { $network['type'] = strtolower($network['type']); if ($network['type'] == 'local') { $network['type'] = 'loopback'; } if (!in_array($network['type'], $this->schema_patterns['networks_types'])) { unset($network['type']); } } } } if (isset($data['content']['cpus'])) { foreach ($data['content']['cpus'] as &$cpu) { if (isset($cpu['arch'])) { $cpu['arch'] = strtolower($cpu['arch']); } } } //plurals if (isset($data['content']['firewall'])) { $data['content']['firewalls'] = $data['content']['firewall']; unset($data['content']['firewall']); } if (isset($data['content']['local_groups'])) { foreach ($data['content']['local_groups'] as &$group) { if (isset($group['member'])) { $group['members'] = $group['member']; unset($group['member']); } } } //processes dates misses seconds! $ns_pattern = '/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2} [0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/'; if (isset($data['content']['processes'])) { foreach ($data['content']['processes'] as &$process) { if (isset($process['started'])) { if (preg_match($ns_pattern, $process['started'])) { try { $started = new DateTime($process['started']); $process['started'] = $started->format('Y-m-d H:i:s'); } catch (Exception $e) { //not valid, drop. unset($process['started']); } } else { //not valid, drop. unset($process['started']); } } } } //rename installdate to install_date; change date format if (isset($data['content']['softwares'])) { foreach ($data['content']['softwares'] as &$soft) { $convertedDate = $this->convertDate($soft['install_date'] ?? ''); if (isset($soft['installdate'])) { if ($convertedDate === null) { $convertedDate = $this->convertDate($soft['installdate']); } unset($soft['installdate']); } if ($convertedDate !== null) { $soft['install_date'] = $convertedDate; } else { unset($soft['install_date']); } } } //change dates formats if (isset($data['content']['batteries'])) { foreach ($data['content']['batteries'] as &$battery) { if (($convertedDate = $this->convertDate($battery['date'] ?? '')) !== null) { $battery['date'] = $convertedDate; } else { unset($battery['date']); } } } if (isset($data['content']['bios'])) { if (($convertedDate = $this->convertDate($data['content']['bios']['bdate'] ?? '')) !== null) { $data['content']['bios']['bdate'] = $convertedDate; } else { unset($data['content']['bios']['bdate']); } } if (isset($data['content']['operatingsystem']['boot_time'])) { //convert to 'Y-m-d H:i:s' if format = 'Y-d-m H:i:s' $boot_time = $data['content']['operatingsystem']['boot_time']; $boot_datetime = DateTime::createFromFormat('Y-d-m H:i:s', $boot_time); //check if create from 'Y-d-m H:i:s' format is OK (ie: 2022-21-09 05:21:23) //but he can return a new DateTime instead of false for '2022-10-04 05:21:23' //so check return value from strtotime because he only knows / handle 'English textual datetime' //https://www.php.net/manual/en/function.strtotime.php //if strtotime return false it's already Y-m-d H:i:s format if ($boot_datetime !== false && strtotime($boot_time) === false) { $boot_time = $boot_datetime->format('Y-m-d H:i:s'); $data['content']['operatingsystem']['boot_time'] = $boot_time; } $convertedDate = $this->convertDate($data['content']['operatingsystem']['boot_time'], 'Y-m-d H:i:s'); if ($convertedDate !== null) { $data['content']['operatingsystem']['boot_time'] = $convertedDate; } else { unset($data['content']['operatingsystem']['boot_time']); } } if (isset($data['content']['antivirus'])) { foreach ($data['content']['antivirus'] as &$av) { //expiration date format if (($convertedDate = $this->convertDate($av['expiration'] ?? '')) !== null) { $av['expiration'] = $convertedDate; } else { unset($av['expiration']); } //old properties if (isset($av['datfilecreation'])) { if (!isset($av['base_creation'])) { $av['base_creation'] = $av['datfilecreation']; } unset($av['datfilecreation']); } if (isset($av['datfileversion'])) { if (!isset($av['base_version'])) { $av['base_version'] = $av['datfileversion']; } unset($av['datfileversion']); } if (isset($av['engineversion64'])) { if (!isset($av['base_version'])) { $av['base_version'] = $av['engineversion64']; } unset($av['engineversion64']); } if (isset($av['engineversion32'])) { if (!isset($av['base_version'])) { $av['base_version'] = $av['engineversion32']; } unset($av['engineversion32']); } } } if (isset($data['content']['firmwares'])) { foreach ($data['content']['firmwares'] as &$fw) { if (($convertedDate = $this->convertDate($fw['date'] ?? '')) !== null) { $fw['date'] = $convertedDate; } else { unset($fw['date']); } } } if (isset($data['content']['storages'])) { foreach ($data['content']['storages'] as &$storage) { //storages serial-ata with several cases if (isset($storage['interface']) && strtolower($storage['interface']) == 'serial-ata') { $storage['interface'] = 'SATA'; } //rename serialnumber to serial if (isset($storage['serialnumber'])) { if (!isset($storage['serial'])) { $storage['serial'] = $storage['serialnumber']; } unset($storage['serialnumber']); } } } //some envs may have an empty value, dropped along with all empty nodes if (isset($data['content']['envs'])) { foreach ($data['content']['envs'] as &$env) { if (!isset($env['val'])) { $env['val'] = ''; } } } //slot status in old versions if (isset($data['content']['slots'])) { foreach ($data['content']['slots'] as &$slot) { if (isset($slot['status'])) { switch (strtolower($slot['status'])) { case 'in use': $slot['status'] = 'used'; break; case 'available': $slot['status'] = 'free'; break; case 'unknown': default: unset($slot['status']); break; } } } } //vm status in old versions if (isset($data['content']['virtualmachines'])) { foreach ($data['content']['virtualmachines'] as &$vm) { if (isset($vm['vmtype'])) { $vm['vmtype'] = strtolower($vm['vmtype']); switch (strtolower($vm['vmtype'])) { case 'hyper-v': $vm['vmtype'] = 'hyperv'; break; case 'solaris zones': case 'solaris zone': $vm['vmtype'] = 'solariszone'; break; } } if (isset($vm['status'])) { switch (strtolower($vm['status'])) { case 'pause': $vm['status'] = 'paused'; break; case 'stopped': $vm['status'] = 'off'; break; case 'unknown': unset($vm['status']); break; } } } } if (isset($data['content']['hardware']['versionclient'])) { if (!isset($data['content']['versionclient'])) { $data['content']['versionclient'] = $data['content']['hardware']['versionclient']; } unset($data['content']['hardware']['versionclient']); } if (isset($data['content']['accountinfo'])) { $ainfos = $data['content']['accountinfo']; if ( isset($ainfos['keyname']) && $ainfos['keyname'] == 'TAG' && isset($ainfos['keyvalue']) && $ainfos['keyvalue'] != '' ) { if (!isset($data['tag'])) { $data['tag'] = $ainfos['keyvalue']; } } unset($data['content']['accountinfo']); } //missing hour in timezone offset if (isset($data['content']['operatingsystem']['timezone'])) { $timezone = &$data['content']['operatingsystem']['timezone']; if (preg_match('/^[+-][0-9]{2}$/', $timezone['offset'])) { $timezone['offset'] .= '00'; } if (!isset($timezone['name'])) { $timezone['name'] = $timezone['offset']; } } if (isset($data['content']['operatingsystem'])) { $os = &$data['content']['operatingsystem']; if (($convertedDate = $this->convertDate($os['install_date'] ?? '')) !== null) { $os['install_date'] = $convertedDate; } else { unset($os['install_date']); } } if (isset($data['content']['operatingsystem'])) { $os = &$data['content']['operatingsystem']; if (($convertedDate = $this->convertDate($os['install_date'] ?? '')) !== null) { $os['install_date'] = $convertedDate; } else { unset($os['install_date']); } } //Fix batteries capacities & voltages if (isset($data['content']['batteries'])) { foreach ($data['content']['batteries'] as &$battery) { $powers = [ 'capacity', 'real_capacity', 'power_max' ]; foreach ($powers as $power) { if (isset($battery[$power])) { $value = $this->convertBatteryPower($battery[$power]); if (!$value) { unset($battery[$power]); } else { $battery[$power] = $value; } } } if (isset($battery['voltage'])) { $voltage = $this->convertBatteryVoltage($battery['voltage']); if (!$voltage) { unset($battery['voltage']); } else { $battery['voltage'] = $voltage; } } } } //Fix powersupplies capacities if (isset($data['content']['powersupplies'])) { foreach ($data['content']['powersupplies'] as &$psupply) { if (isset($psupply['power_max'])) { $value = $this->convertBatteryPower($psupply['power_max']); if (!$value) { unset($psupply['power_max']); } else { $psupply['power_max'] = $value; } } } } //type on ports is required if (isset($data['content']['ports'])) { foreach ($data['content']['ports'] as &$port) { if (!isset($port['type'])) { $port['type'] = 'None'; } } } if (!isset($data['itemtype'])) { //set a default $data['itemtype'] = 'Computer'; } //rename macaddr to mac for networks if (isset($data['content']['networks'])) { foreach ($data['content']['networks'] as &$network) { if (isset($network['macaddr'])) { if (!isset($network['mac'])) { $network['mac'] = $network['macaddr']; } unset($network['macaddr']); } } } //fix memories that can have a unit if (isset($data['content']['hardware']['memory'])) { $data['content']['hardware']['memory'] = $this->convertMemory($data['content']['hardware']['memory']); } if (isset($data['content']['hardware']['swap'])) { $data['content']['hardware']['swap'] = $this->convertMemory($data['content']['hardware']['swap']); } if (isset($data['content']['memories'])) { foreach ($data['content']['memories'] as &$memory) { if (isset($memory['capacity'])) { $memory['capacity'] = $this->convertMemory($memory['capacity']); } } } if (isset($data['content']['videos'])) { foreach ($data['content']['videos'] as &$video) { if (isset($video['memory'])) { $video['memory'] = $this->convertMemory($video['memory']); } } } if (isset($data['content']['virtualmachines'])) { foreach ($data['content']['virtualmachines'] as &$vm) { if (isset($vm['memory'])) { $vm['memory'] = $this->convertMemory($vm['memory']); } } } if (isset($data['content']['network_device'])) { $netdev = &$data['content']['network_device']; if (isset($netdev['memory'])) { $netdev['memory'] = $this->convertMemory($netdev['memory']); } if (isset($netdev['ram'])) { $netdev['ram'] = $this->convertMemory($netdev['ram']); } } //no longer existing $drops = [ 'registry', 'userslist', 'mib_applications', 'mib_components', 'jvms' ]; $drops_objects = [ 'hardware' => [ 'archname', 'osname', 'checksum', 'etime', 'ipaddr', 'osversion', 'oscomments', 'processorn', 'processors', 'processort', 'userid', 'lastdate', 'userdomain' ], 'operatingsystem' => [ 'boot_date' ], 'bios' => [ 'type' ], 'network_device' => [ 'comments', 'id' ] ]; $drops_arrays = [ 'licenseinfos' => [ 'oem' ], 'drives' => [ 'numfiles' ], 'inputs' => [ 'pointtype' ], 'networks' => [ 'typemib' ], 'softwares' => [ 'filename', 'source', 'language', 'is64bit', 'releasetype' ], 'controllers' => [ 'description', 'version' ], 'slots' => [ 'shared' ], 'physical_volumes' => [ 'pv_name' ], 'videos' => [ 'pciid' ], 'cpus' => [ 'type' ], 'sensors' => [ 'power' ], 'batteries' => [ 'temperature', 'level', 'health', 'status' ], 'network_components' => [ 'ip', 'mac' ], 'virtualmachines' => [ 'vmid' ] ]; foreach ($drops as $drop) { unset($data['content'][$drop]); } foreach ($drops_objects as $parent => $children) { foreach ($children as $child) { unset($data['content'][$parent][$child]); } } foreach ($drops_arrays as $parent => $children) { if (!isset($data['content'][$parent])) { continue; } foreach ($data['content'][$parent] as &$entry) { foreach ($children as $child) { unset($entry[$child]); } } } unset( $data['uuid'], $data['user'], $data['userdefinedproperties'], $data['agentsname'], $data['machineid'], $data['cfkey'], $data['policies'], $data['hostname'], $data['processors'], $data['policy_server'] ); //pciid to vendorid:productid $pciids_nodes = [ 'controllers' ]; foreach ($pciids_nodes as $pciid_node) { if (isset($data['content'][$pciid_node])) { foreach ($data['content'][$pciid_node] as &$node) { $this->checkPciid($node); } } } //handle user node on some phone inventories if (isset($data['content']['user'])) { if (!isset($data['content']['users'])) { $data['content']['users'] = $data['content']['user']; } unset($data['content']['user']); } //wrong name if (isset($data['content']['simcards'])) { foreach ($data['content']['simcards'] as &$simcard) { if (isset($simcard['line_number'])) { if (!isset($simcard['phone_number'])) { $simcard['phone_number'] = $simcard['line_number']; } unset($simcard['line_number']); } } } return $data; } /** * Set convert types * * @param array<string, array<int, string>> $convert_types Convert types configuration * * @return Converter */ public function setConvertTypes(array $convert_types): self { $this->convert_types = $convert_types; return $this; } /** * Converts values for all entries of name to requested type * * Method must populate $convert_types array. * @see Converter::convert_types parameter * * @param array<string, mixed> $data Input data, will be modified * * @return void */ public function convertTypes(array &$data): void { $types = $this->convert_types; foreach ($types as $type => $names) { foreach ($names as $name) { $keys = explode('/', $name); if (count($keys) != 2) { throw new RuntimeException($name . ' not supported!'); } if (isset($data['content'][$keys[0]])) { if (is_array($data['content'][$keys[0]])) { foreach ($data['content'][$keys[0]] as $key => $value) { if (isset($data['content'][$keys[0]][$key][$keys[1]])) { $data['content'][$keys[0]][$key][$keys[1]] = $this->getCastedValue( $data['content'][$keys[0]][$key][$keys[1]], $type ); } } } else { if (isset($data['content'][$keys[0]][$keys[1]])) { $data['content'][$keys[0]][$keys[1]] = $this->getCastedValue( $data['content'][$keys[0]][$keys[1]], $type ); } } } if (isset($data['content'][$keys[0]][$keys[1]])) { $data['content'][$keys[0]][$keys[1]] = $this->getCastedValue( $data['content'][$keys[0]][$keys[1]], $type ); } } } } /** * Get value casted * * @param string $value Original value * @param string $type Requested type * * @return mixed */ public function getCastedValue(string $value, string $type) { switch ($type) { case 'boolean': return (bool)$value; case 'integer': $casted = (int)$value; if (is_numeric($value) && $value == $casted) { return $casted; } else { return null; } default: throw new UnexpectedValueException('Type ' . $type . ' not known.'); } } /** * Change array keys case recursively * * @param array<string, mixed> $array Input array * * @return array<string, mixed> */ public function arrayChangeKeyCaseRecursive(array $array): array { return array_map( function ($item) { if (is_array($item)) { $item = $this->arrayChangeKeyCaseRecursive($item); } return $item; }, array_change_key_case($array) ); } /** * Convert a date * * @param string $value Current value * @param string $format Format for output * * @return string|null */ public function convertDate(string $value, string $format = 'Y-m-d'): ?string { $nullables = ['n/a', 'boot_time']; if (empty($value) || isset(array_flip($nullables)[strtolower($value)])) { return null; } $formats = [ 'D M d H:i:s Y', //Thu Mar 14 15:05:41 2013 'Y-m-d\TH:i:sZ', 'd/m/Y H:i:s', 'Y-m-d H:i:s', 'd/m/Y H:i', 'Y-m-d H:i', 'd/m/Y', 'm/d/Y', 'Y-m-d', 'd.m.Y', 'Ymd' ]; while ($current = array_shift($formats)) { $d = DateTime::createFromFormat($current, $value); if ($d !== false) { break; } } if ($d !== false) { return $d->format($format); } return $value; } /** * Load schema patterns that will be used to validate * * @return void */ public function loadSchemaPatterns(): void { $string = file_get_contents($this->getSchemaPath()); if ($string === false) { throw new RuntimeException('Unable to read schema file'); } $json = json_decode($string, true); $this->schema_patterns['networks_types'] = explode( '|', str_replace( ['^(', ')$'], ['', ''], $json['properties']['content']['properties']['networks']['items']['properties']['type']['pattern'] ) ); } /** * Convert battery capacity * * @param integer|string $capacity Inventoried capacity * * @return integer|false */ public function convertBatteryPower($capacity) { if (is_int($capacity)) { return $capacity; } $capa_pattern = "/^([0-9]+(\.[0-9]+)?) Wh$/i"; $matches = []; if (preg_match($capa_pattern, $capacity, $matches)) { return (int)round((float)$matches[1] * 1000); } $capa_pattern = '/^([0-9]+) mWh$/i'; $matches = []; if (preg_match($capa_pattern, $capacity, $matches)) { return (int)$matches[1]; } if (is_string($capacity) && ctype_digit($capacity)) { return (int)$capacity; } $capa_pattern = '/^([0-9]+)\.0+$/'; $matches = []; if (preg_match($capa_pattern, $capacity, $matches)) { return (int)$matches[1]; } return false; } /** * Convert battery voltage * * @param string $voltage Inventoried voltage * * @return integer|false */ public function convertBatteryVoltage(string $voltage) { $volt_pattern = "/^([0-9]+(\.[0-9]+)?) ?V$/i"; $matches = []; if (preg_match($volt_pattern, $voltage, $matches)) { return (int)round((float)$matches[1] * 1000); } $volt_pattern = '/^([0-9]+) mV$/i'; $matches = []; if (preg_match($volt_pattern, $voltage, $matches)) { return (int)$matches[1]; } if (ctype_digit($voltage)) { return (int)$voltage; } return false; } /** * Convert memory capacity * * @param string $capacity Inventoried capacity * * @return ?integer */ public function convertMemory(string $capacity) { if (is_int($casted_capa = $this->getCastedValue($capacity, 'integer'))) { return $casted_capa; } $mem_pattern = "/^([0-9]+([\.|,][0-9])?) ?(.?B)$/i"; $matches = []; if (preg_match($mem_pattern, $capacity, $matches)) { //we got a memory with a unit. first, convert to bytes $real_value = $this->getCastedValue($matches[1], 'integer'); switch (strtolower($matches[3])) { case 'pb': $real_value *= 1024; //no break, continue to next case 'tb': $real_value *= 1024; //no break, continue to next case 'gb': $real_value *= 1024; //no break, continue to next case 'mb': $real_value *= 1024; //no break, continue to next case 'kb': $real_value *= 1024; //no break, continue to next case 'b': break; default: return null; } //then return as Mb. return $real_value / 1024 / 1024; } return null; } /** * Handle network inventory XML format * * @param array<string, mixed> $data Contents * * @return array<string, mixed> */ public function convertNetworkInventory(array $data): array { if (!$this->isNetworkInventory($data)) { //not a network inventory XML return $data; } //pre handle for network discoveries $data = $this->convertNetworkDiscovery($data); if (!isset($data['content']['versionclient']) && isset($data['content']['moduleversion'])) { $data['content']['versionclient'] = $data['content']['moduleversion']; unset($data['content']['moduleversion']); } if (isset($data['content']['processnumber'])) { $data['jobid'] = (int)$data['content']['processnumber']; unset($data['content']['processnumber']); } $device = $data['content']['device']; foreach ($device as $key => $device_data) { switch ($key) { case 'info': $device_info = $device['info']; if (isset($device_info['cpu'])) { $device_info['cpu'] = (int)$device_info['cpu']; } if (isset($device_info['id'])) { $device_info['id'] = (int)$device_info['id']; } if (isset($device_info['comments'])) { $device_info['description'] = $device_info['comments']; } //Fix network inventory type if (isset($device_info['type'])) { $device_info['type'] = ucfirst(strtolower($device_info['type'])); if ($device_info['type'] == 'Kvm') { $device_info['type'] = 'KVM'; } } if (isset($device_info['macaddr'])) { $device_info['mac'] = $device_info['macaddr']; } if (isset($device_info['ips'])) { $device_info['ips'] = is_array($device_info['ips']['ip']) ? $device_info['ips']['ip'] : [$device_info['ips']['ip']]; } $data['content']['network_device'] = $device_info; //Prior to agent 2.3.22, we get only a firmware version in device information if ( (!isset($device['firmwares']) || !count($device['firmwares'])) && isset($device_data['firmware']) ) { $data['content']['firmwares'] = array_merge( $data['content']['firmwares'] ?? [], [ 'version' => $device_data['firmware'], 'name' => $device_data['firmware'] ] ); } //Guess itemtype from device type info if (!isset($data['itemtype'])) { $itemtype = 'Computer'; if (isset($device_info['type'])) { switch ($device_info['type']) { case 'Computer': case 'Phone': case 'Printer': case 'Unmanaged': $itemtype = $device_info['type']; break; case 'Networking': case 'Storage': case 'Power': case 'Video': case 'KVM': $itemtype = 'NetworkEquipment'; break; default: throw new RuntimeException('Unhandled device type: ' . $device_info['type']); } } $data['itemtype'] = $itemtype; } break; case 'ports': $data['content']['network_ports'] = array_is_list($device['ports']['port']) ? $device['ports']['port'] : [$device['ports']['port']]; //check for arrays foreach ($data['content']['network_ports'] as &$netport) { if (isset($netport['vlans'])) { $netport['vlans'] = array_is_list($netport['vlans']['vlan']) ? $netport['vlans']['vlan'] : [$netport['vlans']['vlan']]; } if (isset($netport['connections']['cdp'])) { $netport['lldp'] = (bool)$netport['connections']['cdp']; unset($netport['connections']['cdp']); } //rename ifinoctets and ifoutoctets if (isset($netport['ifinoctets'])) { if (!isset($netport['ifinbytes'])) { $netport['ifinbytes'] = $netport['ifinoctets']; } unset($netport['ifinoctets']); } if (isset($netport['ifoutoctets'])) { if (!isset($netport['ifoutbytes'])) { $netport['ifoutbytes'] = $netport['ifoutoctets']; } unset($netport['ifoutoctets']); } if (isset($netport['connections'])) { $netport['connections'] = array_is_list($netport['connections']['connection']) ? $netport['connections']['connection'] : [$netport['connections']['connection']]; //replace bad typed values... foreach ($netport['connections'] as &$connection) { if (isset($connection['ifnumber'])) { $connection['ifnumber'] = $this->getCastedValue($connection['ifnumber'], 'integer'); } } } if (isset($netport['aggregate'])) { $netport['aggregate'] = is_array($netport['aggregate']['port']) && array_is_list($netport['aggregate']['port']) ? $netport['aggregate']['port'] : [$netport['aggregate']['port']]; $netport['aggregate'] = array_map('intval', $netport['aggregate']); } if (isset($netport['ip'])) { if (!isset($netport['ips'])) { $netport['ips']['ip'] = [$netport['ip']]; } unset($netport['ip']); } if (isset($netport['ips'])) { $netport['ips'] = is_array($netport['ips']['ip']) ? $netport['ips']['ip'] : [$netport['ips']['ip']]; } } break; case 'firmwares': case 'modems': case 'simcards': //first, retrieve data from device $elements = $device_data; if (!array_is_list($elements)) { $elements = [$elements]; } //second, append them to data if (isset($data['content'][$key])) { if (!array_is_list($data['content'][$key])) { $data['content'][$key] = [$data['content'][$key]]; } } $data['content'][$key] = array_merge( $data['content'][$key] ?? [], $elements ); break; case 'components': $data['content']['network_components'] = array_is_list($device['components']['component']) ? $device['components']['component'] : [$device['components']['component']]; foreach ($data['content']['network_components'] as &$netcomp) { if (isset($netcomp['containedinindex'])) { if (!isset($netcomp['contained_index'])) { $netcomp['contained_index'] = (int)$netcomp['containedinindex']; } unset($netcomp['containedinindex']); } if (isset($netcomp['revision'])) { unset($netcomp['revision']); } if (isset($netcomp['version'])) { if (!isset($netcomp['firmware'])) { $netcomp['firmware'] = $netcomp['version']; } unset($netcomp['version']); } } break; case "cartridges": case "pagecounters": case "drives": case "error": $data['content'][$key] = $device_data; break; default: throw new RuntimeException('Key ' . $key . ' is not handled in network devices conversion'); } } if (!isset($data['content']['versionclient']) && $data['itemtype'] == 'Printer') { $data['content']['versionclient'] = 'missing'; } unset($data['content']['device']); return $data; } /** * Pre-handle network inventory XML format * * @param array<string, mixed> $data Contents * * @return array<string, mixed> */ public function convertNetworkDiscovery(array $data): array { if (!$this->isNetworkDiscovery($data)) { //not a network discovery XML return $data; } $device = &$data['content']['device']; if (!isset($device['info'])) { $device['info'] = ['type' => 'Unmanaged']; } $device_info = &$data['content']['device']['info']; if (!empty($device['snmphostname'])) { //SNMP hostname has precedence if (isset($device['dnshostname'])) { unset($device['dnshostname']); } if (isset($device['netbiosname'])) { unset($device['netbiosname']); } } if (!empty($device['netbiosname']) && isset($device['dnshostname'])) { //NETBIOS name has precedence unset($device['dnshostname']); } if (isset($device['ip'])) { if (!isset($device['ips'])) { $device['ips']['ip'] = [$device['ip']]; } unset($device['ip']); } foreach ($device as $key => $device_data) { switch ($key) { case 'info': //empty for discovery break; case 'dnshostname': case 'snmphostname': case 'netbiosname': $device_info['name'] = $device_data; unset($device[$key]); break; case 'entity': case 'usersession': unset($device[$key]); //not used break; case 'ips': $device_info['ips'] = $device_data; unset($device[$key]); break; case 'mac': case 'contact': case 'firmware': case 'location': case 'manufacturer': case 'model': case 'uptime': case 'type': case 'description': case 'serial': case 'assettag': $device_info[$key] = $device_data; unset($device[$key]); break; case 'netportvendor': //translate as manufacturer - if not present if (!isset($device['manufacturer'])) { $device_info['manufacturer'] = $device_data; } unset($device[$key]); break; case 'workgroup': $data['content']['hardware']['workgroup'] = $device_data; unset($device[$key]); break; case 'authsnmp': $device_info['credentials'] = $device_data; unset($device[$key]); break; default: throw new RuntimeException('Key ' . $key . ' is not handled in network discovery conversion'); } } return $data; } /** * Explode old pciid to vendorid:productid * * @param array<string, mixed> $data Node data * * @return void */ private function checkPciid(array &$data): void { if (isset($data['pciid'])) { list($vid, $pid) = explode(':', $data['pciid']); if (!isset($data['vendorid']) && $vid) { $data['vendorid'] = $vid; } if (!isset($data['productid']) && $pid) { $data['productid'] = $pid; } unset($data['pciid']); } } /** * Is a network inventory? * * @param array<string, mixed> $data Data * * @return boolean */ private function isNetworkInventory(array $data): bool { return isset($data['content']['device']); } /** * Is a network discovery? * * @param array<string, mixed> $data Data * * @return boolean */ private function isNetworkDiscovery(array $data): bool { return isset($data['content']['device']) && $data['action'] == 'netdiscovery'; } }