<?php
/**
 * Extension to self monitor Pandora FMS Console
 *
 * @category   Console Class
 * @package    Pandora FMS
 * @subpackage Supervisor
 * @version    1.0.0
 * @license    See below
 *
 *    ______                 ___                    _______ _______ ________
 * |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
 * |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
 * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
 *
 * ============================================================================
 * Copyright (c) 2005-2023 Pandora FMS
 * Please see https://pandorafms.com/community/ for full contribution list
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation for version 2.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * ============================================================================
 */

use PandoraFMS\Tools\Files;

global $config;

require_once $config['homedir'].'/include/functions_db.php';
require_once $config['homedir'].'/include/functions_io.php';
require_once $config['homedir'].'/include/functions_notifications.php';
require_once $config['homedir'].'/include/functions_servers.php';
require_once $config['homedir'].'/vendor/autoload.php';

// Enterprise includes.
enterprise_include_once('include/functions_metaconsole.php');
enterprise_include_once('include/functions_license.php');
enterprise_include_once('include/functions_cron.php');

/**
 * Base class to run scheduled tasks in cron extension
 */
class ConsoleSupervisor
{

    /**
     * Minimum modules to check performance.
     */
    public const MIN_PERFORMANCE_MODULES = 100;


    /**
     * Minimum queued elements in synchronization queue to be warned..
     */
    public const MIN_SYNC_QUEUE_LENGTH = 200;

    /**
     * Show if console supervisor is enabled or not.
     *
     * @var boolean
     */
    public $enabled;

    /**
     * Value of 'id' from tnotification_source
     * where description is 'System status'
     *
     * @var integer
     */
    public $sourceId;

    /**
     * Target groups to be notified.
     *
     * @var array
     */
    public $targetGroups;

    /**
     * Target users to be notified.
     *
     * @var array
     */
    public $targetUsers;

    /**
     * Targets up to date.
     *
     * @var boolean
     */
    public $targetUpdated;

    /**
     * Show messages or not.
     *
     * @var boolean
     */
    public $interactive;


    /**
     * Constructor.
     *
     * @param boolean $interactive Show output while executing or not.
     *
     * @return class This object
     */
    public function __construct(bool $interactive=true)
    {
        $source = db_get_row(
            'tnotification_source',
            'description',
            io_safe_input('System status')
        );

        $this->interactive = $interactive;

        if ($source === false) {
            $this->enabled = false;
            $this->sourceId = null;

            $this->targetGroups = null;
            $this->targetUsers = null;
        } else {
            $this->enabled = (bool) $source['enabled'];
            $this->sourceId = $source['id'];
        }

        return $this;
    }


    /**
     * Warn a message.
     *
     * @param string $msg Message.
     *
     * @return void
     */
    public function warn(string $msg)
    {
        if ($this->verbose === true) {
            echo date('M  j G:i:s').' ConsoleSupervisor: '.$msg."\n";
        }
    }


    /**
     * Manage scheduled tasks (basic).
     *
     * @return void
     */
    public function runBasic()
    {
        // Ensure functions are installed and up to date.
        enterprise_hook('cron_extension_install_functions');

        /*
         * PHP configuration warnings:
         *  NOTIF.PHP.SAFE_MODE
         *  NOTIF.PHP.INPUT_TIME
         *  NOTIF.PHP.EXECUTION_TIME
         *  NOTIF.PHP.UPLOAD_MAX_FILESIZE
         *  NOTIF.PHP.MEMORY_LIMIT
         *  NOTIF.PHP.DISABLE_FUNCTIONS
         *  NOTIF.PHP.CHROMIUM
         *  NOTIF.PHP.VERSION
         *  NOTIF.PHP.VERSION.SUPPORT
         */

        $this->checkPHPSettings();

        /*
         * Check license.
         *  NOTIF.LICENSE.EXPIRATION
         */

        $this->checkLicense();

        /*
         * Check component statuses (servers down - frozen).
         *  NOTIF.SERVER.STATUS.ID_SERVER
         */

        $this->checkPandoraServers();

        /*
         * Check at least 1 server running in master mode.
         *  NOTIF.SERVER.MASTER
         */

        $this->checkPandoraServerMasterAvailable();

        /*
         * Check if CRON is running.
         *  NOTIF.CRON.CONFIGURED
         */

        if (enterprise_installed()) {
            $this->checkCronRunning();
        }

        /*
         * Check if instance is registered.
         *  NOTIF.UPDATEMANAGER.REGISTRATION
         */

        $this->checkUpdateManagerRegistration();

        /*
         * Check if has API access.
         *  NOTIF.API.ACCESS
         */
        $this->checkApiAccess();

        /*
         * Check if there're new messages in UM.
         *  NOTIF.UPDATEMANAGER.MESSAGES
         */

        $this->getUMMessages();

        /*
         * Check if the Server and Console has
         * the same versions.
         *  NOTIF.SERVER.MISALIGNED
         */

        $this->checkConsoleServerVersions();

        /*
         * Check if AllowOverride is None or All.
         *  NOTIF.ALLOWOVERIDE.MESSAGE
         */

        $this->checkAllowOverrideEnabled();

        /*
         * Check if the Pandora Console log
         * file remains in old location.
         *  NOTIF.PANDORACONSOLE.LOG.OLD
         */

        $this->checkPandoraConsoleLogOldLocation();

        /*
         * Check if the audit log file
         * remains in old location.
         *  NOTIF.AUDIT.LOG.OLD
         */

        $this->checkAuditLogOldLocation();

        /*
         * Check if performance variables are corrects
         */
        $this->checkPerformanceVariables();

        /*
         * Checks if sync queue is longer than limits.
         *  NOTIF.SYNCQUEUE.LENGTH
         */

        if (is_metaconsole() === true) {
            $this->checkSyncQueueLength();
            $this->checkSyncQueueStatus();
        }

        /*
         * Check number of agents is equals and more than 200.
         * NOTIF.ACCESSSTASTICS.PERFORMANCE
         */

        $this->checkAccessStatisticsPerformance();

        /*
         * Checkc agent missing libraries.
         * NOTIF.AGENT.LIBRARY
         */

        if ((bool) enterprise_installed() === true) {
            $this->checkLibaryError();
        }

        /*
         * Check MYSQL Support Version
         */

        $this->checkMYSQLSettings();
    }


    /**
     * Manage scheduled tasks.
     *
     * @return void
     */
    public function run()
    {
        global $config;

        $this->maintenanceOperations();

        if ($this->enabled === false) {
            // Notifications not enabled.
            return;
        }

        if ($this->sourceId === null) {
            // Source not detected.
            return;
        }

        if ($this->verbose === true) {
            if (enterprise_hook('cron_supervisor_lock') === false) {
                // Cannot continue. Locked.
                exit;
            }
        }

        // Enterprise support.
        if (file_exists($config['homedir'].'/'.ENTERPRISE_DIR.'/load_enterprise.php')) {
            include_once $config['homedir'].'/'.ENTERPRISE_DIR.'/load_enterprise.php';
        }

        // Automatic checks launched by supervisor.
        $this->warn('running.');

        /*
         * Check license.
         *  NOTIF.LICENSE.EXPIRATION
         *  NOTIF.LICENSE.LIMITED
         */

        $this->checkLicense();

        /*
         * Check number of files in attachment:
         *  NOTIF.FILES.ATTACHMENT
         */

        $this->checkAttachment();

        /*
         * Files in data_in:
         *  NOTIF.FILES.DATAIN  (>1000)
         *  NOTIF.FILES.DATAIN.BADXML (>150)
         */

        $this->checkDataIn();

        /*
         * Check module queues not growing:
         *  NOTIF.SERVER.QUEUE.ID_SERVER
         */

        $this->checkServers();

        /*
         * Check component statuses (servers down - frozen).
         *  NOTIF.SERVER.STATUS.ID_SERVER
         */

        $this->checkPandoraServers();

        /*
         * Check at least 1 server running in master mode.
         *  NOTIF.SERVER.MASTER
         */

        $this->checkPandoraServerMasterAvailable();

        /*
         * PHP configuration warnings:
         *  NOTIF.PHP.SAFE_MODE
         *  NOTIF.PHP.INPUT_TIME
         *  NOTIF.PHP.EXECUTION_TIME
         *  NOTIF.PHP.UPLOAD_MAX_FILESIZE
         *  NOTIF.PHP.MEMORY_LIMIT
         *  NOTIF.PHP.DISABLE_FUNCTIONS
         *  NOTIF.PHP.CHROMIUM
         *  NOTIF.PHP.VERSION
         *  NOTIF.PHP.VERSION.SUPPORT
         */

        $this->checkPHPSettings();

        /*
         *  Check connection with historical DB (if enabled).
         *  NOTIF.HISTORYDB
         */

        $this->checkPandoraHistoryDB();

        /*
         * Check pandoradb running in main DB.
         * Check pandoradb running in historical DB.
         *  NOTIF.PANDORADB
         *  NOTIF.PANDORADB.HISTORICAL
         */

        $this->checkPandoraDBMaintenance();

        /*
         * Check historical DB MR version.
         *  NOTIF.HISTORYDB.MR
         */

        $this->checkPandoraHistoryDBMR();

        /*
         * Check external components.
         *  NOTIF.EXT.ELASTICSEARCH
         *  NOTIF.EXT.LOGSTASH
         *
         */

        $this->checkExternalComponents();

        /*
         * Check Metaconsole synchronization issues.
         *  NOTIF.METACONSOLE.DB_CONNECTION
         */

        $this->checkMetaconsole();

        /*
         * Check incoming scheduled downtimes (< 15d).
         *  NOTIF.DOWNTIME
         */

        $this->checkDowntimes();

        /*
         * Check if instance is registered.
         *  NOTIF.UPDATEMANAGER.REGISTRATION
         */

        $this->checkUpdateManagerRegistration();

        /*
         * Check if has API access.
         *  NOTIF.API.ACCESS
         */
        $this->checkApiAccess();

        /*
         * Check if event storm protection is activated.
         *  NOTIF.MISC.EVENTSTORMPROTECTION
         */

        $this->checkEventStormProtection();

        /*
         * Check if develop_bypass is enabled.
         *  NOTIF.MISC.DEVELOPBYPASS
         */

        $this->checkDevelopBypass();

        /*
         * Check if fontpath exists.
         *  NOTIF.MISC.FONTPATH
         */

        $this->checkFont();

        /*
         * Check if default user and password exists.
         *  NOTIF.SECURITY.DEFAULT_PASSWORD
         */

        $this->checkDefaultPassword();

        /*
         * Check if there're new updates.
         *  NOTIF.UPDATEMANAGER.OPENSETUP
         *  NOTIF.UPDATEMANAGER.UPDATE
         */

        $this->checkUpdates();

        /*
         * Check if there're new minor updates available.
         *  NOTIF.UPDATEMANAGER.MINOR
         */

        $this->checkMinorRelease();

        if ((bool) enterprise_installed() === true) {
            // Release the lock.
            enterprise_hook('cron_supervisor_release_lock');
        }

        /*
         * Check if CRON is running.
         *  NOTIF.CRON.CONFIGURED
         */

        if (enterprise_installed()) {
            $this->checkCronRunning();
        }

        /*
         * Check if instance is registered.
         *  NOTIF.UPDATEMANAGER.REGISTRATION
         */

        $this->checkUpdateManagerRegistration();

        /*
         * Check if has API access.
         *  NOTIF.API.ACCESS
         */
        $this->checkApiAccess();

        /*
         * Check if there're new messages in UM.
         *  NOTIF.UPDATEMANAGER.MESSAGES
         */

        $this->getUMMessages();

        /*
         * Check if the Server and Console has
         * the same versions.
         *  NOTIF.SERVER.MISALIGNED
         */

        $this->checkConsoleServerVersions();

        /*
         * Check if AllowOverride is None or All.
         *  NOTIF.ALLOWOVERRIDE.MESSAGE
         */

        $this->checkAllowOverrideEnabled();

        /*
         * Check if HA status.
         */

        if ((bool) enterprise_installed() === true) {
            $this->checkHaStatus();
        }

        /*
         * Check if the audit log file
         * remains in old location.
         */

        $this->checkAuditLogOldLocation();

        /*
         * Check if performance variables are corrects
         */
        $this->checkPerformanceVariables();

        /*
         * Checks if sync queue is longer than limits.
         *  NOTIF.SYNCQUEUE.LENGTH
         */

        if (is_metaconsole() === true) {
            $this->checkSyncQueueLength();
            $this->checkSyncQueueStatus();
        }

        /*
         * Check number of agents is equals and more than 200.
         * NOTIF.ACCESSSTASTICS.PERFORMANCE
         */

        $this->checkAccessStatisticsPerformance();

        /*
         * Checkc agent missing libraries.
         * NOTIF.AGENT.LIBRARY
         */

        if ((bool) enterprise_installed() === true) {
            $this->checkLibaryError();
        }

        /*
         * Check MYSQL Support Version
         *
         */
        $this->checkMYSQLSettings();

    }


