diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index 436742c998..43c4217843 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.738-190918 +Version: 7.0NG.738-190926 Architecture: all Priority: optional Section: admin diff --git a/pandora_agents/unix/DEBIAN/make_deb_package.sh b/pandora_agents/unix/DEBIAN/make_deb_package.sh index 9d1267941f..7dbbd80d0e 100644 --- a/pandora_agents/unix/DEBIAN/make_deb_package.sh +++ b/pandora_agents/unix/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.738-190918" +pandora_version="7.0NG.738-190926" echo "Test if you has the tools for to make the packages." whereis dpkg-deb | cut -d":" -f2 | grep dpkg-deb > /dev/null diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 8c23906074..4c1f64c58c 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -42,7 +42,7 @@ my $Sem = undef; my $ThreadSem = undef; use constant AGENT_VERSION => '7.0NG.738'; -use constant AGENT_BUILD => '190918'; +use constant AGENT_BUILD => '190926'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; @@ -239,6 +239,15 @@ my $tentacle_pid = undef; # PID of udp_server my $udp_server_pid = undef; +# BrokerFlag +my $BrokerFlag = 0; + +# Global loop counter. +my $LoopCounter = 0; + +# Define a max value for loopCounter to avoid overflow. +use constant MAX_LOOP_COUNTER => 1000000000; + ################################################################################ # Print usage information and exit. ################################################################################ @@ -1385,6 +1394,7 @@ sub sleep_agent { exit (0); } + $LoopCounter = ($LoopCounter + 1) % MAX_LOOP_COUNTER; return $iter_base_time; } @@ -1699,6 +1709,14 @@ sub exec_module { } # Check module interval + if ($BrokerFlag > 0) { + if ($LoopCounter == 0) { + $module->{'counter'} = $module->{'intensive_interval'}; + } else { + $module->{'counter'} = (($LoopCounter -1 ) % $module->{'intensive_interval'}); + } + } + if (++($module->{'counter'}) < $module->{'intensive_interval'}) { $ThreadSem->up () if (defined ($ThreadSem) && $Conf{'agent_threads'} > 1); return; @@ -2985,7 +3003,6 @@ while (1) { @BrokerPid = (); my @broker_agents = read_config ('broker_agent'); foreach my $broker_agent (@broker_agents) { - # Create broker conf file if it does not exist if (! -e "$ConfDir/${broker_agent}.conf") { write_broker_conf($broker_agent); @@ -2995,7 +3012,9 @@ while (1) { # Broker agent if ($main_agent == 0) { - + # Mark broker flag. + $BrokerFlag = 1; + # Set the configuration file $ConfFile = "${broker_agent}.conf"; diff --git a/pandora_agents/unix/pandora_agent.redhat.spec b/pandora_agents/unix/pandora_agent.redhat.spec index de15423433..b443c3098d 100644 --- a/pandora_agents/unix/pandora_agent.redhat.spec +++ b/pandora_agents/unix/pandora_agent.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.738 -%define release 190918 +%define release 190926 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent.spec b/pandora_agents/unix/pandora_agent.spec index a3df706fbd..b38dc26c97 100644 --- a/pandora_agents/unix/pandora_agent.spec +++ b/pandora_agents/unix/pandora_agent.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.738 -%define release 190918 +%define release 190926 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent_installer b/pandora_agents/unix/pandora_agent_installer index ae22fe6afa..cdbabaa899 100755 --- a/pandora_agents/unix/pandora_agent_installer +++ b/pandora_agents/unix/pandora_agent_installer @@ -10,7 +10,7 @@ # ********************************************************************** PI_VERSION="7.0NG.738" -PI_BUILD="190918" +PI_BUILD="190926" OS_NAME=`uname -s` FORCE=0 @@ -162,7 +162,15 @@ uninstall () { rm -Rf $PANDORA_BASE$PANDORA_EXEC_BIN 2> /dev/null rm -Rf $PANDORA_BASE$PANDORA_REVENT_BIN 2> /dev/null rm -f $DESTDIR/etc/logrotate.d/pandora_agent - + + # Remove systemd service if exists + if [ $(systemctl --v | grep systemd | wc -l) != 0 ] + then + PANDORA_AGENT_SERVICE="/etc/systemd/system/pandora_agent_daemon.service" + rm -f $PANDORA_AGENT_SERVICE + systemctl reset-failed + fi + #Test if exist Pandora Server in this machine if [ -d $PANDORA_BASE$PANDORA_TEMP/data_in ] then @@ -465,6 +473,22 @@ install () { else RCDIRS="/etc/rc2.d /etc/rc3.d" fi + + # Create systemd service + if [ $(systemctl --v | grep systemd | wc -l) != 0 ] + then + echo "Creating systemd service for pandora_agent_daemon" + + PANDORA_AGENT_SERVICE="/etc/systemd/system/pandora_agent_daemon.service" + EXEC_START='ExecStart='$PANDORA_BASE$PANDORA_BIN' '$PANDORA_BASE$PANDORA_CFG + + rm -f $PANDORA_AGENT_SERVICE + cp pandora_agent_daemon.service $PANDORA_AGENT_SERVICE + + sed -i "s|^ExecStart=.*$|$EXEC_START|g" $PANDORA_AGENT_SERVICE + + systemctl daemon-reload + fi fi [ "$RCDIRS" ] && for RCDIR in $RCDIRS do diff --git a/pandora_agents/win32/installer/pandora.mpi b/pandora_agents/win32/installer/pandora.mpi index 197edbf033..a5423ead04 100644 --- a/pandora_agents/win32/installer/pandora.mpi +++ b/pandora_agents/win32/installer/pandora.mpi @@ -186,7 +186,7 @@ UpgradeApplicationID {} Version -{190918} +{190926} ViewReadme {Yes} diff --git a/pandora_agents/win32/modules/pandora_module.cc b/pandora_agents/win32/modules/pandora_module.cc index e7c751d367..7cbde21ca7 100644 --- a/pandora_agents/win32/modules/pandora_module.cc +++ b/pandora_agents/win32/modules/pandora_module.cc @@ -1422,48 +1422,48 @@ Pandora_Module::evaluatePreconditions () { buffer[read] = '\0'; output += (char *) buffer; } - - try { - double_output = Pandora_Strutils::strtodouble (output); - } catch (Pandora_Strutils::Invalid_Conversion e) { - double_output = 0; + + try { + double_output = Pandora_Strutils::strtodouble (output); + } catch (Pandora_Strutils::Invalid_Conversion e) { + double_output = 0; + } + + if (dwRet == WAIT_OBJECT_0) { + break; + } else if(this->getTimeout() < GetTickCount() - tickbase) { + /* STILL_ACTIVE */ + TerminateProcess(pi.hThread, STILL_ACTIVE); + pandoraLog ("evaluatePreconditions: %s timed out (retcode: %d)", this->module_name.c_str (), STILL_ACTIVE); + break; + } } - if (dwRet == WAIT_OBJECT_0) { - break; - } else if(this->getTimeout() < GetTickCount() - tickbase) { - /* STILL_ACTIVE */ - TerminateProcess(pi.hThread, STILL_ACTIVE); - pandoraLog ("evaluatePreconditions: %s timed out (retcode: %d)", this->module_name.c_str (), STILL_ACTIVE); - break; + GetExitCodeProcess (pi.hProcess, &retval); + + if (retval != 0) { + if (! TerminateJobObject (job, 0)) { + pandoraLog ("evaluatePreconditions: TerminateJobObject failed. (error %d)", + GetLastError ()); + } + if (retval != STILL_ACTIVE) { + pandoraLog ("evaluatePreconditions: %s did not executed well (retcode: %d)", + this->module_name.c_str (), retval); + } + /* Close job, process and thread handles. */ + CloseHandle (job); + CloseHandle (pi.hProcess); + CloseHandle (pi.hThread); + CloseHandle (new_stdout); + CloseHandle (out_read); + return 0; } - } - - GetExitCodeProcess (pi.hProcess, &retval); - - if (retval != 0) { - if (! TerminateJobObject (job, 0)) { - pandoraLog ("evaluatePreconditions: TerminateJobObject failed. (error %d)", - GetLastError ()); - } - if (retval != STILL_ACTIVE) { - pandoraLog ("evaluatePreconditions: %s did not executed well (retcode: %d)", - this->module_name.c_str (), retval); - } + /* Close job, process and thread handles. */ CloseHandle (job); CloseHandle (pi.hProcess); CloseHandle (pi.hThread); - CloseHandle (new_stdout); - CloseHandle (out_read); - return 0; } - - /* Close job, process and thread handles. */ - CloseHandle (job); - CloseHandle (pi.hProcess); - CloseHandle (pi.hThread); - } CloseHandle (new_stdout); CloseHandle (out_read); @@ -1693,4 +1693,18 @@ Pandora_Module::getAsync () { return this->async; } +/** + * Get current exections + */ +long +Pandora_Module::getExecutions () { + return this->executions; +} +/** + * Set current execution (global) used for brokers. + */ +void +Pandora_Module::setExecutions (long executions) { + this->executions = executions; +} diff --git a/pandora_agents/win32/modules/pandora_module.h b/pandora_agents/win32/modules/pandora_module.h index c766766950..3b9d6c15a4 100644 --- a/pandora_agents/win32/modules/pandora_module.h +++ b/pandora_agents/win32/modules/pandora_module.h @@ -234,6 +234,8 @@ namespace Pandora_Modules { int getTimeout (); string getSave (); bool getAsync (); + void setExecutions(long executions=0); + long getExecutions(); virtual string getXml (); diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index 6081bda4d4..e572687b07 100644 --- a/pandora_agents/win32/pandora.cc +++ b/pandora_agents/win32/pandora.cc @@ -30,7 +30,7 @@ using namespace Pandora; using namespace Pandora_Strutils; #define PATH_SIZE _MAX_PATH+1 -#define PANDORA_VERSION ("7.0NG.738(Build 190918)") +#define PANDORA_VERSION ("7.0NG.738(Build 190926)") string pandora_path; string pandora_dir; diff --git a/pandora_agents/win32/pandora_windows_service.cc b/pandora_agents/win32/pandora_windows_service.cc index 275ee60724..b62fc1f70d 100644 --- a/pandora_agents/win32/pandora_windows_service.cc +++ b/pandora_agents/win32/pandora_windows_service.cc @@ -1849,7 +1849,7 @@ Pandora_Windows_Service::sendBufferedXml (string path) { } void -Pandora_Windows_Service::pandora_run_broker (string config) { +Pandora_Windows_Service::pandora_run_broker (string config, long executions) { Pandora_Agent_Conf *conf = NULL; string server_addr; unsigned char data_flag = 0; @@ -1876,7 +1876,10 @@ Pandora_Windows_Service::pandora_run_broker (string config) { Pandora_Module *module; module = this->broker_modules->getCurrentValue (); - + + /* Keep executions matching main agent */ + module->setExecutions(executions); + /* Check preconditions */ if (module->evaluatePreconditions () == 0) { pandoraDebug ("Preconditions not matched for module %s", module->getName ().c_str ()); @@ -2074,7 +2077,7 @@ Pandora_Windows_Service::pandora_run (int forced_run) { check_broker_agents(all_conf); for (i=0;i".__(' Pandora FMS Licence Information').''; render_row(html_print_textarea('keys[customer_key]', 10, 255, $settings->customer_key, 'style="height:40px; width:450px;"', true), 'Customer key'); - render_row($license['expiry_date'], 'Expires'); + render_row($license['expiry_date'], $license['expiry_caption']); render_row($license['limit'].' agents', 'Platform Limit'); render_row($license['count'].' agents', 'Current Platform Count'); render_row($license['count_enabled'].' agents', 'Current Platform Count (enabled: items)'); diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql index 704ab61d74..a449004ce8 100644 --- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql @@ -219,14 +219,17 @@ CREATE TABLE IF NOT EXISTS `tdashboard` ( -- Table `tdatabase` -- --------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `tdatabase` ( - `id` int(10) unsigned NOT NULL auto_increment, - `host` varchar(100) default '', - `os_port` int(4) unsigned default '22', - `os_user` varchar(100) default '', - `db_port` int(4) unsigned default '3306', + `id` INT(10) unsigned NOT NULL auto_increment, + `host` VARCHAR(255) default '', + `label` VARCHAR(255) default '', + `os_port` INT UNSIGNED NOT NULL DEFAULT 22, + `os_user` VARCHAR(255) default '', + `db_port` INT UNSIGNED NOT NULL DEFAULT 3306, `status` tinyint(1) unsigned default '0', `action` tinyint(1) unsigned default '0', - `last_error` varchar(255) default '', + `ssh_key` TEXT, + `ssh_pubkey` TEXT, + `last_error` TEXT, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8 ; @@ -798,6 +801,7 @@ ALTER TABLE `treport_content_template` ADD COLUMN `agent_min_value` TINYINT(1) D ALTER TABLE `treport_content_template` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; ALTER TABLE `treport_content_template` ADD COLUMN `failover_mode` tinyint(1) DEFAULT '1'; ALTER TABLE `treport_content_template` ADD COLUMN `failover_type` tinyint(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `uncompressed_module` TINYINT DEFAULT '0'; -- ----------------------------------------------------- -- Table `treport_content_sla_com_temp` (treport_content_sla_combined_template) @@ -1454,6 +1458,7 @@ ALTER TABLE `treport_content` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; ALTER TABLE `treport_content` ADD COLUMN `failover_mode` tinyint(1) DEFAULT '0'; ALTER TABLE `treport_content` ADD COLUMN `failover_type` tinyint(1) DEFAULT '0'; ALTER table `treport_content` MODIFY COLUMN `name` varchar(300) NULL; +ALTER TABLE `treport_content` ADD COLUMN `uncompressed_module` TINYINT DEFAULT '0'; -- --------------------------------------------------------------------- -- Table `tmodule_relationship` diff --git a/pandora_console/general/firts_task/HA_cluster_builder.php b/pandora_console/general/firts_task/HA_cluster_builder.php index fa85e28381..6d82d94ad0 100644 --- a/pandora_console/general/firts_task/HA_cluster_builder.php +++ b/pandora_console/general/firts_task/HA_cluster_builder.php @@ -1,15 +1,25 @@ - true, 'message' => __('There are no HA clusters defined yet.') ]); ?> @@ -44,8 +52,9 @@ ui_print_info_message(['no_close' => true, 'message' => __('There are no HA clus

"; + echo "
"; ?> true, 'message' => __('There are no HA clus ?> - diff --git a/pandora_console/godmode/groups/credential_store.php b/pandora_console/godmode/groups/credential_store.php index 3273e1c038..46d1a929b7 100644 --- a/pandora_console/godmode/groups/credential_store.php +++ b/pandora_console/godmode/groups/credential_store.php @@ -29,599 +29,43 @@ // Begin. global $config; -// Check access. -check_login(); +require_once $config['homedir'].'/include/class/CredentialStore.class.php'; -if (! check_acl($config['id_user'], 0, 'PM')) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access event viewer' - ); +$ajaxPage = 'godmode/groups/credential_store'; +// Control call flow. +try { + // User access and validation is being processed on class constructor. + $cs = new CredentialStore($ajaxPage); +} catch (Exception $e) { if (is_ajax()) { - return ['error' => 'noaccess']; + echo json_encode(['error' => '[CredentialStore]'.$e->getMessage() ]); + exit; + } else { + echo '[CredentialStore]'.$e->getMessage(); } - include 'general/noaccess.php'; + // Stop this execution, but continue 'globally'. return; } -// Required files. -ui_require_css_file('credential_store'); -require_once $config['homedir'].'/include/functions_credential_store.php'; -require_once $config['homedir'].'/include/functions_io.php'; - +// AJAX controller. if (is_ajax()) { - $draw = get_parameter('draw', 0); - $filter = get_parameter('filter', []); - $get_key = get_parameter('get_key', 0); - $new_form = get_parameter('new_form', 0); - $new_key = get_parameter('new_key', 0); - $update_key = get_parameter('update_key', 0); - $delete_key = get_parameter('delete_key', 0); + $method = get_parameter('method'); - if ($new_form) { - echo print_inputs(); - exit; - } - - if ($delete_key) { - $identifier = get_parameter('identifier', null); - - if (empty($identifier)) { - ajax_msg('error', __('identifier cannot be empty')); - } - - if (db_process_sql_delete( - 'tcredential_store', - ['identifier' => $identifier] - ) === false - ) { - ajax_msg('error', $config['dbconnection']->error, true); + if (method_exists($cs, $method) === true) { + if ($cs->ajaxMethod($method) === true) { + $cs->{$method}(); } else { - ajax_msg('result', $identifier, true); + $cs->error('Unavailable method.'); } + } else { + $cs->error('Method not found. ['.$method.']'); } - if ($update_key) { - $data = get_parameter('values', null); - - if ($data === null || !is_array($data)) { - echo json_encode(['error' => __('Invalid parameters, please retry')]); - exit; - } - - $values = []; - foreach ($data as $key => $value) { - if ($key == 'identifier') { - $identifier = base64_decode($value); - } else if ($key == 'product') { - $product = base64_decode($value); - } else { - $values[$key] = base64_decode($value); - } - } - - if (empty($identifier)) { - ajax_msg('error', __('identifier cannot be empty')); - } - - if (empty($product)) { - ajax_msg('error', __('product cannot be empty')); - } - - if (db_process_sql_update( - 'tcredential_store', - $values, - ['identifier' => $identifier] - ) === false - ) { - ajax_msg('error', $config['dbconnection']->error); - } else { - ajax_msg('result', $identifier); - } - - exit; - } - - if ($new_key) { - $data = get_parameter('values', null); - - if ($data === null || !is_array($data)) { - echo json_encode(['error' => __('Invalid parameters, please retry')]); - exit; - } - - $values = []; - foreach ($data as $key => $value) { - $values[$key] = base64_decode($value); - if ($key == 'identifier') { - $values[$key] = preg_replace('/\s+/', '-', trim($values[$key])); - } - } - - $identifier = $values['identifier']; - - if (empty($identifier)) { - ajax_msg('error', __('identifier cannot be empty')); - } - - if (empty($values['product'])) { - ajax_msg('error', __('product cannot be empty')); - } - - if (db_process_sql_insert('tcredential_store', $values) === false) { - ajax_msg('error', $config['dbconnection']->error); - } else { - ajax_msg('result', $identifier); - } - - exit; - } - - if ($get_key) { - $identifier = get_parameter('identifier', null); - - $key = get_key($identifier); - echo print_inputs($key); - - exit; - } - - if ($draw) { - // Datatables offset, limit and order. - $start = get_parameter('start', 0); - $length = get_parameter('length', $config['block_size']); - $order = get_datatable_order(true); - try { - ob_start(); - - $fields = [ - 'cs.*', - 'tg.nombre as `group`', - ]; - - // Retrieve data. - $data = credentials_get_all( - // Fields. - $fields, - // Filter. - $filter, - // Offset. - $start, - // Limit. - $length, - // Order. - $order['direction'], - // Sort field. - $order['field'] - ); - - // Retrieve counter. - $count = credentials_get_all( - 'count', - $filter - ); - - if ($data) { - $data = array_reduce( - $data, - function ($carry, $item) { - // Transforms array of arrays $data into an array - // of objects, making a post-process of certain fields. - $tmp = (object) $item; - $tmp->username = io_safe_output($tmp->username); - - if (empty($tmp->group)) { - $tmp->group = __('All'); - } else { - $tmp->group = io_safe_output($tmp->group); - } - - $carry[] = $tmp; - return $carry; - } - ); - } - - // Datatables format: RecordsTotal && recordsfiltered. - echo json_encode( - [ - 'data' => $data, - 'recordsTotal' => $count, - 'recordsFiltered' => $count, - ] - ); - // Capture output. - $response = ob_get_clean(); - } catch (Exception $e) { - return json_encode(['error' => $e->getMessage()]); - } - - // If not valid, show error with issue. - json_decode($response); - if (json_last_error() == JSON_ERROR_NONE) { - // If valid dump. - echo $response; - } else { - echo json_encode( - ['error' => $response] - ); - } - - - exit; - } - + // Stop any execution. exit; +} else { + // Run. + $cs->run(); } - -// Datatables list. -try { - $columns = [ - 'group', - 'identifier', - 'product', - 'username', - 'options', - ]; - - $column_names = [ - __('Group'), - __('Identifier'), - __('Product'), - __('User'), - [ - 'text' => __('Options'), - 'class' => 'action_buttons', - ], - ]; - - $table_id = 'keystore'; - // Load datatables user interface. - ui_print_datatable( - [ - 'id' => $table_id, - 'class' => 'info_table', - 'style' => 'width: 100%', - 'columns' => $columns, - 'column_names' => $column_names, - 'ajax_url' => 'godmode/groups/credential_store', - 'ajax_postprocess' => 'process_datatables_item(item)', - 'no_sortable_columns' => [-1], - 'order' => [ - 'field' => 'identifier', - 'direction' => 'asc', - ], - 'search_button_class' => 'sub filter float-right', - 'form' => [ - 'inputs' => [ - [ - 'label' => __('Group'), - 'type' => 'select', - 'id' => 'filter_id_group', - 'name' => 'filter_id_group', - 'options' => users_get_groups_for_select( - $config['id_user'], - 'AR', - true, - true, - false - ), - ], - [ - 'label' => __('Free search'), - 'type' => 'text', - 'class' => 'mw250px', - 'id' => 'free_search', - 'name' => 'free_search', - ], - ], - ], - ] - ); -} catch (Exception $e) { - echo $e->getMessage(); -} - -// Auxiliar div. -$new = ''; -$details = ''; -$aux = ''; - - -echo $new.$details.$aux; - -// Create button. -echo '
'; -html_print_submit_button( - __('Add key'), - 'create', - false, - 'class="sub next"' -); -echo '
'; - -?> - - diff --git a/pandora_console/godmode/reporting/reporting_builder.item_editor.php b/pandora_console/godmode/reporting/reporting_builder.item_editor.php index 7407ba93fd..fa015300d6 100755 --- a/pandora_console/godmode/reporting/reporting_builder.item_editor.php +++ b/pandora_console/godmode/reporting/reporting_builder.item_editor.php @@ -153,6 +153,7 @@ $checks_in_ok_status = true; $unknown_checks = true; $agent_max_value = true; $agent_min_value = true; +$uncompressed_module = true; switch ($action) { case 'new': @@ -427,6 +428,7 @@ switch ($action) { ); $idAgentModule = $item['id_agent_module']; $period = $item['period']; + $uncompressed_module = $item['uncompressed_module']; break; case 'historical_data': @@ -810,7 +812,6 @@ switch ($action) { break; } - $urlForm = $config['homeurl'].'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder&tab=item_editor&action='.$actionParameter.'&id_report='.$idReport; echo '
'; @@ -2792,6 +2793,23 @@ $class = 'databox filters'; ?> + + + + + + + + + + @@ -4524,6 +4542,7 @@ function chooseType() { $('#row_select_fields').hide(); $("#row_select_fields2").hide(); $("#row_select_fields3").hide(); + $("#row_uncompressed_module").hide(); // SLA list default state. $("#sla_list").hide(); @@ -4732,6 +4751,7 @@ function chooseType() { $("#row_module").show(); $("#row_period").show(); $("#row_historical_db_check").hide(); + $("#row_uncompressed_module").show(); break; case 'historical_data': diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index aa89496ef7..fa2e7440fa 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -1908,6 +1908,11 @@ switch ($action) { $values['id_agent'] = get_parameter('group'); } + if ($values['type'] == 'sumatory') { + $values['uncompressed_module'] = get_parameter('uncompressed_module', 0); + } + + $values['header_definition'] = get_parameter('header'); $values['column_separator'] = get_parameter('field'); $values['line_separator'] = get_parameter('line'); @@ -2464,6 +2469,10 @@ switch ($action) { $values['id_agent'] = get_parameter('group'); } + if ($values['type'] == 'sumatory') { + $values['uncompressed_module'] = get_parameter('uncompressed_module', 0); + } + $values['header_definition'] = get_parameter('header'); $values['column_separator'] = get_parameter('field'); $values['line_separator'] = get_parameter('line'); diff --git a/pandora_console/godmode/setup/license.php b/pandora_console/godmode/setup/license.php index 06d7d2e7e3..c459cdb39f 100644 --- a/pandora_console/godmode/setup/license.php +++ b/pandora_console/godmode/setup/license.php @@ -107,7 +107,7 @@ $table->data = []; $table->data[0][0] = ''.__('Customer key').''; $table->data[0][1] = html_print_textarea('keys[customer_key]', 10, 255, $settings->customer_key, 'style="height:50px; width:450px;"', true); -$table->data[1][0] = ''.__('Expires').''; +$table->data[1][0] = ''.__($license['expiry_caption']).''; $table->data[1][1] = html_print_input_text('expires', $license['expiry_date'], '', 10, 255, true, true); $table->data[2][0] = ''.__('Platform Limit').''; diff --git a/pandora_console/godmode/setup/setup_general.php b/pandora_console/godmode/setup/setup_general.php index ab1d1ef269..1385a9f193 100644 --- a/pandora_console/godmode/setup/setup_general.php +++ b/pandora_console/godmode/setup/setup_general.php @@ -58,6 +58,16 @@ global $config; check_login(); +if (is_ajax()) { + enterprise_include_once('include/functions_cron.php'); + + $test_address = get_parameter('test_address', ''); + + $res = enterprise_hook('send_email_attachment', [$test_address, __('This is an email test sent from Pandora FMS. If you can read this, your configuration works.'), __('Testing Pandora FMS email'), null]); + + echo $res; +} + $table = new StdClass(); $table->class = 'databox filters'; $table->id = 'setup_general'; @@ -68,6 +78,12 @@ $table->size[0] = '30%'; $table->style[0] = 'font-weight:bold'; $table->size[1] = '70%'; +$table_mail_conf = new stdClass(); +$table_mail_conf->width = '100%'; +$table_mail_conf->class = 'databox filters'; +$table_mail_conf->data = []; +$table_mail_conf->style[0] = 'font-weight: bold'; + // Current config["language"] could be set by user, not taken from global setup ! $current_system_lang = db_get_sql( 'SELECT `value` FROM tconfig WHERE `token` = "language"' @@ -330,6 +346,49 @@ echo ''.__('General options').''; html_print_input_hidden('update_config', 1); html_print_table($table); +$encryption = [ + 'ssl' => 'SSL/TLS', + 'sslv2' => 'SSLv2', + 'sslv3' => 'SSLv3', + 'tls' => 'STARTTLS', +]; + +echo ''; + +echo '
'; +echo ''.__('Mail configuration').''; + +$table_mail_conf->data[0][0] = __('From address'); +$table_mail_conf->data[0][1] = html_print_input_text('email_from_dir', $config['email_from_dir'], '', 30, 100, true); + +$table_mail_conf->data[1][0] = __('From name'); +$table_mail_conf->data[1][2] = html_print_input_text('email_from_name', $config['email_from_name'], '', 30, 100, true); + +$table_mail_conf->data[2][0] = __('SMTP Server'); +$table_mail_conf->data[2][1] = html_print_input_text('email_smtpServer', $config['email_smtpServer'], '', 30, 100, true); + +$table_mail_conf->data[3][0] = __('SMTP Port'); +$table_mail_conf->data[3][1] = html_print_input_text('email_smtpPort', $config['email_smtpPort'], '', 30, 100, true); + +$table_mail_conf->data[4][0] = __('Encryption'); +$table_mail_conf->data[4][1] = html_print_select($encryption, 'email_encryption', $config['email_encryption'], '', __('none'), 0, true); + +$table_mail_conf->data[5][0] = __('Email user'); +$table_mail_conf->data[5][1] = html_print_input_text('email_username', $config['email_username'], '', 30, 100, true); + +$table_mail_conf->data[6][0] = __('Email password'); +$table_mail_conf->data[6][1] = html_print_input_password('email_password', io_output_password($config['email_password']), '', 30, 100, true); + +$uniqid = uniqid(); + +$table_mail_conf->data[7][0] = html_print_button(__('Email test'), 'email_test_dialog', false, "show_email_test('$uniqid');", 'class="sub next"', true).ui_print_help_tip(__('Check the current saved email configuration by sending a test email to a desired account.'), true); + +print_email_test_modal_window($uniqid); + +html_print_input_hidden('update_config', 1); +html_print_table($table_mail_conf); + + echo '
'; echo '
'; @@ -337,6 +396,25 @@ html_print_submit_button(__('Update'), 'update_button', false, 'class="sub upd"' echo '
'; echo '
'; +// Print the modal window for the summary of each alerts group +function print_email_test_modal_window($id) +{ + // Email config table. + $table_mail_test = new stdClass(); + $table_mail_test->width = '100%'; + $table_mail_test->class = 'databox filters'; + $table_mail_test->data = []; + $table_mail_test->style[0] = 'font-weight: bold'; + $table_mail_test->colspan[1][0] = 2; + + $table_mail_test->data[0][0] = __('Address').ui_print_help_tip(__('Email address to which the test email will be sent. Please check your inbox after email is sent.'), true); + $table_mail_test->data[0][1] = html_print_input_text('email_test_address', '', '', 40, 100, true); + + $table_mail_test->data[1][0] = html_print_button(__('Send'), 'email_test', false, '', 'class="sub next"', true).'  '; + + echo ''; +} + ?> diff --git a/pandora_console/images/console/background/fondo-keep-alive.jpg b/pandora_console/images/console/background/fondo-keep-alive.jpg new file mode 100644 index 0000000000..fbb5b90a5c Binary files /dev/null and b/pandora_console/images/console/background/fondo-keep-alive.jpg differ diff --git a/pandora_console/images/console/icons/status.png b/pandora_console/images/console/icons/status.png new file mode 100644 index 0000000000..6fba320b6f Binary files /dev/null and b/pandora_console/images/console/icons/status.png differ diff --git a/pandora_console/images/console/icons/status_bad.png b/pandora_console/images/console/icons/status_bad.png new file mode 100644 index 0000000000..a6935a5a19 Binary files /dev/null and b/pandora_console/images/console/icons/status_bad.png differ diff --git a/pandora_console/images/console/icons/status_ok.png b/pandora_console/images/console/icons/status_ok.png new file mode 100644 index 0000000000..a23d4dad2e Binary files /dev/null and b/pandora_console/images/console/icons/status_ok.png differ diff --git a/pandora_console/images/console/icons/status_warning.png b/pandora_console/images/console/icons/status_warning.png new file mode 100644 index 0000000000..387de1c755 Binary files /dev/null and b/pandora_console/images/console/icons/status_warning.png differ diff --git a/pandora_console/include/class/ConsoleSupervisor.php b/pandora_console/include/class/ConsoleSupervisor.php index 9652856b56..7a16e47a1a 100644 --- a/pandora_console/include/class/ConsoleSupervisor.php +++ b/pandora_console/include/class/ConsoleSupervisor.php @@ -873,18 +873,20 @@ class ConsoleSupervisor { global $config; + $remote_config_dir = io_safe_output($config['remote_config']); + if (enterprise_installed() && isset($config['license_nms']) && $config['license_nms'] != 1 ) { - if (is_readable($config['remote_config']) !== true) { + if (is_readable($remote_config_dir) !== true) { $this->notify( [ 'type' => 'NOTIF.PERMISSIONS.REMOTE_CONFIG', 'title' => __('Remote configuration directory is not readable'), 'message' => __( 'Remote configuration directory %s is not readable. Please, adjust configuration.', - $config['remote_config'] + $remote_config_dir ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=general'), ] @@ -896,14 +898,14 @@ class ConsoleSupervisor ); } - if (is_writable($config['remote_config'].'/conf') !== true) { + if (is_writable($remote_config_dir.'/conf') !== true) { $this->notify( [ 'type' => 'NOTIF.PERMISSIONS.REMOTE_CONFIG.CONF', 'title' => __('Remote configuration directory is not writable'), 'message' => __( 'Remote configuration directory %s is not writable. Please, adjust configuration.', - $config['remote_config'].'/conf' + $remote_config_dir.'/conf' ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=general'), ] @@ -914,14 +916,14 @@ class ConsoleSupervisor ); } - if (is_writable($config['remote_config'].'/collections') !== true) { + if (is_writable($remote_config_dir.'/collections') !== true) { $this->notify( [ 'type' => 'NOTIF.PERMISSIONS.REMOTE_CONFIG.COLLECTIONS', 'title' => __('Remote collections directory is not writable'), 'message' => __( 'Collections directory %s is not writable. Please, adjust configuration.', - $config['remote_config'].'/collections' + $remote_config_dir.'/collections' ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=general'), ] @@ -932,14 +934,14 @@ class ConsoleSupervisor ); } - if (is_writable($config['remote_config'].'/md5') !== true) { + if (is_writable($remote_config_dir.'/md5') !== true) { $this->notify( [ 'type' => 'NOTIF.PERMISSIONS.REMOTE_CONFIG.MD5', 'title' => __('Remote md5 directory is not writable'), 'message' => __( 'MD5 directory %s is not writable. Please, adjust configuration.', - $config['remote_config'].'/md5' + $remote_config_dir.'/md5' ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=general'), ] @@ -957,7 +959,7 @@ class ConsoleSupervisor $MAX_BADXML_FILES_DATA_IN = 150; $filecount = $this->countFiles( - $config['remote_config'], + $remote_config_dir, '', $MAX_FILES_DATA_IN ); @@ -970,7 +972,7 @@ class ConsoleSupervisor 'message' => __( 'There are more than %d files in %s. Consider checking DataServer performance', $MAX_FILES_DATA_IN, - $config['remote_config'] + $remote_config_dir ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=perf'), ] @@ -980,7 +982,7 @@ class ConsoleSupervisor } $filecount = $this->countFiles( - $config['remote_config'], + $remote_config_dir, '/^.*BADXML$/', $MAX_BADXML_FILES_DATA_IN ); @@ -993,7 +995,7 @@ class ConsoleSupervisor 'message' => __( 'There are more than %d files in %s. Consider checking software agents.', $MAX_BADXML_FILES_DATA_IN, - $config['remote_config'] + $remote_config_dir ), 'url' => ui_get_full_url('index.php?sec=general&sec2=godmode/setup/setup§ion=perf'), ] @@ -1266,7 +1268,8 @@ class ConsoleSupervisor $PHPSerialize_precision = ini_get('serialize_precision'); // PhantomJS status. - $result_ejecution = exec($config['phantomjs_bin'].'/phantomjs --version'); + $phantomjs_dir = io_safe_output($config['phantomjs_bin']); + $result_ejecution = exec($phantomjs_dir.'/phantomjs --version'); // PHP version checks. $php_version = phpversion(); @@ -1452,7 +1455,8 @@ class ConsoleSupervisor 'title' => sprintf( __("Not recommended '%s' value in PHP configuration"), 'serialize_precision' - ), 'message' => sprintf( + ), + 'message' => sprintf( __('Recommended value is: %s'), sprintf('-1') ).'

'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator'), @@ -2061,8 +2065,10 @@ class ConsoleSupervisor { global $config; - if (($config['fontpath'] == '') - || (file_exists($config['fontpath']) === false) + $fontpath = io_safe_output($config['fontpath']); + + if (($fontpath == '') + || (file_exists($fontpath) === false) ) { $this->notify( [ diff --git a/pandora_console/include/class/CredentialStore.class.php b/pandora_console/include/class/CredentialStore.class.php new file mode 100644 index 0000000000..188950d92a --- /dev/null +++ b/pandora_console/include/class/CredentialStore.class.php @@ -0,0 +1,1203 @@ +AJAXMethods); + } + + + /** + * Generates a JSON error. + * + * @param string $msg Error message. + * + * @return void + */ + public function error($msg) + { + echo json_encode( + ['error' => $msg] + ); + } + + + /** + * Minor function to dump json message as ajax response. + * + * @param string $type Type: result || error. + * @param string $msg Message. + * @param boolean $delete Deletion messages. + * + * @return void + */ + private function ajaxMsg($type, $msg, $delete=false) + { + $msg_err = 'Failed while saving: %s'; + $msg_ok = 'Successfully saved into keystore '; + + if ($delete) { + $msg_err = 'Failed while removing: %s'; + $msg_ok = 'Successfully deleted '; + } + + if ($type == 'error') { + echo json_encode( + [ + $type => ui_print_error_message( + __( + $msg_err, + $msg + ), + '', + true + ), + ] + ); + } else { + echo json_encode( + [ + $type => ui_print_success_message( + __( + $msg_ok, + $msg + ), + '', + true + ), + ] + ); + } + + exit; + } + + + /** + * Initializes object and validates user access. + * + * @param string $ajax_controller Path of ajaxController, is the 'page' + * variable sent in ajax calls. + * + * @return Object + */ + public function __construct($ajax_controller) + { + global $config; + + // Check access. + check_login(); + + if (! check_acl($config['id_user'], 0, 'AR')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access event viewer' + ); + + if (is_ajax()) { + echo json_encode(['error' => 'noaccess']); + } + + include 'general/noaccess.php'; + exit; + } + + $this->ajaxController = $ajax_controller; + + return $this; + } + + + /** + * Returns an array with all the credentials matching filter and ACL. + * + * @param array $fields Fields array or 'count' keyword to retrieve count. + * @param array $filter Filters to be applied. + * @param integer $offset Offset (pagination). + * @param integer $limit Limit (pagination). + * @param string $order Sort order. + * @param string $sort_field Sort field. + * + * @return array With all results or false if error. + * @throws Exception On error. + */ + public static function getAll( + $fields, + $filter, + $offset=null, + $limit=null, + $order=null, + $sort_field=null + ) { + $sql_filters = []; + $order_by = ''; + $pagination = ''; + + global $config; + + if (!is_array($filter)) { + error_log('[credential_get_all] Filter must be an array.'); + throw new Exception('[credential_get_all] Filter must be an array.'); + } + + $count = false; + if (!is_array($fields) && $fields == 'count') { + $fields = ['cs.*']; + $count = true; + } else if (!is_array($fields)) { + error_log('[credential_get_all] Fields must be an array or "count".'); + throw new Exception('[credential_get_all] Fields must be an array or "count".'); + } + + if (isset($filter['product']) && !empty($filter['product'])) { + $sql_filters[] = sprintf(' AND cs.product = "%s"', $filter['product']); + } + + if (isset($filter['free_search']) && !empty($filter['free_search'])) { + $sql_filters[] = vsprintf( + ' AND (lower(cs.username) like lower("%%%s%%") + OR cs.identifier like "%%%s%%" + OR lower(cs.product) like lower("%%%s%%"))', + array_fill(0, 3, $filter['free_search']) + ); + } + + if (isset($filter['filter_id_group']) && $filter['filter_id_group'] > 0) { + $propagate = db_get_value( + 'propagate', + 'tgrupo', + 'id_grupo', + $filter['filter_id_group'] + ); + + if (!$propagate) { + $sql_filters[] = sprintf( + ' AND cs.id_group = %d ', + $filter['filter_id_group'] + ); + } else { + $groups = [ $filter['filter_id_group'] ]; + $childrens = groups_get_childrens($id_group, null, true); + if (!empty($childrens)) { + foreach ($childrens as $child) { + $groups[] = (int) $child['id_grupo']; + } + } + + $filter['filter_id_group'] = $groups; + $sql_filters[] = sprintf( + ' AND cs.id_group IN (%s) ', + join(',', $filter['filter_id_group']) + ); + } + } + + if (isset($filter['group_list']) && is_array($filter['group_list'])) { + $sql_filters[] = sprintf( + ' AND cs.id_group IN (%s) ', + join(',', $filter['group_list']) + ); + } else if (users_is_admin() !== true) { + $user_groups = users_get_groups( + $config['id_user'], + 'AR' + ); + + // Always add group 'ALL' because 'ALL' group credentials + // must be available for all users. + if (is_array($user_groups) === true) { + $user_groups = ([0] + array_keys($user_groups)); + } else { + $user_groups = [0]; + } + + $sql_filters[] = sprintf( + ' AND cs.id_group IN (%s) ', + join(',', $user_groups) + ); + } + + if (isset($filter['identifier'])) { + $sql_filters[] = sprintf( + ' AND cs.identifier = "%s" ', + $filter['identifier'] + ); + } + + if (isset($order)) { + $dir = 'asc'; + if ($order == 'desc') { + $dir = 'desc'; + }; + + if (in_array( + $sort_field, + [ + 'group', + 'identifier', + 'product', + 'username', + 'options', + ] + ) + ) { + $order_by = sprintf( + 'ORDER BY `%s` %s', + $sort_field, + $dir + ); + } + } + + if (isset($limit) && $limit > 0 + && isset($offset) && $offset >= 0 + ) { + $pagination = sprintf( + ' LIMIT %d OFFSET %d ', + $limit, + $offset + ); + } + + $sql = sprintf( + 'SELECT %s + FROM tcredential_store cs + LEFT JOIN tgrupo tg + ON tg.id_grupo = cs.id_group + WHERE 1=1 + %s + %s + %s', + join(',', $fields), + join(' ', $sql_filters), + $order_by, + $pagination + ); + + if ($count) { + $sql = sprintf('SELECT count(*) as n FROM ( %s ) tt', $sql); + + return db_get_value_sql($sql); + } + + return db_get_all_rows_sql($sql); + } + + + /** + * Retrieves target key from keystore or false in case of error. + * + * @param string $identifier Key identifier. + * + * @return array Key or false if error. + */ + public static function getKey($identifier) + { + global $config; + + if (empty($identifier)) { + return false; + } + + $keys = self::getAll( + [ + 'cs.*', + 'tg.nombre as `group`', + ], + ['identifier' => $identifier] + ); + + if (is_array($keys) === true) { + // Only 1 must exist. + $key = $keys[0]; + + // Decrypt content. + $key['username'] = io_output_password($key['username']); + $key['password'] = io_output_password($key['password']); + + return $key; + } + + return false; + } + + + /** + * Return all keys avaliable for current user. + * + * @param string $product Filter by product. + * + * @return array Keys or false if error. + */ + public static function getKeys($product=false) + { + global $config; + + if ($product !== false) { + $filter['product'] = $product; + } + + $keys = self::getAll( + [ + 'cs.*', + 'tg.nombre as `group`', + ], + $filter + ); + + if (is_array($keys) === true) { + // Improve usage and decode output. + $return = array_reduce( + $keys, + function ($carry, $item) { + $item['username'] = io_output_password($item['username']); + $item['password'] = io_output_password($item['password']); + $carry[$item['identifier']] = $item['identifier']; + return $carry; + } + ); + + return $return; + } + + return false; + } + + + /** + * Ajax method invoked by datatables to draw content. + * + * @return void + */ + public function draw() + { + // Datatables offset, limit and order. + $filter = get_parameter('filter', []); + $start = get_parameter('start', 0); + $length = get_parameter('length', $config['block_size']); + $order = get_datatable_order(true); + try { + ob_start(); + + $fields = [ + 'cs.*', + 'tg.nombre as `group`', + ]; + + // Retrieve data. + $data = $this->getAll( + // Fields. + $fields, + // Filter. + $filter, + // Offset. + $start, + // Limit. + $length, + // Order. + $order['direction'], + // Sort field. + $order['field'] + ); + + // Retrieve counter. + $count = $this->getAll( + 'count', + $filter + ); + + if ($data) { + $data = array_reduce( + $data, + function ($carry, $item) { + // Transforms array of arrays $data into an array + // of objects, making a post-process of certain fields. + $tmp = (object) $item; + $tmp->username = io_output_password($tmp->username); + + if (empty($tmp->group)) { + $tmp->group = __('All'); + } else { + $tmp->group = io_safe_output($tmp->group); + } + + $carry[] = $tmp; + return $carry; + } + ); + } + + // Datatables format: RecordsTotal && recordsfiltered. + echo json_encode( + [ + 'data' => $data, + 'recordsTotal' => $count, + 'recordsFiltered' => $count, + ] + ); + // Capture output. + $response = ob_get_clean(); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + exit; + } + + // If not valid, show error with issue. + json_decode($response); + if (json_last_error() == JSON_ERROR_NONE) { + // If valid dump. + echo $response; + } else { + echo json_encode( + ['error' => $response] + ); + } + + exit; + } + + + /** + * Prints inputs for modal "Add key". + * + * @return void + */ + public function loadModal() + { + $identifier = get_parameter('identifier', null); + $key = self::getKey($identifier); + + echo $this->printInputs($key); + } + + + /** + * Prepare variables received using form. AJAX environment only. + * + * @return array of values processed or false in case of error. + */ + private function prepareKeyValues() + { + $identifier = get_parameter('identifier', null); + $id_group = get_parameter('id_group', null); + $product = get_parameter('product', null); + $username = get_parameter('username', null); + $password = get_parameter('password', null); + $extra_1 = get_parameter('extra_1', null); + $extra_2 = get_parameter('extra_2', null); + + if (empty($identifier)) { + $error = __('Key identifier is required'); + } else if ($id_group === null) { + $error = __('You must select a group where store this key!'); + } else if (empty($product)) { + $error = __('You must specify a product type'); + } else if (empty($username) && (empty($password))) { + $error = __('You must specify a username and/or password'); + } + + // Encrypt content (if needed). + $values = [ + 'identifier' => $identifier, + 'id_group' => $id_group, + 'product' => $product, + 'username' => io_input_password($username), + 'password' => io_input_password($password), + 'extra_1' => $extra_1, + 'extra_2' => $extra_2, + ]; + + // Spaces are not allowed. + $values['identifier'] = preg_replace('/\s+/', '-', trim($identifier)); + + return $values; + } + + + /** + * Stores a key into credential store. + * + * @param array $values Key definition. + * @param string $identifier Update or create. + * + * @return boolean True if ok, false if not ok. + */ + private function storeKey($values, $identifier=false) + { + if ($identifier === false) { + // New. + return db_process_sql_insert('tcredential_store', $values); + } else { + // Update. + return db_process_sql_update( + 'tcredential_store', + $values, + ['identifier' => $identifier] + ); + } + + } + + + /** + * Add a new key into Credential Store + * + * @return void + */ + public function addKey() + { + global $config; + + $values = $this->prepareKeyValues(); + + if ($this->storeKey($values) === false) { + $this->ajaxMsg('error', $config['dbconnection']->error); + } else { + $this->ajaxMsg('result', $values['identifier']); + } + + exit; + } + + + /** + * Add a new key into Credential Store + * + * @return void + */ + public function updateKey() + { + global $config; + + $values = $this->prepareKeyValues(); + $identifier = $values['identifier']; + + if ($this->storeKey($values, $identifier) === false) { + $this->ajaxMsg('error', $config['dbconnection']->error); + } else { + $this->ajaxMsg('result', $identifier); + } + + exit; + } + + + /** + * AJAX method. Delete key from keystore. + * + * @return void + */ + public function deleteKey() + { + global $config; + + $identifier = get_parameter('identifier', null); + + if (empty($identifier)) { + $this->ajaxMsg('error', __('identifier cannot be empty'), true); + } + + if (self::getKey($identifier) === false) { + // User has no grants to delete target key. + $this->ajaxMsg('error', __('Not allowed'), true); + } + + if (db_process_sql_delete( + 'tcredential_store', + ['identifier' => $identifier] + ) === false + ) { + $this->ajaxMsg('error', $config['dbconnection']->error, true); + } else { + $this->ajaxMsg('result', $identifier, true); + } + + } + + + /** + * Run CredentialStore (main page). + * + * @return void + */ + public function run() + { + global $config; + + // Require specific CSS and JS. + ui_require_css_file('wizard'); + ui_require_css_file('discovery'); + ui_require_css_file('credential_store'); + + if (!isset($config['encryption_passphrase'])) { + $url = 'https://pandorafms.com/docs/index.php?title=Pandora:Documentation_en:Password_Encryption'; + if ($config['language'] == 'es') { + $url = 'https://pandorafms.com/docs/index.php?title=Pandora:Documentation_es:Cifrado_Contrase%C3%B1as'; + } + + ui_print_warning_message( + __( + 'Database encryption is not enabled. Credentials will be stored in plaintext. %s', + ''.__('How to configure encryption.').'' + ) + ); + } + + // Datatables list. + try { + $columns = [ + 'group', + 'identifier', + 'product', + 'username', + 'options', + ]; + + $column_names = [ + __('Group'), + __('Identifier'), + __('Product'), + __('User'), + [ + 'text' => __('Options'), + 'class' => 'action_buttons', + ], + ]; + + $this->tableId = 'keystore'; + // Load datatables user interface. + ui_print_datatable( + [ + 'id' => $this->tableId, + 'class' => 'info_table', + 'style' => 'width: 100%', + 'columns' => $columns, + 'column_names' => $column_names, + 'ajax_url' => $this->ajaxController, + 'ajax_data' => ['method' => 'draw'], + 'ajax_postprocess' => 'process_datatables_item(item)', + 'no_sortable_columns' => [-1], + 'order' => [ + 'field' => 'identifier', + 'direction' => 'asc', + ], + 'search_button_class' => 'sub filter float-right', + 'form' => [ + 'inputs' => [ + [ + 'label' => __('Group'), + 'type' => 'select', + 'id' => 'filter_id_group', + 'name' => 'filter_id_group', + 'options' => users_get_groups_for_select( + $config['id_user'], + 'AR', + true, + true, + false + ), + ], + [ + 'label' => __('Free search'), + 'type' => 'text', + 'class' => 'mw250px', + 'id' => 'free_search', + 'name' => 'free_search', + ], + ], + ], + ] + ); + } catch (Exception $e) { + echo $e->getMessage(); + } + + // Auxiliar div. + $modal = ''; + $msg = ''; + $aux = ''; + + echo $modal.$msg.$aux; + + // Create button. + echo '
'; + html_print_submit_button( + __('Add key'), + 'create', + false, + 'class="sub next"' + ); + echo '
'; + + echo $this->loadJS(); + + } + + + /** + * Generates inputs for new/update forms. + * + * @param array $values Values or null. + * + * @return string Inputs. + */ + public function printInputs($values=null) + { + if (!is_array($values)) { + $values = []; + } + + $form = [ + 'action' => '#', + 'id' => 'modal_form', + 'onsubmit' => 'return false;', + 'class' => 'modal', + 'extra' => 'autocomplete="new-password"', + ]; + + $inputs = []; + + $inputs[] = [ + 'label' => __('Identifier'), + 'id' => 'div-identifier', + 'arguments' => [ + 'name' => 'identifier', + 'type' => 'text', + 'value' => $values['identifier'], + 'disabled' => (bool) $values['identifier'], + 'return' => true, + ], + ]; + + $inputs[] = [ + 'label' => __('Group'), + 'arguments' => [ + 'name' => 'id_group', + 'id' => 'id_group', + 'input_class' => 'flex-row', + 'type' => 'select_groups', + 'selected' => $values['id_group'], + 'return' => true, + 'class' => 'w50p', + ], + ]; + + $inputs[] = [ + 'label' => __('Product'), + 'id' => 'div-product', + 'arguments' => [ + 'name' => 'product', + 'input_class' => 'flex-row', + 'type' => 'select', + 'script' => 'calculate_inputs()', + 'fields' => [ + 'CUSTOM' => __('Custom'), + 'AWS' => __('Aws'), + 'AZURE' => __('Azure'), + // 'GOOGLE' => __('Google'), + ], + 'selected' => (isset($values['product']) ? $values['product'] : 'CUSTOM'), + 'disabled' => (bool) $values['product'], + 'return' => true, + ], + ]; + + $user_label = __('Username'); + $pass_label = __('Password'); + $extra_1_label = __('Extra'); + $extra_2_label = __('Extra (2)'); + $extra1 = true; + $extra2 = true; + + // Remember to update credential_store.php also. + switch ($values['product']) { + case 'AWS': + $user_label = __('Access key ID'); + $pass_label = __('Secret access key'); + $extra1 = false; + $extra2 = false; + break; + + case 'AZURE': + $user_label = __('Account ID'); + $pass_label = __('Application secret'); + $extra_1_label = __('Tenant or domain name'); + $extra_2_label = __('Subscription id'); + break; + + case 'GOOGLE': + // Need further investigation. + case 'CUSTOM': + $user_label = __('Account ID'); + $pass_label = __('Password'); + $extra1 = false; + $extra2 = false; + default: + // Use defaults. + break; + } + + $inputs[] = [ + 'label' => $user_label, + 'id' => 'div-username', + 'arguments' => [ + 'name' => 'username', + 'input_class' => 'flex-row', + 'type' => 'text', + 'value' => $values['username'], + 'return' => true, + ], + ]; + + $inputs[] = [ + 'label' => $pass_label, + 'id' => 'div-password', + 'arguments' => [ + 'name' => 'password', + 'input_class' => 'flex-row', + 'type' => 'password', + 'value' => $values['password'], + 'return' => true, + ], + ]; + + if ($extra1) { + $inputs[] = [ + 'label' => $extra_1_label, + 'id' => 'div-extra_1', + 'arguments' => [ + 'name' => 'extra_1', + 'input_class' => 'flex-row', + 'type' => 'text', + 'value' => $values['extra_1'], + 'return' => true, + ], + ]; + } + + if ($extra2) { + $inputs[] = [ + 'label' => $extra_2_label, + 'id' => 'div-extra_2', + 'arguments' => [ + 'name' => 'extra_2', + 'input_class' => 'flex-row', + 'type' => 'text', + 'value' => $values['extra_2'], + 'return' => true, + 'display' => $extra2, + ], + + ]; + } + + return $this->printForm( + [ + 'form' => $form, + 'inputs' => $inputs, + ], + true + ); + } + + + /** + * Loads JS content. + * + * @return string JS content. + */ + public function loadJS() + { + ob_start(); + + // Javascript content. + ?> + + 0) { - $propagate = db_get_value( - 'propagate', - 'tgrupo', - 'id_grupo', - $filter['filter_id_group'] - ); - - if (!$propagate) { - $sql_filters[] = sprintf( - ' AND cs.id_group = %d ', - $filter['filter_id_group'] - ); - } else { - $groups = [ $filter['filter_id_group'] ]; - $childrens = groups_get_childrens($id_group, null, true); - if (!empty($childrens)) { - foreach ($childrens as $child) { - $groups[] = (int) $child['id_grupo']; - } - } - - $filter['filter_id_group'] = $groups; - $sql_filters[] = sprintf( - ' AND cs.id_group IN (%s) ', - join(',', $filter['filter_id_group']) - ); - } - } - - if (isset($filter['group_list']) && is_array($filter['group_list'])) { - $sql_filters[] = sprintf( - ' AND cs.id_group IN (%s) ', - join(',', $filter['group_list']) - ); - } - - if (isset($order)) { - $dir = 'asc'; - if ($order == 'desc') { - $dir = 'desc'; - }; - - if (in_array( - $sort_field, - [ - 'group', - 'identifier', - 'product', - 'username', - 'options', - ] - ) - ) { - $order_by = sprintf( - 'ORDER BY `%s` %s', - $sort_field, - $dir - ); - } - } - - if (isset($limit) && $limit > 0 - && isset($offset) && $offset >= 0 - ) { - $pagination = sprintf( - ' LIMIT %d OFFSET %d ', - $limit, - $offset - ); - } - - $sql = sprintf( - 'SELECT %s - FROM tcredential_store cs - LEFT JOIN tgrupo tg - ON tg.id_grupo = cs.id_group - WHERE 1=1 - %s - %s - %s', - join(',', $fields), - join(' ', $sql_filters), - $order_by, - $pagination - ); - - if ($count) { - $sql = sprintf('SELECT count(*) as n FROM ( %s ) tt', $sql); - - return db_get_value_sql($sql); - } - - return db_get_all_rows_sql($sql); -} - - -/** - * Retrieves target key from keystore or false in case of error. - * - * @param string $identifier Key identifier. - * - * @return array Key or false if error. - */ -function get_key($identifier) -{ - return db_get_row_filter( - 'tcredential_store', - [ 'identifier' => $identifier ] - ); -} - - -/** - * Minor function to dump json message as ajax response. - * - * @param string $type Type: result || error. - * @param string $msg Message. - * @param boolean $delete Deletion messages. - * - * @return void - */ -function ajax_msg($type, $msg, $delete=false) -{ - $msg_err = 'Failed while saving: %s'; - $msg_ok = 'Successfully saved into keystore '; - - if ($delete) { - $msg_err = 'Failed while removing: %s'; - $msg_ok = 'Successfully deleted '; - } - - if ($type == 'error') { - echo json_encode( - [ - $type => ui_print_error_message( - __( - $msg_err, - $msg - ), - '', - true - ), - ] - ); - } else { - echo json_encode( - [ - $type => ui_print_success_message( - __( - $msg_ok, - $msg - ), - '', - true - ), - ] - ); - } - - exit; -} - - -/** - * Generates inputs for new/update forms. - * - * @param array $values Values or null. - * - * @return string Inputs. - */ -function print_inputs($values=null) -{ - if (!is_array($values)) { - $values = []; - } - - $return = ''; - $return .= html_print_input( - [ - 'label' => __('Identifier'), - 'name' => 'identifier', - 'input_class' => 'flex-row', - 'type' => 'text', - 'value' => $values['identifier'], - 'disabled' => (bool) $values['identifier'], - 'return' => true, - 'script' => 'alert(\'puta\')', - ] - ); - $return .= html_print_input( - [ - 'label' => __('Group'), - 'name' => 'id_group', - 'id' => 'id_group', - 'input_class' => 'flex-row', - 'type' => 'select_groups', - 'selected' => $values['id_group'], - 'return' => true, - 'class' => 'w50p', - ] - ); - $return .= html_print_input( - [ - 'label' => __('Product'), - 'name' => 'product', - 'input_class' => 'flex-row', - 'type' => 'select', - 'script' => 'calculate_inputs()', - 'fields' => [ - 'CUSTOM' => __('Custom'), - 'AWS' => __('Aws'), - 'AZURE' => __('Azure'), - // 'GOOGLE' => __('Google'), - ], - 'selected' => $values['product'], - 'disabled' => (bool) $values['product'], - 'return' => true, - ] - ); - $user_label = __('Username'); - $pass_label = __('Password'); - $extra_1_label = __('Extra'); - $extra_2_label = __('Extra (2)'); - $extra1 = true; - $extra2 = true; - - // Remember to update credential_store.php also. - switch ($values['product']) { - case 'AWS': - $user_label = __('Access key ID'); - $pass_label = __('Secret access key'); - $extra1 = false; - $extra2 = false; - break; - - case 'AZURE': - $user_label = __('Account ID'); - $pass_label = __('Application secret'); - $extra_1_label = __('Tenant or domain name'); - $extra_2_label = __('Subscription id'); - break; - - case 'GOOGLE': - // Need further investigation. - case 'CUSTOM': - $user_label = __('Account ID'); - $pass_label = __('Password'); - $extra1 = false; - $extra2 = false; - default: - // Use defaults. - break; - } - - $return .= html_print_input( - [ - 'label' => $user_label, - 'name' => 'username', - 'input_class' => 'flex-row', - 'type' => 'text', - 'value' => $values['username'], - 'return' => true, - ] - ); - $return .= html_print_input( - [ - 'label' => $pass_label, - 'name' => 'password', - 'input_class' => 'flex-row', - 'type' => 'password', - 'value' => $values['password'], - 'return' => true, - ] - ); - if ($extra1) { - $return .= html_print_input( - [ - 'label' => $extra_1_label, - 'name' => 'extra_1', - 'input_class' => 'flex-row', - 'type' => 'text', - 'value' => $values['extra_1'], - 'return' => true, - ] - ); - } - - if ($extra2) { - $return .= html_print_input( - [ - 'label' => $extra_2_label, - 'name' => 'extra_2', - 'input_class' => 'flex-row', - 'type' => 'text', - 'value' => $values['extra_2'], - 'return' => true, - 'display' => $extra2, - ] - ); - } - - return $return; -} - - -/** - * Retrieve all identifiers available for current user. - * - * @param string $product Target product. - * - * @return array Of account identifiers. - */ -function credentials_list_accounts($product) -{ - global $config; - - check_login(); - - include_once $config['homedir'].'/include/functions_users.php'; - - static $user_groups; - - if (!isset($user_groups)) { - $user_groups = users_get_groups( - $config['id_user'], - 'AR' - ); - - // Always add group 'ALL' because 'ALL' group credentials - // must be available for all users. - if (is_array($user_groups)) { - $user_groups = ([0] + array_keys($user_groups)); - } else { - $user_groups = [0]; - } - } - - $creds = credentials_get_all( - ['identifier'], - [ - 'product' => $product, - 'group_list' => $user_groups, - ] - ); - - if ($creds === false) { - return []; - } - - $ret = array_reduce( - $creds, - function ($carry, $item) { - $carry[$item['identifier']] = $item['identifier']; - return $carry; - } - ); - - return $ret; -} +// Deprecated. diff --git a/pandora_console/include/functions_db.php b/pandora_console/include/functions_db.php index 3efbf59628..a99ab2e41e 100644 --- a/pandora_console/include/functions_db.php +++ b/pandora_console/include/functions_db.php @@ -46,6 +46,25 @@ function db_select_engine() } +/** + * Connects to target DB. + * + * @param array $setup Database definition. + * + * @return mixed Dbconnection or null. + */ +function get_dbconnection(array $setup) +{ + return mysqli_connect( + $setup['dbhost'], + $setup['dbuser'], + $setup['dbpass'], + $setup['dbname'], + $setup['dbport'] + ); +} + + function db_connect($host=null, $db=null, $user=null, $pass=null, $port=null, $critical=true, $charset=null) { global $config; diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index ffb36aa3ae..75533f7f56 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -2739,6 +2739,10 @@ function events_get_agent( $date = time_w_fixed_tz($date); } + if (is_metaconsole() && $events_group === false) { + $id_server = true; + } + if (empty($date)) { $date = get_system_time(); } diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index d0e844c3bf..cbc0bceee5 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -302,6 +302,15 @@ function grafico_modulo_sparse_data( $array_data['sum'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; $array_data['sum'.$series_suffix]['unit'] = $data_module_graph['unit']; + if ($params['percentil']) { + $array_data['percentil'.$series_suffix]['agent_module_id'] = $agent_module_id; + $array_data['percentil'.$series_suffix]['id_module_type'] = $data_module_graph['id_module_type']; + $array_data['percentil'.$series_suffix]['agent_name'] = $data_module_graph['agent_name']; + $array_data['percentil'.$series_suffix]['module_name'] = $data_module_graph['module_name']; + $array_data['percentil'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + $array_data['percentil'.$series_suffix]['unit'] = $data_module_graph['unit']; + } + // This is for a specific type of report that consists in passing // an interval and doing the average sum and avg. if ($params['force_interval'] != '') { @@ -378,12 +387,6 @@ function grafico_modulo_sparse_data( $array_data['sum1']['data'] = $acum_array_data; } - if ($params['percentil']) { - $percentil_value = $array_data['percentil'.$series_suffix]['data'][0][1]; - } else { - $percentil_value = 0; - } - $events = []; if (isset($array_data['sum'.$series_suffix]['max'])) { $max = $array_data['sum'.$series_suffix]['max']; @@ -1471,8 +1474,6 @@ function graphic_combined_module( $min = $array_data['sum'.$i]['min']; $avg = $array_data['sum'.$i]['avg']; - $percentil_value = $array_data['percentil'.$i]['data'][0][1]; - if ($config['fixed_graph'] == false) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 08d60dbb6e..12d4aecc01 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -98,6 +98,23 @@ function hd($var, $file='', $oneline=false) } +/** + * Encapsulation (ob) for debug print function. + * + * @param mixed $var Variable to be dumped. + * @param string $file Target file path. + * @param boolean $oneline Show in oneline. + * + * @return string Dump string. + */ +function obhd($var, $file='', $oneline=false) +{ + ob_start(); + hd($var, $file, $oneline); + return ob_get_clean(); +} + + function debug() { $args_num = func_num_args(); @@ -1458,6 +1475,14 @@ function html_print_input_password( $attr['class'] = $class; } + if ($disabled === false) { + // Trick to avoid password completion on most browsers. + if ($autocomplete !== 'on') { + $disabled = true; + $attr['onfocus'] = "this.removeAttribute('readonly');"; + } + } + return html_print_input_text_extended($name, $value, 'password-'.$name, $alt, $size, $maxlength, $disabled, '', $attr, $return, true, '', $autocomplete); } @@ -1570,6 +1595,7 @@ function html_print_input_image($name, $src, $value, $style='', $return=false, $ 'onkeypress', 'onkeydown', 'onkeyup', + 'class', ]; foreach ($attrs as $attribute) { @@ -3152,7 +3178,8 @@ function html_print_input($data, $wrapper='div', $input_only=false) ((isset($data['return']) === true) ? $data['return'] : false), ((isset($data['disabled']) === true) ? $data['disabled'] : false), ((isset($data['required']) === true) ? $data['required'] : false), - ((isset($data['class']) === true) ? $data['class'] : '') + ((isset($data['class']) === true) ? $data['class'] : ''), + ((isset($data['autocomplete']) === true) ? $data['autocomplete'] : 'off') ); break; diff --git a/pandora_console/include/functions_io.php b/pandora_console/include/functions_io.php index 32c66ca1e3..baddb18531 100755 --- a/pandora_console/include/functions_io.php +++ b/pandora_console/include/functions_io.php @@ -507,7 +507,7 @@ function ___($string /*, variable arguments */) } -/* +/** * json_encode for multibyte characters. * * @param string Text string to be encoded. @@ -528,7 +528,7 @@ function io_json_mb_encode($string, $encode_options=0) } -/* +/** * Prepare the given password to be stored in the Pandora FMS Database, * encrypting it if necessary. * @@ -541,16 +541,22 @@ function io_input_password($password) global $config; enterprise_include_once('include/functions_crypto.php'); - $ciphertext = enterprise_hook('openssl_encrypt_decrypt', ['encrypt', io_safe_output($password)]); + $ciphertext = enterprise_hook( + 'openssl_encrypt_decrypt', + [ + 'encrypt', + io_safe_input($password), + ] + ); if ($ciphertext === ENTERPRISE_NOT_HOOK) { - return $password; + return io_safe_input($password); } return $ciphertext; } -/* +/** * Process the given password read from the Pandora FMS Database, * decrypting it if necessary. * @@ -563,10 +569,17 @@ function io_output_password($password) global $config; enterprise_include_once('include/functions_crypto.php'); - $plaintext = enterprise_hook('openssl_encrypt_decrypt', ['decrypt', io_safe_output($password)]); + $plaintext = enterprise_hook( + 'openssl_encrypt_decrypt', + [ + 'decrypt', + $password, + ] + ); + if ($plaintext === ENTERPRISE_NOT_HOOK) { - return $password; + return io_safe_output($password); } - return $plaintext; + return io_safe_output($plaintext); } diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index 3494eed389..4edd0ef642 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -4903,7 +4903,8 @@ function reporting_value($report, $content, $type, $pdf=false) $value = reporting_get_agentmodule_data_sum( $content['id_agent_module'], $content['period'], - $report['datetime'] + $report['datetime'], + $content['uncompressed_module'] ); if (!$config['simple_module_value']) { $formated_value = $value; @@ -10725,17 +10726,19 @@ function reporting_get_agentmodule_data_min($id_agent_module, $period=0, $date=0 * @param int Agent module id to get the sumatory. * @param int Period of time to check (in seconds) * @param int Top date to check the values. Default current time. + * @param boolean Show uncompressed data from module * * @return float The sumatory of the module values in the interval. */ function reporting_get_agentmodule_data_sum( $id_agent_module, $period=0, - $date=0 + $date=0, + $uncompressed_module=true ) { global $config; - // Initialize variables + // Initialize variables. if (empty($date)) { $date = get_system_time(); } @@ -10757,21 +10760,24 @@ function reporting_get_agentmodule_data_sum( $id_module_type ); $module_interval = modules_get_interval($id_agent_module); - $uncompressed_module = is_module_uncompressed($module_name); + // Check if module must be compressed. + if (!$uncompressed_module) { + $uncompressed_module = is_module_uncompressed($module_name); + } // Wrong module type if (is_module_data_string($module_name)) { return 0; } - // Incremental modules are treated differently + // Incremental modules are treated differently. $module_inc = is_module_inc($module_name); - if ($uncompressed_module) { - // Get module data + if (!$uncompressed_module) { + // Get module data. $interval_data = db_get_all_rows_sql( ' - SELECT * FROM tagente_datos + SELECT * FROM tagente_datos WHERE id_agente_modulo = '.(int) $id_agent_module.' AND utimestamp > '.(int) $datelimit.' AND utimestamp < '.(int) $date.' @@ -10792,7 +10798,7 @@ function reporting_get_agentmodule_data_sum( return false; } - // Set initial conditions + // Set initial conditions. $total = 0; $partial_total = 0; $count_sum = 0; @@ -10801,18 +10807,9 @@ function reporting_get_agentmodule_data_sum( $partial_total = 0; $count_sum = 0; - switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - // Do none - break; - - case 'oracle': - $data['datos'] = oracle_format_float_to_php($data['datos']); - break; - } - - if (!$module_inc) { + if (!$uncompressed_module) { + $total += $data['datos']; + } else if (!$module_inc) { foreach ($data['data'] as $val) { if (is_numeric($val['datos'])) { $partial_total += $val['datos']; @@ -10824,7 +10821,7 @@ function reporting_get_agentmodule_data_sum( continue; } - $total += ($partial_total / $count_sum); + $total += $partial_total; } else { $last = end($data['data']); $total += $last['datos']; diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index 008c05b8d7..06c4b579ea 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -2679,6 +2679,7 @@ function get_shape_status_set($type) case STATUS_MODULE_UNKNOWN: case STATUS_AGENT_UNKNOWN: case STATUS_AGENT_DOWN: + case STATUS_AGENT_NO_MONITORS: $return = ['class' => 'status_rounded_rectangles']; break; diff --git a/pandora_console/include/gettext.php b/pandora_console/include/gettext.php index 152040f327..5d3abd83fe 100644 --- a/pandora_console/include/gettext.php +++ b/pandora_console/include/gettext.php @@ -104,7 +104,7 @@ class gettext_reader { * @param boolean enable_cache Enable or disable caching of strings (default on) */ function gettext_reader($Reader, $enable_cache = true) { - $machine = @shell_exec('uname -m'); + $machine = php_uname("m"); $enabled64Bits = false; if (preg_match('/64/', $machine)) { diff --git a/pandora_console/include/javascript/pandora.js b/pandora_console/include/javascript/pandora.js index 05009e7292..33ab956a1f 100644 --- a/pandora_console/include/javascript/pandora.js +++ b/pandora_console/include/javascript/pandora.js @@ -1890,6 +1890,16 @@ function load_modal(settings) { width = settings.onshow.width; } + settings.target.html("Loading modal..."); + settings.target + .dialog({ + title: "Loading", + close: false, + width: 200, + buttons: [] + }) + .show(); + $.ajax({ method: "post", url: settings.url, @@ -1898,6 +1908,9 @@ function load_modal(settings) { data: data, success: function(data) { settings.target.html(data); + if (settings.onload != undefined) { + settings.onload(data); + } settings.target.dialog({ resizable: true, draggable: true, @@ -1915,7 +1928,9 @@ function load_modal(settings) { text: settings.modal.cancel, click: function() { $(this).dialog("close"); - settings.cleanup(); + if (typeof settings.cleanup == "function") { + settings.cleanup(); + } } }, { @@ -1925,6 +1940,9 @@ function load_modal(settings) { click: function() { if (AJAX_RUNNING) return; AJAX_RUNNING = 1; + if (settings.onsubmit.preaction != undefined) { + settings.onsubmit.preaction(); + } var formdata = new FormData(); if (settings.extradata) { settings.extradata.forEach(function(item) { @@ -1952,7 +1970,9 @@ function load_modal(settings) { contentType: false, data: formdata, success: function(data) { - settings.ajax_callback(data); + if (settings.ajax_callback != undefined) { + settings.ajax_callback(data); + } AJAX_RUNNING = 0; } }); diff --git a/pandora_console/include/styles/credential_store.css b/pandora_console/include/styles/credential_store.css index aa77985188..5707d90780 100644 --- a/pandora_console/include/styles/credential_store.css +++ b/pandora_console/include/styles/credential_store.css @@ -10,3 +10,33 @@ #new_key select { width: 60%; } + +ul.wizard li > label:not(.p-switch) { + width: auto; +} + +form.top-action-buttons ul.wizard { + display: flex; + flex-direction: row; +} + +ul.wizard li { + margin-right: 1em; +} + +form.modal ul.wizard li { + display: flex; + flex-direction: row; + width: 90%; + margin: 0 auto; + justify-items: center; +} + +form.modal ul.wizard li * { + flex: 1; +} + +ul.wizard li.flex-indep { + flex: 1; + margin: 0; +} diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index 8c12d11d04..27b3094027 100644 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -452,6 +452,9 @@ select:-internal-list-box { .mw120px { min-width: 120px; } +.mw180px { + min-width: 180px; +} .mw250px { min-width: 250px; } @@ -3182,6 +3185,7 @@ table#policy_modules td * { #news_board { min-width: 530px; + width: 100%; } #right_column_logon_ok { diff --git a/pandora_console/include/styles/pandoraPDF.css b/pandora_console/include/styles/pandoraPDF.css index 9a55b6a1e4..4d78d5c81b 100644 --- a/pandora_console/include/styles/pandoraPDF.css +++ b/pandora_console/include/styles/pandoraPDF.css @@ -27,9 +27,6 @@ * GNU General Public License for more details. * ============================================================================ */ -table { - text-align: center; -} table.header_table { width: 100%; @@ -72,6 +69,7 @@ table.table_beauty tbody tr td { table.databox { margin-bottom: 20px; + text-align: center; } th.title_table_pdf { diff --git a/pandora_console/include/visual-console-client/vc.main.css b/pandora_console/include/visual-console-client/vc.main.css index 16ecacd33c..22c3cce748 100644 --- a/pandora_console/include/visual-console-client/vc.main.css +++ b/pandora_console/include/visual-console-client/vc.main.css @@ -13,27 +13,27 @@ display: flex; -webkit-box-orient: initial; -webkit-box-direction: initial; - -ms-flex-direction: initial; - flex-direction: initial; + -ms-flex-direction: initial; + flex-direction: initial; justify-items: center; -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; + -ms-flex-align: center; + align-items: center; -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; } .visual-console-item.is-editing { border: 2px dashed #b2b2b2; -webkit-transform: translateX(-2px) translateY(-2px); - transform: translateX(-2px) translateY(-2px); + transform: translateX(-2px) translateY(-2px); cursor: move; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .visual-console-item.is-editing > .resize-draggable { @@ -60,17 +60,17 @@ display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; + -ms-flex-pack: center; + justify-content: center; justify-items: center; -ms-flex-line-pack: center; - align-content: center; + align-content: center; -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; + -ms-flex-align: center; + align-items: center; } .visual-console-item .digital-clock > span { @@ -89,7 +89,7 @@ } .visual-console-item .digital-clock > span.timezone { - font-size: 25px; + font-size: 28px; } /* Analog clock */ @@ -100,18 +100,17 @@ .visual-console-item .analogic-clock .hour-hand { -webkit-animation: rotate-hour 43200s infinite linear; - animation: rotate-hour 43200s infinite linear; + animation: rotate-hour 43200s infinite linear; } .visual-console-item .analogic-clock .minute-hand { -webkit-animation: rotate-minute 3600s infinite linear; - animation: rotate-minute 3600s infinite linear; + animation: rotate-minute 3600s infinite linear; } .visual-console-item .analogic-clock .second-hand { -webkit-animation: rotate-second 60s infinite linear; - animation: rotate-second 60s infinite linear; + animation: rotate-second 60s infinite linear; } - -/*# sourceMappingURL=vc.main.css.map*/ \ No newline at end of file +/*# sourceMappingURL=vc.main.css.map*/ diff --git a/pandora_console/include/web2image.js b/pandora_console/include/web2image.js index bf183a73c4..09f840e45e 100644 --- a/pandora_console/include/web2image.js +++ b/pandora_console/include/web2image.js @@ -61,6 +61,7 @@ page.onConsoleMessage = function(msg) { page.onError = function(msg) { console.log(msg); page.close(); + phantom.exit(); }; page.onCallback = function(st) { @@ -74,4 +75,9 @@ page.onCallback = function(st) { phantom.exit(); }; -page.open(url, "POST", post_data, function(status) {}); +page.open(url, "POST", post_data, function(status) { + if (status == "fail") { + console.out("Failed to generate chart."); + phantom.exit(); + } +}); diff --git a/pandora_console/install.php b/pandora_console/install.php index 900cd0ccb0..6bb3c247c3 100644 --- a/pandora_console/install.php +++ b/pandora_console/install.php @@ -129,7 +129,7 @@
'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder', 'text' => __('Reporting')]); diff --git a/pandora_console/pandora_console.redhat.spec b/pandora_console/pandora_console.redhat.spec index fd56e93b6f..ae62a05f25 100644 --- a/pandora_console/pandora_console.redhat.spec +++ b/pandora_console/pandora_console.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.738 -%define release 190918 +%define release 190926 # User and Group under which Apache is running %define httpd_name httpd diff --git a/pandora_console/pandora_console.rhel7.spec b/pandora_console/pandora_console.rhel7.spec index 803b1cc1b8..758fa635ce 100644 --- a/pandora_console/pandora_console.rhel7.spec +++ b/pandora_console/pandora_console.rhel7.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.738 -%define release 190918 +%define release 190926 # User and Group under which Apache is running %define httpd_name httpd diff --git a/pandora_console/pandora_console.spec b/pandora_console/pandora_console.spec index f344e71b4b..6b57abd3db 100644 --- a/pandora_console/pandora_console.spec +++ b/pandora_console/pandora_console.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.738 -%define release 190918 +%define release 190926 %define httpd_name httpd # User and Group under which Apache is running %define httpd_name apache2 diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index d1de66ab07..3786c4dad3 100644 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -1455,6 +1455,7 @@ CREATE TABLE IF NOT EXISTS `treport_content` ( `current_month` TINYINT(1) DEFAULT '1', `failover_mode` tinyint(1) DEFAULT '1', `failover_type` tinyint(1) DEFAULT '1', + `uncompressed_module` TINYINT DEFAULT '0', PRIMARY KEY(`id_rc`), FOREIGN KEY (`id_report`) REFERENCES treport(`id_report`) ON UPDATE CASCADE ON DELETE CASCADE @@ -2438,14 +2439,17 @@ CREATE TABLE IF NOT EXISTS `tdashboard` ( -- Table `tdatabase` -- --------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `tdatabase` ( - `id` int(10) unsigned NOT NULL auto_increment, - `host` varchar(100) default '', - `os_port` int(4) unsigned default '22', - `os_user` varchar(100) default '', - `db_port` int(4) unsigned default '3306', + `id` INT(10) unsigned NOT NULL auto_increment, + `host` VARCHAR(255) default '', + `label` VARCHAR(255) default '', + `os_port` INT UNSIGNED NOT NULL DEFAULT 22, + `os_user` VARCHAR(255) default '', + `db_port` INT UNSIGNED NOT NULL DEFAULT 3306, `status` tinyint(1) unsigned default '0', `action` tinyint(1) unsigned default '0', - `last_error` varchar(255) default '', + `ssh_key` TEXT, + `ssh_pubkey` TEXT, + `last_error` TEXT, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8 ; @@ -2998,6 +3002,7 @@ CREATE TABLE IF NOT EXISTS `treport_content_template` ( `current_month` TINYINT(1) DEFAULT '1', `failover_mode` tinyint(1) DEFAULT '1', `failover_type` tinyint(1) DEFAULT '1', + `uncompressed_module` TINYINT DEFAULT '0', PRIMARY KEY(`id_rc`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql index a02f88fe5f..01f069b8b6 100644 --- a/pandora_console/pandoradb_data.sql +++ b/pandora_console/pandoradb_data.sql @@ -1317,7 +1317,8 @@ UPDATE `tnotification_source` SET `enabled`=1 WHERE `description` = 'System -- INSERT INTO `tlayout` VALUES - (1, 'Demo visual console', 0, 'fondo.jpg', 1080, 1920, 'white', 0); + (1, 'Demo visual console', 0, 'fondo.jpg', 1080, 1920, 'white', 0), + (2,'Demo visual console 2',0,'fondo-keep-alive.jpg',1080,1920,'#FFF',0); -- -- Dumping data for table `tlayout_data` @@ -1420,5 +1421,20 @@ VALUES (94,1,580,904,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 2</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), (95,1,132,907,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 1</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), (96,1,733,20,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_48pt"><strong><span style="color: #ffffff; font-family: opensans;">OFFICE RACKS</span></strong></span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (97,1,1479,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60) -; \ No newline at end of file + (97,1,1479,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), + (98,2,709,103,0,400,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','timedate','Europe/Madrid',0,0), + (99,2,178,481,111,111,'','status',0,3600,11556,430,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (100,2,542,481,111,111,'','status',0,3600,13,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (101,2,905,481,111,111,'','status',0,3600,114,11,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (102,2,1276,481,111,111,'','status',0,3600,7,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (103,2,1631,482,111,111,'','status',0,3600,11547,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (104,2,157,393,0,0,'

Backups

\n

 

','white', +4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (105,2,512,382,96,172,'<p style="overflow: hidden;"><span class="visual_font_size_28pt" style="font-family: opensans; color: #ffffff;">DB Status</span></p> <p style="overflow: hidden;">&nbsp;</p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (106,2,886,382,0,0,'

Disk slave

\n

 

','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (107,2,1251,382,0,0,'

Disk /var

\n

 

','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (108,2,1547,382,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Authentification</span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (109,2,126,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Processing</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (110,2,755,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Network</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (111,2,1281,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="color: #ffffff; font-family: opensans;">Storage</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0) +; diff --git a/pandora_server/DEBIAN/control b/pandora_server/DEBIAN/control index ee8554bbe8..de64d3f6cd 100644 --- a/pandora_server/DEBIAN/control +++ b/pandora_server/DEBIAN/control @@ -1,10 +1,10 @@ package: pandorafms-server -Version: 7.0NG.738-190918 +Version: 7.0NG.738-190926 Architecture: all Priority: optional Section: admin Installed-Size: 640 Maintainer: ÁRTICA ST Homepage: http://pandorafms.org/ -Depends: perl (>= 5.8), libdbi-perl, libdbd-mysql-perl, libtime-format-perl, libnetaddr-ip-perl, libtime-format-perl, libxml-simple-perl, libxml-twig-perl, libhtml-parser-perl, snmp, snmpd, traceroute, xprobe2, nmap, sudo, libwww-perl, libsocket6-perl, libio-socket-inet6-perl, snmp-mibs-downloader, libjson-perl, libnet-telnet-perl, libencode-locale-perl, libgeo-ip-perl +Depends: perl (>= 5.8), libdbi-perl, libdbd-mysql-perl, libtime-format-perl, libnetaddr-ip-perl, libtime-format-perl, libxml-simple-perl, libxml-twig-perl, libhtml-parser-perl, snmp, snmpd, traceroute, xprobe2, nmap, sudo, libwww-perl, libsocket6-perl, libio-socket-inet6-perl, libio-socket-ssl-perl, snmp-mibs-downloader, libjson-perl, libnet-telnet-perl, libencode-locale-perl, libgeo-ip-perl Description: Pandora FMS is a monitoring system for big IT environments. It uses remote tests, or local agents to grab information. Pandora supports all standard OS (Linux, AIX, HP-UX, Solaris and Windows XP,2000/2003), and support multiple setups in HA enviroments. This is the server package. Server makes the remote checks and process information transfer by Pandora FMS agents to the server. diff --git a/pandora_server/DEBIAN/make_deb_package.sh b/pandora_server/DEBIAN/make_deb_package.sh index 64aa380443..9955cc5359 100644 --- a/pandora_server/DEBIAN/make_deb_package.sh +++ b/pandora_server/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.738-190918" +pandora_version="7.0NG.738-190926" package_cpan=0 package_pandora=1 diff --git a/pandora_server/bin/pandora_server b/pandora_server/bin/pandora_server index bb177be783..1209922537 100755 --- a/pandora_server/bin/pandora_server +++ b/pandora_server/bin/pandora_server @@ -365,9 +365,11 @@ sub pandora_server_tasks ($) { # COMMON TASKS (master and non-master) # --------------------------------------------------------------- - - # Rotate Log File if (($counter % 30) == 0) { + # Update configuration options from the console. + pandora_get_sharedconfig ($pa_config, $dbh); + + # Rotate the log file. pandora_rotate_logfile($pa_config); # Set event storm protection diff --git a/pandora_server/conf/pandora_server.conf.new b/pandora_server/conf/pandora_server.conf.new index 33d34082c0..6d92d7417d 100644 --- a/pandora_server/conf/pandora_server.conf.new +++ b/pandora_server/conf/pandora_server.conf.new @@ -226,8 +226,9 @@ recon_threads 1 dataserver_threads 1 # mta_address: External Mailer (MTA) IP Address to be used by Pandora FMS internal email capabilities +# If not set, the MTA configuration specified in the Pandora FMS Console will be used. -mta_address localhost +#mta_address localhost # mta_port, this is the mail server port (default 25) @@ -250,6 +251,10 @@ mta_address localhost #mta_from Pandora FMS +# SMTP encryption protocol (none, ssl, starttls) + +#mta_encryption none + # Set 1 if want eMail deliver alert in separate mail (default). # Set 0 if want eMail deliver shared mail by all destination. mail_in_separate 1 diff --git a/pandora_server/conf/pandora_server.conf.windows b/pandora_server/conf/pandora_server.conf.windows index a602f7abc4..c7c5db64b4 100644 --- a/pandora_server/conf/pandora_server.conf.windows +++ b/pandora_server/conf/pandora_server.conf.windows @@ -214,6 +214,7 @@ recon_threads 2 dataserver_threads 2 # mta_address: External Mailer (MTA) IP Address to be used by Pandora FMS internal email capabilities +# If not set, the MTA configuration specified in the Pandora FMS Console will be used. #mta_address localhost @@ -235,6 +236,10 @@ dataserver_threads 2 # probably you need to change it to avoid problems with your antispam #mta_from pandora@sampledomain.com +# SMTP encryption protocol (none, ssl, starttls) + +#mta_encryption none + # xprobe2: Optional package to detect OS types using advanced TCP/IP # fingerprinting tecniques, much more accurates than stadard nmap. # If not provided, nmap is used insted xprobe2 diff --git a/pandora_server/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index 9e946576b0..0d4a80516b 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -45,7 +45,7 @@ our @EXPORT = qw( # version: Defines actual version of Pandora Server for this module only my $pandora_version = "7.0NG.738"; -my $pandora_build = "190918"; +my $pandora_build = "190926"; our $VERSION = $pandora_version." ".$pandora_build; # Setup hash @@ -187,6 +187,33 @@ sub pandora_get_sharedconfig ($$) { [$dbh] ); $pa_config->{'rb_product_name'} = 'Pandora FMS' unless (defined ($pa_config->{'rb_product_name'}) && $pa_config->{'rb_product_name'} ne ''); + + # Mail transport agent configuration. Local configuration takes precedence. + if ($pa_config->{"mta_local"} eq 0) { + $pa_config->{"mta_address"} = pandora_get_tconfig_token ($dbh, 'email_smtpServer', ''); + $pa_config->{"mta_from"} = '"' . pandora_get_tconfig_token ($dbh, 'email_from_name', 'Pandora FMS') . '" <' . + pandora_get_tconfig_token ($dbh, 'email_from_dir', 'pandora@pandorafms.org') . '>'; + $pa_config->{"mta_pass"} = pandora_get_tconfig_token ($dbh, 'email_password', ''); + $pa_config->{"mta_port"} = pandora_get_tconfig_token ($dbh, 'email_smtpPort', ''); + $pa_config->{"mta_user"} = pandora_get_tconfig_token ($dbh, 'email_username', ''); + $pa_config->{"mta_encryption"} = pandora_get_tconfig_token ($dbh, 'email_encryption', ''); + + # Auto-negotiate the auth mechanism, since it cannot be set from the console. + # Do not include PLAIN, it generates the following error: + # 451 4.5.0 SMTP protocol violation, see RFC 2821 + $pa_config->{"mta_auth"} = 'DIGEST-MD5 CRAM-MD5 LOGIN'; + + # Fix the format of mta_encryption. + if ($pa_config->{"mta_encryption"} eq 'tls') { + $pa_config->{"mta_encryption"} = 'starttls'; + } + elsif ($pa_config->{"mta_encryption"} =~ m/^ssl/) { + $pa_config->{"mta_encryption"} = 'ssl'; + } + else { + $pa_config->{"mta_encryption"} = 'none'; + } + } } ########################################################################## @@ -303,12 +330,14 @@ sub pandora_load_config { $pa_config->{"dynamic_constant"} = 10; # 7.0 # Internal MTA for alerts, each server need its own config. - $pa_config->{"mta_address"} = '127.0.0.1'; # Introduced on 2.0 - $pa_config->{"mta_port"} = '25'; # Introduced on 2.0 + $pa_config->{"mta_address"} = ''; # Introduced on 2.0 + $pa_config->{"mta_port"} = ''; # Introduced on 2.0 $pa_config->{"mta_user"} = ''; # Introduced on 2.0 $pa_config->{"mta_pass"} = ''; # Introduced on 2.0 $pa_config->{"mta_auth"} = 'none'; # Introduced on 2.0 (Support LOGIN PLAIN CRAM-MD5 DIGEST-MD) $pa_config->{"mta_from"} = 'pandora@localhost'; # Introduced on 2.0 + $pa_config->{"mta_encryption"} = 'none'; # 7.0 739 + $pa_config->{"mta_local"} = 0; # 7.0 739 $pa_config->{"mail_in_separate"} = 1; # 1: eMail deliver alert mail in separate mails. # 0: eMail deliver 1 mail with all destination. @@ -328,7 +357,15 @@ sub pandora_load_config { # Xprobe2 for recon OS fingerprinting and tcpscan (optional) $pa_config->{"xprobe2"} = "/usr/bin/xprobe2"; + # Winexe allows to exec commands on remote windows systems (optional) + $pa_config->{"winexe"} = "/usr/bin/winexe"; + + # PsExec allows to exec commands on remote windows systems from windows servers (optional) + $pa_config->{"psexec"} = 'C:\PandoraFMS\Pandora_Server\bin\PsExec.exe'; + # plink allows to exec commands on remote linux systems from windows servers (optional) + $pa_config->{"plink"} = 'C:\PandoraFMS\Pandora_Server\bin\plink.exe'; + # Snmpget for snmpget system command (optional) $pa_config->{"snmpget"} = "/usr/bin/snmpget"; @@ -582,6 +619,7 @@ sub pandora_load_config { } elsif ($parametro =~ m/^mta_address\s(.*)/i) { $pa_config->{'mta_address'}= clean_blank($1); + $pa_config->{'mta_local'}=1; } elsif ($parametro =~ m/^mta_port\s(.*)/i) { $pa_config->{'mta_port'}= clean_blank($1); @@ -592,6 +630,9 @@ sub pandora_load_config { elsif ($parametro =~ m/^mta_from\s(.*)/i) { $pa_config->{'mta_from'}= clean_blank($1); } + elsif ($parametro =~ m/^mta_encryption\s(.*)/i) { + $pa_config->{'mta_encryption'}= clean_blank($1); + } elsif ($parametro =~ m/^mail_in_separate\s+([0-9]*)/i) { $pa_config->{'mail_in_separate'}= clean_blank($1); } @@ -805,6 +846,15 @@ sub pandora_load_config { elsif ($parametro =~ m/^xprobe2\s(.*)/i) { $pa_config->{'xprobe2'}= clean_blank($1); } + elsif ($parametro =~ m/^winexe\s(.*)/i) { + $pa_config->{'winexe'}= clean_blank($1); + } + elsif ($parametro =~ m/^psexec\s(.*)/i) { + $pa_config->{'psexec'}= clean_blank($1); + } + elsif ($parametro =~ m/^plink\s(.*)/i) { + $pa_config->{'plink'}= clean_blank($1); + } elsif ($parametro =~ m/^snmpget\s(.*)/i) { $pa_config->{'snmpget'}= clean_blank($1); } @@ -1004,7 +1054,7 @@ sub pandora_load_config { $pa_config->{'console_pass'}= safe_input(clean_blank($1)); } elsif ($parametro =~ m/^encryption_passphrase\s(.*)/i) { # 6.0 - $pa_config->{'encryption_passphrase'}= safe_input(clean_blank($1)); + $pa_config->{'encryption_passphrase'} = clean_blank($1); } elsif ($parametro =~ m/^unknown_interval\s+([0-9]*)/i) { # > 5.1SP2 $pa_config->{'unknown_interval'}= clean_blank($1); diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index e1556d74b6..667cd7c909 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -3155,11 +3155,20 @@ sub pandora_get_config_value ($$) { ########################################################################## ## Get credential from credential store ########################################################################## -sub pandora_get_credential ($$) { - my ($dbh, $identifier) = @_; +sub pandora_get_credential ($$$) { + my ($pa_config, $dbh, $identifier) = @_; my $key = get_db_single_row($dbh, 'SELECT * FROM tcredential_store WHERE identifier = ?', $identifier); + $key->{'username'} = pandora_output_password( + $pa_config, + safe_output($key->{'username'}) + ); + $key->{'password'} = pandora_output_password( + $pa_config, + safe_output($key->{'password'}) + ); + return $key; } diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 6f7eab4c68..b2d8e71619 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -343,6 +343,9 @@ sub exec_recon_script ($$$) { sub PandoraFMS::Recon::Base::guess_os($$) { my ($self, $device) = @_; + $DEVNULL = '/dev/null' if (!defined($DEVNULL)); + $DEVNULL = '/NUL' if ($^O =~ /win/i && !defined($DEVNULL)); + # OS detection disabled. Use the device type. if ($self->{'os_detection'} == 0) { my $device_type = $self->get_device_type($device); @@ -354,17 +357,20 @@ sub PandoraFMS::Recon::Base::guess_os($$) { } # Use xprobe2 if available - if (-e $self->{pa_config}->{xprobe2}) { - my $output = `"$self->{pa_config}->{xprobe2}" $device 2>$DEVNULL | grep 'Running OS' | head -1`; + if (-x $self->{'pa_config'}->{'xprobe2'}) { + my $return = `"$self->{pa_config}->{xprobe2}" $device 2>$DEVNULL`; if ($? == 0) { + my ($output) = $a =~ /Running OS:(.*)/; return pandora_get_os($self->{'dbh'}, $output); } } # Use nmap by default - if (-e $self->{pa_config}->{nmap}) { - my $output = `"$self->{pa_config}->{nmap}" -F -O $device 2>$DEVNULL | grep 'Aggressive OS guesses'`; + if (-x $self->{'pa_config'}->{'nmap'}) { + my $return = `"$self->{pa_config}->{nmap}" -F -O $device 2>$DEVNULL`; return OS_OTHER if ($? != 0); + + my ($output) = $return =~ /Aggressive OS guesses:\s*(.*)/; return pandora_get_os($self->{'dbh'}, $output); } @@ -377,7 +383,11 @@ sub PandoraFMS::Recon::Base::guess_os($$) { sub PandoraFMS::Recon::Base::tcp_scan ($$) { my ($self, $host) = @_; - my $open_ports = `"$self->{pa_config}->{nmap}" -p$self->{recon_ports} $host | grep open | wc -l`; + my $r = `"$self->{pa_config}->{nmap}" -p$self->{recon_ports} $host`; + + # Same as ""| grep open | wc -l" but multi-OS; + my $open_ports = () = $r =~ /open/gm; + return $open_ports; } diff --git a/pandora_server/lib/PandoraFMS/PluginTools.pm b/pandora_server/lib/PandoraFMS/PluginTools.pm index 2d2d3d3f46..8bc83565ee 100644 --- a/pandora_server/lib/PandoraFMS/PluginTools.pm +++ b/pandora_server/lib/PandoraFMS/PluginTools.pm @@ -32,7 +32,7 @@ our @ISA = qw(Exporter); # version: Defines actual version of Pandora Server for this module only my $pandora_version = "7.0NG.738"; -my $pandora_build = "190918"; +my $pandora_build = "190926"; our $VERSION = $pandora_version." ".$pandora_build; our %EXPORT_TAGS = ( 'all' => [ qw() ] ); diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index 223c36f248..335504821e 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -36,7 +36,7 @@ use constant { DISCOVERY_DEPLOY_AGENTS => 9, }; -# /dev/null +# $DEVNULL my $DEVNULL = ($^O eq 'MSWin32') ? '/Nul' : '/dev/null'; # Some useful OIDs. @@ -852,7 +852,7 @@ sub get_routes($) { $self->{'routes'} = []; # Parse route's output. - my @output = `route -n 2>/dev/null`; + my @output = `route -n 2>$DEVNULL`; foreach my $line (@output) { chomp($line); if ($line =~ /^0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+).*/) { @@ -1220,7 +1220,7 @@ sub snmp_responds_v3($$) { sub local_arp($) { my ($self) = @_; - my @output = `arp -an 2>/dev/null`; + my @output = `arp -an 2>$DEVNULL`; foreach my $line (@output) { next unless ($line =~ m/\((\S+)\) at ([0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)/); $self->add_mac(parse_mac($2), $1); @@ -1286,7 +1286,7 @@ sub ping ($$$) { for (my $i = 0; $i < $retries; $i++) { # Note: There is no timeout option. - `$ping_command -s -n $host 56 $packets >/dev/null 2>&1`; + `$ping_command -s -n $host 56 $packets >$DEVNULL 2>&1`; return 1 if ($? == 0); } @@ -1299,7 +1299,7 @@ sub ping ($$$) { for (my $i = 0; $i < $retries; $i++) { # Note: There is no timeout option for ping6. - `$ping_command -q -n -c $packets $host >/dev/null 2>&1`; + `$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`; return 1 if ($? == 0); } @@ -1312,7 +1312,7 @@ sub ping ($$$) { for (my $i = 0; $i < $retries; $i++) { # Note: There is no timeout option for ping6. - `$ping_command -q -n -c $packets $host >/dev/null 2>&1`; + `$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`; if ($? == 0) { return 1; } @@ -1324,7 +1324,7 @@ sub ping ($$$) { # Assume Linux by default. my $ping_command = $host =~ /\d+:|:\d+/ ? "ping6" : "ping"; for (my $i = 0; $i < $retries; $i++) { - `$ping_command -q -W $timeout -n -c $packets $host >/dev/null 2>&1`; + `$ping_command -q -W $timeout -n -c $packets $host >$DEVNULL 2>&1`; return 1 if ($? == 0); } @@ -1529,99 +1529,101 @@ sub app_scan($) { $self->{'task_data'} ); - if (!$dbObj->is_connected()) { - call('message', 'Cannot connect to target ' . $target, 3); - $global_percent += $global_step; - $self->{'c_network_percent'} = 90; - # Update progress - $self->call('update_progress', $global_percent + (90 / (scalar @targets))); - $self->{'summary'}->{'not_alive'} += 1; - push @modules, { - name => $type . ' connection', - type => 'generic_proc', - data => 0, - description => $type . ' availability' - }; + if (defined($dbObj)) { + if (!$dbObj->is_connected()) { + call('message', 'Cannot connect to target ' . $target, 3); + $global_percent += $global_step; + $self->{'c_network_percent'} = 90; + # Update progress + $self->call('update_progress', $global_percent + (90 / (scalar @targets))); + $self->{'summary'}->{'not_alive'} += 1; + push @modules, { + name => $type . ' connection', + type => 'generic_proc', + data => 0, + description => $type . ' availability' + }; - } else { - my $dbObjCfg = $dbObj->get_config(); + } else { + my $dbObjCfg = $dbObj->get_config(); - $self->{'summary'}->{'discovered'} += 1; - $self->{'summary'}->{'alive'} += 1; + $self->{'summary'}->{'discovered'} += 1; + $self->{'summary'}->{'alive'} += 1; - push @modules, { - name => $type . ' connection', - type => 'generic_proc', - data => 1, - description => $type . ' availability' - }; + push @modules, { + name => $type . ' connection', + type => 'generic_proc', + data => 1, + description => $type . ' availability' + }; - # Analyze. - $self->{'step'} = STEP_STATISTICS; - $self->{'c_network_percent'} = 30; - $self->call('update_progress', $global_percent + (30 / (scalar @targets))); - $self->{'c_network_name'} = $dbObj->get_host(); + # Analyze. + $self->{'step'} = STEP_STATISTICS; + $self->{'c_network_percent'} = 30; + $self->call('update_progress', $global_percent + (30 / (scalar @targets))); + $self->{'c_network_name'} = $dbObj->get_host(); - # Retrieve connection statistics. - # Retrieve uptime statistics - # Retrieve query stats - # Retrieve connections - # Retrieve innodb - # Retrieve cache - $self->{'c_network_percent'} = 50; - $self->call('update_progress', $global_percent + (50 / (scalar @targets))); - push @modules, $dbObj->get_statistics(); + # Retrieve connection statistics. + # Retrieve uptime statistics + # Retrieve query stats + # Retrieve connections + # Retrieve innodb + # Retrieve cache + $self->{'c_network_percent'} = 50; + $self->call('update_progress', $global_percent + (50 / (scalar @targets))); + push @modules, $dbObj->get_statistics(); - # Custom queries. - $self->{'step'} = STEP_CUSTOM_QUERIES; - $self->{'c_network_percent'} = 80; - $self->call('update_progress', $global_percent + (80 / (scalar @targets))); - push @modules, $dbObj->execute_custom_queries(); + # Custom queries. + $self->{'step'} = STEP_CUSTOM_QUERIES; + $self->{'c_network_percent'} = 80; + $self->call('update_progress', $global_percent + (80 / (scalar @targets))); + push @modules, $dbObj->execute_custom_queries(); - if (defined($dbObjCfg->{'scan_databases'}) - && $dbObjCfg->{'scan_databases'} == 1) { - # Skip database scan in Oracle tasks - next if $self->{'type'} == DISCOVERY_APP_ORACLE; + if (defined($dbObjCfg->{'scan_databases'}) + && "$dbObjCfg->{'scan_databases'}" eq "1") { + # Skip database scan in Oracle tasks + next if $self->{'type'} == DISCOVERY_APP_ORACLE; - my $__data = $dbObj->scan_databases(); + my $__data = $dbObj->scan_databases(); - if (ref($__data) eq "ARRAY") { - if (defined($dbObjCfg->{'agent_per_database'}) - && $dbObjCfg->{'agent_per_database'} == 1) { - # Agent per database detected. - push @data, @{$__data}; - } else { - # Merge modules into engine agent. - my @_modules = map { - map { $_ } @{$_->{'module_data'}} - } @{$__data}; + if (ref($__data) eq "ARRAY") { + if (defined($dbObjCfg->{'agent_per_database'}) + && $dbObjCfg->{'agent_per_database'} == 1) { + # Agent per database detected. + push @data, @{$__data}; + } else { + # Merge modules into engine agent. + my @_modules = map { + map { $_ } @{$_->{'module_data'}} + } @{$__data}; - push @modules, @_modules; + push @modules, @_modules; + } } } } + + # Put engine agent at the beginning of the list. + my $version = $dbObj->get_version(); + unshift @data,{ + 'agent_data' => { + 'agent_name' => $dbObj->get_agent_name(), + 'os' => $type, + 'os_version' => (defined($version) ? $version : 'Discovery'), + 'interval' => $self->{'task_data'}->{'interval_sweep'}, + 'id_group' => $self->{'task_data'}->{'id_group'}, + 'address' => $dbObj->get_host(), + 'description' => '', + }, + 'module_data' => \@modules, + }; + + $self->call('create_agents', \@data); + + # Destroy item. + undef($dbObj); } - # Put engine agent at the beginning of the list. - my $version = $dbObj->get_version(); - unshift @data,{ - 'agent_data' => { - 'agent_name' => $dbObj->get_agent_name(), - 'os' => $type, - 'os_version' => (defined($version) ? $version : 'Discovery'), - 'interval' => $self->{'task_data'}->{'interval_sweep'}, - 'id_group' => $self->{'task_data'}->{'id_group'}, - 'address' => $dbObj->get_host(), - 'description' => '', - }, - 'module_data' => \@modules, - }; - - $self->call('create_agents', \@data); - - # Destroy item. - undef($dbObj); - $global_percent += $global_step; $self->{'c_network_percent'} = 100; $self->call('update_progress', $global_percent); @@ -1674,7 +1676,7 @@ sub deploy_scan($) { ########################################################################## sub scan($) { my ($self) = @_; - my ($progress, $step); + my ($progress, $step) = 1, 0; # 1% $self->call('update_progress', 1); @@ -1697,7 +1699,7 @@ sub scan($) { } # Find devices. - $self->call('message', "[1/5] Scanning the network...", 3); + $self->call('message', "[1/4] Scanning the network...", 3); $self->{'step'} = STEP_SCANNING; $self->call('update_progress', $progress); $self->scan_subnet(); @@ -1713,7 +1715,7 @@ sub scan($) { $self->call('delete_connections'); # Connectivity from address forwarding tables. - $self->call('message', "[1/4] Finding address forwarding table connectivity...", 3); + $self->call('message', "[2/4] Finding address forwarding table connectivity...", 3); $self->{'step'} = STEP_AFT; ($progress, $step) = (50, 20.0 / scalar(@hosts)); # From 50% to 70%. for (my $i = 0; defined($hosts[$i]); $i++) { @@ -1831,7 +1833,7 @@ sub snmp_get_command { my ($self, $device, $oid, $community, $vlan) = @_; $vlan = defined($vlan) ? "\@" . $vlan : ''; - my $command = "snmpwalk -M/dev/null -r$self->{'snmp_checks'} -t$self->{'snmp_timeout'} -v$self->{'snmp_version'} -On -Oe "; + my $command = "snmpwalk -M$DEVNULL -r$self->{'snmp_checks'} -t$self->{'snmp_timeout'} -v$self->{'snmp_version'} -On -Oe "; if ($self->{'snmp_version'} eq "3") { if ($self->{'community'}) { # Context $command .= " -N $self->{'community'} "; @@ -1847,7 +1849,7 @@ sub snmp_get_command { $command .= " -c$community$vlan "; } - return "$command $device $oid 2>/dev/null"; + return "$command $device $oid 2>$DEVNULL"; } diff --git a/pandora_server/lib/PandoraFMS/Sendmail.pm b/pandora_server/lib/PandoraFMS/Sendmail.pm index 394add6d1f..08819c78ab 100644 --- a/pandora_server/lib/PandoraFMS/Sendmail.pm +++ b/pandora_server/lib/PandoraFMS/Sendmail.pm @@ -32,7 +32,9 @@ $VERSION = '0.79_16'; 'tz' => '', # only to override automatic detection 'port' => 25, # change it if you always use a non-standard port - 'debug' => 0 # prints stuff to STDERR + 'debug' => 0, # prints stuff to STDERR + 'encryption' => 'none', # no, ssl or starttls + 'timeout' => 5, # timeout for socket reads/writes in seconds ); # ******************************************************************* @@ -54,7 +56,8 @@ use vars qw( $auth_support ); -use Socket; +use IO::Socket::INET; +use IO::Select; use Time::Local; # for automatic time zone detection use Sys::Hostname; # for use of hostname in HELO @@ -62,6 +65,12 @@ use Sys::Hostname; # for use of hostname in HELO $auth_support = 'DIGEST-MD5 CRAM-MD5 PLAIN LOGIN'; +# IO::Socket object. +my $S; + +# IO::Select object. +my $Sel; + # use MIME::QuotedPrint if available and configured in %mailcfg eval("use MIME::QuotedPrint"); $mailcfg{'mime'} &&= (!$@); @@ -178,9 +187,9 @@ sub sendmail { local $_; my (%mail, $k, - $smtp, $server, $port, $connected, $localhost, + $smtp, $server, $port, $localhost, $fromaddr, $recip, @recipients, $to, $header, - %esmtp, @wanted_methods, + %esmtp, @wanted_methods, $encryption ); use vars qw($server_reply); # -------- a few internal subs ---------- @@ -191,7 +200,7 @@ sub sendmail { $error .= "Server said: $server_reply\n"; print STDERR "Server said: $server_reply\n" if $^W; } - close S; + close $S if defined($S); return 0; } @@ -200,31 +209,40 @@ sub sendmail { for $i (0..$#_) { # accept references, so we don't copy potentially big data my $data = ref($_[$i]) ? $_[$i] : \$_[$i]; - if ($mailcfg{'debug'} > 5) { + if ($mailcfg{'debug'} > 9) { if (length($$data) < 500) { - print ">", $$data; + print STDERR ">", $$data; } else { - print "> [...", length($$data), " bytes sent ...]\n"; + print STDERR "> [...", length($$data), " bytes sent ...]\n"; } } - print(S $$data) || return 0; + my @sockets = $Sel->can_write($mailcfg{'timeout'}); + return 0 if (!@sockets); + syswrite($sockets[0], $$data) || return 0; } 1; } sub socket_read { + my $buffer; $server_reply = ""; - do { - $_ = ; - $server_reply .= $_; - #chomp $_; - print "<$_" if $mailcfg{'debug'} > 5; - if (/^[45]/ or !$_) { - chomp $server_reply; - return; # return false - } - } while (/^[\d]+-/); + + while (my @sockets = $Sel->can_read($mailcfg{'timeout'})) { + return if (!@sockets); + # 16kByte is the maximum size of an SSL frame and because sysread + # returns data from only a single SSL frame you can guarantee that + # there are no pending data. + sysread($sockets[0], $buffer, 65535) || return; + $server_reply .= $buffer; + last if ($buffer =~ m/\n$/); + } + + print STDERR "<$server_reply" if $mailcfg{'debug'} > 9; + if ($server_reply =~ /^[45]/) { + chomp $server_reply; + return; # return false + } chomp $server_reply; return $server_reply; } @@ -260,13 +278,15 @@ sub sendmail { } $smtp = $mail{'Smtp'} || $mail{'Server'}; - unshift @{$mailcfg{'smtp'}}, $smtp if ($smtp and $mailcfg{'smtp'}->[0] ne $smtp); + $mailcfg{'smtp'}->[0] = $smtp if ($smtp and $mailcfg{'smtp'}->[0] ne $smtp); + + $encryption = $mail{'Encryption'} || $mail{'Encryption'}; # delete non-header keys, so we don't send them later as mail headers # I like this syntax, but it doesn't seem to work with AS port 5.003_07: # delete @mail{'Smtp', 'Server'}; # so instead: - delete $mail{'Smtp'}; delete $mail{'Server'}; + delete $mail{'Smtp'}; delete $mail{'Server'}; delete $mail{'Encryption'}; $mailcfg{'port'} = $mail{'Port'} || $mailcfg{'port'} || 25; delete $mail{'Port'}; @@ -343,48 +363,36 @@ sub sendmail { $localhost = hostname() || 'localhost'; foreach $server ( @{$mailcfg{'smtp'}} ) { - # open socket needs to be inside this foreach loop on Linux, - # otherwise all servers fail if 1st one fails !??! why? - unless ( socket S, AF_INET, SOCK_STREAM, scalar(getprotobyname 'tcp') ) { - return fail("socket failed ($!)") - } - - print "- trying $server\n" if $mailcfg{'debug'} > 1; + print STDERR "- trying $server\n" if $mailcfg{'debug'} > 9; $server =~ s/\s+//go; # remove spaces just in case of a typo # extract port if server name like "mail.domain.com:2525" $port = ($server =~ s/:(\d+)$//o) ? $1 : $mailcfg{'port'}; $smtp = $server; # save $server for use outside foreach loop - my $smtpaddr = inet_aton $server; - unless ($smtpaddr) { - $error .= "$server not found\n"; - next; # next server + # load IO::Socket SSL if needed + if ($encryption ne 'none') { + eval "require IO::Socket::SSL" || return fail("IO::Socket::SSL is not available"); } - my $retried = 0; # reset retries for each server - while ( ( not $connected = connect S, pack_sockaddr_in($port, $smtpaddr) ) - and ( $retried < $mailcfg{'retries'} ) - ) { - $retried++; - $error .= "connect to $server failed ($!)\n"; - print "- connect to $server failed ($!)\n" if $mailcfg{'debug'} > 1; - print "retrying in $mailcfg{'delay'} seconds...\n" if $mailcfg{'debug'} > 1; - sleep $mailcfg{'delay'}; + if ($encryption ne 'ssl') { + $S = new IO::Socket::INET(PeerPort => $port, PeerAddr => $server, Proto => 'tcp'); } - - if ( $connected ) { - print "- connected to $server\n" if $mailcfg{'debug'} > 3; + else { + $S = new IO::Socket::SSL(PeerPort => $port, PeerAddr => $server, Proto => 'tcp', SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), Domain => AF_INET); + } + if ( $S ) { + print STDERR "- connected to $server\n" if $mailcfg{'debug'} > 9; last; } else { $error .= "connect to $server failed\n"; - print "- connect to $server failed, next server...\n" if $mailcfg{'debug'} > 1; + print STDERR "- connect to $server failed, next server...\n" if $mailcfg{'debug'} > 9; next; # next server } } - unless ( $connected ) { + unless ( $S ) { return fail("connect to $smtp failed ($!) no (more) retries!") }; @@ -397,8 +405,9 @@ sub sendmail { ; } - my($oldfh) = select(S); $| = 1; select($oldfh); - + $Sel = new IO::Select() || return fail("IO::Select error"); + $Sel->add($S); + socket_read() || return fail("Connection error from $smtp on port $port ($_)"); socket_write("EHLO $localhost$CRLF") @@ -418,8 +427,37 @@ sub sendmail { || return fail("send HELO error (lost connection?)"); } - if ($auth) { - warn "AUTH requested\n" if ($mailcfg{debug} > 4); + # STARTTLS + if ($encryption eq 'starttls') { + defined($esmtp{'STARTTLS'}) + || return fail('STARTTLS not supported'); + socket_write("STARTTLS$CRLF") || return fail("send STARTTLS error"); + socket_read() + || return fail('STARTTLS error'); + IO::Socket::SSL->start_SSL($S, SSL_hostname => $server, SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE()) + || return fail("start_SSL failed"); + + # The client SHOULD send an EHLO command as the + # first command after a successful TLS negotiation. + socket_write("EHLO $localhost$CRLF") + || return fail("send EHLO error (lost connection?)"); + my $ehlo = socket_read(); + if ($ehlo) { + # The server MUST discard any knowledge + # obtained from the client. + %esmtp = (); + + # parse EHLO response + map { + s/^\d+[- ]//; + my ($k, $v) = split /\s+/, $_, 2; + $esmtp{$k} = $v || 1 if $k; + } split(/\n/, $ehlo); + } + } + + if (defined($auth) && $auth->{'user'} ne '') { + warn "AUTH requested\n" if ($mailcfg{debug} > 9); # reduce wanted methods to those supported my @methods = grep {$esmtp{'AUTH'}=~/(^|\s)$_(\s|$)/i} grep {$auth_support =~ /(^|\s)$_(\s|$)/i} @@ -480,9 +518,9 @@ sub sendmail { my $challenge = socket_read() || return fail("AUTH DIGEST-MD5 failed: $server_reply"); $challenge =~ s/^\d+\s+//; $challenge =~ s/[\r\n]+$//; - warn "\nCHALLENGE=", decode_base64($challenge), "\n" if ($mailcfg{debug} > 10); + warn "\nCHALLENGE=", decode_base64($challenge), "\n" if ($mailcfg{debug} > 9); my $response = _digest_md5($auth->{user}, $auth->{password}, decode_base64($challenge), $auth->{realm}); - warn "\nRESPONSE=$response\n" if ($mailcfg{debug} > 10); + warn "\nRESPONSE=$response\n" if ($mailcfg{debug} > 9); socket_write(encode_base64($response, ""), $CRLF) || return fail("AUTH DIGEST-MD5 failed: $server_reply"); my $status = socket_read() @@ -562,7 +600,7 @@ sub sendmail { socket_write("QUIT$CRLF") || return fail("send QUIT error"); socket_read(); - close S; + close $S; return 1; } # end sub sendmail diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index 36050e4275..e07a4e9ae6 100755 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -140,6 +140,7 @@ our @EXPORT = qw( generate_agent_name_hash long_to_ip ip_to_long + get_enabled_servers ); # ID of the different servers @@ -518,7 +519,14 @@ sub pandora_sendmail { Smtp => $pa_config->{"mta_address"}, Port => $pa_config->{"mta_port"}, From => $pa_config->{"mta_from"}, + Encryption => $pa_config->{"mta_encryption"}, ); + + # Set the timeout. + $PandoraFMS::Sendmail::mailcfg{'timeout'} = $pa_config->{"tcp_timeout"}; + + # Enable debugging. + $PandoraFMS::Sendmail::mailcfg{'debug'} = $pa_config->{"verbosity"}; if (defined($content_type)) { $mail{'Content-Type'} = $content_type; @@ -535,15 +543,12 @@ sub pandora_sendmail { $mail{auth} = {user=>$pa_config->{"mta_user"}, password=>$pa_config->{"mta_pass"}, method=>$pa_config->{"mta_auth"}, required=>1 }; } - if (sendmail %mail) { - return; - } - else { - logger ($pa_config, "[ERROR] Sending email to $to_address with subject $subject", 1); - if (defined($Mail::Sendmail::error)){ - logger ($pa_config, "ERROR Code: $Mail::Sendmail::error", 5); + eval { + if (!sendmail(%mail)) { + logger ($pa_config, "[ERROR] Sending email to $to_address with subject $subject", 1); + logger ($pa_config, "ERROR Code: $Mail::Sendmail::error", 5) if (defined($Mail::Sendmail::error)); } - } + }; } ########################################################################## @@ -619,7 +624,7 @@ sub logger ($$;$) { $message = safe_output ($message); $level = 1 unless defined ($level); - return if ($level > $pa_config->{'verbosity'}); + return if (!defined ($pa_config->{'verbosity'}) || $level > $pa_config->{'verbosity'}); if (!defined($pa_config->{'log_file'})) { print strftime ("%Y-%m-%d %H:%M:%S", localtime()) . " [V". $level ."] " . $message . "\n"; @@ -989,7 +994,8 @@ sub load_average { $load_average = ((split(/\s+/, `/sbin/sysctl -n vm.loadavg`))[1]); } elsif ($OSNAME eq "MSWin32") { # Windows hasn't got load average. - $load_average = undef; + $load_average = `powershell "(Get-WmiObject win32_processor | Measure-Object -property LoadPercentage -Average).average"`; + chop($load_average); } # by default LINUX calls else { @@ -2049,6 +2055,25 @@ sub long_to_ip { return inet_ntoa pack("N", ($ip_long)); } +############################################################################### +# Returns a list with enabled servers. +############################################################################### +sub get_enabled_servers { + my $conf = shift; + + if (ref($conf) ne "HASH") { + return (); + } + + my @server_list = map { + if ($_ =~ /server$/i && $conf->{$_} > 0) { + $_ + } else { + } + } keys %{$conf}; + + return @server_list; +} # End of function declaration # End of defined Code diff --git a/pandora_server/pandora_server.redhat.spec b/pandora_server/pandora_server.redhat.spec index 6327df5d87..b2f64b8c23 100644 --- a/pandora_server/pandora_server.redhat.spec +++ b/pandora_server/pandora_server.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.738 -%define release 190918 +%define release 190926 Summary: Pandora FMS Server Name: %{name} @@ -27,7 +27,7 @@ Requires: perl(DBI) perl(DBD::mysql) Requires: perl(HTTP::Request::Common) perl(LWP::Simple) perl(LWP::UserAgent) Requires: perl(XML::Simple) perl(XML::Twig) net-snmp-utils Requires: perl(NetAddr::IP) net-snmp net-tools -Requires: perl(IO::Socket::INET6) perl(Net::Telnet) +Requires: perl(IO::Socket::INET6) perl(IO::Socket::SSL) perl(Net::Telnet) Requires: nmap sudo perl(JSON) Requires: perl(Time::HiRes) perl(Encode::Locale) Requires: perl perl(Sys::Syslog) perl(HTML::Entities) perl(Geo::IP) diff --git a/pandora_server/pandora_server.spec b/pandora_server/pandora_server.spec index 02bb3d40fb..cd4132747c 100644 --- a/pandora_server/pandora_server.spec +++ b/pandora_server/pandora_server.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.738 -%define release 190918 +%define release 190926 Summary: Pandora FMS Server Name: %{name} @@ -24,7 +24,7 @@ Provides: %{name}-%{version} Requires: perl-DBI perl-DBD-mysql perl-libwww-perl Requires: perl-NetAddr-IP net-snmp net-tools perl-XML-Twig Requires: nmap sudo perl-HTML-Tree perl-XML-Simple perl-Net-Telnet -Requires: perl-IO-Socket-INET6 perl-Socket6 snmp-mibs perl-JSON +Requires: perl-IO-Socket-INET6 perl-Socket6 perl-IO-Socket-SSL snmp-mibs perl-JSON Requires: perl-Encode-Locale perl-Geo-IP %description diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer index c2a295f2e8..5fec09c3f4 100755 --- a/pandora_server/pandora_server_installer +++ b/pandora_server/pandora_server_installer @@ -9,7 +9,7 @@ # ********************************************************************** PI_VERSION="7.0NG.738" -PI_BUILD="190918" +PI_BUILD="190926" MODE=$1 if [ $# -gt 1 ]; then diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 3a248e4bfc..7a50832361 100644 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -34,7 +34,7 @@ use PandoraFMS::Config; use PandoraFMS::DB; # version: define current version -my $version = "7.0NG.738 PS190918"; +my $version = "7.0NG.738 PS190926"; # Pandora server configuration my %conf; diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index ed0e29eab2..a55cba4acb 100755 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -36,7 +36,7 @@ use Encode::Locale; Encode::Locale::decode_argv; # version: define current version -my $version = "7.0NG.738 PS190918"; +my $version = "7.0NG.738 PS190926"; # save program name for logging my $progname = basename($0);