diff --git a/pandora_console/godmode/menu.php b/pandora_console/godmode/menu.php
index 6f36df074b..32c19ac4f7 100644
--- a/pandora_console/godmode/menu.php
+++ b/pandora_console/godmode/menu.php
@@ -30,6 +30,7 @@
// Begin.
require_once 'include/config.php';
require_once 'include/functions_menu.php';
+require_once $config['homedir'].'/godmode/wizards/ManageExtensions.class.php';
check_login();
@@ -78,10 +79,91 @@ if ((bool) check_acl($config['id_user'], 0, 'AR') === true
}
if ((bool) check_acl($config['id_user'], 0, 'AW') === true) {
- enterprise_hook('applications_menu');
- enterprise_hook('cloud_menu');
- enterprise_hook('custom_menu');
- enterprise_hook('manage_extensions_menu');
+ // Applications.
+ $sub2 = [];
+ if (enterprise_installed() === true) {
+ $sub2['godmode/servers/discovery&wiz=app&mode=MicrosoftSQLServer']['text'] = __('Microsoft SQL Server');
+ $sub2['godmode/servers/discovery&wiz=app&mode=mysql']['text'] = __('Mysql');
+ $sub2['godmode/servers/discovery&wiz=app&mode=oracle']['text'] = __('Oracle');
+ $sub2['godmode/servers/discovery&wiz=app&mode=vmware']['text'] = __('VMware');
+ $sub2['godmode/servers/discovery&wiz=app&mode=SAP']['text'] = __('SAP');
+ $sub2['godmode/servers/discovery&wiz=app&mode=DB2']['text'] = __('DB2');
+ }
+
+ $extensions = ManageExtensions::getExtensionBySection('app');
+ if ($extensions !== false) {
+ foreach ($extensions as $key => $extension) {
+ $url = sprintf(
+ 'godmode/servers/discovery&wiz=app&mode=%s',
+ $extension['short_name']
+ );
+ $sub2[$url]['text'] = __($extension['name']);
+ }
+ }
+
+ if ($extensions !== false || enterprise_installed() === true) {
+ $sub['godmode/servers/discovery&wiz=app']['text'] = __('Applications');
+ $sub['godmode/servers/discovery&wiz=app']['id'] = 'app';
+ $sub['godmode/servers/discovery&wiz=app']['type'] = 'direct';
+ $sub['godmode/servers/discovery&wiz=app']['subtype'] = 'nolink';
+ $sub['godmode/servers/discovery&wiz=app']['sub2'] = $sub2;
+ }
+
+ // Cloud.
+ $sub2 = [];
+ if (enterprise_installed() === true) {
+ $sub2['godmode/servers/discovery&wiz=cloud&mode=amazonws']['text'] = __('Amazon Web Services');
+ $sub2['godmode/servers/discovery&wiz=cloud&mode=azure']['text'] = __('Microsoft Azure');
+ $sub2['godmode/servers/discovery&wiz=cloud&mode=gcp']['text'] = __('Google Compute Platform');
+ }
+
+
+ $extensions = ManageExtensions::getExtensionBySection('cloud');
+ if ($extensions !== false) {
+ foreach ($extensions as $key => $extension) {
+ $url = sprintf(
+ 'godmode/servers/discovery&wiz=cloud&mode=%s',
+ $extension['short_name']
+ );
+ $sub2[$url]['text'] = __($extension['name']);
+ }
+ }
+
+ if ($extensions !== false || enterprise_installed() === true) {
+ $sub['godmode/servers/discovery&wiz=cloud']['text'] = __('Cloud');
+ $sub['godmode/servers/discovery&wiz=cloud']['id'] = 'cloud';
+ $sub['godmode/servers/discovery&wiz=cloud']['type'] = 'direct';
+ $sub['godmode/servers/discovery&wiz=cloud']['subtype'] = 'nolink';
+ $sub['godmode/servers/discovery&wiz=cloud']['sub2'] = $sub2;
+ }
+
+ // Custom.
+ $sub2 = [];
+ $extensions = ManageExtensions::getExtensionBySection('custom');
+ if ($extensions !== false) {
+ foreach ($extensions as $key => $extension) {
+ $url = sprintf(
+ 'godmode/servers/discovery&wiz=custom&mode=%s',
+ $extension['short_name']
+ );
+ $sub2[$url]['text'] = __($extension['name']);
+ }
+
+ $sub['godmode/servers/discovery&wiz=custom']['text'] = __('Custom');
+ $sub['godmode/servers/discovery&wiz=custom']['id'] = 'customExt';
+ $sub['godmode/servers/discovery&wiz=custom']['type'] = 'direct';
+ $sub['godmode/servers/discovery&wiz=custom']['subtype'] = 'nolink';
+ $sub['godmode/servers/discovery&wiz=custom']['sub2'] = $sub2;
+ }
+
+ if (check_acl($config['id_user'], 0, 'RW')
+ || check_acl($config['id_user'], 0, 'RM')
+ || check_acl($config['id_user'], 0, 'PM')
+ ) {
+ $sub['godmode/servers/discovery&wiz=magextensions']['text'] = __('Manage extensions');
+ $sub['godmode/servers/discovery&wiz=magextensions']['id'] = 'mextensions';
+ }
+
enterprise_hook('console_task_menu');
}
}
diff --git a/pandora_console/godmode/wizards/Applications.class.php b/pandora_console/godmode/wizards/Applications.class.php
new file mode 100644
index 0000000000..2237fdbe73
--- /dev/null
+++ b/pandora_console/godmode/wizards/Applications.class.php
@@ -0,0 +1,221 @@
+setBreadcrum([]);
+
+ $this->access = 'AW';
+ $this->task = [];
+ $this->msg = $msg;
+ $this->icon = $icon;
+ $this->class = $class_style;
+ $this->label = $label;
+ $this->page = $page;
+ $this->url = ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=app'
+ );
+
+ return $this;
+ }
+
+
+ /**
+ * Run wizard manager.
+ *
+ * @return mixed Returns null if wizard is ongoing. Result if done.
+ */
+ public function run()
+ {
+ global $config;
+
+ // Load styles.
+ parent::run();
+
+ // Load current wiz. sub-styles.
+ ui_require_css_file(
+ 'application',
+ ENTERPRISE_DIR.'/include/styles/wizards/'
+ );
+
+ $mode = get_parameter('mode', null);
+
+ // Load application wizards.
+ $enterprise_classes = glob(
+ $config['homedir'].'/'.ENTERPRISE_DIR.'/include/class/*.app.php'
+ );
+ $extensions = new ExtensionsDiscovery('app', $mode);
+
+ foreach ($enterprise_classes as $classpath) {
+ enterprise_include_once(
+ 'include/class/'.basename($classpath)
+ );
+ }
+
+ switch ($mode) {
+ case 'DB2':
+ $classname_selected = 'DB2';
+ break;
+
+ case 'SAP':
+ $classname_selected = 'SAP';
+ break;
+
+ case 'vmware':
+ $classname_selected = 'VMware';
+ break;
+
+ case 'mysql':
+ $classname_selected = 'MySQL';
+ break;
+
+ case 'oracle':
+ $classname_selected = 'Oracle';
+ break;
+
+ case 'MicrosoftSQLServer':
+ $classname_selected = 'MicrosoftSQLServer';
+ break;
+
+ default:
+ $classname_selected = null;
+ break;
+ }
+
+ // Else: class not found pseudo exception.
+ if ($classname_selected !== null) {
+ $wiz = new $classname_selected($this->page);
+ $result = $wiz->run();
+ if (is_array($result) === true) {
+ return $result;
+ }
+ }
+
+ if ($classname_selected === null) {
+ if ($mode !== null) {
+ // Load extension if exist.
+ $extensions->run();
+ return;
+ }
+
+ // Load classes and print selector.
+ $wiz_data = [];
+ foreach ($enterprise_classes as $classpath) {
+ $classname = basename($classpath, '.app.php');
+ $obj = new $classname();
+ $wiz_data[] = $obj->load();
+ }
+
+ $wiz_data = array_merge($wiz_data, $extensions->loadExtensions());
+
+ $this->prepareBreadcrum(
+ [
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery'
+ ),
+ 'label' => __('Discovery'),
+ ],
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=app'
+ ),
+ 'label' => __('Applications'),
+ 'selected' => true,
+ ],
+ ]
+ );
+
+ // Header.
+ ui_print_page_header(
+ __('Applications'),
+ '',
+ false,
+ '',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ Wizard::printBigButtonsList($wiz_data);
+
+ echo '
*'.__('All company names used here are for identification purposes only. Use of these names, logos, and brands does not imply endorsement.').'
';
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Check if section have extensions.
+ *
+ * @return boolean Return true if section is empty.
+ */
+ public function isEmpty()
+ {
+ $extensions = new ExtensionsDiscovery('app');
+ $listExtensions = $extensions->getExtensionsApps();
+ if ($listExtensions > 0 || enterprise_installed() === true) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+}
diff --git a/pandora_console/godmode/wizards/Cloud.class.php b/pandora_console/godmode/wizards/Cloud.class.php
new file mode 100644
index 0000000000..4664b1a566
--- /dev/null
+++ b/pandora_console/godmode/wizards/Cloud.class.php
@@ -0,0 +1,661 @@
+setBreadcrum([]);
+
+ $this->access = 'AW';
+ $this->task = [];
+ $this->msg = $msg;
+ $this->icon = $icon;
+ $this->label = $label;
+ $this->page = $page;
+ $this->url = ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=cloud'
+ );
+
+ return $this;
+ }
+
+
+ /**
+ * Run wizard manager.
+ *
+ * @return mixed Returns null if wizard is ongoing. Result if done.
+ */
+ public function run()
+ {
+ global $config;
+
+ // Load styles.
+ parent::run();
+
+ // Load current wiz. sub-styles.
+ ui_require_css_file(
+ 'cloud',
+ ENTERPRISE_DIR.'/include/styles/wizards/'
+ );
+
+ $mode = get_parameter('mode', null);
+
+ // Load cloud wizards.
+ $enterprise_classes = glob(
+ $config['homedir'].'/'.ENTERPRISE_DIR.'/include/class/*.cloud.php'
+ );
+ $extensions = new ExtensionsDiscovery('cloud', $mode);
+
+ foreach ($enterprise_classes as $classpath) {
+ enterprise_include_once(
+ 'include/class/'.basename($classpath)
+ );
+ }
+
+ switch ($mode) {
+ case 'amazonws':
+ $classname_selected = 'Aws';
+ break;
+
+ case 'azure':
+ $classname_selected = 'Azure';
+ break;
+
+ case 'gcp':
+ $classname_selected = 'Google';
+ break;
+
+ default:
+ $classname_selected = null;
+ break;
+ }
+
+ // Else: class not found pseudo exception.
+ if ($classname_selected !== null) {
+ $wiz = new $classname_selected($this->page);
+ $result = $wiz->run();
+ if (is_array($result) === true) {
+ return $result;
+ }
+ }
+
+ if ($classname_selected === null) {
+ if ($mode !== null) {
+ // Load extension if exist.
+ $extensions->run();
+ return;
+ }
+
+ // Load classes and print selector.
+ $wiz_data = [];
+ foreach ($enterprise_classes as $classpath) {
+ $classname = basename($classpath, '.cloud.php');
+ $obj = new $classname();
+ $wiz_data[] = $obj->load();
+ }
+
+ $wiz_data = array_merge($wiz_data, $extensions->loadExtensions());
+
+ $this->prepareBreadcrum(
+ [
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery'
+ ),
+ 'label' => __('Discovery'),
+ ],
+ [
+ 'link' => $this->url,
+ 'label' => __('Cloud'),
+ 'selected' => true,
+ ],
+ ],
+ true
+ );
+
+ // Header.
+ ui_print_page_header(
+ __('Cloud'),
+ '',
+ false,
+ '',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ Wizard::printBigButtonsList($wiz_data);
+
+ echo '*'.__('All company names used here are for identification purposes only. Use of these names, logos, and brands does not imply endorsement.').'
';
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Run credentials wizard.
+ *
+ * @return boolean True if credentials wizard is displayed and false if not.
+ */
+ public function runCredentials()
+ {
+ global $config;
+
+ if ($this->status === false) {
+ $empty_account = true;
+ }
+
+ // Checks credentials. If check not passed. Show the form to fill it.
+ if ($this->checkCredentials()) {
+ return true;
+ }
+
+ // Add breadcrum and print header.
+ $this->prepareBreadcrum(
+ [
+ [
+ 'link' => $this->url.'&credentials=1',
+ 'label' => __('%s credentials', $this->product),
+ 'selected' => true,
+ ],
+ ],
+ true
+ );
+ // Header.
+ ui_print_page_header(
+ __('%s credentials', $this->product),
+ '',
+ false,
+ $this->product.'_credentials_tab',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ if ($this->product === 'Aws') {
+ ui_print_warning_message(
+ __(
+ 'If a task with the selected credentials is already running, it will be edited. To create a new one, another account from the credential store must be selected.'
+ )
+ );
+ }
+
+ if ($this->status === true) {
+ ui_print_success_message($this->msg);
+ } else if ($this->status === false) {
+ ui_print_error_message($this->msg);
+ }
+
+ if ($empty_account === true) {
+ ui_print_error_message($this->msg);
+ }
+
+ $link_to_cs = '';
+ if (check_acl($config['id_user'], 0, 'UM')) {
+ $link_to_cs = '';
+ $link_to_cs .= __('Manage accounts').'';
+ }
+
+ $this->getCredentials();
+ $this->printFormAsList(
+ [
+ 'form' => [
+ 'action' => $this->url,
+ 'method' => 'POST',
+ 'id' => 'form-credentials',
+ ],
+ 'inputs' => [
+ [
+ 'label' => __('Cloud tool full path'),
+ 'arguments' => [
+ 'name' => 'cloud_util_path',
+ 'value' => isset($config['cloud_util_path']) ? io_safe_output($config['cloud_util_path']) : '/usr/bin/pandora-cm-api',
+ 'type' => 'text',
+ ],
+ ],
+ [
+ 'label' => __('Account'),
+ 'extra' => $link_to_cs,
+ 'arguments' => [
+ 'name' => 'account_identifier',
+ 'type' => 'select',
+ 'fields' => CredentialStore::getKeys($this->keyStoreType),
+ 'selected' => $this->keyIdentifier,
+ 'return' => true,
+ ],
+ ],
+ [
+ 'arguments' => [
+ 'name' => 'parse_credentials',
+ 'value' => 1,
+ 'type' => 'hidden',
+ 'return' => true,
+ ],
+ ],
+ ],
+ ]
+ );
+
+ $buttons_form = $this->printInput(
+ [
+ 'name' => 'submit',
+ 'label' => __('Validate'),
+ 'type' => 'submit',
+ 'attributes' => [
+ 'icon' => 'wand',
+ 'form' => 'form-credentials',
+ ],
+ 'return' => true,
+ 'width' => 'initial',
+ ]
+ );
+
+ $buttons_form .= $this->printGoBackButton(
+ ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=cloud'
+ ),
+ true
+ );
+
+ html_print_action_buttons($buttons_form);
+ return false;
+ }
+
+
+ /**
+ * Check credentials.
+ *
+ * @return boolean True if credentials are OK.
+ */
+ public function checkCredentials()
+ {
+ global $config;
+
+ $pandora = io_safe_output($config['cloud_util_path']);
+
+ if (isset($pandora) === false) {
+ config_update_value('cloud_util_path', '/usr/bin/pandora-cm-api');
+ }
+
+ if ((bool) get_parameter('disconnect_account', false) === true) {
+ $this->status = null;
+ return false;
+ }
+
+ if ($this->keyIdentifier === null) {
+ // Ask user for available credentials.
+ $this->msg = __('Select a set of credentials from the list');
+ $this->status = null;
+ return false;
+ }
+
+ $credentials = $this->getCredentials($this->keyIdentifier);
+
+ if (empty($credentials['username']) === true
+ || empty($credentials['password']) === true
+ || isset($pandora) === false
+ || is_executable($pandora) === false
+ ) {
+ if (is_executable($pandora) === false) {
+ $this->msg = (__('Path %s is not executable.', $pandora));
+ $this->status = false;
+ } else {
+ $this->msg = __('Invalid username or password');
+ $this->status = false;
+ }
+
+ return false;
+ }
+
+ try {
+ $value = $this->executeCMCommand('--get availability');
+ } catch (Exception $e) {
+ $this->msg = $e->getMessage();
+ $this->status = false;
+ return false;
+ }
+
+ if ($value == '1') {
+ return true;
+ }
+
+ $this->status = false;
+
+ // Error message directly from pandora-cm-api.
+ $this->msg = str_replace('"', '', $value);
+
+ return false;
+ }
+
+
+ /**
+ * Handle the click on disconnect account link.
+ *
+ * @return void But it prints some info to user.
+ */
+ protected function parseDisconnectAccount()
+ {
+ // Check if disconection account link is pressed.
+ if ((bool) get_parameter('disconnect_account') === false) {
+ return;
+ }
+
+ $ret = $this->setCredentials(null);
+ if ($ret) {
+ $this->msg = __('Account disconnected');
+ } else {
+ $this->msg = __('Failed disconnecting account');
+ }
+
+ $this->status = $ret;
+ $this->page = 0;
+ }
+
+
+ /**
+ * Build an array with Product credentials.
+ *
+ * @return array with credentials (pass and id).
+ */
+ public function getCredentials()
+ {
+ return CredentialStore::getKey($this->keyIdentifier);
+ }
+
+
+ /**
+ * Set Product credentials.
+ *
+ * @param string|null $identifier Credential store identifier.
+ *
+ * @return boolean True if success.
+ */
+ public function setCredentials($identifier)
+ {
+ if ($identifier === null) {
+ unset($this->keyIdentifier);
+ return true;
+ }
+
+ if (isset($identifier) === false) {
+ return false;
+ }
+
+ $all = CredentialStore::getKeys($this->type);
+
+ if (in_array($identifier, $all) === true) {
+ $this->keyIdentifier = $identifier;
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Parse credentials form.
+ *
+ * @return void But it prints a message.
+ */
+ protected function parseCredentials()
+ {
+ global $config;
+
+ if (!$this->keyIdentifier) {
+ $this->setCredentials(get_parameter('ki', null));
+ }
+
+ // Check if credentials form is submitted.
+ if ((bool) get_parameter('parse_credentials') === false) {
+ return;
+ }
+
+ $this->page = 0;
+ $ret = $this->setCredentials(
+ get_parameter('account_identifier')
+ );
+
+ $path = get_parameter('cloud_util_path');
+ $ret_path = config_update_value('cloud_util_path', $path);
+ if ($ret_path) {
+ $config['cloud_util_path'] = $path;
+ }
+
+ if ($ret && $ret_path) {
+ $this->msg = __('Credentials successfully updated');
+ } else {
+ $this->msg = __('Failed updating credentials process');
+ }
+
+ $this->status = ($ret && $ret_path);
+ }
+
+
+ /**
+ * This method must be implemented.
+ *
+ * Execute a pandora-cm-api request.
+ *
+ * @param string $command Command to execute.
+ *
+ * @return void But must return string STDOUT of executed command.
+ * @throws Exception If not implemented.
+ */
+ protected function executeCMCommand($command)
+ {
+ throw new Exception('executeCMCommand must be implemented.');
+ }
+
+
+ /**
+ * Get a recon token value
+ *
+ * @param string $token The recon key to retrieve.
+ *
+ * @return string String with the value.
+ */
+ protected function getConfigReconElement($token)
+ {
+ if ($this->reconConfig === false
+ || isset($this->reconConfig[0][$token]) === false
+ ) {
+ if (is_array($this->task) === true
+ && isset($this->task[$token]) === true
+ ) {
+ return $this->task[$token];
+ } else {
+ return '';
+ }
+ } else {
+ return $this->reconConfig[0][$token];
+ }
+ }
+
+
+ /**
+ * Print global inputs
+ *
+ * @param boolean $last True if is last element.
+ *
+ * @return array Array with all global inputs.
+ */
+ protected function getGlobalInputs(bool $last=false)
+ {
+ $task_id = $this->task['id_rt'];
+ if (!$task_id) {
+ $task_id = $this->getConfigReconElement('id_rt');
+ }
+
+ return [
+ [
+ 'arguments' => [
+ 'name' => 'page',
+ 'value' => ($this->page + 1),
+ 'type' => 'hidden',
+ 'return' => true,
+ ],
+ ],
+ [
+ 'arguments' => [
+ 'name' => 'submit',
+ 'label' => ($last) ? __('Finish') : __('Next'),
+ 'type' => 'submit',
+ 'attributes' => 'class="sub '.(($last) ? 'wand' : 'next').'"',
+ 'return' => true,
+ ],
+ ],
+ [
+ 'arguments' => [
+ 'name' => 'task',
+ 'value' => $task_id,
+ 'type' => 'hidden',
+ 'return' => true,
+ ],
+ ],
+ [
+ 'arguments' => [
+ 'name' => 'parse_form',
+ 'value' => 1,
+ 'type' => 'hidden',
+ 'return' => true,
+ ],
+ ],
+ ];
+ }
+
+
+ /**
+ * Print required css in some points.
+ *
+ * @return string With js code.
+ */
+ protected function cloudJS()
+ {
+ return '
+ function toggleCloudSubmenu(curr_elem, id_csm){
+ if (document.getElementsByName(curr_elem)[0].checked){
+ $("#li-"+id_csm).show();
+ } else {
+ $("#li-"+id_csm).hide();
+ }
+ };
+ ';
+ }
+
+
+ /**
+ * Check if section have extensions.
+ *
+ * @return boolean Return true if section is empty.
+ */
+ public function isEmpty()
+ {
+ $extensions = new ExtensionsDiscovery('cloud');
+ $listExtensions = $extensions->getExtensionsApps();
+ if ($listExtensions > 0 || enterprise_installed() === true) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+}
diff --git a/pandora_console/godmode/wizards/Custom.class.php b/pandora_console/godmode/wizards/Custom.class.php
new file mode 100644
index 0000000000..41a177b3e3
--- /dev/null
+++ b/pandora_console/godmode/wizards/Custom.class.php
@@ -0,0 +1,160 @@
+setBreadcrum([]);
+
+ $this->access = 'AW';
+ $this->task = [];
+ $this->msg = $msg;
+ $this->icon = $icon;
+ $this->class = $class_style;
+ $this->label = $label;
+ $this->page = $page;
+ $this->url = ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=custom'
+ );
+
+ return $this;
+ }
+
+
+ /**
+ * Run wizard manager.
+ *
+ * @return mixed Returns null if wizard is ongoing. Result if done.
+ */
+ public function run()
+ {
+ global $config;
+
+ // Load styles.
+ parent::run();
+
+ // Load current wiz. sub-styles.
+ ui_require_css_file(
+ 'custom',
+ ENTERPRISE_DIR.'/include/styles/wizards/'
+ );
+
+ $mode = get_parameter('mode', null);
+ $extensions = new ExtensionsDiscovery('custom', $mode);
+ if ($mode !== null) {
+ // Load extension if exist.
+ $extensions->run();
+ return;
+ }
+
+ // Load classes and print selector.
+ $wiz_data = $extensions->loadExtensions();
+
+ $this->prepareBreadcrum(
+ [
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery'
+ ),
+ 'label' => __('Discovery'),
+ ],
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=custom'
+ ),
+ 'label' => __('Custom'),
+ 'selected' => true,
+ ],
+ ]
+ );
+
+ // Header.
+ ui_print_page_header(
+ __('Custom'),
+ '',
+ false,
+ '',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ Wizard::printBigButtonsList($wiz_data);
+
+ echo '*'.__('All company names used here are for identification purposes only. Use of these names, logos, and brands does not imply endorsement.').'
';
+ return $result;
+ }
+
+
+ /**
+ * Check if section have extensions.
+ *
+ * @return boolean Return true if section is empty.
+ */
+ public function isEmpty()
+ {
+ $extensions = new ExtensionsDiscovery('custom');
+ $listExtensions = $extensions->getExtensionsApps();
+ if ($listExtensions > 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+}
diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php
index 596703ee3f..f8c8396f93 100644
--- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php
+++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php
@@ -164,11 +164,9 @@ class DiscoveryTaskList extends HTML
if (is_reporting_console_node() === false) {
$ret2 = $this->showList(__('Host & devices tasks'), [0, 1]);
- if (enterprise_installed()) {
- $ret2 .= $this->showList(__('Applications tasks'), [3, 4, 5, 10, 11, 12], 'app');
- $ret2 .= $this->showList(__('Cloud tasks'), [6, 7, 8, 13, 14], 'cloud');
- $ret2 .= $this->showList(__('Custom tasks'), [-1], 'custom');
- }
+ $ret2 .= $this->showList(__('Applications tasks'), [3, 4, 5, 10, 11, 12], 'app');
+ $ret2 .= $this->showList(__('Cloud tasks'), [6, 7, 8, 13, 14], 'cloud');
+ $ret2 .= $this->showList(__('Custom tasks'), [-1], 'custom');
}
if ($ret === false && $ret2 === false) {
diff --git a/pandora_console/godmode/wizards/ManageExtensions.class.php b/pandora_console/godmode/wizards/ManageExtensions.class.php
new file mode 100644
index 0000000000..965b005b30
--- /dev/null
+++ b/pandora_console/godmode/wizards/ManageExtensions.class.php
@@ -0,0 +1,986 @@
+ajaxController = $config['homedir'].'/include/ajax/manage_extensions.ajax';
+ $this->url = ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=magextensions'
+ );
+ }
+
+
+ /**
+ * Checks if target method is available to be called using AJAX.
+ *
+ * @param string $method Target method.
+ *
+ * @return boolean True allowed, false not.
+ */
+ public function ajaxMethod($method)
+ {
+ // Check access.
+ check_login();
+
+ return in_array($method, $this->AJAXMethods);
+ }
+
+
+ /**
+ * Implements load method.
+ *
+ * @return mixed Skeleton for button.
+ */
+ public function load()
+ {
+ return [
+ 'icon' => $this->icon,
+ 'label' => $this->label,
+ 'url' => $this->url,
+
+ ];
+
+ }
+
+
+ /**
+ * Generates a JSON error.
+ *
+ * @param string $msg Error message.
+ *
+ * @return void
+ */
+ public function errorAjax(string $msg)
+ {
+ echo json_encode(
+ ['error' => $msg]
+ );
+ }
+
+
+ /**
+ * Implements run method.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ global $config;
+ // Load styles.
+ parent::run();
+
+ $uploadDisco = get_parameter('upload_disco', '');
+ $action = get_parameter('action', '');
+ $shortName = get_parameter('short_name', '');
+
+ if (empty($uploadDisco) === false) {
+ if ($_FILES['file']['error'] == 0) {
+ $result = $this->uploadExtension($_FILES['file']);
+ if ($result === true) {
+ ui_print_success_message(
+ __('Uploaded extension')
+ );
+ } else {
+ if (is_string($result)) {
+ echo $this->error($result);
+ } else {
+ echo $this->error(__('Failed to upload extension'));
+ }
+ }
+ } else {
+ echo $this->error(__('Failed to upload extension'));
+ }
+ }
+
+ if (empty($action) === false && empty($shortName) === false) {
+ switch ($action) {
+ case 'delete':
+ $result = $this->uninstallExtension($shortName);
+ if ($result === true) {
+ ui_print_success_message(
+ __('Deleted extension')
+ );
+ } else {
+ echo $this->error(__('Fail delete extension'));
+ }
+
+ case 'sync_server':
+ $syncAction = get_parameter('sync_action', '');
+ if ($syncAction === 'refresh') {
+ $installationFolder = $config['homedir'].'/'.$this->path.'/'.$shortName;
+ $result = $this->copyExtensionToServer($installationFolder, $shortName);
+ if ($result === true) {
+ ui_print_success_message(
+ __('Extension folder created successfully')
+ );
+ } else {
+ echo $this->error(__('Fail created extension folder'));
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+ }
+
+ $this->prepareBreadcrum(
+ [
+ [
+ 'link' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery'
+ ),
+ 'label' => __('Discovery'),
+ ],
+ [
+ 'link' => '',
+ 'label' => _('Manage extensions'),
+ 'selected' => 1,
+ ],
+ ]
+ );
+
+ // Header.
+ ui_print_page_header(
+ __('Manage extensions'),
+ '',
+ false,
+ '',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ $table = new stdClass();
+ $table->width = '100%';
+ $table->class = 'databox filters';
+ $table->size = [];
+ $table->size[0] = '80%';
+ $table->align[3] = 'right';
+ $table->data = [];
+ $table->data[0][0] = html_print_label_input_block(
+ __('Load DISCO'),
+ html_print_div(
+ [
+ 'id' => 'upload_file',
+ 'content' => html_print_input_file(
+ 'file',
+ true,
+ ['style' => 'width:100%']
+ ),
+ 'class' => 'mrgn_top_15px',
+ ],
+ true
+ )
+ );
+ $table->data[0][3] = html_print_submit_button(
+ __('Upload DISCO'),
+ 'upload_button',
+ false,
+ [
+ 'class' => 'sub ok float-right',
+ 'icon' => 'next',
+ ],
+ true
+ );
+
+ echo '';
+
+ echo '';
+ ui_require_javascript_file('manage_extensions');
+ try {
+ $columns = [
+ 'name',
+ 'short_name',
+ 'section',
+ 'description',
+ 'version',
+ [
+ 'text' => 'actions',
+ 'class' => 'flex flex-items-center',
+ ],
+ ];
+
+ $columnNames = [
+ __('Name'),
+ __('Short name'),
+ __('Section'),
+ __('Description'),
+ __('Version'),
+ __('Actions'),
+ ];
+
+ // Load datatables user interface.
+ ui_print_datatable(
+ [
+ 'id' => 'list_extensions',
+ 'class' => 'info_table',
+ 'style' => 'width: 99%',
+ 'dom_elements' => 'plfti',
+ 'filter_main_class' => 'box-flat white_table_graph fixed_filter_bar',
+ 'columns' => $columns,
+ 'column_names' => $columnNames,
+ 'ajax_url' => $this->ajaxController,
+ 'ajax_data' => ['method' => 'getExtensionsInstalled'],
+ 'no_sortable_columns' => [-1],
+ 'order' => [
+ 'field' => 'name',
+ 'direction' => 'asc',
+ ],
+ 'search_button_class' => 'sub filter float-right',
+ ]
+ );
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ }
+
+ }
+
+
+ /**
+ * Upload extension to server.
+ *
+ * @param array $disco File disco tu upload.
+ *
+ * @return boolean $result Of operation, true if is ok.
+ */
+ private function uploadExtension($disco)
+ {
+ global $config;
+ if (substr($disco['name'], -6) !== '.disco') {
+ return false;
+ }
+
+ $nameFile = str_replace('.disco', '.zip', $disco['name']);
+ $nameTempDir = $config['attachment_store'].'/downloads/';
+ if (file_exists($nameTempDir) === false) {
+ mkdir($nameTempDir);
+ }
+
+ $tmpPath = Files::tempdirnam(
+ $nameTempDir,
+ 'extensions_uploaded_'
+ );
+ $result = move_uploaded_file($disco['tmp_name'], $tmpPath.'/'.$nameFile);
+ if ($result === true) {
+ $unzip = Files::unzip($tmpPath.'/'.$nameFile, $tmpPath);
+ if ($unzip === true) {
+ unlink($tmpPath.'/'.$nameFile);
+ db_process_sql_begin();
+ $this->iniFile = parse_ini_file($tmpPath.'/discovery_definition.ini', true, INI_SCANNER_TYPED);
+ if ($this->iniFile === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return __('Failed to upload extension: Error while parsing dicovery_definition.ini');
+ }
+
+ $error = ExtensionsDiscovery::validateIni($this->iniFile);
+ if ($error !== false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return $error;
+ }
+
+ $id = $this->installExtension();
+ if ($id === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return false;
+ }
+
+ $result = $this->autoLoadConfigExec($id);
+ if ($result === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return false;
+ }
+
+ $result = $this->autoUpdateDefaultMacros($id);
+ if ($result === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return false;
+ }
+
+ $nameFolder = $this->iniFile['discovery_extension_definition']['short_name'];
+ $installationFolder = $config['homedir'].'/'.$this->path.'/'.$nameFolder;
+ if (file_exists($installationFolder) === false) {
+ mkdir($installationFolder, 0777, true);
+ } else {
+ Files::rmrf($installationFolder, true);
+ }
+
+ $result = Files::move($tmpPath, $installationFolder, true);
+ if ($result === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return false;
+ }
+
+ chmod($installationFolder, 0777);
+ foreach (glob($installationFolder.'/*') as $file) {
+ chmod($file, 0777);
+ }
+
+ $result = $this->copyExtensionToServer($installationFolder, $nameFolder);
+ if ($result === false) {
+ db_process_sql_rollback();
+ Files::rmrf($tmpPath);
+ return false;
+ }
+
+ Files::rmrf($tmpPath);
+ db_process_sql_commit();
+ return true;
+ }
+ } else {
+ Files::rmrf($tmpPath);
+ return false;
+ }
+ }
+
+
+ /**
+ * Copy the extension folder into remote path server.
+ *
+ * @param string $path Path extension folder.
+ * @param string $nameFolder Name of extension folder.
+ *
+ * @return boolean Result of operation.
+ */
+ public function copyExtensionToServer($path, $nameFolder)
+ {
+ global $config;
+ $filesToExclude = [
+ 'discovery_definition.ini',
+ 'logo.png',
+ ];
+ $serverPath = $config['remote_config'].'/discovery/'.$nameFolder;
+ if (file_exists($serverPath) === false) {
+ mkdir($serverPath, 0777, true);
+ } else {
+ Files::rmrf($serverPath, true);
+ }
+
+ $result = $this->copyFolder($path, $serverPath, $filesToExclude);
+ chmod($serverPath, 0777);
+ foreach (glob($serverPath.'/*') as $file) {
+ chmod($file, 0777);
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Copy from $source path to $destination
+ *
+ * @param string $source Initial folder path.
+ * @param string $destination Destination folder path.
+ * @param array $exclude Files to exlcude in copy.
+ *
+ * @return boolean Result of operation.
+ */
+ public function copyFolder($source, $destination, $exclude=[])
+ {
+ if (file_exists($destination) === false) {
+ mkdir($destination, 0777, true);
+ }
+
+ $files = scandir($source);
+ foreach ($files as $file) {
+ if ($file !== '.' && $file !== '..') {
+ if (is_dir($source.'/'.$file)) {
+ $result = $this->copyFolder($source.'/'.$file, $destination.'/'.$file);
+ if ($result === false) {
+ return false;
+ }
+ } else {
+ if (in_array($file, $exclude) === false) {
+ $result = copy($source.'/'.$file, $destination.'/'.$file);
+ if ($result === false) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Delete extension from database and delete folder
+ *
+ * @param integer $shortName Short name app for delete.
+ *
+ * @return boolean Result of operation.
+ */
+ private function uninstallExtension($shortName)
+ {
+ global $config;
+
+ $result = db_process_sql_delete(
+ 'tdiscovery_apps',
+ ['short_name' => $shortName]
+ );
+
+ if ($result !== false) {
+ Files::rmrf($config['homedir'].'/'.$this->path.'/'.$shortName);
+ Files::rmrf($config['remote_config'].'/discovery/'.$shortName);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Load the basic information of the app into database.
+ *
+ * @return boolean Result of query.
+ */
+ private function installExtension()
+ {
+ $exist = db_get_row_filter(
+ 'tdiscovery_apps',
+ [
+ 'short_name' => $this->iniFile['discovery_extension_definition']['short_name'],
+ ]
+ );
+ $version = $this->iniFile['discovery_extension_definition']['version'];
+ if ($version === null) {
+ $version = '';
+ }
+
+ $description = $this->iniFile['discovery_extension_definition']['description'];
+ if ($description === null) {
+ $description = '';
+ }
+
+ if ($exist === false) {
+ return db_process_sql_insert(
+ 'tdiscovery_apps',
+ [
+ 'short_name' => $this->iniFile['discovery_extension_definition']['short_name'],
+ 'name' => io_safe_input($this->iniFile['discovery_extension_definition']['name']),
+ 'description' => io_safe_input($description),
+ 'section' => $this->iniFile['discovery_extension_definition']['section'],
+ 'version' => $version,
+ ]
+ );
+ } else {
+ $result = db_process_sql_update(
+ 'tdiscovery_apps',
+ [
+ 'name' => io_safe_input($this->iniFile['discovery_extension_definition']['name']),
+ 'description' => io_safe_input($description),
+ 'section' => $this->iniFile['discovery_extension_definition']['section'],
+ 'version' => $version,
+ ],
+ [
+ 'short_name' => $this->iniFile['discovery_extension_definition']['short_name'],
+ ]
+ );
+
+ if ($result !== false) {
+ return $exist['id_app'];
+ }
+ }
+ }
+
+
+ /**
+ * Return all extension installed by ajax.
+ *
+ * @return void
+ */
+ public function getExtensionsInstalled()
+ {
+ global $config;
+
+ $data = [];
+ $start = get_parameter('start', 0);
+ $length = get_parameter('length', $config['block_size']);
+ $orderDatatable = get_datatable_order(true);
+ $pagination = '';
+ $order = '';
+
+ try {
+ ob_start();
+
+ if (isset($orderDatatable)) {
+ $order = sprintf(
+ ' ORDER BY %s %s',
+ $orderDatatable['field'],
+ $orderDatatable['direction']
+ );
+ }
+
+ if (isset($length) && $length > 0
+ && isset($start) && $start >= 0
+ ) {
+ $pagination = sprintf(
+ ' LIMIT %d OFFSET %d ',
+ $length,
+ $start
+ );
+ }
+
+ $sql = sprintf(
+ 'SELECT short_name, name, section, description, version
+ FROM tdiscovery_apps
+ %s %s',
+ $order,
+ $pagination
+ );
+
+ $data = db_get_all_rows_sql($sql);
+
+ $sqlCount = sprintf(
+ 'SELECT short_name, name, section, description, version
+ FROM tdiscovery_apps
+ %s',
+ $order,
+ );
+
+ $count = db_get_num_rows($sqlCount);
+
+ foreach ($data as $key => $row) {
+ $logo = $this->path.'/'.$row['short_name'].'/logo.png';
+ if (file_exists($logo) === false) {
+ $logo = $this->defaultLogo;
+ }
+
+ $logo = html_print_image($logo, true, ['style' => 'max-width: 30px; margin-right: 15px;']);
+ $data[$key]['name'] = $logo.io_safe_output($row['name']);
+ $data[$key]['short_name'] = $row['short_name'];
+ $data[$key]['description'] = io_safe_output($row['description']);
+ $data[$key]['version'] = $row['version'];
+ $data[$key]['actions'] = '';
+ if ($this->checkFolderConsole($row['short_name']) === true) {
+ $data[$key]['actions'] .= '';
+ } else {
+ $data[$key]['actions'] .= html_print_image(
+ 'images/error_red.png',
+ true,
+ [
+ 'title' => __('The extension directory or .ini does not exist in console.'),
+ 'alt' => __('The extension directory or .ini does not exist in console.'),
+ 'class' => 'main_menu_icon invert_filter',
+ ],
+ );
+ }
+ }
+
+ if (empty($data) === true) {
+ $total = 0;
+ $data = [];
+ } else {
+ $total = $count;
+ }
+
+ echo json_encode(
+ [
+ 'data' => $data,
+ 'recordsTotal' => $total,
+ 'recordsFiltered' => $total,
+ ]
+ );
+ // Capture output.
+ $response = ob_get_clean();
+ } catch (Exception $e) {
+ echo json_encode(['error' => $e->getMessage()]);
+ exit;
+ }
+
+ json_decode($response);
+ if (json_last_error() === JSON_ERROR_NONE) {
+ echo $response;
+ } else {
+ echo json_encode(
+ [
+ 'success' => false,
+ 'error' => $response,
+ ]
+ );
+ }
+
+ exit;
+ }
+
+
+ /**
+ * Insert new the default values for extension.
+ *
+ * @param integer $id Id of extension.
+ *
+ * @return boolean Result of query.
+ */
+ private function autoUpdateDefaultMacros($id)
+ {
+ $defaultValues = $this->iniFile['discovery_extension_definition']['default_value'];
+
+ foreach ($defaultValues as $macro => $value) {
+ $sql = 'INSERT IGNORE INTO `tdiscovery_apps_tasks_macros`
+ (`id_task`, `macro`, `type`, `value`, `temp_conf`)
+ SELECT `id_rt`, "'.$macro.'", "custom", "'.(string) io_safe_input($value).'", "0"
+ FROM `trecon_task`
+ WHERE `id_app` = "'.$id.'";';
+ $result = db_process_sql($sql);
+ if ($result === false) {
+ return false;
+ }
+ }
+
+ $tempFiles = $this->iniFile['tempfile_confs']['file'];
+ foreach ($tempFiles as $macro => $value) {
+ $sql = 'INSERT IGNORE INTO `tdiscovery_apps_tasks_macros`
+ (`id_task`, `macro`, `type`, `value`, `temp_conf`)
+ SELECT `id_rt`, "'.$macro.'", "custom", "'.(string) io_safe_input($value).'", "1"
+ FROM `trecon_task`
+ WHERE `id_app` = "'.$id.'";';
+ $result = db_process_sql($sql);
+ if ($result === false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Load the exec files in database
+ *
+ * @param integer $id Id of extension.
+ *
+ * @return boolean Result of query.
+ */
+ private function autoLoadConfigExec($id)
+ {
+ $executionFiles = $this->iniFile['discovery_extension_definition']['execution_file'];
+
+ foreach ($executionFiles as $key => $value) {
+ $exist = db_get_row_filter(
+ 'tdiscovery_apps_scripts',
+ [
+ 'id_app' => $id,
+ 'macro' => $key,
+ ]
+ );
+ if ($exist === false) {
+ $result = db_process_sql_insert(
+ 'tdiscovery_apps_scripts',
+ [
+ 'id_app' => $id,
+ 'macro' => $key,
+ 'value' => io_safe_input($value),
+ ]
+ );
+ if ($result === false) {
+ return false;
+ }
+ } else {
+ $result = db_process_sql_update(
+ 'tdiscovery_apps_scripts',
+ ['value' => io_safe_input($value)],
+ [
+ 'id_app' => $id,
+ 'macro' => $key,
+ ]
+ );
+ if ($result === false) {
+ return false;
+ }
+ }
+ }
+
+ $execCommands = $this->iniFile['discovery_extension_definition']['exec'];
+ $result = db_process_sql_delete(
+ 'tdiscovery_apps_executions',
+ ['id_app' => $id]
+ );
+ if ($result === false) {
+ return false;
+ }
+
+ foreach ($execCommands as $key => $value) {
+ $result = db_process_sql_insert(
+ 'tdiscovery_apps_executions',
+ [
+ 'id_app' => $id,
+ 'execution' => io_safe_input($value),
+ ]
+ );
+ if ($result === false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Check if exist folder extension in console.
+ *
+ * @param string $shortName Name of folder.
+ *
+ * @return boolean Return true if exist folder
+ */
+ private function checkFolderConsole($shortName)
+ {
+ global $config;
+
+ $folderPath = $config['homedir'].'/'.$this->path.'/'.$shortName;
+ $iniPath = $config['homedir'].'/'.$this->path.'/'.$shortName.'/discovery_definition.ini';
+ if (file_exists($folderPath) === false || file_exists($iniPath) === false) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+ /**
+ * Validate the ini name by ajax.
+ *
+ * @return void
+ */
+ public function validateIniName()
+ {
+ global $config;
+ $uploadDisco = get_parameter('upload_disco', '');
+ if (empty($uploadDisco) === false) {
+ if ($_FILES['file']['error'] == 0) {
+ $disco = $_FILES['file'];
+ } else {
+ echo json_encode(['success' => false, 'message' => 'Failed to upload extension']);
+ return;
+ }
+ }
+
+ if (substr($disco['name'], -6) !== '.disco') {
+ echo json_encode(['success' => false, 'message' => 'Failed to upload extension']);
+ return;
+ }
+
+ $nameFile = str_replace('.disco', '.zip', $disco['name']);
+ $nameTempDir = $config['attachment_store'].'/downloads/';
+ if (file_exists($nameTempDir) === false) {
+ mkdir($nameTempDir);
+ }
+
+ $tmpPath = Files::tempdirnam(
+ $nameTempDir,
+ 'extensions_uploaded_'
+ );
+ $result = move_uploaded_file($disco['tmp_name'], $tmpPath.'/'.$nameFile);
+ if ($result === true) {
+ $unzip = Files::unzip($tmpPath.'/'.$nameFile, $tmpPath);
+ if ($unzip === true) {
+ unlink($tmpPath.'/'.$nameFile);
+ $this->iniFile = parse_ini_file($tmpPath.'/discovery_definition.ini', true, INI_SCANNER_TYPED);
+ if ($this->iniFile === false) {
+ Files::rmrf($tmpPath);
+ echo json_encode(['success' => false, 'message' => __('Failed to upload extension: Error while parsing dicovery_definition.ini')]);
+ return;
+ }
+
+ $message = false;
+ $shortName = $this->iniFile['discovery_extension_definition']['short_name'];
+ if (strpos($shortName, 'pandorafms.') === 0) {
+ $message = __('The \'short_name\' starting with \'pandorafms.\' is reserved for Pandora FMS applications. If this is not an official Pandora FMS application, consider changing the \'short_name\'. Do you want to continue?');
+ }
+
+ $exist = db_get_row_filter(
+ 'tdiscovery_apps',
+ ['short_name' => $shortName]
+ );
+
+ if ($exist !== false) {
+ $message = __('There is another application with the same \'short_name\': \'%s\'. Do you want to overwrite the application and all of its contents?', $shortName);
+ }
+
+ if ($message !== false) {
+ echo json_encode(
+ [
+ 'success' => true,
+ 'warning' => true,
+ 'message' => $message,
+ ]
+ );
+ } else {
+ echo json_encode(['success' => true]);
+ }
+
+ Files::rmrf($tmpPath);
+ return;
+ }
+ } else {
+ Files::rmrf($tmpPath);
+ echo json_encode(['success' => false, 'message' => __('Failed to upload extension')]);
+ return;
+ }
+ }
+
+
+ /**
+ * Return all extensions from section.
+ *
+ * @param string $section Section to filter.
+ *
+ * @return array List of sections.
+ */
+ static public function getExtensionBySection($section)
+ {
+ return db_get_all_rows_filter(
+ 'tdiscovery_apps',
+ ['section' => $section]
+ );
+ }
+
+
+}
diff --git a/pandora_console/images/wizard/Configurar_app@svg.svg b/pandora_console/images/wizard/Configurar_app@svg.svg
new file mode 100644
index 0000000000..59507e2cf4
--- /dev/null
+++ b/pandora_console/images/wizard/Configurar_app@svg.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/pandora_console/images/wizard/Custom_apps@svg.svg b/pandora_console/images/wizard/Custom_apps@svg.svg
new file mode 100644
index 0000000000..23e251912d
--- /dev/null
+++ b/pandora_console/images/wizard/Custom_apps@svg.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/pandora_console/images/wizard/app_generico.svg b/pandora_console/images/wizard/app_generico.svg
new file mode 100644
index 0000000000..99d3e5cf42
--- /dev/null
+++ b/pandora_console/images/wizard/app_generico.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/pandora_console/images/wizard/applications.png b/pandora_console/images/wizard/applications.png
new file mode 100644
index 0000000000..01c10178bd
Binary files /dev/null and b/pandora_console/images/wizard/applications.png differ
diff --git a/pandora_console/images/wizard/cloud.png b/pandora_console/images/wizard/cloud.png
new file mode 100644
index 0000000000..a016599e2f
Binary files /dev/null and b/pandora_console/images/wizard/cloud.png differ
diff --git a/pandora_console/images/wizard/consoletasks.png b/pandora_console/images/wizard/consoletasks.png
new file mode 100644
index 0000000000..0087897495
Binary files /dev/null and b/pandora_console/images/wizard/consoletasks.png differ
diff --git a/pandora_console/include/ajax/manage_extensions.ajax.php b/pandora_console/include/ajax/manage_extensions.ajax.php
new file mode 100644
index 0000000000..b116277a23
--- /dev/null
+++ b/pandora_console/include/ajax/manage_extensions.ajax.php
@@ -0,0 +1,60 @@
+ajaxMethod($method) === true) {
+ $actions->{$method}();
+ } else {
+ $actions->errorAjax('Unavailable method.');
+ }
+} else {
+ $actions->errorAjax('Method not found. ['.$method.']');
+}
+
+
+// Stop any execution.
+exit;
diff --git a/pandora_console/include/class/ExtensionsDiscovery.class.php b/pandora_console/include/class/ExtensionsDiscovery.class.php
new file mode 100644
index 0000000000..175a8ade64
--- /dev/null
+++ b/pandora_console/include/class/ExtensionsDiscovery.class.php
@@ -0,0 +1,2524 @@
+section = $_section;
+ $this->mode = $_mode;
+ $this->url = 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz='.$_section;
+ $this->loadConfig();
+ }
+
+
+ /**
+ * Load config from extension.
+ *
+ * @return void
+ */
+ private function loadConfig()
+ {
+ $row = db_get_row('tdiscovery_apps', 'short_name', $this->mode);
+ $this->id = $row['id_app'];
+ $this->name = $row['name'];
+ $this->description = $row['description'];
+ }
+
+
+ /**
+ * Return array extensions filtered by section
+ *
+ * @return array Extensions for
+ */
+ public function loadExtensions()
+ {
+ global $config;
+ // Check access.
+ check_login();
+ $extensions = [];
+ $rows = $this->getExtensionsApps();
+ foreach ($rows as $key => $extension) {
+ $logo = $this->path.'/'.$extension['short_name'].'/'.$this->icon;
+ if (file_exists($config['homedir'].$logo) === false) {
+ $logo = $this->defaultLogo;
+ }
+
+ $extensions[] = [
+ 'icon' => $logo,
+ 'label' => $extension['name'],
+ 'url' => ui_get_full_url(
+ 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz='.$this->section.'&mode='.$extension['short_name']
+ ),
+ ];
+ }
+
+ return $extensions;
+ }
+
+
+ /**
+ * Return all extensions from apps section
+ *
+ * @return array extensions.
+ */
+ public function getExtensionsApps()
+ {
+ return db_get_all_rows_filter('tdiscovery_apps', ['section' => $this->section]);
+
+ }
+
+
+ /**
+ * Load the extension information from discovery_definition.ini.
+ *
+ * @return array Information ini file.
+ */
+ public function loadIni()
+ {
+ global $config;
+ $iniFile = parse_ini_file($config['homedir'].$this->path.'/'.$this->mode.'/discovery_definition.ini', true, INI_SCANNER_TYPED);
+
+ return $iniFile;
+ }
+
+
+ /**
+ * Return next page from config_steps.
+ *
+ * @return integer Return the number of next page.
+ */
+ public function nextPage()
+ {
+ $pages = array_keys($this->iniFile['config_steps']['name']);
+ if ($this->currentPage === 0 || empty($this->currentPage) === true) {
+ return $pages[0];
+ }
+
+ foreach ($pages as $k => $page) {
+ if ($page === $this->currentPage) {
+ if (end($pages) === $this->currentPage) {
+ return $this->currentPage;
+ } else {
+ return $pages[($k + 1)];
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Draw the extension forms.
+ *
+ * @return boolean Return boolean if exist error.
+ */
+ public function run()
+ {
+ $_iniFile = $this->loadIni();
+ if ($_iniFile === false) {
+ include 'general/noaccess.php';
+ return false;
+ }
+
+ $this->iniFile = $_iniFile;
+ if (empty($this->iniFile['config_steps']) === false) {
+ $this->lastPage = end(array_keys($this->iniFile['config_steps']['name']));
+ } else {
+ $this->lastPage = 0;
+ }
+
+ $this->currentPage = (int) get_parameter('page', '0');
+ $this->idTask = get_parameter('id_task', '');
+ $action = get_parameter('action', '');
+ $isTheEnd = get_parameter('complete_button', '');
+
+ // Control parameters and errors.
+ $error = false;
+
+ if ($action === 'task_definition_form') {
+ $error = $this->processTaskDefinition();
+ }
+
+ if ($action === 'process_macro') {
+ $error = $this->processCustomMacro();
+ }
+
+ $task = $this->getTask();
+
+ if ($task === false && $this->currentPage > 0) {
+ $error = __('Task not defined');
+ }
+
+ // Build breadcrum.
+ $breadcrum = [
+ [
+ 'link' => 'index.php?sec=gservers&sec2=godmode/servers/discovery',
+ 'label' => 'Discovery',
+ ],
+ ];
+
+ switch ($this->section) {
+ case 'app':
+ $breadcrum[] = [
+ 'link' => $this->url,
+ 'label' => __('Application'),
+ ];
+ break;
+
+ case 'cloud':
+ $breadcrum[] = [
+ 'link' => $this->url,
+ 'label' => __('Cloud'),
+ ];
+ break;
+
+ case 'custom':
+ $breadcrum[] = [
+ 'link' => $this->url,
+ 'label' => __('Custom'),
+ ];
+ break;
+
+ default:
+ $breadcrum[] = [
+ 'link' => $this->url,
+ 'label' => __('Custom'),
+ ];
+ break;
+ }
+
+ $parameters = '';
+ if (empty($this->idTask) === false) {
+ $parameters .= '&id_task='.$this->idTask;
+ }
+
+ $breadcrum[] = [
+ 'link' => $this->url.'&mode='.$this->mode.$parameters,
+ 'label' => 'Task definition',
+ 'selected' => ((0 === (int) $this->currentPage) ? 1 : 0),
+ ];
+
+ foreach ($this->iniFile['config_steps']['name'] as $key => $step) {
+ $parameters = '&mode='.$this->mode.'&page='.$key;
+ if (empty($this->idTask) === false) {
+ $parameters .= '&id_task='.$this->idTask;
+ }
+
+ $breadcrum[] = [
+ 'link' => $this->url.$parameters,
+ 'label' => $step,
+ 'selected' => (($key === (int) $this->currentPage) ? 1 : 0),
+ ];
+ }
+
+ // Avoid to print header out of wizard.
+ $this->prepareBreadcrum($breadcrum);
+
+ // Header.
+ ui_print_page_header(
+ $this->iniFile['discovery_extension_definition']['name'],
+ '',
+ false,
+ '',
+ true,
+ '',
+ false,
+ '',
+ GENERIC_SIZE_TEXT,
+ '',
+ $this->printHeader(true)
+ );
+
+ if ($error !== false) {
+ ui_print_error_message(
+ $error
+ );
+ return;
+ } else if ($action !== '') {
+ ui_print_success_message(__('Operation realized'));
+
+ if (empty($isTheEnd) === false) {
+ header('Location:'.$config['homeurl'].'index.php?sec=discovery&sec2=godmode/servers/discovery&wiz=tasklist');
+ }
+ }
+
+ $_url = ui_get_full_url(
+ sprintf(
+ $this->url.'&mode=%s&page=%s%s',
+ $this->mode,
+ $this->nextPage(),
+ (empty($this->idTask) === false) ? '&id_task='.$this->idTask : '',
+ )
+ );
+
+ $table = new StdClass();
+ $table->id = 'form_editor';
+ $table->width = '100%';
+ $table->class = 'databox filter-table-adv max_floating_element_size';
+
+ $table->style = [];
+ $table->style[0] = 'width: 50%';
+ $table->style[1] = 'width: 50%';
+ $table->data = [];
+ if ($this->currentPage === 0) {
+ // If page is 0 then create form for task definition.
+ $table->data = $this->viewTaskDefinition();
+ } else {
+ // If page is bigger than 0 then render form .ini.
+ $table->data = $this->viewMacroForm();
+ }
+
+ echo '';
+
+ }
+
+
+ /**
+ * Draw a select with the pandora data
+ *
+ * @param string $selectData Type of select.
+ * @param string $name Name of select.
+ * @param string $defaultValue Default value.
+ * @param boolean $multiple Define if the select is multiple.
+ * @param boolean $required Define if field is required.
+ *
+ * @return string Return the html select.
+ */
+ private function drawSelectPandora($selectData, $name, $defaultValue, $multiple=false, $required=false)
+ {
+ if ($multiple === true && $selectData !== 'interval') {
+ $name .= '[]';
+ $defaultValue = json_decode($defaultValue);
+ } else {
+ $defaultValue = io_safe_input($defaultValue);
+ }
+
+ switch ($selectData) {
+ case 'agent_groups':
+ $input = html_print_select_groups(
+ false,
+ 'AR',
+ true,
+ $name,
+ $defaultValue,
+ '',
+ '',
+ '',
+ true,
+ $multiple,
+ false,
+ '',
+ false,
+ false,
+ false,
+ false,
+ 'id_grupo',
+ true,
+ false,
+ false,
+ false,
+ false,
+ $required
+ );
+ break;
+
+ case 'agents':
+ $input = html_print_select_from_sql(
+ 'SELECT nombre, alias as n FROM tagente',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%;',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'module_groups':
+ $input = html_print_select_from_sql(
+ 'SELECT id_mg, name
+ FROM tmodule_group ORDER BY name',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'modules':
+ $input = html_print_select_from_sql(
+ 'select name, name as n from tmodule',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'module_types':
+ $input = html_print_select_from_sql(
+ 'select nombre, descripcion from ttipo_modulo',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'status':
+ $module_status_arr = [];
+ // Default.
+ $module_status_arr[AGENT_MODULE_STATUS_NORMAL] = __('Normal');
+ $module_status_arr[AGENT_MODULE_STATUS_WARNING] = __('Warning');
+ $module_status_arr[AGENT_MODULE_STATUS_CRITICAL_BAD] = __('Critical');
+ $module_status_arr[AGENT_MODULE_STATUS_UNKNOWN] = __('Unknown');
+ $module_status_arr[AGENT_MODULE_STATUS_NOT_INIT] = __('Not init');
+ $input = html_print_select(
+ $module_status_arr,
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ '',
+ false,
+ 'width:100%',
+ false,
+ false,
+ false,
+ '',
+ false,
+ false,
+ $required,
+ false,
+ true,
+ true
+ );
+ break;
+
+ case 'alert_templates':
+ $input = html_print_select_from_sql(
+ 'select id, name from talert_templates',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'alert_actions':
+ $input = html_print_select_from_sql(
+ 'select id, name from talert_actions',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'interval':
+ $input = html_print_extended_select_for_time(
+ $name,
+ (string) $defaultValue,
+ '',
+ '',
+ '0',
+ false,
+ true
+ );
+ break;
+
+ case 'tags':
+ $input = html_print_select_from_sql(
+ 'select id_tag, name from ttag',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.custom':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "custom"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.aws':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "AWS"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.azure':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "AZURE"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.sap':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "SAP"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.snmp':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "SNMP"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.gcp':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "GOOGLE"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'credentials.wmi':
+ $input = html_print_select_from_sql(
+ 'select identifier, identifier as i from tcredential_store WHERE product = "WMI"',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ case 'os':
+ $input = html_print_select_from_sql(
+ 'SELECT id_os, name FROM tconfig_os ORDER BY name',
+ $name,
+ $defaultValue,
+ '',
+ ($multiple === false) ? __('None selected') : '',
+ '',
+ true,
+ $multiple,
+ true,
+ false,
+ 'width: 100%',
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ $required
+ );
+ break;
+
+ default:
+ $input = html_print_select(
+ [],
+ $name,
+ $defaultValue,
+ '',
+ '',
+ 0,
+ true
+ );
+ break;
+ }
+
+ return $input;
+ }
+
+
+ /**
+ * Draw input from parameters of .ini.
+ *
+ * @param array $parameters Configuration of input.
+ * @param boolean $implicit Indicates if all the configuration is indicated in the array.
+ *
+ * @return string Html from input.
+ */
+ public function drawInput($parameters, $implicit=false)
+ {
+ $input = '';
+ $defaultValue = $this->macrosValues[$parameters['macro']];
+ switch ($parameters['type']) {
+ case 'string':
+ $input = html_print_input_text(
+ $parameters['macro'],
+ $defaultValue,
+ '',
+ 50,
+ 255,
+ true,
+ false,
+ ($parameters['mandatory_field'] === false) ? false : true
+ );
+ break;
+
+ case 'number':
+ $config = [
+ 'type' => 'number',
+ 'name' => $parameters['macro'],
+ 'value' => $defaultValue,
+ 'return' => true,
+ ];
+ if ($parameters['mandatory_field'] !== false) {
+ $config['required'] = true;
+ }
+
+ $input = html_print_input($config);
+ break;
+
+ case 'password':
+ $isEncrypted = (bool) $this->macrosValues[$parameters['encrypt_on_true']];
+ if ($isEncrypted === true) {
+ $defaultValueEncrypted = $this->encryptPassword($defaultValue, true);
+ if (empty($defaultValueEncrypted) === false) {
+ $defaultValue = $defaultValueEncrypted;
+ }
+ }
+
+ $input = html_print_input_password(
+ $parameters['macro'],
+ $defaultValue,
+ '',
+ 50,
+ 255,
+ true,
+ false,
+ ($parameters['mandatory_field'] === false) ? false : true,
+ '',
+ 'on'
+ );
+ if (empty($parameters['encrypt_on_true']) === false) {
+ $input .= html_print_input_hidden(
+ $parameters['macro'].'_encrypt',
+ $parameters['encrypt_on_true'],
+ true
+ );
+ }
+ break;
+
+ case 'checkbox':
+ $input = html_print_checkbox_switch(
+ $parameters['macro'],
+ 1,
+ (bool) $defaultValue,
+ true
+ );
+ break;
+
+ case 'textarea':
+ $input = html_print_textarea(
+ $parameters['macro'],
+ 5,
+ 20,
+ $defaultValue,
+ ($parameters['mandatory_field'] === false) ? '' : 'required="required"',
+ true
+ );
+ break;
+
+ case 'select':
+ if (in_array($parameters['select_data'], $this->pandoraSelectData) === true) {
+ $input = $this->drawSelectPandora(
+ $parameters['select_data'],
+ $parameters['macro'],
+ $defaultValue,
+ false,
+ ($parameters['mandatory_field'] === false) ? false : true,
+ );
+ $parameters['type'] = $parameters['select_data'];
+ } else {
+ if ($implicit === false) {
+ $options = $this->iniFile[$parameters['select_data']]['option'];
+ } else {
+ $options = $parameters['select_data'];
+ }
+
+ $input = html_print_select(
+ $options,
+ $parameters['macro'],
+ $defaultValue,
+ '',
+ __('None selected'),
+ '',
+ true,
+ false,
+ true,
+ '',
+ false,
+ 'width: 100%;',
+ false,
+ false,
+ false,
+ '',
+ false,
+ false,
+ ($parameters['mandatory_field'] === false) ? false : true,
+ );
+ }
+ break;
+
+ case 'multiselect':
+ if (in_array($parameters['select_data'], $this->pandoraSelectData) === true) {
+ $input = $this->drawSelectPandora(
+ $parameters['select_data'],
+ $parameters['macro'],
+ $defaultValue,
+ true,
+ );
+ $parameters['type'] = $parameters['select_data'];
+ } else {
+ if ($implicit === false) {
+ $options = $this->iniFile[$parameters['select_data']]['option'];
+ } else {
+ $options = $parameters['select_data'];
+ }
+
+ $input = html_print_select(
+ $options,
+ $parameters['macro'].'[]',
+ json_decode($defaultValue, true),
+ '',
+ '',
+ 0,
+ true,
+ true,
+ true,
+ '',
+ false,
+ 'width: 100%',
+ false,
+ false,
+ false,
+ '',
+ false,
+ false,
+ false,
+ false,
+ true,
+ true
+ );
+ }
+ break;
+
+ case 'tree':
+ // Show bucket tree explorer.
+ ui_require_javascript_file('pandora_snmp_browser');
+ if ($implicit === false) {
+ $treeData = $this->iniFile[$parameters['tree_data']];
+ $treeInfo = $this->getTreeStructure($parameters, $treeData);
+ } else {
+ $treeData = $parameters['tree_data'];
+ $treeInfo = $this->getTreeStructureByScript($parameters, $treeData);
+ }
+
+ $input = ui_print_tree(
+ $treeInfo,
+ // Id.
+ 0,
+ // Depth.
+ 0,
+ // Last.
+ 0,
+ // Last_array.
+ [],
+ // Sufix.
+ true,
+ // Return.
+ true,
+ // Descriptive ids.
+ false
+ );
+ break;
+
+ default:
+ $input = html_print_input_text(
+ $parameters['macro'],
+ $defaultValue,
+ '',
+ 50,
+ 255,
+ true,
+ false,
+ ($parameters['mandatory_field'] === false) ? false : true
+ );
+ break;
+ }
+
+ $input .= html_print_input_hidden(
+ $parameters['macro'].'type',
+ $parameters['type'],
+ true
+ );
+ $class = '';
+ if ($parameters['show_on_true'] !== null) {
+ $class = $parameters['macro'].'_hide';
+ $input .= $this->showOnTrue($parameters['show_on_true'], $class);
+ }
+
+ $name = $parameters['name'];
+ if (empty($parameters['tip']) === false) {
+ $name .= ui_print_help_tip($parameters['tip'], true);
+ }
+
+ return html_print_label_input_block(
+ $name,
+ $input,
+ ['div_class' => $class]
+ );
+ }
+
+
+ /**
+ * Return the task app from database.
+ *
+ * @return array $task Task of database.
+ */
+ private function getTask()
+ {
+ return db_get_row_filter(
+ 'trecon_task',
+ [
+ 'id_app' => $this->id,
+ 'id_rt' => $this->idTask,
+ 'type' => 15,
+ ],
+ );
+ }
+
+
+ /**
+ * Returns the value of the macro.
+ *
+ * @param string $macro Name of macro for filter.
+ *
+ * @return mixed Value of the macro.
+ */
+ private function getValueMacro($macro)
+ {
+ return db_get_value_filter(
+ 'value',
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $macro,
+ ]
+ );
+ }
+
+
+ /**
+ * Return form for macro form.
+ *
+ * @return array $form Form macro.
+ */
+ private function viewMacroForm()
+ {
+ $data = [];
+
+ $macros = db_get_all_rows_filter(
+ 'tdiscovery_apps_tasks_macros',
+ ['id_task' => $this->idTask],
+ ['*']
+ );
+ if ($macros !== false) {
+ foreach ($macros as $key => $macro) {
+ $this->macrosValues[$macro['macro']] = io_safe_output($macro['value']);
+ }
+ }
+
+ // Process ini or script.
+ $customFields = $this->iniFile['config_steps']['custom_fields'][$this->currentPage];
+ $customFieldsByScript = $this->getStructureFormByScript($this->iniFile['config_steps']['script_data_fields'][$this->currentPage]);
+
+ if ($customFields === null && $customFieldsByScript === null) {
+ $data[0][0] = html_print_image(
+ 'images/no_data_toshow.png',
+ true,
+ ['class' => 'w200px']
+ );
+ $data[1][0] = html_print_input_hidden(
+ 'action',
+ 'process_macro',
+ true
+ );
+ return $data;
+ }
+
+ $columns = 2;
+ if ($this->iniFile['config_steps']['fields_columns'][$this->currentPage] !== null
+ && $this->iniFile['config_steps']['fields_columns'][$this->currentPage] === 1
+ ) {
+ $columns = 1;
+ }
+
+ $row = 0;
+ $col = 0;
+ foreach ($customFieldsByScript as $key => $value) {
+ $this->nameFields[] = $value['macro'];
+ $data[$row][$col] = $this->drawInput($value, true);
+ $col++;
+ if ($col == $columns) {
+ $row++;
+ $col = 0;
+ }
+ }
+
+ foreach ($this->iniFile[$customFields]['macro'] as $key => $id) {
+ $parameters = [
+ 'macro' => $id,
+ 'name' => $this->iniFile[$customFields]['name'][$key],
+ 'tip' => $this->iniFile[$customFields]['tip'][$key],
+ 'type' => $this->iniFile[$customFields]['type'][$key],
+ 'placeholder' => $this->iniFile[$customFields]['placeholder'][$key],
+ 'mandatory_field' => $this->iniFile[$customFields]['mandatory_field'][$key],
+ 'show_on_true' => $this->iniFile[$customFields]['show_on_true'][$key],
+ 'encrypt_on_true' => $this->iniFile[$customFields]['encrypt_on_true'][$key],
+ 'select_data' => $this->iniFile[$customFields]['select_data'][$key],
+ 'tree_data' => $this->iniFile[$customFields]['tree_data'][$key],
+ ];
+ $this->nameFields[] = $id;
+ $data[$row][$col] = $this->drawInput($parameters);
+ $col++;
+ if ($col == $columns) {
+ $row++;
+ $col = 0;
+ }
+ }
+
+ $data[($row + 1)][1] = html_print_input_hidden(
+ 'action',
+ 'process_macro',
+ true
+ );
+ $data[($row + 1)][1] .= html_print_input_hidden(
+ 'name_fields',
+ implode(',', $this->nameFields),
+ true
+ );
+
+ return $data;
+ }
+
+
+ /**
+ * Return form for task definition.
+ *
+ * @return array $form Form for task definition.
+ */
+ private function viewTaskDefinition()
+ {
+ $task = $this->getTask();
+
+ $data = [];
+ $data[0][0] = html_print_label_input_block(
+ __('Task name'),
+ html_print_input_text(
+ 'task_name',
+ $task['name'],
+ '',
+ 50,
+ 255,
+ true,
+ false,
+ true
+ )
+ );
+
+ $data[1][0] = html_print_label_input_block(
+ __('Description'),
+ html_print_textarea(
+ 'description',
+ 5,
+ 20,
+ $task['description'],
+ '',
+ true
+ )
+ );
+
+ $data[2][0] = html_print_label_input_block(
+ __('Discovery server'),
+ html_print_select_from_sql(
+ sprintf(
+ 'SELECT id_server, name
+ FROM tserver
+ WHERE server_type = %d
+ ORDER BY name',
+ SERVER_TYPE_DISCOVERY
+ ),
+ 'discovery_server',
+ $task['id_recon_server'],
+ '',
+ '',
+ '0',
+ true,
+ false,
+ true,
+ false,
+ false,
+ false,
+ GENERIC_SIZE_TEXT,
+ '',
+ false
+ )
+ );
+
+ $data[3][0] = html_print_label_input_block(
+ __('Group'),
+ html_print_select_groups(
+ false,
+ 'AR',
+ false,
+ 'group',
+ $task['id_group'],
+ '',
+ '',
+ 0,
+ true,
+ false,
+ false,
+ '',
+ false,
+ false,
+ false,
+ false,
+ 'id_grupo',
+ false,
+ false,
+ false,
+ false,
+ false,
+ true
+ )
+ );
+
+ $data[4][0] = html_print_label_input_block(
+ __('Interval'),
+ html_print_extended_select_for_time(
+ 'interval',
+ (empty($task['interval_sweep']) === true) ? '300' : $task['interval_sweep'],
+ '',
+ '',
+ '0',
+ false,
+ true
+ )
+ );
+
+ $data[5][0] = html_print_label_input_block(
+ __('Timeout').ui_print_help_tip('This timeout will be applied for each task execution', true),
+ html_print_extended_select_for_time(
+ 'tiemout',
+ (empty($task['executions_timeout']) === true) ? '60' : $task['executions_timeout'],
+ '',
+ '',
+ '0',
+ false,
+ true
+ ),
+ );
+
+ $data[6][0] = html_print_input_hidden(
+ 'action',
+ 'task_definition_form',
+ true
+ );
+
+ $data[7][0] = html_print_input_hidden(
+ 'id_task',
+ $task['id_rt'],
+ true
+ );
+
+ return $data;
+ }
+
+
+ /**
+ * Sabe data from task definition form.
+ *
+ * @return string $error Error string if exist.
+ */
+ private function processTaskDefinition()
+ {
+ $taskName = get_parameter('task_name', '');
+ $description = get_parameter('description', '');
+ $discoveryServer = get_parameter('discovery_server', '');
+ $group = get_parameter('group', 0);
+ $interval = get_parameter('interval', '');
+ $tiemout = get_parameter('tiemout', 60);
+ $completeTask = get_parameter('complete_button', '');
+
+ $error = false;
+
+ if ($taskName === ''
+ || $discoveryServer === ''
+ || $group === ''
+ || $interval === ''
+ ) {
+ $error = __('Fields empties');
+ return $error;
+ }
+
+ if ($this->idTask === '') {
+ db_process_sql_begin();
+ try {
+ $_idTask = db_process_sql_insert(
+ 'trecon_task',
+ [
+ 'id_app' => $this->id,
+ 'name' => $taskName,
+ 'description' => $description,
+ 'id_group' => $group,
+ 'interval_sweep' => $interval,
+ 'id_recon_server' => $discoveryServer,
+ 'type' => 15,
+ 'setup_complete' => (empty($completeTask) === false) ? 1 : 0,
+ 'executions_timeout' => $tiemout,
+ ]
+ );
+
+ if ($_idTask === false) {
+ $error = __('Error creating the discovery task');
+ } else {
+ $this->idTask = $_idTask;
+ $this->autoLoadConfigMacro();
+ }
+ } catch (Exception $e) {
+ $error = __('Error creating the discovery task');
+ }
+
+ if ($error === false) {
+ db_process_sql_commit();
+ } else {
+ db_process_sql_rollback();
+ }
+ } else {
+ $result = db_process_sql_update(
+ 'trecon_task',
+ [
+ 'id_app' => $this->id,
+ 'name' => $taskName,
+ 'description' => $description,
+ 'id_group' => $group,
+ 'interval_sweep' => $interval,
+ 'id_recon_server' => $discoveryServer,
+ 'type' => 15,
+ 'setup_complete' => (empty($completeTask) === false) ? 1 : 0,
+ 'executions_timeout' => $tiemout,
+ ],
+ ['id_rt' => $this->idTask]
+ );
+
+ if ($result === false) {
+ $error = __('Error updating the discovery task');
+ }
+ }
+
+ return $error;
+ }
+
+
+ /**
+ * Process the values of input from macro defined in .ini
+ *
+ * @return string $error Error string if exist.
+ */
+ private function processCustomMacro()
+ {
+ $error = false;
+
+ $keyParameters = explode(',', get_parameter('name_fields', ''));
+
+ foreach ($keyParameters as $v => $key) {
+ $type = get_parameter($key.'type', '');
+ switch ($type) {
+ case 'checkbox':
+ $value = get_parameter_switch($key, 0);
+ break;
+
+ case 'multiselect':
+ $value = io_safe_input(json_encode(get_parameter($key, '')));
+ break;
+
+ case 'password':
+ $value = get_parameter($key, '');
+ $encryptKey = get_parameter($key.'_encrypt', '');
+ if ($encryptKey !== '') {
+ $encrypt = (bool) get_parameter_switch($encryptKey, 0);
+ if ($encrypt === true) {
+ $valueEncrypt = $this->encryptPassword($value);
+ if (empty($valueEncrypt) === false) {
+ $value = $valueEncrypt;
+ }
+ }
+ }
+ break;
+
+ default:
+ $value = get_parameter($key, '');
+ break;
+ }
+
+ if (is_array($value) === true) {
+ $value = io_safe_input(json_encode($value));
+ }
+
+ $exist = db_get_row_filter(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $key,
+ ]
+ );
+
+ if (in_array($type, $this->pandoraSelectData) === false) {
+ $type = 'custom';
+ }
+
+ if ($exist === false) {
+ $result = db_process_sql_insert(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $key,
+ 'value' => $value,
+ 'type' => $type,
+ ]
+ );
+ if ($result === false) {
+ $error = __('Field %s not insert', $key);
+ }
+ } else {
+ $result = db_process_sql_update(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'value' => $value,
+ 'type' => $type,
+ ],
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $key,
+ ]
+ );
+ if ($result === false) {
+ $error = __('Field %s not updated', $key);
+ }
+ }
+ }
+
+ $completeTask = get_parameter('complete_button', '');
+ if (empty($completeTask) === false) {
+ $result = db_process_sql_update(
+ 'trecon_task',
+ ['setup_complete' => 1],
+ ['id_rt' => $this->idTask]
+ );
+ if ($result === false) {
+ $error = __('Task not updated');
+ }
+ }
+
+ return $error;
+ }
+
+
+ /**
+ * Check if name of input macro is correct.
+ *
+ * @param string $name Name of input.
+ *
+ * @return boolean value true if name is correct.
+ */
+ private function isCorrectNameInput($name)
+ {
+ if (substr($name, 0, 1) === '_' && substr($name, -1) === '_') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Return logic for component show on true.
+ *
+ * @param string $checkbox Name the checkbox for hide input.
+ * @param string $elementToHide Name the element to hide HIS PARENT.
+ *
+ * @return string String Name the element
+ */
+ private function showOnTrue($checkbox, $elementToHide)
+ {
+ return '';
+ }
+
+
+ /**
+ * Load the macros task in database
+ *
+ * @throws Exception Excepcion to control possible error for default value.
+ *
+ * @return void
+ */
+ private function autoLoadConfigMacro()
+ {
+ $defaultValues = $this->iniFile['discovery_extension_definition']['default_value'];
+
+ foreach ($defaultValues as $key => $value) {
+ if ($value === false) {
+ $value = 0;
+ }
+
+ $result = db_process_sql_insert(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $key,
+ 'value' => (string) io_safe_input($value),
+ 'type' => 'custom',
+ ]
+ );
+ if ($result === false) {
+ throw new Exception('Error creating task');
+ }
+ }
+
+ $tempFiles = $this->iniFile['tempfile_confs']['file'];
+
+ foreach ($tempFiles as $key => $value) {
+ $result = db_process_sql_insert(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'id_task' => $this->idTask,
+ 'macro' => $key,
+ 'value' => (string) io_safe_input($value),
+ 'type' => 'custom',
+ 'temp_conf' => 1,
+ ]
+ );
+ if ($result === false) {
+ throw new Exception('Error creating task');
+ }
+ }
+ }
+
+
+ /**
+ * Return array structure for draw tree when array is by .ini.
+ *
+ * @param array $parent Parent from the tree.
+ * @param array $firstChildren First children from parent.
+ *
+ * @return array $treeInfo Return the array with format for render treee
+ */
+ private function getTreeStructure($parent, $firstChildren)
+ {
+ $treeInfo = [];
+ foreach ($firstChildren['name'] as $key => $value) {
+ $checked = false;
+ $name = (empty($firstChildren['macro'][$key]) === false) ? $firstChildren['macro'][$key].'[]' : $parent['id'].'[]';
+ $nameField = (empty($firstChildren['macro'][$key]) === false) ? $firstChildren['macro'][$key] : $parent['id'];
+ if (in_array($nameField, $this->nameFields) === false) {
+ $this->nameFields[] = $nameField;
+ }
+
+ $checkedValues = json_decode(io_safe_output($this->macrosValues[$nameField]), true);
+ if (empty($checkedValues) === false) {
+ if (in_array($firstChildren['value'][$key], $checkedValues)) {
+ $checked = true;
+ }
+ }
+
+ $treeInfo['__LEAVES__'][$key] = [
+ 'label' => $value,
+ 'selectable' => (bool) $firstChildren['selectable'][$key],
+ 'name' => $name,
+ 'value' => $firstChildren['value'][$key],
+ 'checked' => $checked,
+ ];
+
+ if (empty($firstChildren['children'][$key]) === false) {
+ $children = $this->iniFile[$firstChildren['children'][$key]];
+ $treeInfo['__LEAVES__'][$key]['sublevel'] = $this->getTreeStructure($parent, $children);
+ }
+ }
+
+ return $treeInfo;
+ }
+
+
+ /**
+ * Return array structure for draw tree when array is by script.
+ *
+ * @param array $parent Parent from the tree.
+ * @param array $firstChildren First children from parent.
+ *
+ * @return array $treeInfo Return the array with format for render treee
+ */
+ private function getTreeStructureByScript($parent, $firstChildren)
+ {
+ $treeInfo = [];
+ foreach ($firstChildren as $key => $value) {
+ $checked = false;
+ $name = (empty($value['macro']) === false) ? $value['macro'].'[]' : $parent['macro'].'[]';
+ $nameField = (empty($value['macro']) === false) ? $value['macro'] : $parent['macro'];
+ if (in_array($nameField, $this->nameFields) === false) {
+ $this->nameFields[] = $nameField;
+ }
+
+ $checkedValues = json_decode(io_safe_output($this->macrosValues[$nameField]), true);
+ if (empty($checkedValues) === false) {
+ if (in_array($value['value'], $checkedValues, true) === true) {
+ $checked = true;
+ }
+ }
+
+ $treeInfo['__LEAVES__'][$key] = [
+ 'label' => $value['name'],
+ 'selectable' => (bool) $value['selectable'],
+ 'name' => $name,
+ 'value' => $value['value'],
+ 'checked' => $checked,
+ ];
+
+ if (empty($value['children']) === false) {
+ $children = $value['children'];
+ $treeInfo['__LEAVES__'][$key]['sublevel'] = $this->getTreeStructureByScript($parent, $children);
+ }
+ }
+
+ return $treeInfo;
+ }
+
+
+ /**
+ * Return a json with the form structure for draw.
+ *
+ * @param mixed $command String.
+ *
+ * @return array Result of command.
+ */
+ private function getStructureFormByScript($command)
+ {
+ global $config;
+ $executionFiles = $this->iniFile['discovery_extension_definition']['execution_file'];
+ foreach ($executionFiles as $key => $file) {
+ $file = $config['homedir'].$this->path.'/'.$this->mode.'/'.$file;
+ $command = str_replace($key, $file, $command);
+ }
+
+ $values = $this->replaceValues($command);
+ $command = $values['command'];
+ $toDelete = $values['delete'];
+ if (empty($command) === false) {
+ $result = $this->executeCommand($command);
+ }
+
+ if (count($toDelete) > 0) {
+ foreach ($toDelete as $key => $folder) {
+ Files::rmrf($folder);
+ }
+ }
+
+ return json_decode($result, true);
+ }
+
+
+ /**
+ * Replace values in command
+ *
+ * @param string $command String command for replace macros.
+ *
+ * @return array $values Command and files to delete.
+ */
+ private function replaceValues($command)
+ {
+ preg_match_all('/\b_[a-zA-Z0-9]*_\b/', $command, $matches);
+ $foldersToDelete = [];
+ foreach ($matches[0] as $key => $macro) {
+ $row = db_get_row_filter(
+ 'tdiscovery_apps_tasks_macros',
+ [
+ 'macro' => $macro,
+ 'id_task' => $this->idTask,
+ ]
+ );
+ if ($row !== false) {
+ if (in_array($row['type'], $this->pandoraSelectData) === true) {
+ $value = $this->getValuePandoraSelect($row['type'], $row['value']);
+ $command = str_replace($macro, $value, $command);
+ } else if ((int) $row['temp_conf'] === 1) {
+ $nameFile = $row['id_task'].'_'.$row['id_task'].'_'.uniqid();
+ $value = $this->getValueTempFile($nameFile, $row['value']);
+ $command = str_replace($macro, $value, $command);
+ $foldersToDelete[] = str_replace($nameFile, '', $value);
+ } else {
+ $command = str_replace($macro, io_safe_output($row['value']), $command);
+ }
+ }
+ }
+
+ return [
+ 'command' => $command,
+ 'delete' => $foldersToDelete,
+ ];
+ }
+
+
+ /**
+ * Create a temp file for tempfile_confs macros.
+ *
+ * @param string $nameFile Name file only.
+ * @param string $content Content to save to file.
+ *
+ * @return string $pathNameFile Name file and with path for replace.
+ */
+ private function getValueTempFile($nameFile, $content)
+ {
+ global $config;
+ $content = io_safe_output($content);
+ $content = $this->replaceValues($content)['command'];
+ $nameTempDir = $config['attachment_store'].'/temp_files/';
+ if (file_exists($nameTempDir) === false) {
+ mkdir($nameTempDir);
+ }
+
+ $tmpPath = Files::tempdirnam(
+ $nameTempDir,
+ 'temp_files_'
+ );
+ $pathNameFile = $tmpPath.'/'.$nameFile;
+ file_put_contents($pathNameFile, $content);
+
+ return $pathNameFile;
+ }
+
+
+ /**
+ * Return the correct value for pandora select
+ *
+ * @param string $type Type of input.
+ * @param string $id Value of the row macro.
+ *
+ * @return string $id New id with the values replaced
+ */
+ private function getValuePandoraSelect($type, $id)
+ {
+ $id = io_safe_output($id);
+ $idsArray = json_decode($id);
+ if (is_array($idsArray) === false) {
+ $idsArray = [$id];
+ }
+
+ foreach ($idsArray as $key => $v) {
+ $value = false;
+ switch ($type) {
+ case 'agent_groups':
+ $value = groups_get_name($v);
+ break;
+
+ case 'module_groups':
+ $value = modules_get_modulegroup_name($v);
+ break;
+
+ case 'tags':
+ $value = tags_get_name($v);
+ break;
+
+ case 'alert_templates':
+ $value = alerts_get_alert_template_name($v);
+ break;
+
+ case 'alert_actions':
+ $value = alerts_get_alert_action_name($v);
+ break;
+
+ case 'credentials.custom':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode(
+ json_encode(
+ [
+ 'user' => $credentials['username'],
+ 'password' => $credentials['password'],
+ ]
+ )
+ );
+ break;
+
+ case 'credentials.aws':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode(
+ json_encode(
+ [
+ 'access_key_id' => $credentials['username'],
+ 'secret_access_key' => $credentials['password'],
+ ]
+ )
+ );
+ break;
+
+ case 'credentials.azure':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode(
+ json_encode(
+ [
+ 'client_id' => $credentials['username'],
+ 'application_secret' => $credentials['password'],
+ 'tenant_domain' => $credentials['extra_1'],
+ 'subscription_id' => $credentials['extra_2'],
+ ]
+ )
+ );
+ break;
+
+ case 'credentials.gcp':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode($credentials['extra_1']);
+ break;
+
+ case 'credentials.sap':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode(
+ json_encode(
+ [
+ 'user' => $credentials['username'],
+ 'password' => $credentials['password'],
+ ]
+ )
+ );
+ break;
+
+ case 'credentials.snmp':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode($credentials['extra_1']);
+ break;
+
+ case 'credentials.wmi':
+ $credentials = CredentialStore::getKey($v);
+ $value = base64_encode(
+ json_encode(
+ [
+ 'user' => $credentials['username'],
+ 'password' => $credentials['password'],
+ 'namespace' => $credentials['extra_1'],
+ ]
+ )
+ );
+ break;
+
+ case 'os':
+ $value = get_os_name($v);
+ break;
+
+ default:
+ continue;
+ }
+
+ if ($value !== false) {
+ $id = str_replace($v, io_safe_output($value), $id);
+ }
+ }
+
+ return $id;
+ }
+
+
+ /**
+ * Encrypt and decode password with the user script.
+ *
+ * @param string $password Password to encrypt.
+ * @param boolean $decode True for decode password.
+ *
+ * @return string Password encrypted
+ */
+ private function encryptPassword($password, $decode=false)
+ {
+ global $config;
+ if ($decode === false) {
+ $command = $this->iniFile['discovery_extension_definition']['passencrypt_exec'];
+ $nameFile = $this->iniFile['discovery_extension_definition']['passencrypt_script'];
+ $file = $config['homedir'].$this->path.'/'.$this->mode.'/'.$nameFile;
+ $command = str_replace('_passencrypt_script_', $file, $command);
+ } else {
+ $command = $this->iniFile['discovery_extension_definition']['passdecrypt_exec'];
+ $nameFile = $this->iniFile['discovery_extension_definition']['passdecrypt_script'];
+ $file = $config['homedir'].$this->path.'/'.$this->mode.'/'.$nameFile;
+ $command = str_replace('_passdecrypt_script_', $file, $command);
+ }
+
+ $command = str_replace('_password_', $password, $command);
+
+ if (empty($command) === false) {
+ return $this->executeCommand($command);
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Valid the .ini
+ *
+ * @param array $iniForValidate IniFile to validate.
+ *
+ * @return mixed Return false if is ok and string for error.
+ */
+ public static function validateIni($iniForValidate)
+ {
+ $discoveryExtension = $iniForValidate['discovery_extension_definition'];
+
+ if (!$discoveryExtension) {
+ return __('The file does not contain the block \'discovery_extension_definition\'');
+ }
+
+ if (!array_key_exists('short_name', $discoveryExtension)) {
+ return __('The \'discovery_extension_definition\' block must contain a \'short_name\' parameter');
+ }
+
+ $defaultValues = $discoveryExtension['default_value'];
+ foreach ($defaultValues as $key => $value) {
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $key)) {
+ return __(
+ 'The \'discovery_extension_definition\' block \'default_value\' parameter has a key with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'',
+ $key
+ );
+ }
+ }
+
+ $shortName = $discoveryExtension['short_name'];
+
+ if (!preg_match('/^[A-Za-z0-9._-]+$/', $shortName)) {
+ return __('The \'discovery_extension_definition\' block \'short_name\' parameter contains illegal characters. Use only letters (A-Z and a-z), numbers (0-9), points (.), hyphens (-) and underscores (_)');
+ }
+
+ if (!array_key_exists('section', $discoveryExtension) || !array_key_exists('name', $discoveryExtension)) {
+ return __('The \'discovery_extension_definition\' block must contain a \'section\' and a \'name\' parameters');
+ }
+
+ $section = $discoveryExtension['section'];
+ $name = $discoveryExtension['name'];
+
+ if (!in_array($section, ['app', 'cloud', 'custom'])) {
+ return __('The \'discovery_extension_definition\' block \'section\' parameter must be \'app\', \'cloud\' or \'custom\'');
+ }
+
+ if (empty($name)) {
+ return __('The \'discovery_extension_definition\' block \'name\' parameter can not be empty');
+ }
+
+ if (!array_key_exists('exec', $discoveryExtension)) {
+ return __('The \'discovery_extension_definition\' block must contain an \'exec\' parameter');
+ }
+
+ $execs = $discoveryExtension['exec'];
+
+ foreach ($execs as $exec) {
+ if (empty($exec)) {
+ return __('All the \'discovery_extension_definition\' block \'exec\' parameter definitions can not be empty');
+ }
+ }
+
+ $checkEmptyFields = [
+ 'passencrypt_script',
+ 'passencrypt_exec',
+ 'passdecrypt_script',
+ 'passdecrypt_exec',
+ ];
+
+ foreach ($checkEmptyFields as $key) {
+ if ($discoveryExtension[$key] !== null && empty($discoveryExtension[$key]) === true) {
+ return __('The \'discovery_extension_definition\' block \'%s\' parameter can not be empty', $key);
+ }
+ }
+
+ foreach ($discoveryExtension['execution_file'] as $key => $value) {
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $key)) {
+ return __('The \'discovery_extension_definition\' block \'execution_file\' parameter has a key with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'', $key);
+ }
+
+ if (empty($value) === true) {
+ return __('All the \'discovery_extension_definition\' block \'execution_file\' parameter definitions can not be empty: \'%s\'', $key);
+ }
+ }
+
+ if ($iniForValidate['config_steps'] !== null && empty($iniForValidate['config_steps']) === true) {
+ return __('The \'config_steps\' block must contain a \'name\' parameter that can not be empty.');
+ }
+
+ foreach ($iniForValidate['config_steps'] as $key => $value) {
+ foreach ($value as $innerKey => $inner_value) {
+ if (isset($inner_steps[$innerKey])) {
+ $inner_steps[$innerKey][$key] = $inner_value;
+ } else {
+ $inner_steps[$innerKey] = [$key => $inner_value];
+ }
+ }
+ }
+
+ $customFields = [];
+ foreach ($inner_steps as $key => $step) {
+ if (is_numeric($key) === false || $key === 0) {
+ return __('All the \'config_steps\' block parameters must use numbers greater than 0 as keys: \'%s\'.', $key);
+ }
+
+ if (empty($step['name']) === true) {
+ return __('The \'config_steps\' block must contain a \'name\' parameter for all the configuration steps: \'%s\'', $key);
+ }
+
+ if (empty($step['custom_fields']) === true
+ && empty($step['script_data_fields']) === true
+ ) {
+ return __('The \'config_steps\' block must contain a \'custom_fields\' or \'script_data_fields\' parameter that can not be empty');
+ } else if (empty($step['custom_fields']) === false) {
+ if (empty($iniForValidate[$step['custom_fields']]) === true) {
+ return __('The \'config_steps\' block \'custom_fields\' parameter has a key value reference that does not exist: \'%s\'', $step['custom_fields']);
+ } else {
+ $customFields[] = $step['custom_fields'];
+ }
+ }
+
+ $customFields[] = $step['name'];
+ }
+
+ $requiredKeys = [
+ 'macro',
+ 'name',
+ 'type',
+ ];
+
+ $validTypes = [
+ 'string',
+ 'number',
+ 'password',
+ 'textarea',
+ 'checkbox',
+ 'select',
+ 'multiselect',
+ 'tree',
+ ];
+
+ $validSelectData = [
+ 'agent_groups',
+ 'agents',
+ 'module_groups',
+ 'modules',
+ 'module_types',
+ 'status',
+ 'alert_templates',
+ 'alert_actions',
+ 'interval',
+ 'tags',
+ 'credentials.custom',
+ 'credentials.aws',
+ 'credentials.azure',
+ 'credentials.sap',
+ 'credentials.snmp',
+ 'credentials.gcp',
+ 'credentials.wmi',
+ 'os',
+ ];
+
+ $selectDataNames = [];
+ $treeDataNames = [];
+
+ foreach ($customFields as $key => $customField) {
+ $innerFields = [];
+ foreach ($iniForValidate[$customField] as $key => $value) {
+ foreach ($value as $innerKey => $innerValue) {
+ if (isset($innerFields[$innerKey])) {
+ $innerFields[$innerKey][$key] = $innerValue;
+ } else {
+ $innerFields[$innerKey] = [$key => $innerValue];
+ }
+ }
+ }
+
+ foreach ($innerFields as $key => $field) {
+ if (is_numeric($key) === false || $key === 0) {
+ return __('All the \'%s\' block parameters must use numbers greater than 0 as keys: \'%s\'.', $customField, $key);
+ }
+
+ foreach ($requiredKeys as $k => $value) {
+ if (empty($field[$value]) === true) {
+ return __('The \'%s\' block \'%s\' parameter definitions can not be empty: \'%s\'.', $customField, $value, $key);
+ }
+ }
+
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $field['macro'])) {
+ return __('The \'%s\' block \'macro\' parameter has a definition with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'', $customField, $field['macro']);
+ }
+
+ if (in_array($field['type'], $validTypes) === false) {
+ return __('The \'%s\' block \'type\' parameter has a definition with invalid value. Must be \'string\', \'number\', \'password\', \'textarea\', \'checkbox\', \'select\', \'multiselect\' or \'tree\': \'%s\'', $customField, $field['type']);
+ }
+
+ if ($field['type'] === 'select' || $field['type'] === 'multiselect') {
+ if (empty($field['select_data']) === true) {
+ return __('All the \'%s\' block \'select_data\' parameter definitions can not be empty: \'%s\'.', $customField, $key);
+ } else if ($iniForValidate[$field['select_data']] === null && in_array($field['select_data'], $validSelectData) === false) {
+ return __(
+ 'The \'%s\' block \'select_data\' parameter has a definition with invalid select type. Must be \'agent_groups\', \'agents\', \'module_groups\', \'modules\', \'module_types\', \'tags\', \'status\', \'alert_templates\', \'alert_actions\', \'interval\', \'credentials.custom\', \'credentials.aws\', \'credentials.azure\', \'credentials.gcp\', \'credentials.sap\', \'credentials.snmp\', \'os\' or an existint reference: \'%s\'',
+ $customField,
+ $field['select_data']
+ );
+ } else if ($iniForValidate[$field['select_data']] !== null) {
+ $selectDataNames[] = $field['select_data'];
+ }
+ }
+
+ if ($field['type'] === 'tree') {
+ if (empty($field['tree_data']) === true) {
+ return __('All the \'%s\' block \'tree_data\' parameter definitions can not be empty: \'%s\'', $field['macro'], $key);
+ } else if ($iniForValidate[$field['tree_data']] === null) {
+ return __('The \'%s\' block \'tree_data\' parameter has a key value reference that does not exist: \'%s\'', $customField, $field['tree_data']);
+ } else {
+ $treeDataNames[] = $field['tree_data'];
+ }
+ }
+
+ if (empty($field['mandatory_field']) === false) {
+ $validValues = [
+ 'true',
+ 'false',
+ '1',
+ '0',
+ 'yes',
+ 'no',
+ ];
+
+ if (in_array($field['mandatory_field'], $validValues) === false) {
+ return __(
+ 'The \'%s\' block \'mandatory_field\' parameter has a definition with invalid value. Must be \'true\' or \'false\', \'1\' or \'0\', \'yes\' or \'no\': \'%s\'',
+ $customField,
+ $field['mandatory_field']
+ );
+ }
+ }
+
+ if ($field['tip'] !== null && empty($field['tip']) === true) {
+ return __('All the \'%s\' block \'tip\' parameter definitions can not be empty: \'%s\'.', $customField, $key);
+ }
+
+ if ($field['placeholder'] !== null && empty($field['placeholder']) === true) {
+ return __('All the \'%s\' block \'placeholder\' parameter definitions can not be empty: \'%s\'.', $customField, $key);
+ }
+
+ if (empty($field['show_on_true']) === false) {
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $field['show_on_true'])) {
+ return __(
+ 'The \'%s\' block \'show_on_true\' parameter has a definition with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'',
+ $customField,
+ $field['show_on_true']
+ );
+ }
+ }
+
+ if (empty($field['encrypt_on_true']) === false) {
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $field['encrypt_on_true'])) {
+ return __(
+ 'The \'%s\' block \'encrypt_on_true\' parameter has a definition with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'',
+ $customField,
+ $field['encrypt_on_true']
+ );
+ }
+ }
+ }
+ }
+
+ foreach ($treeDataNames as $key => $name) {
+ $error = self::validateTreeRecursive($name, $iniForValidate);
+ if ($error !== false) {
+ return $error;
+ }
+ }
+
+ foreach ($selectDataNames as $key => $name) {
+ if (empty($iniForValidate[$name]['option']) === true) {
+ return __('The \'%s\' block must contain an \'option\' parameter', $name);
+ }
+
+ foreach ($iniForValidate[$name]['option'] as $key => $option) {
+ if (empty($option) === true) {
+ return __('All the \'%s\' block \'option\' parameter definitions can not be empty: \'%s\'.', $name, $key);
+ }
+ }
+ }
+
+ if ($iniForValidate['tempfile_confs'] !== null && empty($iniForValidate['tempfile_confs']['file']) === true) {
+ return __('The \'tempfile_confs\' block must contain a \'file\' parameter.');
+ }
+
+ foreach ($iniForValidate['tempfile_confs']['file'] as $key => $tempfile) {
+ if (!preg_match('/^_[a-zA-Z0-9]+_$/', $key)) {
+ return __(
+ 'The \'tempfile_confs\' block \'file\' parameter has a key with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'',
+ $key
+ );
+ }
+
+ if (empty($tempfile) === true) {
+ return __('All the \'tempfile_confs\' block \'file\' parameter definitions can not be empty: \'%s\'.', $key);
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Validate a tree recursively
+ *
+ * @param string $dataTree Name of parent data_tree.
+ * @param array $iniFile Inifile for search children.
+ * @param array $parents Array of parents for recursive action, DO NOT SET.
+ *
+ * @return boolean True if tree is correct.
+ */
+ public static function validateTreeRecursive($dataTree, $iniFile, $parents=[])
+ {
+ $innerData = [];
+ $parents[] = $dataTree;
+ foreach ($iniFile[$dataTree] as $key => $value) {
+ foreach ($value as $innerKey => $innerValue) {
+ if (isset($innerData[$innerKey])) {
+ $innerData[$innerKey][$key] = $innerValue;
+ } else {
+ $innerData[$innerKey] = [$key => $innerValue];
+ }
+ }
+ }
+
+ if (count($innerData) === 0) {
+ return __('The \'%s\' block must contain a \'name\' parameter that can not be empty.', $dataTree);
+ }
+
+ foreach ($innerData as $key => $prop) {
+ if (is_numeric($key) === false || $key === 0) {
+ return __('All the \'%s\' block parameters must use numbers greater than 0 as keys: \'%s\'.', $dataTree, $key);
+ }
+
+ if (empty($prop['name']) === true) {
+ return __('The \'%s\' block must contain a \'name\' parameter for all the tree elements: \'%s\'.', $dataTree, $key);
+ }
+
+ if ($prop['selectable'] !== null && $prop['selectable'] === '') {
+ return __('All the \'%s\' block \'selectable\' parameter definitions can not be empty: \'%s\'.', $dataTree, $key);
+ } else {
+ $validValues = [
+ 'true',
+ 'false',
+ '1',
+ '0',
+ 'yes',
+ 'no',
+ ];
+
+ if (in_array($prop['selectable'], $validValues) === false) {
+ return __(
+ 'The \'%s\' block \'selectable\' parameter has a definition with invalid value. Must be \'true\' or \'false\', \'1\' or \'0\', \'yes\' or \'no\': \'%s\'',
+ $dataTree,
+ $prop['selectable']
+ );
+ }
+ }
+
+ if ($prop['macro'] !== null && !preg_match('/^_[a-zA-Z0-9]+_$/', $prop['macro'])) {
+ return __(
+ 'The \'%s\' block \'macro\' parameter has a definition with invalid format. Use only letters (A-Z and a-z) and numbers (0-9) between opening and ending underscores (_): \'%s\'',
+ $dataTree,
+ $prop['macro']
+ );
+ }
+
+ if ($prop['children'] !== null && empty($iniFile[$prop['children']]) === true) {
+ return __('The \'%s\' block \'children\' parameter has a key value reference that does not exist: \'%s\'', $dataTree, $prop['children']);
+ } else if (in_array($prop['children'], $parents) === true) {
+ return __('The \'%s\' block \'children\' parameter has a key value reference to a parent tree element: \'%s\'', $dataTree, $prop['children']);
+ } else if (empty($iniFile[$prop['children']]) === false) {
+ $result = self::validateTreeRecursive($prop['children'], $iniFile, $parents);
+ if ($result !== false) {
+ return $result;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Excute command with the timeout of the task.
+ *
+ * @param string $command Command to execute.
+ *
+ * @return string Output of command
+ */
+ private function executeCommand($command)
+ {
+ $task = $this->getTask();
+ $timeout = $task['executions_timeout'];
+
+ $descriptors = [
+ 0 => [
+ 'pipe',
+ 'r',
+ ],
+ 1 => [
+ 'pipe',
+ 'w',
+ ],
+ 2 => [
+ 'pipe',
+ 'w',
+ ],
+ ];
+
+ $process = proc_open($command, $descriptors, $pipes);
+
+ if (!is_resource($process)) {
+ return false;
+ }
+
+ stream_set_blocking($pipes[1], 0);
+
+ stream_set_blocking($pipes[2], 0);
+
+ if (!$timeout) {
+ $timeout = 5;
+ }
+
+ $real_timeout = ($timeout * 1000000);
+
+ $buffer = '';
+
+ while ($real_timeout > 0) {
+ $start = microtime(true);
+
+ $read = [$pipes[1]];
+ $other = [];
+ stream_select($read, $other, $other, 0, $real_timeout);
+
+ $status = proc_get_status($process);
+
+ $buffer .= stream_get_contents($pipes[1]);
+
+ if ($status['running'] === false) {
+ break;
+ }
+
+ $real_timeout -= ((microtime(true) - $start) * 1000000);
+ }
+
+ if ($real_timeout <= 0) {
+ proc_terminate($process, 9);
+
+ fclose($pipes[0]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+
+ proc_close($process);
+
+ return false;
+ }
+
+ $errors = stream_get_contents($pipes[2]);
+
+ if (empty($errors) === false && empty($buffer)) {
+ proc_terminate($process, 9);
+
+ fclose($pipes[0]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+
+ proc_close($process);
+
+ return false;
+ }
+
+ proc_terminate($process, 9);
+
+ fclose($pipes[0]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+
+ proc_close($process);
+
+ return $buffer;
+ }
+
+
+}
diff --git a/pandora_console/include/javascript/manage_extensions.js b/pandora_console/include/javascript/manage_extensions.js
new file mode 100644
index 0000000000..e5c98c6a58
--- /dev/null
+++ b/pandora_console/include/javascript/manage_extensions.js
@@ -0,0 +1,72 @@
+/* globals $, page, url, textsToTranslate, confirmDialog*/
+$(document).ready(function() {
+ function loading(status) {
+ if (status) {
+ $(".spinner-fixed").show();
+ $("#button-upload_button").attr("disabled", "true");
+ } else {
+ $(".spinner-fixed").hide();
+ $("#button-upload_button").removeAttr("disabled");
+ }
+ }
+
+ $("#uploadExtension").submit(function(e) {
+ e.preventDefault();
+ var formData = new FormData(this);
+ formData.append("page", page);
+ formData.append("method", "validateIniName");
+ loading(true);
+ $.ajax({
+ method: "POST",
+ url: url,
+ data: formData,
+ processData: false,
+ contentType: false,
+ success: function(data) {
+ loading(false);
+ data = JSON.parse(data);
+ if (data.success) {
+ if (data.warning) {
+ confirmDialog({
+ title: textsToTranslate["Warning"],
+ message: data.message,
+ strOKButton: textsToTranslate["Confirm"],
+ strCancelButton: textsToTranslate["Cancel"],
+ onAccept: function() {
+ loading(true);
+ $("#uploadExtension")[0].submit();
+ },
+ onDeny: function() {
+ return false;
+ }
+ });
+ } else {
+ $("#uploadExtension")[0].submit();
+ }
+ } else {
+ confirmDialog({
+ title: textsToTranslate["Error"],
+ message: data.message,
+ ok: textsToTranslate["Ok"],
+ hideCancelButton: true,
+ onAccept: function() {
+ return false;
+ }
+ });
+ }
+ },
+ error: function() {
+ loading(false);
+ confirmDialog({
+ title: textsToTranslate["Error"],
+ message: textsToTranslate["Failed to upload extension"],
+ ok: textsToTranslate["Ok"],
+ hideCancelButton: true,
+ onAccept: function() {
+ return false;
+ }
+ });
+ }
+ });
+ });
+});