    /**
     * Check if performance variables are corrects
     *
     * @return void
     */
    public function checkPerformanceVariables()
    {
        global $config;

        $names = [
            'event_purge'                      => 'Max. days before events are deleted',
            'trap_purge'                       => 'Max. days before traps are deleted',
            'audit_purge'                      => 'Max. days before audited events are deleted',
            'string_purge'                     => 'Max. days before string data is deleted',
            'gis_purge'                        => 'Max. days before GIS data is deleted',
            'days_purge'                       => 'Max. days before purge',
            'days_compact'                     => 'Max. days before data is compacted',
            'days_delete_unknown'              => 'Max. days before unknown modules are deleted',
            'days_delete_not_initialized'      => 'Max. days before delete not initialized modules',
            'days_autodisable_deletion'        => 'Max. days before autodisabled agents are deleted',
            'report_limit'                     => 'Item limit for real-time reports',
            'event_view_hr'                    => 'Default hours for event view',
            'big_operation_step_datos_purge'   => 'Big Operation Step to purge old data',
            'small_operation_step_datos_purge' => 'Small Operation Step to purge old data',
            'row_limit_csv'                    => 'Row limit in csv log',
            'limit_parameters_massive'         => 'Limit for bulk operations',
            'block_size'                       => 'Block size for pagination',
            'short_module_graph_data'          => 'Data precision',
            'graph_precision'                  => 'Data precision in graphs',
        ];

        $variables = (array) json_decode(io_safe_output($config['performance_variables_control']));

        foreach ($variables as $variable => $values) {
            if (empty($config[$variable]) === true || $config[$variable] === '') {
                continue;
            }

            $message = '';
            $limit_value = '';
            if ($config[$variable] > $values->max) {
                $message = 'Check the setting of %s, a value greater than %s is not recommended';
                $limit_value = $values->max;
            }

            if ($config[$variable] < $values->min) {
                $message = 'Check the setting of %s, a value less than %s is not recommended';
                $limit_value = $values->min;
            }

            if ($limit_value !== '' && $message !== '') {
                if (is_metaconsole() === true) {
                    $this->notify(
                        [
                            'type'    => 'NOTIF.VARIABLES.PERFORMANCE.'.$variable,
                            'title'   => __('Incorrect config value'),
                            'message' => __(
                                $message,
                                $names[$variable],
                                $limit_value
                            ),
                            'url'     => '__url__index.php?sec=advanced&sec2=advanced/metasetup',
                        ]
                    );
                } else {
                    $this->notify(
                        [
                            'type'    => 'NOTIF.VARIABLES.PERFORMANCE.'.$variable,
                            'title'   => __('Incorrect config value'),
                            'message' => __(
                                $message,
                                $names[$variable],
                                $limit_value
                            ),
                            'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup',
                        ]
                    );
                }
            }
        }

    }


    /**
     * Executes console maintenance operations. Executed ALWAYS through CRON.
     *
     * @return void
     */
    public function maintenanceOperations()
    {

    }


    /**
     * Check number of agents and disable agentaccess token if number
     * is equals and more than 200.
     *
     * @return void
     */
    public function checkAccessStatisticsPerformance()
    {
        global $config;

        $total_agents = db_get_value('count(*)', 'tagente');

        if ($total_agents >= 200) {
            if ($config['agentaccess'] !== 0) {
                db_process_sql_update('tconfig', ['value' => 0], ['token' => 'agentaccess']);
                $this->notify(
                    [
                        'type'    => 'NOTIF.ACCESSSTASTICS.PERFORMANCE',
                        'title'   => __('Access statistics performance'),
                        'message' => __(
                            'Usage of agent access statistics IS NOT RECOMMENDED on systems with more than 200 agents due performance penalty'
                        ),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                    ]
                );
            }
        } else {
            $this->cleanNotifications('NOTIF.ACCESSSTASTICS.PERFORMANCE');
        }
    }


    /**
     * Update targets for given notification using object targets.
     *
     * @param array   $notification Current notification.
     * @param boolean $send_mails   Only update db targets, no email.
     *
     * @return void
     */
    public function updateTargets(
        array $notification,
        bool $send_mails=true
    ) {
        $notification_id = $notification['id_mensaje'];
        $blacklist = [];

        if (is_array($this->targetUsers) === true
            && count($this->targetUsers) > 0
        ) {
            // Process user targets.
            $insertion_string = '';
            $users_sql = 'INSERT IGNORE INTO tnotification_user(id_mensaje,id_user)';
            foreach ($this->targetUsers as $user) {
                $insertion_string .= sprintf(
                    '(%d,"%s")',
                    $notification_id,
                    $user['id_user']
                );
                $insertion_string .= ',';

                if ($send_mails === true) {
                    // Send mail.
                    if (isset($user['also_mail']) && $user['also_mail'] == 1) {
                        enterprise_hook(
                            'send_email_user',
                            [
                                $user['id_user'],
                                io_safe_output($notification['mensaje']).'<br><hl><br>'.$notification['url'],
                                io_safe_output($notification['subject']),
                            ]
                        );
                        array_push($blacklist, $user['id_user']);
                    }
                }
            }

            $insertion_string = substr($insertion_string, 0, -1);
            db_process_sql($users_sql.' VALUES '.$insertion_string);
        }

        if (is_array($this->targetGroups) === true
            && count($this->targetGroups) > 0
        ) {
            // Process group targets.
            $insertion_string = '';
            $groups_sql = 'INSERT IGNORE INTO tnotification_group(id_mensaje,id_group)';
            foreach ($this->targetGroups as $group) {
                $insertion_string .= sprintf(
                    '(%d,"%s")',
                    $notification_id,
                    $group['id_group']
                );
                $insertion_string .= ',';

                if ($send_mails === true) {
                    // Send mail.
                    if (isset($group['also_mail']) && $group['also_mail'] == 1) {
                        enterprise_hook(
                            'send_email_group',
                            [
                                $group['id_group'],
                                io_safe_output($notification['mensaje']).'<br><hl><br>'.$notification['url'],
                                io_safe_output($notification['subject']),
                                null,
                                $blacklist,
                            ]
                        );
                    }
                }
            }

            $insertion_string = substr($insertion_string, 0, -1);

            db_process_sql($groups_sql.' VALUES '.$insertion_string);
        }

    }


