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 '
'; + html_print_input_hidden('upload_disco', 1); + html_print_table($table); + echo '
'; + + echo '
'; + 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'] = '
'; + $data[$key]['actions'] .= html_print_input_image( + 'button_delete', + 'images/delete.svg', + '', + '', + true, + [ + 'onclick' => 'if (!confirm(\''.__('Deleting this application will also delete all the discovery tasks using it. Do you want to delete it?').'\')) return false;', + 'class' => 'main_menu_icon invert_filter action_button_hidden', + ] + ); + $data[$key]['actions'] .= html_print_input_hidden('short_name', $row['short_name'], true); + $data[$key]['actions'] .= '
'; + if ($this->checkFolderConsole($row['short_name']) === true) { + $data[$key]['actions'] .= '
'; + $data[$key]['actions'] .= html_print_input_image( + 'button_refresh', + 'images/refresh@svg.svg', + '', + '', + true, + [ + 'onclick' => 'if (!confirm(\''.__('Are you sure you want to reapply?').'\')) return false;', + 'class' => 'main_menu_icon invert_filter action_button_hidden', + ] + ); + $data[$key]['actions'] .= html_print_input_hidden('sync_action', 'refresh', true); + $data[$key]['actions'] .= html_print_input_hidden('short_name', $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 @@ + + + + Configurar app@svg + Created with Sketch. + + + + + + + + + + + + + + + + + \ 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 @@ + + + + Custom apps@svg + Created with Sketch. + + + + + + + + + + + + + + + + \ 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 @@ + + + + App genérico@svg + Created with Sketch. + + + + + + + + + + + + + \ 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 '
'; + html_print_table($table); + + $actionButtons = ''; + + if ($this->currentPage !== $this->nextPage()) { + $actionButtons = html_print_submit_button( + __('Next'), + 'next_button', + false, + [ + 'class' => 'sub', + 'icon' => 'plus', + ], + true + ); + } + + $actionButtons .= html_print_submit_button( + __('Complete setup'), + 'complete_button', + false, + [ + 'class' => 'sub', + 'icon' => 'update', + 'value' => '1', + ], + true + ); + + html_print_action_buttons($actionButtons); + 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; + } + }); + } + }); + }); +});