    /**
     * Generates notifications for target users and groups.
     *
     * @param array   $data      Message to be delivered:
     *                  - boolean status (false: notify, true: do not notify)
     *                  - string title
     *                  - string message
     *                  - string url.
     * @param integer $source_id Target source_id, by default $this->sourceId.
     * @param integer $max_age   Maximum age for generated notification.
     *
     * @return void
     */
    public function notify(
        array $data,
        int $source_id=0,
        int $max_age=SECONDS_1DAY
    ) {
        // Uses 'check failed' logic.
        if (is_array($data) === false) {
            // Skip.
            return;
        }

        if ($source_id === 0) {
            $source_id = $this->sourceId;
        }

        static $_cache_targets;
        $key = $source_id.'|'.$data['type'];

        if ($_cache_targets === null) {
            $_cache_targets = [];
        }

        if (isset($_cache_targets[$key]) === true
            && $_cache_targets[$key] !== null
        ) {
            $targets = $_cache_targets[$key];
        } else {
            $targets = get_notification_source_targets(
                $source_id,
                $data['type']
            );
            $this->targetGroups = ($targets['groups'] ?? null);
            $this->targetUsers = ($targets['users'] ?? null);

            $_cache_targets[$key] = $targets;
        }

        switch ($data['type']) {
            case 'NOTIF.LICENSE.LIMITED':
                $max_age = 0;
            break;

            case 'NOTIF.FILES.ATTACHMENT':
            case 'NOTIF.FILES.DATAIN':
            case 'NOTIF.FILES.DATAIN.BADXML':
            case 'NOTIF.PHP.SAFE_MODE':
            case 'NOTIF.PHP.INPUT_TIME':
            case 'NOTIF.PHP.EXECUTION_TIME':
            case 'NOTIF.PHP.UPLOAD_MAX_FILESIZE':
            case 'NOTIF.PHP.MEMORY_LIMIT':
            case 'NOTIF.PHP.DISABLE_FUNCTIONS':
            case 'NOTIF.PHP.CHROMIUM':
            case 'NOTIF.PHP.VERSION':
            case 'NOTIF.PHP.VERSION.SUPPORT':
            case 'NOTIF.HISTORYDB':
            case 'NOTIF.PANDORADB':
            case 'NOTIF.PANDORADB.HISTORICAL':
            case 'NOTIF.HISTORYDB.MR':
            case 'NOTIF.EXT.ELASTICSEARCH':
            case 'NOTIF.METACONSOLE.DB_CONNECTION':
            case 'NOTIF.DOWNTIME':
            case 'NOTIF.UPDATEMANAGER.REGISTRATION':
            case 'NOTIF.API.ACCESS':
            case 'NOTIF.MISC.EVENTSTORMPROTECTION':
            case 'NOTIF.MISC.DEVELOPBYPASS':
            case 'NOTIF.MISC.FONTPATH':
            case 'NOTIF.SECURITY.DEFAULT_PASSWORD':
            case 'NOTIF.UPDATEMANAGER.OPENSETUP':
            case 'NOTIF.UPDATEMANAGER.UPDATE':
            case 'NOTIF.UPDATEMANAGER.MINOR':
            case 'NOTIF.UPDATEMANAGER.MESSAGES':
            case 'NOTIF.CRON.CONFIGURED':
            case 'NOTIF.ALLOWOVERRIDE.MESSAGE':
            case 'NOTIF.HAMASTER.MESSAGE':
            case 'NOTIF.MYSQL.VERSION':

            default:
                // NOTIF.SERVER.STATUS.
                // NOTIF.SERVER.STATUS.ID_SERVER.
                // NOTIF.SERVER.QUEUE.ID_SERVER.
                // NOTIF.SERVER.MASTER.
                // NOTIF.SERVER.STATUS.ID_SERVER.
                if (preg_match('/^NOTIF.SERVER/', $data['type']) === true) {
                    // Send notification once a day.
                    $max_age = SECONDS_1DAY;
                }

                // Else ignored.
            break;
        }

        // Get previous notification.
        $prev = db_get_row(
            'tmensajes',
            'subtype',
            $data['type'],
            false,
            false
        );

        if ($prev !== false
            && (time() - $prev['timestamp']) > $max_age
        ) {
            // Clean previous notification.
            $this->cleanNotifications($data['type']);
        } else if ($prev !== false) {
            // Avoid creation. Previous notification is still valid.
            // Update message with latest information.
            $r = db_process_sql_update(
                'tmensajes',
                [
                    'mensaje' => io_safe_input($data['message']),
                    'subject' => io_safe_input($data['title']),
                ],
                ['id_mensaje' => $prev['id_mensaje']]
            );
            $this->updateTargets($prev, false);
            return;
        }

        if (isset($data['type']) === false) {
            $data['type'] = '';
        }

        // Create notification.
        $notification = [];
        $notification['timestamp'] = time();
        $notification['id_source'] = $source_id;
        $notification['mensaje'] = io_safe_input($data['message']);
        $notification['subject'] = io_safe_input($data['title']);
        $notification['subtype'] = $data['type'];
        $notification['url'] = io_safe_input($data['url']);

        $id = db_process_sql_insert('tmensajes', $notification);

        if ($id === false) {
            // Failed to generate notification.
            $this->warn('Failed to generate notification');
            return;
        }

        // Update reference to update targets.
        $notification['id_mensaje'] = $id;

        $this->updateTargets($notification);

    }


    /**
     * Deletes useless notifications.
     *
     * @param string $subtype Subtype to be deleted.
     *
     * @return mixed False in case of error or invalid values passed.
     *               Affected rows otherwise
     */
    public function cleanNotifications(string $subtype)
    {
        $not_count = db_get_value_sql(
            sprintf(
                'SELECT count(*) as n
                FROM tmensajes
                WHERE subtype like "%s"',
                $subtype
            )
        );

        if ($not_count > 0) {
            return db_process_sql_delete(
                'tmensajes',
                sprintf('subtype like "%s"', $subtype)
            );
        }

        return true;
    }


    /**
     * Check license status and validity.
     *
     * @return boolean Return true if license is valid, false if not.
     */
    public function checkLicense()
    {
        global $config;

        $license = enterprise_hook('license_get_info');
        if ($license === ENTERPRISE_NOT_HOOK) {
            return false;
        }

        $days_to_expiry = ((strtotime($license['expiry_date']) - time()) / (60 * 60 * 24));

        // Limited mode.
        if (isset($config['limited_mode'])) {
            // Warn user if license is going to expire in 15 days or less.
            $this->notify(
                [
                    'type'    => 'NOTIF.LICENSE.LIMITED',
                    'title'   => __('Limited mode.'),
                    'message' => io_safe_output($config['limited_mode']),
                    'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/setup/license',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.LICENSE.LIMITED');
        }

        // Expiry.
        if (($days_to_expiry <= 15) && ($days_to_expiry > 0)
            && ((is_user_admin($config['id_user'])) || (check_acl($config['id_user'], 0, 'PM')))
        ) {
            if ($config['license_mode'] == 1) {
                $title = __('License is about to expire');
                $msg = 'Your license will expire in %d days. Please, contact our sales department.';
            } else {
                $title = __('Support is about to expire');
                $msg = 'Your support license will expire in %d days. Please, contact our sales department.';
            }

            // Warn user if license is going to expire in 15 days or less.
            $this->notify(
                [
                    'type'    => 'NOTIF.LICENSE.EXPIRATION',
                    'title'   => $title,
                    'message' => __(
                        $msg,
                        $days_to_expiry
                    ),
                    'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/setup/license',
                ]
            );
        } else if (($days_to_expiry <= 0) && ((is_user_admin($config['id_user'])) || (check_acl($config['id_user'], 0, 'PM')))) {
            if ($config['license_mode'] == 1) {
                $title = __('Expired license');
                $msg = __('Your license has expired. Please, contact our sales department.');
            } else {
                $title = __('Support expired');
                $msg = __('This license is outside of support. Please, contact our sales department.');
            }

            // Warn user, license has expired.
            $this->notify(
                [
                    'type'    => 'NOTIF.LICENSE.EXPIRATION',
                    'title'   => $title,
                    'message' => $msg,
                    'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/setup/license',
                ]
            );
            return false;
        } else {
            $this->cleanNotifications('NOTIF.LICENSE.EXPIRATION');
        }

        return true;

    }


    /**
     * Count files in target path.
     *
     * @param string  $path      Path to be checked.
     * @param string  $regex     Regular expression to find files.
     * @param integer $max_files Maximum number of files to find.
     *
     * @return integer Number of files in target path.
     */
    public function countFiles(
        string $path='',
        string $regex='',
        int $max_files=500
    ) {
        if (empty($path) === true) {
            return -1;
        }

        $nitems = 0;

        // Count files up to max_files.
        $dir = opendir($path);

        if ($dir !== false) {
            // Used instead of glob to avoid check directories with
            // more than 1M files.
            while (false !== ($file = readdir($dir)) && $nitems <= $max_files) {
                if ($file != '.' && $file != '..') {
                    if (empty($regex) === false) {
                        if (preg_match($regex, $file) === 1) {
                            $nitems++;
                            continue;
                        }
                    } else {
                        $nitems++;
                    }
                }
            }

            closedir($dir);
        }

        return $nitems;
    }


    /**
     * Check excesive files in attachment directory.
     *
     * @return void
     */
    public function checkAttachment()
    {
        global $config;

        if (is_writable($config['attachment_store']) !== true) {
            $this->notify(
                [
                    'type'    => 'NOTIF.WRITABLE.ATTACHMENT',
                    'title'   => __('Attachment directory is not writable'),
                    'message' => __(
                        'Directory %s is not writable. Please, configure corresponding permissions.',
                        $config['attachment_store']
                    ),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=general',
                ]
            );
            return;
        } else {
            $this->cleanNotifications('NOTIF.WRITABLE.ATTACHMENT');
        }

        $filecount = $this->countFiles(
            $config['attachment_store'],
            '',
            $config['num_files_attachment']
        );
        if ($filecount > $config['num_files_attachment']) {
            $this->notify(
                [
                    'type'    => 'NOTIF.FILES.ATTACHMENT',
                    'title'   => __('There are too many files in attachment directory'),
                    'message' => __(
                        'There are more than %d files in attachment, consider cleaning up attachment directory manually.',
                        $config['num_files_attachment']
                    ),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.FILES.ATTACHMENT');
        }

    }


    /**
     * Check excesive files in data_in directory.
     *
     * @return void
     */
    public function checkDataIn()
    {
        global $config;

        $remote_config_dir = (string) io_safe_output($config['remote_config']);

        if (enterprise_installed()
            && isset($config['license_nms'])
            && $config['license_nms'] != 1
        ) {
            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.',
                            $remote_config_dir
                        ),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=general',
                    ]
                );
                return;
            } else {
                $this->cleanNotifications(
                    'NOTIF.PERMISSIONS.REMOTE_CONFIG'
                );
            }

            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.',
                            $remote_config_dir.'/conf'
                        ),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=general',
                    ]
                );
            } else {
                $this->cleanNotifications(
                    'NOTIF.PERMISSIONS.REMOTE_CONFIG.CONF'
                );
            }

            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.',
                            $remote_config_dir.'/collections'
                        ),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=general',
                    ]
                );
            } else {
                $this->cleanNotifications(
                    'NOTIF.PERMISSIONS.REMOTE_CONFIG.COLLECTIONS'
                );
            }

            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.',
                            $remote_config_dir.'/md5'
                        ),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=general',
                    ]
                );
            } else {
                $this->cleanNotifications(
                    'NOTIF.PERMISSIONS.REMOTE_CONFIG.MD5'
                );
            }
        } else {
            $this->cleanNotifications('NOTIF.PERMISSIONS.REMOTE_CONF%');
        }

        $MAX_FILES_DATA_IN = 1000;
        $MAX_BADXML_FILES_DATA_IN = 150;

        $filecount = $this->countFiles(
            $remote_config_dir,
            '',
            $MAX_FILES_DATA_IN
        );
        // If cannot open directory, count is '-1', skip.
        if ($filecount > $MAX_FILES_DATA_IN) {
            $this->notify(
                [
                    'type'    => 'NOTIF.FILES.DATAIN',
                    'title'   => __('There are too much files in spool').'.',
                    'message' => __(
                        'There are more than %d files in %s. Consider checking DataServer performance',
                        $MAX_FILES_DATA_IN,
                        $remote_config_dir
                    ),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.FILES.DATAIN');
        }

        $filecount = $this->countFiles(
            $remote_config_dir,
            '/^.*BADXML$/',
            $MAX_BADXML_FILES_DATA_IN
        );
        // If cannot open directory, count is '-1', skip.
        if ($filecount > $MAX_BADXML_FILES_DATA_IN) {
            $this->notify(
                [
                    'type'    => 'NOTIF.FILES.DATAIN.BADXML',
                    'title'   => __('There are too many BADXML files in spool'),
                    'message' => __(
                        'There are more than %d files in %s. Consider checking software agents.',
                        $MAX_BADXML_FILES_DATA_IN,
                        $remote_config_dir
                    ),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.FILES.DATAIN.BADXML');
        }
    }


    /**
     * Check growing queues in servers.
     *
     * @return void
     */
    public function checkServers()
    {
        global $config;

        include_once $config['homedir'].'/include/functions_servers.php';

        $idx_file = $config['attachment_store'].'/.cron.supervisor.servers.idx';

        $MAX_QUEUE = 1500;
        $total_modules = servers_get_total_modules();

        $queue_state = [];
        $previous = [];
        $new = [];

        if (file_exists($idx_file) === true) {
            // Read previous values from file.
            $previous = json_decode(file_get_contents($idx_file), true);
        }

        // DataServer queue status.
        $queue_state = db_get_all_rows_sql(
            'SELECT id_server,name,server_type,queued_modules,status
            FROM tserver ORDER BY 1'
        );

        $time = time();
        if (is_array($queue_state) === true) {
            foreach ($queue_state as $queue) {
                $key = $queue['id_server'];
                $type = $queue['server_type'];
                $new_data[$key] = $queue['queued_modules'];
                $max_grown = 0;

                if (is_array($total_modules)
                    && isset($total_modules[$queue['server_type']])
                ) {
                    $max_grown = ($total_modules[$queue['server_type']] * 0.40);
                }

                if ($total_modules[$queue['server_type']] < self::MIN_PERFORMANCE_MODULES) {
                    $this->cleanNotifications('NOTIF.SERVER.QUEUE.'.$key);
                    // Skip.
                    continue;
                }

                // Compare queue increments in a not over 900 seconds.
                if (empty($previous[$key]['modules'])
                    || ($time - $previous[$key]['utime']) > 900
                ) {
                    $previous[$key]['modules'] = 0;
                }

                $modules_queued = ($queue['queued_modules'] - $previous[$key]['modules']);

                // 40% Modules queued since last check. If any.
                if ($max_grown > 0
                    && $modules_queued > $max_grown
                ) {
                    $msg = 'Queue has grown %d modules. Total %d';
                    if ($modules_queued <= 0) {
                        $msg = 'Queue is decreasing in %d modules. But there are %d queued.';
                        $modules_queued *= -1;
                    }

                    $this->notify(
                        [
                            'type'    => 'NOTIF.SERVER.QUEUE.'.$key,
                            'title'   => __(
                                '%s (%s) is lacking performance.',
                                servers_get_server_string_name($type),
                                $queue['name']
                            ),
                            'message' => __(
                                $msg,
                                $modules_queued,
                                $queue['queued_modules']
                            ),
                            'url'     => '__url__/index.php?sec=gservers&sec2=godmode/servers/modificar_server&refr=60',
                        ]
                    );
                } else {
                    $this->cleanNotifications('NOTIF.SERVER.QUEUE.'.$key);
                }

                $new[$key]['modules'] = $queue['queued_modules'];
                $new[$key]['utime'] = $time;
            }

            // Update file content.
            file_put_contents($idx_file, json_encode($new));
        } else {
            // No queue data, ignore.
            unlink($idx_file);

            // Clean notifications.
            $this->cleanNotifications('NOTIF.SERVER.QUEUE.%');
        }
    }


    /**
     * Check Pandora component statuses.
     *
     * @return void
     */
    public function checkPandoraServers()
    {
        global $config;

        $servers = db_get_all_rows_sql(
            'SELECT
                id_server,
                name,
                server_type,
                server_keepalive,
                status,
                unix_timestamp() - unix_timestamp(keepalive) as downtime
            FROM tserver
            WHERE 
                unix_timestamp() - unix_timestamp(keepalive) > server_keepalive'
        );

        if ($servers === false) {
            $nservers = db_get_value_sql(
                'SELECT count(*) as nservers
                 FROM tserver'
            );
            if ($nservers == 0) {
                $url = 'https://pandorafms.com/manual/en/documentation/02_installation/04_configuration';
                if ($config['language'] == 'es') {
                    $url = 'https://pandorafms.com/manual/es/documentation/02_installation/04_configuration';
                }

                $this->notify(
                    [
                        'type'    => 'NOTIF.SERVER.STATUS',
                        'title'   => __('No servers available.'),
                        'message' => __('There are no servers registered in this console. Please, check installation guide.'),
                        'url'     => $url,
                    ]
                );
            }

            // At this point there's no servers with issues.
            $this->cleanNotifications('NOTIF.SERVER.STATUS%');
            return;
        } else {
            // Clean notifications. Only show notif for down servers
            // ONLY FOR RECOVERED ONES.
            $servers_working = db_get_all_rows_sql(
                'SELECT
                    id_server,
                    name,
                    server_type,
                    server_keepalive,
                    status,
                    unix_timestamp() - unix_timestamp(keepalive) as downtime
                FROM tserver
                WHERE 
                    unix_timestamp() - unix_timestamp(keepalive) <= server_keepalive
                    AND status = 1'
            );
            if (is_array($servers_working) === true) {
                foreach ($servers_working as $server) {
                    $this->cleanNotifications(
                        'NOTIF.SERVER.STATUS.'.$server['id_server']
                    );
                }
            }
        }

        foreach ($servers as $server) {
            if ($server['server_type'] == SERVER_TYPE_ENTERPRISE_SATELLITE) {
                if ($server['downtime'] < ($server['server_keepalive'] * 2)) {
                    // Satellite uses different keepalive mode.
                    continue;
                }
            }

            if ($server['status'] == 1) {
                // Fatal error. Component has die.
                $msg = __(
                    '%s (%s) has crashed.',
                    servers_get_server_string_name($server['server_type']),
                    $server['name']
                );

                $description = __(
                    '%s (%s) has crashed, please check log files.',
                    servers_get_server_string_name($server['server_type']),
                    $server['name']
                );
            } else {
                // Non-fatal error. Controlated exit. Component is not running.
                $msg = __(
                    '%s (%s) is not running.',
                    servers_get_server_string_name($server['server_type']),
                    $server['name']
                );
                $description = __(
                    '%s (%s) is not running. Please, check configuration file or remove this server from server list.',
                    servers_get_server_string_name($server['server_type']),
                    $server['name']
                );
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.SERVER.STATUS.'.$server['id_server'],
                    'title'   => $msg,
                    'message' => $description,
                    'url'     => '__url__/index.php?sec=gservers&sec2=godmode/servers/modificar_server&refr=60',
                ]
            );
        }
    }


    /**
     * Checks if there's at last one server running in master mode.
     *
     * @return void
     */
    public function checkPandoraServerMasterAvailable()
    {
        global $config;

        $n_masters = db_get_value_sql(
            'SELECT
                count(*) as n
            FROM tserver
            WHERE 
                unix_timestamp() - unix_timestamp(keepalive) <= server_keepalive
                AND master > 0
                AND status = 1'
        );

        if ($n_masters === false) {
            // Failed to retrieve server list.
            return;
        }

        if ($n_masters <= 0) {
            // No server running in master.
            $url = 'https://pandorafms.com/manual/en/documentation/02_installation/04_configuration#master';
            if ($config['language'] == 'es') {
                $url = 'https://pandorafms.com/manual/es/documentation/02_installation/04_configuration#master';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.SERVER.MASTER',
                    'title'   => __('No master servers found.'),
                    'message' => __('At least one server must be defined to run as master. Please, check documentation.'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.SERVER.MASTER%');
        }

    }


    /**
     * Checks PHP settings to be correct. Generates system notifications if not.
     *
     * @return void
     */
    public function checkPHPSettings()
    {
        global $config;

        $PHPupload_max_filesize = config_return_in_bytes(
            ini_get('upload_max_filesize')
        );

        // PHP configuration.
        $PHPmax_input_time = ini_get('max_input_time');
        $PHPmemory_limit = config_return_in_bytes(ini_get('memory_limit'));
        $PHPmax_execution_time = ini_get('max_execution_time');
        $PHPsafe_mode = ini_get('safe_mode');
        $PHPdisable_functions = ini_get('disable_functions');
        $PHPupload_max_filesize_min = config_return_in_bytes('800M');
        $PHPmemory_limit_min = config_return_in_bytes('800M');
        $PHPSerialize_precision = ini_get('serialize_precision');

        if (is_metaconsole() === true) {
            $PHPmemory_limit_min = config_return_in_bytes('-1');
        }

        // Chromium status.
        $chromium_dir = io_safe_output($config['chromium_path']);
        $result_ejecution = exec($chromium_dir.' --version');

        // PHP version checks.
        $php_version = phpversion();
        $php_version_array = explode('.', $php_version);

        if ($PHPsafe_mode === '1') {
            $url = 'http://php.net/manual/en/features.safe-mode.php';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/features.safe-mode.php';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.SAFE_MODE',
                    'title'   => __('PHP safe mode is enabled. Some features may not work properly'),
                    'message' => __('To disable it, go to your PHP configuration file (php.ini) and put safe_mode = Off (Do not forget to restart apache process after changes)'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.SAFE_MODE');
        }

        if ($PHPmax_input_time !== '-1') {
            $url = 'http://php.net/manual/en/info.configuration.php#ini.max-input-time';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/info.configuration.php#ini.max-input-time';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.INPUT_TIME',
                    'title'   => sprintf(
                        __('%s value in PHP configuration is not recommended'),
                        'max_input_time'
                    ),
                    'message' => sprintf(
                        __('Recommended value is %s'),
                        '-1 ('.__('Unlimited').')'
                    ).'<br><br>'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator (Do not forget to restart Apache process after)'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.INPUT_TIME');
        }

        if ((int) $PHPmax_execution_time !== 0) {
            $url = 'http://php.net/manual/en/info.configuration.php#ini.max-execution-time';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/info.configuration.php#ini.max-execution-time';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.EXECUTION_TIME',
                    'title'   => sprintf(
                        __("Not recommended '%s' value in PHP configuration"),
                        'max_execution_time'
                    ),
                    'message' => sprintf(
                        __('Recommended value is: %s'),
                        '0 ('.__('Unlimited').')'
                    ).'<br><br>'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator (Dont forget restart apache process after changes)'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.EXECUTION_TIME');
        }

        if ($PHPupload_max_filesize < $PHPupload_max_filesize_min) {
            $url = 'http://php.net/manual/en/ini.core.php#ini.upload-max-filesize';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/ini.core.php#ini.upload-max-filesize';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.UPLOAD_MAX_FILESIZE',
                    'title'   => sprintf(
                        __("Not recommended '%s' value in PHP configuration"),
                        'upload_max_filesize'
                    ),
                    'message' => sprintf(
                        __('Recommended value is: %s'),
                        sprintf(__('%s or greater'), '800M')
                    ).'<br><br>'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator (Dont forget restart apache process after changes)'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.UPLOAD_MAX_FILESIZE');
        }

        if ($PHPmemory_limit < $PHPmemory_limit_min && (int) $PHPmemory_limit !== -1) {
            $url = 'http://php.net/manual/en/ini.core.php#ini.memory-limit';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/ini.core.php#ini.memory-limit';
            }

            $recommended_memory = '800M';
            if (is_metaconsole() === true) {
                $recommended_memory = '-1';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.MEMORY_LIMIT',
                    'title'   => sprintf(
                        __("Not recommended '%s' value in PHP configuration"),
                        'memory_limit'
                    ),
                    'message' => sprintf(
                        __('Recommended value is: %s'),
                        sprintf(__('%s or greater'), $recommended_memory)
                    ).'<br><br>'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.MEMORY_LIMIT');
        }

        if (preg_match('/system/', $PHPdisable_functions) || preg_match('/exec/', $PHPdisable_functions)) {
            $url = 'http://php.net/manual/en/ini.core.php#ini.disable-functions';
            if ($config['language'] == 'es') {
                $url = 'http://php.net/manual/es/ini.core.php#ini.disable-functions';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.DISABLE_FUNCTIONS',
                    'title'   => __('Problems with disable_functions in php.ini'),
                    'message' => __('The variable disable_functions contains functions system() or exec() in PHP configuration file (php.ini)').'<br /><br />'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator (Dont forget restart apache process after changes)'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.DISABLE_FUNCTIONS');
        }

        if (!isset($result_ejecution) || $result_ejecution == '') {
            $url = 'https://www.chromium.org/getting-involved/download-chromium/';
            // if ($config['language'] == 'es') {
            // $url = 'https://pandorafms.com/manual/es/documentation/02_installation/04_configuration#Phantomjs';
            // }
            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.CHROMIUM',
                    'title'   => __('chromium is not installed'),
                    'message' => __('To be able to create images of the graphs for PDFs, please install the chromium extension. For that, it is necessary to follow these steps:'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.CHROMIUM');
        }

        if ($php_version_array[0] < 8) {
            $url = 'https://pandorafms.com/manual/en/documentation/07_technical_annexes/18_php_8';
            if ($config['language'] == 'es') {
                $url = 'https://pandorafms.com/manual/es/documentation/07_technical_annexes/18_php_8';
            }

            if ($config['language'] == 'ja') {
                $url = 'https://pandorafms.com/manual/ja/documentation/07_technical_annexes/18_php_8';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.VERSION',
                    'title'   => __('PHP UPDATE REQUIRED'),
                    'message' => __('For a correct operation of PandoraFMS, PHP must be updated to version 8.0 or higher.').'<br>'.__('Otherwise, functionalities will be lost.').'<br>'."<ol><li class='color_67'>".__('Report download in PDF format').'</li>'."<li class='color_67'>".__('Emails Sending').'</li><li class="color_67">'.__('Metaconsole Collections').'</li><li class="color_67">...</li></ol>',
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.VERSION');
        }

        if ($PHPSerialize_precision != -1) {
            $url = 'https://www.php.net/manual/en/ini.core.php#ini.serialize-precision';
            if ($config['language'] == 'es') {
                $url = 'https://www.php.net/manual/es/ini.core.php#ini.serialize-precision';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.SERIALIZE_PRECISION',
                    'title'   => sprintf(
                        __("Not recommended '%s' value in PHP configuration"),
                        'serialize_precision'
                    ),
                    'message' => sprintf(
                        __('Recommended value is: %s'),
                        sprintf('-1')
                    ).'<br><br>'.__('Please, change it on your PHP configuration file (php.ini) or contact with administrator'),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.SERIALIZE_PRECISION');
        }

        // If PHP_VERSION is lower than 8.0.27 version_compare() returns 1.
        if (version_compare('8.0.27', PHP_VERSION) === 1) {
            $url = 'https://www.php.net/supported-versions.php';
            $this->notify(
                [
                    'type'    => 'NOTIF.PHP.VERSION.SUPPORT',
                    'title'   => __('PHP UPDATE REQUIRED'),
                    'message' => __('You should update your PHP version because it will be out of official support').'<br>'.__('Current PHP version: ').PHP_VERSION,
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PHP.VERSION.SUPPORT');
        }
    }


    /**
     * Checks if MYSQL version is supported.
     *
     * @return void
     */
    public function checkMYSQLSettings()
    {
        global $config;

        $mysql_version = $config['dbconnection']->server_info;
        if (version_compare('8.0', $mysql_version) >= 0) {
            $url = 'https://www.mysql.com/support/eol-notice.html';
            $this->notify(
                [
                    'type'    => 'NOTIF.MYSQL.VERSION',
                    'title'   => __('MYSQL UPDATE REQUIRED'),
                    'message' => __('You should update your MYSQL version because it will be out of official support').'<br>'.__('Current MYSQL version: ').$mysql_version,
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.MYSQL.VERSION');
        }
    }


    /**
     * Checks if history DB is available.
     *
     * @return void
     */
    public function checkPandoraHistoryDB()
    {
        global $config;

        if (isset($config['history_db_enabled'])
            && $config['history_db_enabled'] == 1
        ) {
            if (! isset($config['history_db_connection'])
                || $config['history_db_connection'] === false
            ) {
                ob_start();
                $config['history_db_connection'] = db_connect(
                    $config['history_db_host'],
                    $config['history_db_name'],
                    $config['history_db_user'],
                    io_output_password($config['history_db_pass']),
                    $config['history_db_port'],
                    false
                );
                ob_get_clean();
            }

            if ($config['history_db_connection'] === false) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.HISTORYDB',
                        'title'   => __('Historical database not available'),
                        'message' => __('Historical database is enabled, though not accessible with the current configuration.'),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=hist_db',
                    ]
                );
            } else {
                $this->cleanNotifications('NOTIF.HISTORYDB');
            }
        } else {
            $this->cleanNotifications('NOTIF.HISTORYDB');
        }
    }


    /**
     * Check if pandora_db is running in all available DB instances.
     * Generating notifications.
     *
     * @return void
     */
    public function checkPandoraDBMaintenance()
    {
        global $config;

        // Main DB db_maintenance value.
        $db_maintance = db_get_value(
            'value',
            'tconfig',
            'token',
            'db_maintance'
        );

        // If never was executed, it means we are in the first Pandora FMS execution. Set current timestamp.
        if (empty($db_maintance)) {
            config_update_value('db_maintance', date('U'));
        }

        $last_maintance = (date('U') - $db_maintance);

        // Limit 48h.
        if ($last_maintance > 172800) {
            $this->notify(
                [
                    'type'    => 'NOTIF.PANDORADB',
                    'title'   => __('Database maintenance problem'),
                    'message' => __(
                        'Your database hasn\'t been through maintenance for 48hrs. Please, check documentation on how to perform this maintenance process on %s and enable it as soon as possible.',
                        io_safe_output(get_product_name())
                    ),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PANDORADB');
        }

        if (isset($config['history_db_enabled'])
            && $config['history_db_enabled'] == 1
        ) {
            // History DB db_maintenance value.
            $db_maintenance = db_get_value(
                'value',
                'tconfig',
                'token',
                'db_maintenance',
                true
            );

            // History db connection is supossed to be enabled since we use
            // db_get_value, wich initializes target db connection.
            if (empty($db_maintance)) {
                $sql = sprintf(
                    'UPDATE tconfig SET `value`=%d WHERE `token`="%s"',
                    date('U'),
                    'db_maintenance'
                );
                $affected_rows = db_process_sql(
                    $sql,
                    $rettype = 'affected_rows',
                    $dbconnection = $config['history_db_connection']
                );

                if ($affected_rows == 0) {
                        // Failed to update. Maybe the row does not exist?
                        $sql = sprintf(
                            'INSERT INTO tconfig(`token`,`value`) VALUES("%s",%d)',
                            'db_maintenance',
                            date('U')
                        );

                        $affected_rows = db_process_sql(
                            $sql,
                            $rettype = 'affected_rows',
                            $dbconnection = $config['history_db_connection']
                        );
                }
            }

            $last_maintance = (date('U') - $db_maintance);

            // Limit 48h.
            if ($last_maintance > 172800) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.PANDORADB.HISTORY',
                        'title'   => __(
                            'Historical database maintenance problem.'
                        ),
                        'message' => __('Your historical database hasn\'t been through maintenance for 48hrs. Please, check documentation on how to perform this maintenance process on %s and enable it as soon as possible.', get_product_name()),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=perf',
                    ]
                );
            } else {
                // Historical db working fine.
                $this->cleanNotifications('NOTIF.PANDORADB.HISTORY');
            }
        } else {
            // Disabled historical db.
            $this->cleanNotifications('NOTIF.PANDORADB.HISTORY');
        }
    }


    /**
     * Check MR package applied in historical DB
     *
     * @return void
     */
    public function checkPandoraHistoryDBMR()
    {
        global $config;

        if (isset($config['history_db_enabled'])
            && $config['history_db_enabled'] == 1
        ) {
            $mrh_version = db_get_value(
                'value',
                'tconfig',
                'token',
                'MR',
                true
            );
            if ($mrh_version != $config['MR']) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.HISTORYDB.MR',
                        'title'   => __('Historical database MR mismatch'),
                        'message' => __('Your historical database is not using the same schema as the main DB. This could produce anomalies while storing historical data.'),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=hist_db',
                    ]
                );
            } else {
                // MR version OK.
                $this->cleanNotifications('NOTIF.HISTORYDB.MR');
            }
        } else {
            // Disabled historical db.
            $this->cleanNotifications('NOTIF.HISTORYDB.MR');
        }
    }


    /**
     * Check if elasticsearch is available.
     *
     * @return void
     */
    public function checkExternalComponents()
    {
        global $config;

        // Cannot check selenium, configuration is only available from server.
        if (isset($config['log_collector'])
            && $config['log_collector'] == 1
        ) {
            $elasticsearch = @fsockopen(
                $config['elasticsearch_ip'],
                $config['elasticsearch_port'],
                $errno,
                $errstr,
                5
            );

            if ($elasticsearch === false) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.EXT.ELASTICSEARCH',
                        'title'   => __('Log collector cannot connect to ElasticSearch'),
                        'message' => __('ElasticSearch is not available using current configuration.'),
                        'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=log',
                    ]
                );
            } else {
                fclose($elasticsearch);
                $this->cleanNotifications('NOTIF.EXT.ELASTICSEARCH');
            }
        } else {
            $this->cleanNotifications('NOTIF.EXT.ELASTICSEARCH');
        }

    }


    /**
     * Checks if metaconsole DB connection is ready.
     *
     * @return void
     */
    public function checkMetaconsole()
    {
        global $config;

        $check_ok = true;

        if (license_free()
            && is_metaconsole() === false
            && isset($config['node_metaconsole'])
            && $config['node_metaconsole'] == 1
        ) {
            // Check if node is successfully registered in MC.
            $server_name = db_get_value(
                'distinct(name)',
                'tserver',
                'server_type',
                1
            );

            $mc_db_conn = enterprise_hook(
                'metaconsole_load_external_db',
                [
                    [
                        'dbhost' => $config['replication_dbhost'],
                        'dbuser' => $config['replication_dbuser'],
                        'dbpass' => io_output_password(
                            $config['replication_dbpass']
                        ),
                        'dbname' => $config['replication_dbname'],
                    ],
                ]
            );

            if ($mc_db_conn === NOERR) {
                $check_ok = true;
            } else {
                $check_ok = false;
            }

            // Restore the default connection.
            enterprise_hook('metaconsole_restore_db');
        }

        if ($check_ok === true) {
            $this->cleanNotifications('NOTIF.METACONSOLE.DB_CONNECTION');
        } else {
            $this->notify(
                [
                    'type'    => 'NOTIF.METACONSOLE.DB_CONNECTION',
                    'title'   => __('Metaconsole DB is not available.'),
                    'message' => __('Cannot connect with Metaconsole DB using current configuration.'),
                    'url'     => '__url__/index.php?sec=general&sec2=godmode/setup/setup&section=enterprise',
                ]
            );
        }
    }


    /**
     * Check if there are any incoming scheduled downtime in less than 15d.
     *
     * @return void
     */
    public function checkDowntimes()
    {
        // 15 Days.
        $THRESHOLD_SECONDS = (15 * 3600 * 24);

        // Check first if any planned runtime is running.
        $currently_running = (int) db_get_value_sql(
            'SELECT count(*) as "n" FROM tplanned_downtime
            WHERE executed = 1'
        );

        if ($currently_running > 0) {
            $this->notify(
                [
                    'type'    => 'NOTIF.DOWNTIME',
                    'title'   => __('Scheduled downtime running.'),
                    'message' => __('A scheduled downtime is running. Some monitoring data won\'t be available while downtime is taking place.'),
                    'url'     => '__url__/index.php?sec=gagente&sec2=godmode/agentes/planned_downtime.list',
                ]
            );
            return;
        } else {
            // Retrieve downtimes.
            $downtimes = db_get_all_rows_sql(
                'SELECT * FROM tplanned_downtime
                WHERE 
                (type_execution="once" AND date_from > now())
                OR type_execution!="once" ORDER BY `id` DESC'
            );

            // Initialize searchers.
            $next_downtime_begin = PHP_INT_MAX;
            $now = time();

            if ($downtimes === false) {
                $this->cleanNotifications('NOTIF.DOWNTIME');
                return;
            }

            $weekdays = [
                'monday',
                'tuesday',
                'wednesday',
                'thursday',
                'friday',
                'saturday',
                'sunday',
            ];

            foreach ($downtimes as $dt) {
                if ($dt['type_execution'] == 'once'
                    && ($dt['date_from'] - $now) < $THRESHOLD_SECONDS
                ) {
                    if ($next_downtime_begin > $dt['date_from']) {
                        // Store datetime for next downtime.
                        $next_downtime_begin = $dt['date_from'];
                        $next_downtime_end = $dt['date_to'];
                    }
                } else if ($dt['type_periodicity'] == 'monthly') {
                    $schd_time_begin = explode(
                        ':',
                        $dt['periodically_time_from']
                    );
                    $schd_time_end = explode(
                        ':',
                        $dt['periodically_time_to']
                    );

                    $begin = mktime(
                        // Hour.
                        $schd_time_begin[0],
                        // Minute.
                        $schd_time_begin[1],
                        // Second.
                        $schd_time_begin[2],
                        // Month.
                        date('n', $now),
                        // Day.
                        $dt['periodically_day_from'],
                        // Year.
                        date('Y', $now)
                    );

                    $end = mktime(
                        // Hour.
                        $schd_time_end[0],
                        // Minute.
                        $schd_time_end[1],
                        // Second.
                        $schd_time_end[2],
                        // Month.
                        date('n', $now),
                        // Day.
                        $dt['periodically_day_to'],
                        // Year.
                        date('Y', $now)
                    );

                    if ($next_downtime_begin > $begin) {
                        $next_downtime_begin = $begin;
                        $next_downtime_end = $end;
                    }
                } else if ($dt['type_periodicity'] == 'weekly') {
                    // Always applies.
                    $current_week_day = date('N', $now);

                    $schd_time_begin = explode(
                        ':',
                        $dt['periodically_time_from']
                    );
                    $schd_time_end = explode(
                        ':',
                        $dt['periodically_time_to']
                    );

                    $i = 0;
                    $max = 7;
                    while ($dt[$weekdays[(($current_week_day + $i) % 7)]] != 1
                    && $max-- >= 0
                    ) {
                        // Calculate day of the week matching downtime
                        // definition.
                        $i++;
                    }

                    if ($max < 0) {
                        // No days set.
                        continue;
                    }

                    // Calculate utimestamp.
                    $begin = mktime(
                        // Hour.
                        $schd_time_begin[0],
                        // Minute.
                        $schd_time_begin[1],
                        // Second.
                        $schd_time_begin[2],
                        // Month.
                        date('n', $now),
                        // Day.
                        (date('j', $now) + $i + 1),
                        // Year.
                        date('Y', $now)
                    );

                    $end = mktime(
                        // Hour.
                        $schd_time_end[0],
                        // Minute.
                        $schd_time_end[1],
                        // Second.
                        $schd_time_end[2],
                        // Month.
                        date('n', $now),
                        // Day.
                        (date('j', $now) + $i + 1),
                        // Year.
                        date('Y', $now)
                    );

                    if ($next_downtime_begin > $begin) {
                        $next_downtime_begin = $begin;
                        $next_downtime_end = $end;
                    }
                }
            }

            if ($next_downtime_begin != PHP_INT_MAX) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.DOWNTIME',
                        'title'   => __('Downtime scheduled soon.'),
                        'message' => __(
                            'A scheduled downtime is going to be executed from %s to %s. Some monitoring data won\'t be available while downtime is taking place.',
                            date('M j, G:i:s ', $next_downtime_begin),
                            date('M j, G:i:s ', $next_downtime_end)
                        ),
                        'url'     => '__url__/index.php?sec=gagente&sec2=godmode/agentes/planned_downtime.list',
                    ]
                );
                return;
            } else {
                $this->cleanNotifications('NOTIF.DOWNTIME');
            }
        }
    }


    /**
     * Check if current instance of Pandora FMS is registered in Update Manager.
     *
     * @return void
     */
    public function checkUpdateManagerRegistration()
    {
        global $config;
        include_once $config['homedir'].'/include/functions_update_manager.php';
        $login = get_parameter('login', false);

        if (update_manager_verify_registration() === false) {
            $this->notify(
                [
                    'type'    => 'NOTIF.UPDATEMANAGER.REGISTRATION',
                    'title'   => __('This instance is not registered in the Update manager section'),
                    'message' => __('Click here to start the registration process'),
                    'url'     => '__url__/index.php?sec=messages&sec2=godmode/update_manager/update_manager&tab=online',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.UPDATEMANAGER.REGISTRATION');
        }
    }


    /**
     * Check if has access to the API
     *
     * @return void
     */
    public function checkApiAccess()
    {
        global $config;
        include_once $config['homedir'].'/include/functions_update_manager.php';

        if (update_manager_verify_api() === false) {
            $this->notify(
                [
                    'type'    => 'NOTIF.API.ACCESS',
                    'title'   => __('Cannot access the Pandora FMS API '),
                    'message' => __('Please check the configuration, some components may fail due to this misconfiguration.'),
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.API.ACCESS');
        }
    }


    /**
     * Check if user 'admin' is enabled and using default password.
     *
     * @return void
     */
    public function checkDefaultPassword()
    {
        global $config;
        // Check default password for "admin".
        $admin_with_default_pass = db_get_value_sql(
            'SELECT count(*) FROM tusuario
            WHERE
                id_user="admin"
                AND (password="1da7ee7d45b96d0e1f45ee4ee23da560" OR
                     password="$2y$10$Wv/xoxjI2VAkthJhk/PzeeGIhBKYU/K.TMgUdmW7fEP2NQkdWlB9K")
                AND is_admin=1
                and disabled!=1'
        );

        if ($admin_with_default_pass > 0) {
            $this->notify(
                [
                    'type'    => 'NOTIF.SECURITY.DEFAULT_PASSWORD',
                    'title'   => __('Default password for "Admin" user has not been changed'),
                    'message' => __('Please, change the default password since it is a commonly reported vulnerability.'),
                    'url'     => '__url__/index.php?sec=gusuarios&sec2=godmode/users/user_list',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.SECURITY.DEFAULT_PASSWORD');
        }
    }


    /**
     * Undocumented function
     *
     * @return void
     */
    public function checkFont()
    {
        global $config;

        $fontpath = io_safe_output($config['fontpath']);

        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            // Windows.
            $fontpath = $config['homedir'].'\include\fonts\\'.$fontpath;
        } else {
            $home = str_replace('\\', '/', $config['homedir']);
            $fontpath = $home.'/include/fonts/'.$fontpath;
        }

        if (($fontpath == '')
            || (file_exists($fontpath) === false)
        ) {
            $this->notify(
                [
                    'type'    => 'NOTIF.MISC.FONTPATH',
                    'title'   => __('Default font doesn\'t exist'),
                    'message' => __('Your defined font doesn\'t exist or is not defined. Please, check font parameters in your config'),
                    'url'     => is_metaconsole() === false
                                    ? '__url__/index.php?sec=gsetup&sec2=godmode/setup/setup&section=vis'
                                    : '__url__/index.php?sec=advanced&sec2=advanced/metasetup&tab=visual',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.MISC.FONTPATH');
        }
    }


    /**
     * Checks if develop_bypass is enabbled.
     *
     * @return void
     */
    public function checkDevelopBypass()
    {
        global $develop_bypass;

        if ($develop_bypass == 1) {
            $this->notify(
                [
                    'type'    => 'NOTIF.MISC.DEVELOPBYPASS',
                    'title'   => __('Developer mode is enabled'),
                    'message' => __(
                        'Your %s has the "develop_bypass" mode enabled. This is a developer mode and should be disabled in a production environment. This value is located in the main index.php file',
                        get_product_name()
                    ),
                    'url'     => '__url__/index.php',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.MISC.DEVELOPBYPASS');
        }
    }


    /**
     * Check if event storm protection is enabled.
     *
     * @return void
     */
    public function checkEventStormProtection()
    {
        global $config;
        if ($config['event_storm_protection']) {
            $this->notify(
                [
                    'type'    => 'NOTIF.MISC.EVENTSTORMPROTECTION',
                    'title'   => __('Event storm protection is enabled.'),
                    'message' => __('Some events may get lost while this mode is enabled. The server must be restarted after altering this setting.'),
                    'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/setup/setup&section=general',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.MISC.EVENTSTORMPROTECTION');
        }
    }


    /**
     * Check if there're new updates available.
     *
     * @return void
     */
    public function checkUpdates()
    {
        global $config;

        if (isset($_SESSION['new_update'])) {
            if (!empty($_SESSION['return_installation_open'])) {
                if (!$_SESSION['return_installation_open']['return']) {
                    foreach ($_SESSION['return_installation_open']['text'] as $message) {
                        $this->notify(
                            [
                                'type'    => 'NOTIF.UPDATEMANAGER.OPENSETUP',
                                'title'   => __('Failed to retrieve updates, please configure utility'),
                                'message' => $message,
                                'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/setup/setup&section=general',
                            ]
                        );
                    }
                } else {
                    $this->cleanNotifications('NOTIF.UPDATEMANAGER.OPENSETUP');
                }
            } else {
                $this->cleanNotifications('NOTIF.UPDATEMANAGER.OPENSETUP');
            }

            if ($_SESSION['new_update'] == 'new') {
                $this->notify(
                    [
                        'type'    => 'NOTIF.UPDATEMANAGER.UPDATE',
                        'title'   => __(
                            'New %s Console update',
                            get_product_name()
                        ),
                        'message' => __('There is a new update available. Please<a class="bolder" href="'.ui_get_full_url('index.php?sec=gsetup&sec2=godmode/update_manager/update_manager&tab=online').'"> go to Administration:Setup:Update Manager</a> for more details.'),
                        'url'     => '__url__/index.php?sec=gsetup&sec2=godmode/update_manager/update_manager&tab=online',
                    ]
                );
            } else {
                $this->cleanNotifications('NOTIF.UPDATEMANAGER.UPDATE');
            }
        } else {
            $this->cleanNotifications('NOTIF.UPDATEMANAGER.OPENSETUP');
            $this->cleanNotifications('NOTIF.UPDATEMANAGER.UPDATE');
        }
    }


    /**
     * Check if there're minor updates available.
     *
     * @return void
     */
    public function checkMinorRelease()
    {
        global $config;

        $check_minor_release_available = db_check_minor_relase_available();

        if ($check_minor_release_available) {
            $url = 'https://pandorafms.com/manual/es/documentation/02_installation/02_anexo_upgrade#version_70ng_rolling_release';
            if ($config['language'] == 'es') {
                $url = 'https://pandorafms.com/manual/en/documentation/02_installation/02_anexo_upgrade#version_70ng_rolling_release';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.UPDATEMANAGER.MINOR',
                    'title'   => __('Minor release/s available'),
                    'message' => __(
                        'There is one or more minor releases available. <a id="aviable_updates" target="blank" href="%s">.About minor release update</a>.',
                        $url
                    ),
                    'url'     => '__url__/index.php?sec=messages&sec2=godmode/update_manager/update_manager&tab=online',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.UPDATEMANAGER.MINOR');
        }

    }


    /**
     * Check if CRON utility has been configured.
     *
     * @return void
     */
    public function checkCronRunning()
    {
        global $config;

        // Check if DiscoveryCronTasks is running. Warn user if not.
        if ($config['cron_last_run'] == 0
            || (get_system_time() - $config['cron_last_run']) > SECONDS_10MINUTES
        ) {
            $message_conf_cron = __('DiscoveryConsoleTasks is not running properly');
            if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') {
                $message_conf_cron .= __('Discovery relies on an appropriate cron setup.');
                $message_conf_cron .= '. '.__('Please, add the following line to your crontab file:');
                $message_conf_cron .= '<b><pre class=""ui-dialog>* * * * * &lt;user&gt; wget -q -O - --no-check-certificate --load-cookies /tmp/cron-session-cookies --save-cookies /tmp/cron-session-cookies --keep-session-cookies ';
                $message_conf_cron .= str_replace(
                    ENTERPRISE_DIR.'/meta/',
                    '',
                    ui_get_full_url(false)
                );
                $message_conf_cron .= ENTERPRISE_DIR.'/'.EXTENSIONS_DIR;
                $message_conf_cron .= '/cron/cron.php &gt;&gt; </pre>';
                $message_conf_cron .= $config['homedir'].'/log/cron.log</pre>';
            }

            if (isset($config['cron_last_run']) === true) {
                $message_conf_cron .= __('Last execution').': ';
                $message_conf_cron .= date('Y/m/d H:i:s', $config['cron_last_run']);
                $message_conf_cron .= __('Please, make sure process is not locked.');
            }

            $url = '__url__/index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=tasklist';
            if (is_metaconsole() === true) {
                $url = '__url__index.php?sec=extensions&sec2=enterprise/extensions/cron';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.CRON.CONFIGURED',
                    'title'   => __('DiscoveryConsoleTasks is not configured.'),
                    'message' => __($message_conf_cron),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.CRON.CONFIGURED');
        }

    }


    /**
     * Search for messages.
     *
     * @return void
     */
    public function getUMMessages()
    {
        global $config;
        include_once $config['homedir'].'/include/functions_update_manager.php';

        if (update_manager_verify_registration() === false) {
            // Console not subscribed.
            return;
        }

        // Avoid contact for messages too much often.
        if (isset($config['last_um_check']) === true
            && time() < $config['last_um_check']
        ) {
            return;
        }

        // Only ask for messages once every 2 hours.
        $future = (time() + 2 * SECONDS_1HOUR);
        config_update_value('last_um_check', $future, true);

        $messages = update_manager_get_messages();

        if (is_array($messages) === true) {
            $source_id = get_notification_source_id(
                'Official&#x20;communication'
            );
            foreach ($messages as $message) {
                if (isset($message['url']) === false) {
                    $message['url'] = '#';
                }

                $this->notify(
                    [
                        'type'    => 'NOTIF.UPDATEMANAGER.MESSAGES.'.$message['id'],
                        'title'   => $message['subject'],
                        'message' => base64_decode($message['message_html']),
                        'url'     => $message['url'],
                    ],
                    $source_id
                );
            }
        }
    }


    /**
     * Check if all servers and console versions are the same
     *
     * @return void
     */
    public function checkConsoleServerVersions()
    {
        global $config;
        // List all servers except satellite server.
        $server_version_list = db_get_all_rows_sql(
            sprintf(
                'SELECT `name`, `version`
                FROM tserver
                WHERE server_type != %d
                GROUP BY `version`',
                SERVER_TYPE_ENTERPRISE_SATELLITE
            )
        );
        $missed = 0;
        if (is_array($server_version_list) === true) {
            foreach ($server_version_list as $server) {
                if (strpos(
                    $server['version'],
                    (string) floor((int) $config['current_package'])
                ) === false
                ) {
                    $missed++;
                    $title_ver_misaligned = __(
                        '%s version misaligned with Console',
                        $server['name']
                    );
                    $message_ver_misaligned = __(
                        'Server %s and this console have different versions. This might cause several malfunctions. Please, update this server.',
                        $server['name']
                    );

                    $this->notify(
                        [
                            'type'    => 'NOTIF.SERVER.MISALIGNED',
                            'title'   => __($title_ver_misaligned),
                            'message' => __($message_ver_misaligned),
                            'url'     => '__url__/index.php?sec=messages&sec2=godmode/update_manager/update_manager&tab=online',
                        ]
                    );

                    break;
                }
            }
        }

        // Cleanup notifications if exception is recovered.
        if ($missed == 0) {
            $this->cleanNotifications('NOTIF.SERVER.MISALIGNED');
        }
    }


    /**
     * Check if AllowOveride is None or All.
     *
     * @return void
     */
    public function checkAllowOverrideEnabled()
    {
        global $config;

        $message = 'If AllowOverride is disabled, .htaccess will not works.';
        if (PHP_OS == 'FreeBSD') {
            $message .= '<pre>Please check /usr/local/etc/apache24/httpd.conf to resolve this problem.';
        } else {
            $message .= '<pre>Please check /etc/httpd/conf/httpd.conf to resolve this problem.';
        }

        // Get content file.
        if (PHP_OS == 'FreeBSD') {
            $file = file_get_contents('/usr/local/etc/apache24/httpd.conf');
        } else {
            $file = file_get_contents('/etc/httpd/conf/httpd.conf');
        }

        $file_lines = preg_split("#\r?\n#", $file, -1, PREG_SPLIT_NO_EMPTY);
        $is_none = false;

        $i = 0;
        foreach ($file_lines as $line) {
            $i++;

            // Check Line and content.
            if (preg_match('/ AllowOverride/', $line) && $i === 311) {
                $result = explode(' ', $line);
                if ($result[5] == 'None') {
                    $is_none = true;
                    $this->notify(
                        [
                            'type'    => 'NOTIF.ALLOWOVERRIDE.MESSAGE',
                            'title'   => __('AllowOverride is disabled'),
                            'message' => __($message),
                            'url'     => '__url__/index.php',
                        ]
                    );
                }
            }
        }

        // Cleanup notifications if AllowOverride is All.
        if (!$is_none) {
            $this->cleanNotifications('NOTIF.ALLOWOVERRIDE.MESSAGE');
        }

    }


    /**
     * Check if AllowOveride is None or All.
     *
     * @return void
     */
    public function checkHaStatus()
    {
        global $config;
        enterprise_include_once('include/class/DatabaseHA.class.php');

        $cluster = new DatabaseHA();
        $nodes = $cluster->getNodes();

        foreach ($nodes as $node) {
            if ($node['status'] == HA_DISABLED) {
                continue;
            }

            $cluster_master = $cluster->isClusterMaster($node);
            $db_master = $cluster->isDBMaster($node);

            $message = '<pre>The roles played by node '.$node['host'].' are out of sync:
            Role in the cluster: Master
            Role in the database: Slave Desynchronized operation in the node';

            if ((int) $db_master !== (int) $cluster_master) {
                $this->notify(
                    [
                        'type'    => 'NOTIF.HAMASTER.MESSAGE',
                        'title'   => __('Desynchronized operation on the node '.$node['host']),
                        'message' => __($message),
                        'url'     => '__url__/index.php?sec=gservers&sec2=enterprise/godmode/servers/HA_cluster',
                    ]
                );
            } else {
                $this->cleanNotifications('NOTIF.HAMASTER.MESSAGE');
            }
        }
    }


    /**
     * Check if Pandora console log file remains in old location.
     *
     * @return void
     */
    public function checkPandoraConsoleLogOldLocation()
    {
        global $config;

        if (file_exists($config['homedir'].'/pandora_console.log')) {
            $title_pandoraconsole_old_log = __(
                'Pandora FMS console log file changed location',
                $config['homedir']
            );
            $message_pandoraconsole_old_log = __(
                'Pandora FMS console log file has been moved to new location %s/log. Currently you have an outdated and inoperative version of this file at %s. Please, consider deleting it.',
                $config['homedir'],
                $config['homedir']
            );

            $url = 'https://pandorafms.com/manual/en/quickguides/general_quick_guide#solving_problems_where_to_look_and_who_to_ask';
            if ($config['language'] == 'es') {
                $url = 'https://pandorafms.com//manual/es/quickguides/general_quick_guide#solucion_de_problemas_donde_mirar_a_quien_preguntar';
            }

            $this->notify(
                [
                    'type'    => 'NOTIF.PANDORACONSOLE.LOG.OLD',
                    'title'   => __($title_pandoraconsole_old_log),
                    'message' => __($message_pandoraconsole_old_log),
                    'url'     => $url,
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.PANDORACONSOLE.LOG.OLD');
        }
    }


    /**
     * Check if audit log file remains in old location.
     *
     * @return void
     */
    public function checkAuditLogOldLocation()
    {
        global $config;

        if (file_exists($config['homedir'].'/audit.log')) {
            $title_audit_old_log = __(
                'Pandora FMS audit log file changed location',
                $config['homedir']
            );
            $message_audit_old_log = __(
                'Pandora FMS audit log file has been moved to new location %s/log. Currently you have an outdated and inoperative version of this file at %s. Please, consider deleting it.',
                $config['homedir'],
                $config['homedir']
            );

            $this->notify(
                [
                    'type'    => 'NOTIF.AUDIT.LOG.OLD',
                    'title'   => __($title_audit_old_log),
                    'message' => __($message_audit_old_log),
                    'url'     => '#',
                ]
            );
        } else {
            $this->cleanNotifications('NOTIF.AUDIT.LOG.OLD');
        }
    }


    /**
     * Verifies the status of synchronization queue and warns if something is
     * not working as expected.
     *
     * @return void
     */
    public function checkSyncQueueLength()
    {
        global $config;

        if (is_metaconsole() !== true) {
            return;
        }

        $sync = new PandoraFMS\Enterprise\Metaconsole\Synchronizer(true);
        $counts = $sync->getQueues(true);

        if (count($counts) === 0) {
            // Clean all.
            $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.%');
        }

        $items_min = (isset($config['sync_queue_items_max']) === true) ? $config['sync_queue_items_max'] : 0;
        if (is_numeric($items_min) !== true && $items_min <= 0) {
            $items_min = self::MIN_SYNC_QUEUE_LENGTH;
        }

        foreach ($counts as $node_id => $count) {
            if ($count < $items_min) {
                $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.'.$node_id);
            } else {
                try {
                    $node = new PandoraFMS\Enterprise\Metaconsole\Node($node_id);

                    $url = '__url__/index.php?sec=advanced&sec2=advanced/metasetup&tab=consoles';

                    $this->notify(
                        [
                            'type'    => 'NOTIF.SYNCQUEUE.LENGTH.'.$node_id,
                            'title'   => __('Node %s sync queue length exceeded, ', $node->server_name()),
                            'message' => __(
                                'Synchronization queue lenght for node %s is %d items, this value should be 0 or lower than %d, please check the queue status.',
                                $node->server_name(),
                                $count,
                                $items_min
                            ),
                            'url'     => $url,
                        ]
                    );
                } catch (\Exception $e) {
                    // Clean, exception in node finding.
                    $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.'.$node_id);
                }
            }
        }

    }


    /**
     * Verifies the status of synchronization queue and warns if something is
     * not working as expected.
     *
     * @return void
     */
    public function checkSyncQueueStatus()
    {
        if (is_metaconsole() !== true) {
            return;
        }

        $sync = new PandoraFMS\Enterprise\Metaconsole\Synchronizer(true);
        $queues = $sync->getQueues();
        if (count($queues) === 0) {
            // Clean all.
            $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.%');
        }

        foreach ($queues as $node_id => $queue) {
            if (count($queue) === 0) {
                $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.'.$node_id);
                continue;
            }

            $item = $queue[0];

            if (empty($item->error()) === false) {
                try {
                    $node = new PandoraFMS\Enterprise\Metaconsole\Node($node_id);
                    $url = '__url__/index.php?sec=advanced&sec2=advanced/metasetup&tab=consoles';

                    $this->notify(
                        [
                            'type'    => 'NOTIF.SYNCQUEUE.STATUS.'.$node_id,
                            'title'   => __('Node %s sync queue failed, ', $node->server_name()),
                            'message' => __(
                                'Node %s cannot process synchronization queue due %s, please check the queue status.',
                                $node->server_name(),
                                $item->error()
                            ),
                            'url'     => $url,
                        ]
                    );
                } catch (\Exception $e) {
                    // Clean, exception in node finding.
                    $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.'.$node_id);
                }
            }
        }

    }


    /**
     * Chechs if an agent has a dependency eror on omnishell
     *
     * @return void
     */
    public function checkLibaryError()
    {
        $sql = 'SELECT COUNT(errorlevel) from tremote_command_target WHERE errorlevel = 2';

        $error_dependecies = db_get_sql($sql);
        if ($error_dependecies > 0) {
            $this->notify(
                [
                    'type'    => 'NOTIF.AGENT.LIBRARY',
                    'title'   => __('Agent dependency error'),
                    'message' => __(
                        'There are omnishell agents with dependency errors',
                    ),

                    'url'     => '__url__/index.php?sec=gextensions&sec2=enterprise/tools/omnishell',
                ]
            );
        }
    }


}