<?php
/* ----------------------------------------------------------------------------
 NagiosQL
-------------------------------------------------------------------------------
 (c) 2005-2022 by Martin Willisegger

 Project   : NagiosQL
 Component : Configuration Class
 Website   : https://sourceforge.net/projects/nagiosql/
 Version   : 3.5.0
 GIT Repo  : https://gitlab.com/wizonet/NagiosQL
-----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 Class: Configuration class
-------------------------------------------------------------------------------
 Includes all functions used for handling configuration files with NagiosQL
 Name: NagConfigClass
-----------------------------------------------------------------------------*/

namespace functions;

use FTP\Connection;
use HTML_Template_IT;
use function count;
use function dirname;
use function function_exists;
use function in_array;
use function is_array;
use function is_resource;
use function strlen;

class NagConfigClass
{
    /* Define class variables */
    /**
     * @var Connection $conFTPConId
     * @var resource $resSSHConId
     */
    public $conFTPConId; /* Connection id for FTP connections */
    public $resSSHConId; /* Connection id for SSH connections */
    public $resSFTP; /* SFTP ressource id */
    public $arrSession = array(); /* Session content */
    public $strRelTable = ''; /* Relation table name */
    public $strErrorMessage = ''; /* String including error messages */
    public $strInfoMessage = ''; /* String including information messages */
    public $strPicPath = 'none'; /* Picture path string */
    public $intNagVersion = 0; /* Nagios version id */
    public $intDomainId = 0; /* Configuration domain ID */
    /* PRIVATE */
    /** @var MysqliDbClass */
    public $myDBClass; /* Array includes all global settings */
    /** @var NagDataClass */
    public $myDataClass; /* Connection server name for FTP and SSH connections */
    private $arrSettings = array(); /* Connection type for FTP and SSH connections */
    /* Class includes */
    private $resConnectType = 'none'; /* Database class reference */
    private $resConnectServer = ''; /* Connection server name for FTP and SSH connections */
    private $arrRelData = ''; /* Data processing class reference */

    /**
     * NagConfigClass constructor.
     * @param array $arrSession PHP Session array
     */
    public function __construct(array $arrSession)
    {
        if (isset($arrSession['SETS'])) {
            /* Read global settings */
            $this->arrSettings = $arrSession['SETS'];
        }
        if (isset($arrSession['domain'])) {
            $this->intDomainId = (int)$arrSession['domain'];
        }
        $this->arrSession = $arrSession;
    }

    /**
     * Get last modification date of a database table and any configuration files inside a directory.
     * @param string $strTableName Name of the database table
     * @param string $strConfigName Name of the configuration file
     * @param int $intDataId ID of the dataset for service table
     * @param array|null $arrTimeData Array with the timestamps of the files and the DB table (by reference)
     * @param int|null $intTimeInfo Time status value (by reference)
     *                         0 = all files are newer than the database item
     *                         1 = some files are older than the database item
     *                         2 = one file is missing
     *                         3 = any files are missing
     *                         4 = no configuration targets defined
     * @return int 0 = successful / 1 = error
     * Status messages are stored in class variables
     */
    public function lastModifiedDir(string $strTableName, string $strConfigName, int $intDataId, array &$arrTimeData = null, int &$intTimeInfo = null): int
    {
        /* Variable definitions */
        $intReturn = 0;
        /* Create file name */
        $strFileName = $strConfigName . '.cfg';
        /* Get table times */
        $strActive = 0;
        $arrTimeData = array();
        $arrTimeData['table'] = 'unknown';
        /* Clear status cache */
        clearstatcache();
        /* Get last change on dataset */
        if ($strTableName === 'tbl_host') {
            $strSQL1 = "SELECT DATE_FORMAT(`last_modified`,'%Y-%m-%d %H:%i:%s') FROM `tbl_host` " .
                "WHERE `host_name`='$strConfigName' AND `config_id`=" . $this->intDomainId;
            $strSQL2 = "SELECT `active` FROM `tbl_host`  WHERE `host_name`='$strConfigName' " .
                'AND `config_id`=' . $this->intDomainId;
            $arrTimeData['table'] = $this->myDBClass->getFieldData($strSQL1);
            $strActive = $this->myDBClass->getFieldData($strSQL2);
        } elseif ($strTableName === 'tbl_service') {
            $strSQL1 = "SELECT DATE_FORMAT(`last_modified`,'%Y-%m-%d %H:%i:%s') FROM `tbl_service` " .
                "WHERE `id`='$intDataId' AND `config_id`=" . $this->intDomainId;
            /** @noinspection SqlResolve */
            $strSQL2 = "SELECT * FROM `$strTableName` WHERE `config_name`='$strConfigName' " .
                'AND `config_id`=' . $this->intDomainId . " AND `active`='1'";
            $arrTimeData['table'] = $this->myDBClass->getFieldData($strSQL1);
            $intServiceCount = $this->myDBClass->countRows($strSQL2);
            if ($intServiceCount !== 0) {
                $strActive = 1;
            }
        } else {
            $intReturn = 1;
        }
        /* Get config sets */
        $arrConfigId = array();
        $strTarget = '';
        $strBaseDir = '';
        $intTimeInfo = -1;
        $intRetVal2 = $this->getConfigTargets($arrConfigId);
        if ($intRetVal2 === 0) {
            foreach ($arrConfigId as $intConfigId) {
                /* Get configuration file data */
                $this->getConfigValues($intConfigId, 'target', $strTarget);
                /* Get last change on dataset */
                if ($strTableName === 'tbl_host') {
                    $this->getConfigValues($intConfigId, 'hostconfig', $strBaseDir);
                } elseif ($strTableName === 'tbl_service') {
                    $this->getConfigValues($intConfigId, 'serviceconfig', $strBaseDir);
                }
                $arrTimeData[$strTarget] = 'unknown';
                $intFileStampTemp = -1;
                /* Get time data */
                $intReturn = $this->getFileDate(
                    $intConfigId,
                    $strFileName,
                    $strBaseDir,
                    $intFileStampTemp,
                    $arrTimeData[$strTarget]
                );
                if (($intFileStampTemp === 0) && ($strActive === '1')) {
                    $intTimeInfo = 2;
                }
                if (($strActive === '1') && (strtotime($arrTimeData['table']) > $intFileStampTemp)) {
                    $intTimeInfo = 1;
                }
            }
            $intItems = count($arrTimeData) - 1;
            $intUnknown = 0;
            $intUpToDate = 0;
            foreach ($arrTimeData as $key) {
                if ($key === 'unknown') {
                    $intUnknown++;
                }
                if (strtotime($arrTimeData['table']) < strtotime($key)) {
                    $intUpToDate++;
                }
            }
            if ($intUnknown === $intItems) {
                $intTimeInfo = 3;
            }
            if ($intUpToDate === $intItems) {
                $intTimeInfo = 0;
            }
        } else {
            $intTimeInfo = 4;
        }
        return $intReturn;
    }

    /**
     * Get configuration target IDs
     * @param array|null $arrConfigId Configuration target IDs (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getConfigTargets(array &$arrConfigId = null): int
    {
        /* Variable definition */
        $arrData = array();
        $arrConfigId = array();
        $intDataCount = 0;
        $intReturn = 1;
        /* Request target ID */
        $strSQL = 'SELECT `targets` FROM `tbl_datadomain` WHERE `id`=' . $this->intDomainId;
        $booReturn = $this->myDBClass->hasDataArray($strSQL, $arrData, $intDataCount);
        if ($booReturn && ($intDataCount !== 0)) {
            foreach ($arrData as $elem) {
                $arrConfigId[] = $elem['targets'];
            }
            $intReturn = 0;
        }
        return $intReturn;
    }

    /**
     * Get configuration domain values
     * @param int $intConfigId Configuration ID
     * @param string $strConfigKey Configuration key
     * @param string|null $strValue Configuration value (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getConfigValues(int $intConfigId, string $strConfigKey, string &$strValue = null): int
    {
        /* Define variables */
        $intReturn = 1;
        /* Read database */
        $strSQL = 'SELECT `' . $strConfigKey . '` FROM `tbl_configtarget` WHERE `id`=' . $intConfigId;
        $strValue = $this->myDBClass->getFieldData($strSQL);
        if ($strValue !== '') {
            $intReturn = 0;
        }
        return $intReturn;
    }

    /**
     * Get last modification date of a configuration file.
     * @param int $intConfigId Configuration ID
     * @param string $strFile Configuration file name
     * @param string $strBaseDir Base directory with configuration file
     * @param bool|int $intFileStamp File timestamp (by reference)
     * @param string|null $strTimeData Human-readable string of file time stamp (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getFileDate(int $intConfigId, string $strFile, string $strBaseDir, bool &$intFileStamp, string &$strTimeData = null): int
    {
        $intMethod = 1;
        $intReturn = 0;
        /* Get configuration file data */
        if ($this->getConfigValues($intConfigId, 'method', $strMethod) === 0) {
            $intMethod = (int)$strMethod;
        }
        $strTimeData = 'unknown';
        $intFileStamp = -1;
        /* Lokal file system */
        if (($intMethod === 1) && file_exists($strBaseDir . '/' . $strFile)) {
            $intFileStamp = filemtime($strBaseDir . '/' . $strFile);
            $strTimeData = date('Y-m-d H:i:s', $intFileStamp);
        } elseif ($intMethod === 2) { // FTP file system
            /* Check connection */
            $intReturn = $this->getFTPConnection($intConfigId);
            if ($intReturn === 0) {
                $intFileStamp = ftp_mdtm($this->conFTPConId, $strBaseDir . '/' . $strFile);
                if ($intFileStamp !== -1) {
                    $strTimeData = date('Y-m-d H:i:s', $intFileStamp);
                }
            }
        } elseif ($intMethod === 3) { // SSH file system
            /* Check connection */
            $intReturn = $this->getSSHConnection($intConfigId);
            /* Check file date */
            $strFilePath = str_replace('//', '/', $strBaseDir . '/' . $strFile);
            $strCommand = 'ls ' . $strFilePath;
            $arrResult = array();
            if (($intReturn === 0) && ($this->sendSSHCommand($strCommand, $arrResult) === 0) &&
                isset($arrResult[0]) && ($arrResult[0] === $strFilePath)) {
                $arrInfo = ssh2_sftp_stat($this->resSFTP, $strFilePath);
                $intFileStamp = $arrInfo['mtime'];
                if ($intFileStamp !== -1) {
                    $strTimeData = date('Y-m-d H:i:s', $intFileStamp);
                }
            }
        }
        return $intReturn;
    }

    /**
     * Open an FTP connection
     * @param int $intConfigID Configuration ID
     * @return int 0 = successful / 1 = error
     * Status messages are stored in class variables
     */
    public function getFTPConnection(int $intConfigID): int
    {
        /* Define variables */
        $intReturn = 0;
        $arrError = array();
        $strServer = '';
        $intFtpSecure = 0;
        /* Already connected? */
        if (empty($this->conFTPConId) || !is_resource($this->conFTPConId) || ($this->resConnectType !== 'FTP')) {
            /* Define variables */
            $booLogin = false;
            if ($this->getConfigValues($intConfigID, 'server', $strServerVal) === 0) {
                $strServer = $strServerVal;
            }
            if ($this->getConfigValues($intConfigID, 'ftp_secure', $strFtpSecure) === 0) {
                $intFtpSecure = (int)$strFtpSecure;
            }
            /* Set up basic connection */
            $this->resConnectServer = $strServer;
            $this->resConnectType = 'FTP';
            /* Secure FTP? */
            if ($intFtpSecure === 1) {
                $this->conFTPConId = ftp_ssl_connect($strServer);
            } else {
                $this->conFTPConId = ftp_connect($strServer);
            }
            /* Login with username and password */
            if ($this->conFTPConId) {
                $this->getConfigValues($intConfigID, 'user', $strUser);
                $this->getConfigValues($intConfigID, 'password', $strPasswd);
                $intErrorReporting = error_reporting();
                error_reporting('0');
                $booLogin = ftp_login($this->conFTPConId, $strUser, $strPasswd);
                $arrError = error_get_last();
                error_reporting($intErrorReporting);
                if ($booLogin === false) {
                    ftp_close($this->conFTPConId);
                    $this->resConnectServer = '';
                    $this->resConnectType = 'none';
                    $this->conFTPConId = null;
                    $intReturn = 1;
                } else {
                    /* Change to PASV mode */
                    ftp_pasv($this->conFTPConId, true);
                }
            }
            /* Check connection */
            if ((!$this->conFTPConId) || (!$booLogin)) {
                $this->myDataClass->writeLog(translate('Connection to remote system failed (FTP connection):') .
                    ' ' . $strServer);
                $this->processClassMessage(translate('Connection to remote system failed (FTP connection):') .
                    ' <b>' . $strServer . '</b>::', $this->strErrorMessage);
                if ($arrError !== null && ((string)$arrError['message'] !== '')) {
                    $this->processClassMessage($arrError['message'] . '::', $this->strErrorMessage);
                }
            }
        }
        return $intReturn;
    }

    /**
     * Merge message strings and check for duplicate messages
     * @param string $strNewMessage New message to add
     * @param string|null $strOldMessage Modified message string (by reference)
     */
    public function processClassMessage(string $strNewMessage, string &$strOldMessage = null): void
    {
        $strNewMessage = str_replace('::::', '::', $strNewMessage);
        if (($strOldMessage !== '') && ($strNewMessage !== '') && (substr_count($strOldMessage, $strNewMessage) === 0)) {
            $strOldMessage .= $strNewMessage;
        } elseif ($strOldMessage === '') {
            $strOldMessage .= $strNewMessage;
        }
    }

    /**
     * Open an SSH connection
     * @param int $intConfigID Configuration ID
     * @return int 0 = successful / 1 = error
     * Status messages are stored in class variables
     */
    public function getSSHConnection(int $intConfigID): int
    {
        /* Define variables */
        $intReturn = 0;
        $strPasswordNote = '';
        $strServer = '';
        $intPort = 22;
        /* Already connected? */
        if (empty($this->resSSHConId) || !is_resource($this->resSSHConId) || ($this->resConnectType !== 'SSH')) {
            /* SSH Possible */
            if (!function_exists('ssh2_connect')) {
                $this->processClassMessage(translate('SSH module not loaded!') . '::', $this->strErrorMessage);
                return 1;
            }
            /* Define variables */
            $booLogin = false;
            if ($this->getConfigValues($intConfigID, 'server', $strServerVal) === 0) {
                $strServer = $strServerVal;
            }
            if ($this->getConfigValues($intConfigID, 'port', $strPort) === 0) {
                $intPort = (int)$strPort;
                if ($intPort === 0) {
                    $intPort = 22;
                }
            }
            $this->resConnectServer = $strServer;
            $this->resConnectType = 'SSH';
            $intErrorReporting = error_reporting();
            error_reporting(0);
            $this->resSSHConId = ssh2_connect($strServer, $intPort);
            $arrError = error_get_last();
            error_reporting($intErrorReporting);
            /* Check connection */
            if ($this->resSSHConId) {
                /* Login with username and password */
                $this->getConfigValues($intConfigID, 'user', $strUser);
                $this->getConfigValues($intConfigID, 'password', $strPasswd);
                $this->getConfigValues($intConfigID, 'ssh_key_path', $strSSHKeyPath);
                if ($strSSHKeyPath !== '') {
                    $strPublicKey = str_replace('//', '/', $strSSHKeyPath . '/id_rsa.pub');
                    $strPrivatKey = str_replace('//', '/', $strSSHKeyPath . '/id_rsa');
                    /* Check if ssh key file are readable */
                    if (!file_exists($strPublicKey) || !is_readable($strPublicKey)) {
                        $this->myDataClass->writeLog(translate('SSH public key does not exist or is not readable')
                            . ' ' . $strSSHKeyPath . $strPublicKey);
                        $this->processClassMessage(translate('SSH public key does not exist or is not readable')
                            . ' <b>' . $strSSHKeyPath . $strPublicKey . '</b>::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                    if (!file_exists($strPrivatKey) || !is_readable($strPrivatKey)) {
                        $this->myDataClass->writeLog(translate('SSH private key does not exist or is not readable')
                            . ' ' . $strPrivatKey);
                        $this->processClassMessage(translate('SSH private key does not exist or is not readable') . ' ' .
                            $strPrivatKey . '::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                    $intErrorReporting = error_reporting();
                    error_reporting(0);
                    if ($strPasswd === '') {
                        $booLogin = ssh2_auth_pubkey_file(
                            $this->resSSHConId,
                            $strUser,
                            $strSSHKeyPath . '/id_rsa.pub',
                            $strSSHKeyPath . '/id_rsa'
                        );
                    } else {
                        $booLogin = ssh2_auth_pubkey_file(
                            $this->resSSHConId,
                            $strUser,
                            $strSSHKeyPath . '/id_rsa.pub',
                            $strSSHKeyPath . '/id_rsa',
                            $strPasswd
                        );
                    }
                    $arrError = error_get_last();
                } else {
                    $intErrorReporting = error_reporting();
                    error_reporting(0);
                    $booLogin = ssh2_auth_password($this->resSSHConId, $strUser, $strPasswd);
                    $arrError = error_get_last();
                    $strPasswordNote = 'If you are using ssh2 with user/password - you have to enable ' .
                        'PasswordAuthentication in your sshd_config';
                }
                error_reporting($intErrorReporting);
            } else {
                $this->myDataClass->writeLog(translate('Connection to remote system failed (SSH2 connection):') .
                    ' ' . $strServer . ' / ' . translate('port') . ' : ' . $intPort);
                $this->processClassMessage(translate('Connection to remote system failed (SSH2 connection):') .
                    ' <b>' . $strServer . ' / ' . translate('port') . ' : ' . $intPort . '</b>::', $this->strErrorMessage);
                if ((string)$arrError['message'] !== '') {
                    $this->processClassMessage($arrError['message'] . '::', $this->strErrorMessage);
                }
                $intReturn = 1;
            }
            /* Check connection */
            if ((!$this->resSSHConId) || (!$booLogin)) {
                $this->myDataClass->writeLog(translate('Connection to remote system failed (SSH2 connection):') .
                    ' ' . $strServer . ' / ' . translate('port') . ' : ' . $intPort);
                $this->processClassMessage(translate('Connection to remote system failed (SSH2 connection):')
                    . ' ' . $strServer . ' / ' . translate('port') . ' : ' . $intPort . '::', $this->strErrorMessage);
                if ((string)$arrError['message'] !== '') {
                    $this->processClassMessage($arrError['message'] . '::', $this->strErrorMessage);
                }
                if ($strPasswordNote !== null) {
                    $this->processClassMessage($strPasswordNote . '::', $this->strErrorMessage);
                }
                $this->resConnectServer = '';
                $this->resConnectType = 'none';
                $this->resSSHConId = null;
                $intReturn = 1;
            } else {
                /* Etablish an SFTP connection ressource */
                $this->resSFTP = ssh2_sftp($this->resSSHConId);
            }
        }
        return $intReturn;
    }

    /**
     * Sends a command via SSH and stores the result in an array
     * @param string $strCommand Command string
     * @param array|null $arrResult Output as array (by reference)
     * @param int $intLines Maximal length of output to read
     * @return int 0 = successful / 1 = error
     */
    public function sendSSHCommand(string $strCommand, array &$arrResult = null, int $intLines = 100): int
    {
        /* Define variables */
        $intCount1 = 0; /* empty lines */
        $intCount2 = 0; /* data lines */
        $booBreak = false;
        $this->getConfigTargets($arrConfigSet);
        /* Check connection */
        $intReturn = $this->getSSHConnection($arrConfigSet[0]);
        if (is_resource($this->resSSHConId)) {
            /* Send command */
            $resStream = ssh2_exec($this->resSSHConId, $strCommand . '; echo __END__');
            if ($resStream) {
                /* Read result */
                stream_set_blocking($resStream, true);
                stream_set_timeout($resStream, 2);
                do {
                    $strLine = stream_get_line($resStream, 1024, "\n");
                    if ($strLine === '') {
                        $intCount1++;
                    } elseif (substr_count($strLine, '__END__') !== 1) {
                        $arrResult[] = $strLine;
                        $intReturn = 0;
                    } elseif (substr_count($strLine, '__END__') === 1) {
                        $booBreak = true;
                    }
                    $intCount2++;
                    $arrStatus = stream_get_meta_data($resStream);
                } while ($resStream && !feof($resStream) && ($intCount1 <= 10) && ($intCount2 <= $intLines) &&
                ((bool)$arrStatus['timed_out'] !== true) && $booBreak === false);
                fclose($resStream);
                /* Close SSH connection because of timing problems */
                unset($this->resSSHConId);
            }
        }
        return $intReturn;
    }

    /**
     * Remove a file
     * @param string $strFileName Filename including path to remove
     * @param int $intConfigID Configuration target ID
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function removeFile(string $strFileName, int $intConfigID): int
    {
        /* Variable definitions */
        $intMethod = 1;
        $intReturn = 0;
        $booRetVal = false;
        $strMethod = '';
        /* Get connection method */
        if ($this->getConfigData($intConfigID, 'method', $strMethod) === 0) {
            $intMethod = (int)$strMethod;
        }
        /* Local file system */
        if ($intMethod === 1) {
            /* Save configuration file */
            if (file_exists($strFileName)) {
                if (is_writable($strFileName)) {
                    unlink($strFileName);
                } else {
                    $this->processClassMessage(translate('Cannot delete the file (wrong permissions)!') . '::' .
                        $strFileName . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            } else {
                $this->processClassMessage(translate('Cannot delete the file (file does not exist)!') . '::' .
                    $strFileName . '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        } elseif ($intMethod === 2) { /* Remote file (FTP) */
            /* Check connection */
            $intReturn = $this->getFTPConnection($intConfigID);
            if ($intReturn === 0) {
                /* Save configuration file */
                $intFileStamp = ftp_mdtm($this->conFTPConId, $strFileName);
                if ($intFileStamp > -1) {
                    $intErrorReporting = error_reporting();
                    error_reporting(0);
                    $booRetVal = ftp_delete($this->conFTPConId, $strFileName);
                    error_reporting($intErrorReporting);
                } else {
                    $this->processClassMessage(translate('Cannot delete file because it does not exists (remote '
                            . 'FTP)!') . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            }
            if ($booRetVal === false) {
                $this->processClassMessage(translate('Cannot delete file because the permissions are incorrect '
                        . '(remote FTP)!') . '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        } elseif ($intMethod === 3) { /* Remote file (SFTP) */
            /* Check connection */
            $intReturn = $this->getSSHConnection($intConfigID);
            /* Save configuration file */
            if (($intReturn === 0) && ($this->sendSSHCommand('ls ' . $strFileName, $arrResult) === 0)) {
                if (isset($arrResult[0])) {
                    $booRetVal = ssh2_sftp_unlink($this->resSFTP, $strFileName);
                } else {
                    $this->processClassMessage(translate('Cannot delete file because it does not exists (remote '
                            . 'SSH/SFTP)!') . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            }
            if (($intReturn === 0) && ($booRetVal === false)) {
                $this->processClassMessage(translate('Cannot delete file because the permissions are incorrect '
                        . '(remote SSH/SFTP)!') . '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        }
        return $intReturn;
    }

    /**
     * Get configuration domain parameters
     * @param int $intConfigId Configuration ID
     * @param string $strConfigItem Configuration key
     * @param string $strValue Configuration value (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getConfigData(int $intConfigId, string $strConfigItem, string &$strValue): int
    {
        $intReturn = 1;
        $strSQL = 'SELECT `' . $strConfigItem . '` FROM `tbl_configtarget` WHERE `id` = ' . $intConfigId;
        $strValue = $this->myDBClass->getFieldData($strSQL);
        if ($strValue !== '') {
            $intReturn = 0;
        }
        return $intReturn;
    }

    /**
     * Check a directory for write access
     * @param string $strPath Physical path
     * @return int 0 = successful / 1 = error
     */
    public function isDirWriteable(string $strPath): int
    {
        /* Define variables */
        $intReturnFile = 1;
        $intReturnDir = 1;
        $intReturn = 1;
        /* Is input path a file? */
        if (file_exists($strPath) && is_file($strPath)) {
            $resFile = fopen($strPath, 'ab');
            if ($resFile) {
                $intReturnFile = 0;
            }
        } else {
            $intReturnFile = 0;
        }
        if (is_file($strPath)) {
            $strDirectory = dirname($strPath);
        } else {
            $strDirectory = $strPath;
        }
        $strFile = $strDirectory . '/' . uniqid(mt_rand(), true) . '.tmp';
        /* Check writing in directory directly */
        if (is_dir($strDirectory) && is_writable($strDirectory)) {
            $resFile = fopen($strFile, 'wb');
            if ($resFile) {
                $intReturnDir = 0;
                unlink($strFile);
            }
        } else {
            $intReturnDir = 0;
        }
        if (($intReturnDir === 0) && ($intReturnFile === 0)) {
            $intReturn = 0;
        }
        return $intReturn;
    }

    /**
     * Copy a remote file
     * @param string $strFileRemote Remote file name
     * @param int $intConfigID Configuration target id
     * @param string $strFileLocal Local file name
     * @param int $intDirection 0 = from remote to local / 1 = from local to remote
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function remoteFileCopy(string $strFileRemote, int $intConfigID, string $strFileLocal, int $intDirection = 0): int
    {
        /* Variable definitions */
        $intMethod = 3;
        $intReturn = 0;
        $strMethod = '';
        $arrTemp = array();
        /* Get method */
        if ($this->getConfigData($intConfigID, 'method', $strMethod) === 0) {
            $intMethod = (int)$strMethod;
        }
        if ($intMethod === 2) {
            /* Check connection */
            $intReturn = $this->getFTPConnection($intConfigID);
            if (($intReturn === 0) && ($intDirection === 0)) {
                $intErrorReporting = error_reporting();
                error_reporting(0);
                if (!ftp_get($this->conFTPConId, $strFileLocal, $strFileRemote, FTP_ASCII)) {
                    $this->processClassMessage(translate('Cannot get the remote file (it does not exist or is not '
                            . 'readable) - remote file: ') . $strFileRemote . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
                error_reporting($intErrorReporting);
            } elseif (($intReturn === 0) && ($intDirection === 1)) {
                $intErrorReporting = error_reporting();
                error_reporting(0);
                if (!ftp_put($this->conFTPConId, $strFileRemote, $strFileLocal, FTP_ASCII)) {
                    $this->processClassMessage(translate('Cannot write the remote file (remote file is not writeable)'
                            . '- remote file: ') . $strFileRemote . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
                error_reporting($intErrorReporting);
            }
            ftp_close($this->conFTPConId);
        } elseif ($intMethod === 3) { /* Remote file (SFTP) */
            $intReturn = $this->getSSHConnection($intConfigID);
            if (($intReturn === 0) && ($intDirection === 0)) {
                /* Copy file */
                $intErrorReporting = error_reporting();
                error_reporting(0);
                if (!ssh2_scp_recv($this->resSSHConId, $strFileRemote, $strFileLocal)) {
                    if ($this->sendSSHCommand('ls ' . $strFileRemote, $arrTemp) !== 0) {
                        $this->processClassMessage(translate('Cannot get the remote file (it does not exist or is not '
                                . 'readable) - remote file: ') . $strFileRemote . '::', $this->strErrorMessage);
                    } else {
                        $this->processClassMessage(translate('Remote file is not readable - remote file: ')
                            . $strFileRemote . '::', $this->strErrorMessage);
                    }
                    $intReturn = 1;
                }
                error_reporting($intErrorReporting);
            } elseif (($intReturn === 0) && ($intDirection === 1)) {
                if (file_exists($strFileLocal) && is_readable($strFileLocal)) {
                    $intErrorReporting = error_reporting();
                    error_reporting(0);
                    if (!ssh2_scp_send($this->resSSHConId, $strFileLocal, $strFileRemote, 0644)) {
                        $this->processClassMessage(translate('Cannot write a remote file (remote file is not writeable)'
                                . ' - remote file: ') . $strFileRemote . '::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                    error_reporting($intErrorReporting);
                } else {
                    $this->processClassMessage(translate('Cannot copy a local file to remote because the local file ' .
                            'does not exist or is not readable - local file: ') .
                        $strFileLocal . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            }
        }
        return $intReturn;
    }

    /**
     * Add files of a given directory to an array
     * @param string $strSourceDir Source directory
     * @param string $strIncPattern Include file pattern
     * @param string $strExcPattern Exclude file pattern
     * @param array|null $arrOutput Output array (by reference)
     * @param string|null $strErrorMessage Error messages (by reference)
     */
    public function storeDirToArray(string $strSourceDir, string $strIncPattern, string $strExcPattern, array &$arrOutput = null, string &$strErrorMessage = null): void
    {
        /* Define variables */
        $arrDir = array();
        while (substr($strSourceDir, -1) === '/' or substr($strSourceDir, -1) === "\\") {
            $strSourceDir = substr($strSourceDir, 0, -1);
        }
        $resHandle = opendir($strSourceDir);
        if ($resHandle === false) {
            if ($this->intDomainId !== 0) {
                $strErrorMessage .= translate('Could not open directory') . ': ' . $strSourceDir;
            }
        } else {
            $booBreak = true;
            while ($booBreak) {
                if (!$arrDir[] = readdir($resHandle)) {
                    $booBreak = false;
                }
            }
            closedir($resHandle);
            sort($arrDir);
            /** @var string $file */
            foreach ($arrDir as $file) {
                /** @noinspection StrlenInEmptyStringCheckContextInspection */
                if (!preg_match("/^\.{1,2}/", $file) && strlen($file)) {
                    if (is_dir($strSourceDir . '/' . $file)) {
                        $this->storeDirToArray(
                            $strSourceDir . '/' . $file,
                            $strIncPattern,
                            $strExcPattern,
                            $arrOutput,
                            $strErrorMessage
                        );
                    } else if (preg_match('/' . $strIncPattern . '/', $file) && (($strExcPattern === '') ||
                            !preg_match('/' . $strExcPattern . '/', $file))) {
                        if (0 === stripos(PHP_OS_FAMILY, 'Windows')) {
                            $strSourceDir = str_replace('/', "\\", $strSourceDir);
                            $arrOutput [] = $strSourceDir . "\\" . $file;
                        } else {
                            $arrOutput [] = $strSourceDir . '/' . $file;
                        }
                    }
                }
            }
        }
    }

    /**
     * Determines the dates of the last data table change and the last modification to the configuration files
     * @param string $strTableName Name of the data table
     * @param array|null $arrTimeData Array with time data of table and all config files
     * @param string|null $strCheckConfig Information string (text message)
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function lastModifiedFile(string $strTableName, array &$arrTimeData = null, string &$strCheckConfig = null): int
    {
        /* Variable definitions */
        /** @noinspection PhpUnusedLocalVariableInspection */
        $intEnableCommon = 0;
        $arrDataset = array();
        $strFileName = '';
        $strCheckConfig = '';
        $intReturn = 0;
        // Get configuration filename based on table name
        $arrConfigData = $this->getConfData();
        if (isset($arrConfigData[$strTableName])) {
            $strFileName = $arrConfigData[$strTableName]['filename'];
        } else {
            $intReturn = 1;
        }
        /* Get table times */
        $arrTimeData = array();
        $arrTimeData['table'] = 'unknown';
        $strConfigValue = '';
        /* Clear status cache */
        clearstatcache();
        if ($this->getDomainData('enable_common', $strConfigValue) === 0) {
            $intEnableCommon = (int)$strConfigValue;
            $strSQLAdd = '';
            if ($intEnableCommon === 1) {
                $strSQLAdd = 'OR `domainId`=0';
            }
            $strSQL = 'SELECT `updateTime` FROM `tbl_tablestatus` '
                . 'WHERE (`domainId`=' . $this->intDomainId . " $strSQLAdd) AND `tableName`='" . $strTableName . "' "
                . 'ORDER BY `updateTime` DESC LIMIT 1';
            $booReturn = $this->myDBClass->hasSingleDataset($strSQL, $arrDataset);
            if ($booReturn && isset($arrDataset['updateTime'])) {
                $arrTimeData['table'] = $arrDataset['updateTime'];
            } else {
                /** @noinspection SqlResolve */
                $strSQL = 'SELECT `last_modified` FROM `' . $strTableName . '` '
                    . 'WHERE `config_id`=' . $this->intDomainId . ' ORDER BY `last_modified` DESC LIMIT 1';
                $booReturn = $this->myDBClass->hasSingleDataset($strSQL, $arrDataset);
                if (($booReturn === true) && isset($arrDataset['last_modified'])) {
                    $arrTimeData['table'] = $arrDataset['last_modified'];
                }
            }
        }
        /* Get config sets */
        $arrConfigId = array();
        $strTarget = '';
        $strBaseDir = '';
        $intFileStampTemp = 0;
        $intRetVal2 = $this->getConfigSets($arrConfigId);
        if ($intRetVal2 === 0) {
            foreach ($arrConfigId as $intConfigId) {
                /* Get configuration file data */
                $this->getConfigData($intConfigId, 'target', $strTarget);
                $this->getConfigData($intConfigId, 'basedir', $strBaseDir);
                /* Get time data */
                $intReturn = $this->getFileDate(
                    $intConfigId,
                    $strFileName,
                    $strBaseDir,
                    $intFileStampTemp,
                    $arrTimeData[$strTarget]
                );
                if ($intFileStampTemp !== 0 && strtotime($arrTimeData['table']) > $intFileStampTemp) {
                    $strCheckConfig = translate('Warning: configuration file is out of date!');
                }
                if ((string)$arrTimeData[$strTarget] === 'unknown') {
                    $strCheckConfig = translate('Warning: configuration file is out of date!');
                }
            }
        } else {
            $strCheckConfig = translate('Warning: no configuration target defined!');
        }
        return $intReturn;
    }

    /**
     * Determines the configuration data for each database table
     * @return array filename (configuration file name)
     *               order_field (database order field)
     */
    public function getConfData(): array
    {
        $arrConfData['tbl_timeperiod'] = array('filename' => 'timeperiods.cfg',
            'order_field' => 'timeperiod_name');
        $arrConfData['tbl_command'] = array('filename' => 'commands.cfg',
            'order_field' => 'command_name');
        $arrConfData['tbl_contact'] = array('filename' => 'contacts.cfg',
            'order_field' => 'contact_name');
        $arrConfData['tbl_contacttemplate'] = array('filename' => 'contacttemplates.cfg',
            'order_field' => 'template_name');
        $arrConfData['tbl_contactgroup'] = array('filename' => 'contactgroups.cfg',
            'order_field' => 'contactgroup_name');
        $arrConfData['tbl_hosttemplate'] = array('filename' => 'hosttemplates.cfg',
            'order_field' => 'template_name');
        $arrConfData['tbl_servicetemplate'] = array('filename' => 'servicetemplates.cfg',
            'order_field' => 'template_name');
        $arrConfData['tbl_hostgroup'] = array('filename' => 'hostgroups.cfg',
            'order_field' => 'hostgroup_name');
        $arrConfData['tbl_servicegroup'] = array('filename' => 'servicegroups.cfg',
            'order_field' => 'servicegroup_name');
        $arrConfData['tbl_hostdependency'] = array('filename' => 'hostdependencies.cfg',
            'order_field' => 'dependent_host_name');
        $arrConfData['tbl_servicedependency'] = array('filename' => 'servicedependencies.cfg',
            'order_field' => 'config_name');
        $arrConfData['tbl_hostescalation'] = array('filename' => 'hostescalations.cfg',
            'order_field' => 'host_name`,`hostgroup_name');
        $arrConfData['tbl_serviceescalation'] = array('filename' => 'serviceescalations.cfg',
            'order_field' => 'config_name');
        $arrConfData['tbl_hostextinfo'] = array('filename' => 'hostextinfo.cfg',
            'order_field' => 'host_name');
        $arrConfData['tbl_serviceextinfo'] = array('filename' => 'serviceextinfo.cfg',
            'order_field' => 'host_name');
        return $arrConfData;
    }

    /**
     * Get domain configuration parameters.
     * @param string $strConfigItem Configuration key
     * @param string|null $strValue Configuration value (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getDomainData(string $strConfigItem, string &$strValue = null): int
    {
        /* Variable definition */
        $intReturn = 0;
        /* Request domain data from database */
        $strSQL = 'SELECT `' . $strConfigItem . '` FROM `tbl_datadomain` WHERE `id` = ' . $this->intDomainId;
        $strValue = $this->myDBClass->getFieldData($strSQL);
        if ($strValue === '') {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * Get configuration target IDs
     * @param array|null $arrConfigId Configuration target IDs (by reference)
     * @return int 0 = successful / 1 = error
     */
    public function getConfigSets(array &$arrConfigId = null): int
    {
        /* Variable definition */
        $arrData = array();
        $arrConfigId = array();
        $intDataCount = 0;
        $intReturn = 1;
        /* Request target ID */
        $strSQL = 'SELECT `targets` FROM `tbl_datadomain` WHERE `id`=' . $this->intDomainId;
        $booReturn = $this->myDBClass->hasDataArray($strSQL, $arrData, $intDataCount);
        if ($booReturn && ($intDataCount !== 0)) {
            foreach ($arrData as $elem) {
                $arrConfigId[] = (int)$elem['targets'];
            }
            $intReturn = 0;
        }
        return $intReturn;
    }

    /**
     * Writes a configuration file including all datasets of a configuration table or returns the output as a text
     * file for download. (Public master function)
     * @param string $strTableName Table name
     * @param int $intMode 0 = Write file to filesystem
     *                     1 = Return Textfile for download test
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function createConfig(string $strTableName, int $intMode = 0): int
    {
        /* Define variables */
        $intReturn = 0;
        /* Do not create configs in common domain */
        if ($this->intDomainId === 0) {
            $this->processClassMessage(translate('It is not possible to write config files directly from the common '
                    . 'domain!') . '::', $this->strErrorMessage);
            $intReturn = 1;
        }
        if ($intReturn === 0) {
            /* Get configuration targets */
            $this->getConfigSets($arrConfigID);
            if (is_array($arrConfigID)) {
                foreach ($arrConfigID as $intConfigID) {
                    $intReturn = $this->writeConfTemplate($intConfigID, $strTableName, $intMode);
                }
            } else {
                $this->processClassMessage(translate('Warning: no configuration target defined!') .
                    '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        }
        return $intReturn;
    }

    /**
     * Writes a configuration file including all datasets of a configuration table or returns the output as a text
     * file for download. (Private worker function)
     * @param int $intConfigID Configuration target ID
     * @param string $strTableName Table name
     * @param int $intMode 0 = Write file to filesystem
     *                     1 = Return Textfile for download test
     * @param array $arrTableData Dataset array for host and services only
     * @param int $intID Key for dataset array
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    private function writeConfTemplate(int $intConfigID, string $strTableName, int $intMode, array $arrTableData = array(), int $intID = 0): int
    {
        /* Variable definitions */
        $strSQL = '';
        $strOrderField = '';
        $strFileString = '';
        $arrTplOptions = array('use_preg' => false);
        $strDomainWhere = ' (`config_id`=' . $this->intDomainId . ') ';
        $intType = 0;
        $intReturn = 0;
        $setUTF8Decode = 0;
        $intNagiosVersion = 0;
        $setEnableCommon = 0;
        $strConfigValue = '';
        /* Read some settings and information */
        if ($this->getDomainData('utf8_decode', $strConfigValue) === 0) {
            $setUTF8Decode = (int)$strConfigValue;
        }
        if ($this->getConfigData($intConfigID, 'version', $strConfigValue) === 0) {
            $intNagiosVersion = (int)$strConfigValue;
        }
        if ($this->getDomainData('enable_common', $strConfigValue) === 0) {
            $setEnableCommon = (int)$strConfigValue;
        }
        $arrConfigData = $this->getConfData();
        if (isset($arrConfigData[$strTableName])) {
            $strFileString = str_replace('.cfg', '', $arrConfigData[$strTableName]['filename']);
            $strOrderField = $arrConfigData[$strTableName]['order_field'];
        }
        /* Variable rewritting */
        if ($setEnableCommon !== 0) {
            $strDomainWhere = str_replace(')', ' OR `config_id`=0)', $strDomainWhere);
        }
        /* Special processing for table host and service */
        $setTemplate = $strFileString . '.tpl.dat';
        if (($strTableName === 'tbl_host') || ($strTableName === 'tbl_service')) {
            // Define variable names based on table name
            switch ($strTableName) {
                case 'tbl_host':
                    $strFileString = $arrTableData[$intID]['host_name'];
                    $intDomainId = $arrTableData[$intID]['config_id'];
                    $setTemplate = 'hosts.tpl.dat';
                    $intType = 1;
                    /** @noinspection SqlResolve */
                    $strSQL = 'SELECT * FROM `' . $strTableName . "` WHERE `host_name`='$strFileString' "
                        . "AND `active`='1' AND `config_id`=$intDomainId";
                    break;
                case 'tbl_service':
                    $strFileString = $arrTableData[$intID]['config_name'];
                    $intDomainId = $arrTableData[$intID]['config_id'];
                    $setTemplate = 'services.tpl.dat';
                    $intType = 2;
                    /** @noinspection SqlResolve */
                    $strSQL = 'SELECT * FROM `' . $strTableName . "` WHERE `config_name`='$strFileString' "
                        . "AND `active`='1' AND `config_id`=$intDomainId ORDER BY `service_description`";
                    break;
            }
        } else {
            /** @noinspection SqlResolve */
            $strSQL = 'SELECT * FROM `' . $strTableName . "` WHERE $strDomainWhere AND `active`='1' " .
                'ORDER BY `' . $strOrderField . '`';
        }
        $strFile = $strFileString . '.cfg';
        /* Load configuration template file */
        $tplConf = new HTML_Template_IT($this->arrSettings['path']['base_path'] . '/templates/files/');
        $tplConf->loadTemplatefile($setTemplate);
        $tplConf->setOptions($arrTplOptions);
        $tplConf->setVariable('CREATE_DATE', date('Y-m-d H:i:s'));
        $tplConf->setVariable('NAGIOS_QL_VERSION', $this->arrSettings['db']['version']);
        $tplConf->setVariable('VERSION', $this->getVersionString($intConfigID));
        /* Write data from configuration table */
        $booReturn = $this->myDBClass->hasDataArray($strSQL, $arrData, $intDataCount);
        if ($booReturn && ($intDataCount !== 0) && ($strFileString !== '')) {
            /* Process every data set */
            for ($i = 0; $i < $intDataCount; $i++) {
                $intDataId = 0;
                foreach ($arrData[$i] as $key => $value) {
                    if ((string)$key === 'id') {
                        $intDataId = $value;
                    }
                    if ((string)$key === 'config_name') {
                        $key = '#NAGIOSQL_CONFIG_NAME';
                    }
                    /* UTF8 decoded vaules */
                    if ($setUTF8Decode === 1) {
                        $value = mb_convert_encoding($value, 'UTF-8');
                    }
                    /* Pass special fields (NagiosQL data fields not used by Nagios itselves) */
                    if ($value === null) {
                        $value = '';
                    }
                    if ($this->skipEntries($strTableName, $intNagiosVersion, $key, $value) === 1) {
                        continue;
                    }
                    /* Get relation data */
                    $intSkip = $this->getRelationData($strTableName, $tplConf, $arrData[$i], $key, $value);
                    /* Rename field names */
                    $this->renameFields($strTableName, $intConfigID, $intDataId, $key, $value, $intSkip);
                    /* Inset data field */
                    if ($intSkip !== 1) {
                        /* Insert fill spaces */
                        $strFillLen = (30 - strlen($key));
                        $strSpace = ' ';
                        $strSpace .= str_repeat(' ', $strFillLen);
                        /* Write key and value to template */
                        $tplConf->setVariable('ITEM_TITLE', $key . $strSpace);
                        /* Short values */
                        if (($intNagiosVersion !== 3) || (strlen($value) < 800)) {
                            $tplConf->setVariable('ITEM_VALUE', $value);
                            $tplConf->parse('configline');
                        } else { /* Long values */
                            $arrValueTemp = explode(',', $value);
                            $strValueNew = '';
                            $intArrCount = count($arrValueTemp);
                            $intCounter = 0;
                            $strSpace = ' ';
                            $strSpace .= str_repeat(' ', 30);
                            foreach ($arrValueTemp as $elem) {
                                if (strlen($strValueNew) < 800) {
                                    $strValueNew .= $elem . ',';
                                } else {
                                    if (substr($strValueNew, -1) === ',') {
                                        $strValueNew = substr($strValueNew, 0, -1);
                                    }
                                    if ($intCounter < $intArrCount) {
                                        $strValueNew .= ",\\";
                                    }
                                    $tplConf->setVariable('ITEM_VALUE', $strValueNew);
                                    $tplConf->parse('configline');
                                    $tplConf->setVariable('ITEM_TITLE', $strSpace);
                                    $strValueNew = $elem . ',';
                                }
                                $intCounter++;
                            }
                            if ($strValueNew !== '') {
                                if (substr($strValueNew, -1) === ',') {
                                    $strValueNew = substr($strValueNew, 0, -1);
                                }
                                $tplConf->setVariable('ITEM_VALUE', $strValueNew);
                                $tplConf->parse('configline');
                            }
                        }
                    }
                }
                /* Special processing for time periods */
                if ($strTableName === 'tbl_timeperiod') {
                    $arrDataTime = array();
                    $strSQLTime = 'SELECT `definition`, `range` '
                        . 'FROM `tbl_timedefinition` WHERE `tipId` = ' . $arrData[$i]['id'];
                    $booReturn = $this->myDBClass->hasDataArray($strSQLTime, $arrDataTime, $intDataCountTime);
                    if ($booReturn && $intDataCountTime !== 0) {
                        foreach ($arrDataTime as $data) {
                            /* Skip other values than weekdays in nagios version below 3 */
                            if ($intNagiosVersion < 3) {
                                $arrWeekdays = array('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
                                    'sunday');
                                if (!in_array($data['definition'], $arrWeekdays, true)) {
                                    continue;
                                }
                            }
                            /* Insert fill spaces */
                            $strFillLen = (30 - strlen($data['definition']));
                            $strSpace = ' ';
                            $strSpace .= str_repeat(' ', $strFillLen);
                            /* Write key and value */
                            $tplConf->setVariable('ITEM_TITLE', $data['definition'] . $strSpace);
                            $tplConf->setVariable('ITEM_VALUE', $data['range']);
                            /** @noinspection DisconnectedForeachInstructionInspection */
                            $tplConf->parse('configline');
                        }
                    }
                }
                /* Write configuration set */
                $tplConf->parse('configset');
            }
        } elseif ($booReturn && ($intDataCount === 0) && ($strFileString !== '')) {
            $this->processClassMessage(translate('Error while selecting data from database:')
                . '::', $this->strErrorMessage);
            $this->processClassMessage($this->myDBClass->strErrorMessage, $this->strErrorMessage);
            $intReturn = 1;
        } else {
            $this->myDataClass->writeLog(translate('Writing of the configuration failed - no dataset '
                . 'or not activated dataset found'));
            $this->processClassMessage(translate('Writing of the configuration failed - no dataset '
                    . 'or not activated dataset found') . '::', $this->strErrorMessage);
            $intReturn = 1;
        }
        if ($intMode === 0) {
            $strCfgFile = '';
            $intReturn = $this->getConfigFile($strFile, $intConfigID, $intType, $resCfgFile, $strCfgFile);
            if ($intReturn === 0) {
                $tplConf->parse();
                $strContent = $tplConf->get();
                $intReturn = $this->writeConfigFile(
                    $strContent,
                    $strFile,
                    $intType,
                    $intConfigID,
                    $resCfgFile,
                    $strCfgFile
                );
            }
        } elseif ($intMode === 1) {
            $tplConf->show();
        }
        return $intReturn;
    }

    /* PRIVATE functions */

    /**
     * Get Nagios version string
     * @param int $intConfigID Configuration target ID
     * @return string Version string
     */
    private function getVersionString(int $intConfigID): string
    {
        $strConfigValue = '';
        $intVersion = 0;
        $arrVersion = array(
            'Nagios 2.x config file',
            'Nagios 2.9 config file',
            'Nagios 3.x config file',
            'Nagios 4.x config file'
        );
        if ($this->getConfigData($intConfigID, 'version', $strConfigValue) === 0) {
            $intVersion = (int)$strConfigValue;
        }
        if (($intVersion >= 1) && ($intVersion <= count($arrVersion))) {
            $strVersion = $arrVersion[$intVersion - 1];
        } else {
            $strVersion = '';
        }
        return $strVersion;
    }

    /**
     * Skip database values based on Nagios version
     * @param string $strTableName Table name
     * @param int $intVersionValue Nagios version value
     * @param string $key Data key
     * @param string $value Data value
     * @return int 1 = Skip entry / 0 = Pass entry
     */
    private function skipEntries(string $strTableName, int $intVersionValue, string $key, string &$value): int
    {
        /* Define variables */
        $arrOption = array();
        $intReturn = 0;
        /* Skip common fields */
        $strSpecial = 'id,active,last_modified,access_rights,access_group,config_id,template,nodelete,command_type,';
        $strSpecial .= 'import_hash';

        /* Skip fields of special tables */
        if ($strTableName === 'tbl_hosttemplate') {
            $strSpecial .= ',parents_tploptions,hostgroups_tploptions,contacts_tploptions';
            $strSpecial .= ',contact_groups_tploptions,use_template_tploptions';
        }
        if ($strTableName === 'tbl_servicetemplate') {
            $strSpecial .= ',host_name_tploptions,hostgroup_name_tploptions,parents_tploptions,contacts_tploptions';
            $strSpecial .= ',servicegroups_tploptions,contact_groups_tploptions,use_template_tploptions';
        }
        if ($strTableName === 'tbl_contact') {
            $strSpecial .= ',use_template_tploptions,contactgroups_tploptions';
            $strSpecial .= ',host_notification_commands_tploptions,service_notification_commands_tploptions';
        }
        if ($strTableName === 'tbl_contacttemplate') {
            $strSpecial .= ',use_template_tploptions,contactgroups_tploptions';
            $strSpecial .= ',host_notification_commands_tploptions,service_notification_commands_tploptions';
        }
        if ($strTableName === 'tbl_host') {
            $strSpecial .= ',parents_tploptions,hostgroups_tploptions,contacts_tploptions';
            $strSpecial .= ',contact_groups_tploptions,use_template_tploptions';
        }
        if ($strTableName === 'tbl_service') {
            $strSpecial .= ',host_name_tploptions,hostgroup_name_tploptions,parents_tploptions';
            $strSpecial .= ',servicegroups_tploptions,contacts_tploptions,contact_groups_tploptions';
            $strSpecial .= ',use_template_tploptions';
        }
        if ($strTableName === 'tbl_command') {
            $strSpecial .= ',arg1_info,arg2_info,arg3_info,arg4_info,arg5_info,arg6_info,arg7_info,arg8_info';
        }

        /* Pass fields based on nagios version lower than 3.x */
        if ($intVersionValue < 3) {
            if ($strTableName === 'tbl_timeperiod') {
                $strSpecial .= ',use_template,exclude,name';
            }
            if (($strTableName === 'tbl_contact') || ($strTableName === 'tbl_contacttemplate')) {
                $strSpecial .= ',host_notifications_enabled,service_notifications_enabled,can_submit_commands';
                $strSpecial .= ',retain_status_information,retain_nonstatus_information';
                $arrOption['host_notification_options'] = ',s';
                $arrOption['service_notification_options'] = ',s';
            }
            if ($strTableName === 'tbl_contactgroup') {
                $strSpecial .= ',contactgroup_members';
            }
            if ($strTableName === 'tbl_hostgroup') {
                $strSpecial .= ',hostgroup_members,notes,notes_url,action_url';
            }
            if ($strTableName === 'tbl_servicegroup') {
                $strSpecial .= ',servicegroup_members,notes,notes_url,action_url';
            }
            if ($strTableName === 'tbl_hostdependency') {
                $strSpecial .= ',dependent_hostgroup_name,hostgroup_name,dependency_period';
            }
            if ($strTableName === 'tbl_hostescalation') {
                $strSpecial .= ',contacts';
            }
            if ($strTableName === 'tbl_servicedependency') {
                $strSpecial .= ',dependent_hostgroup_name,hostgroup_name,dependency_period,dependent_servicegroup_name';
                $strSpecial .= ',servicegroup_name';
            }
            if ($strTableName === 'tbl_serviceescalation') {
                $strSpecial .= ',hostgroup_name,contacts,servicegroup_name';
            }
            if (($strTableName === 'tbl_host') || ($strTableName === 'tbl_hosttemplate')) {
                $strSpecial .= ',initial_state,flap_detection_options,contacts,notes,notes_url,action_url';
                $strSpecial .= ',icon_image,icon_image_alt,vrml_image,statusmap_image,2d_coords,3d_coords';
                $arrOption['notification_options'] = ',s';
            }
            /* Services */
            if (($strTableName === 'tbl_service') || ($strTableName === 'tbl_servicetemplate')) {
                $strSpecial .= ',initial_state,flap_detection_options,contacts,notes,notes_url,action_url';
                $strSpecial .= ',icon_image,icon_image_alt';
                $arrOption['notification_options'] = ',s';
            }
        }
        /* Pass fields based on nagios version higher than 2.x */
        if ($intVersionValue > 2) {
            if ($strTableName === 'tbl_servicetemplate') {
                $strSpecial .= ',parallelize_check ';
            }
            if ($strTableName === 'tbl_service') {
                $strSpecial .= ',parallelize_check';
            }
        }
        /* Pass fields based on nagios version lower than 4.x */
        if ($intVersionValue < 4) {
            if (($strTableName === 'tbl_contact') || ($strTableName === 'tbl_contacttemplate')) {
                $strSpecial .= ',minimum_importance';
            }
            if (($strTableName === 'tbl_host') || ($strTableName === 'tbl_hosttemplate')) {
                $strSpecial .= ',importance';
            }
            if (($strTableName === 'tbl_service') || ($strTableName === 'tbl_servicetemplate')) {
                $strSpecial .= ',importance,parents';
            }
        }
        if ($intVersionValue === 1) {
            $strSpecial .= '';
        }
        /* Reduce option values */
        if (array_key_exists($key, $arrOption) && (count($arrOption) !== 0)) {
            $value = str_replace(array($arrOption[$key], str_replace(',', '', $arrOption[$key])), '', $value);
            if ($value === '') {
                $intReturn = 1;
            }
        }
        if ($intReturn === 0) {
            /* Skip entries */
            $arrSpecial = explode(',', $strSpecial);
            if (((string)$value === '') || in_array($key, $arrSpecial, true)) {
                $intReturn = 1;
            }
        }
        if ($intReturn === 0) {
            /* Do not write config data (based on 'skip' option) */
            $strNoTwo = 'active_checks_enabled,passive_checks_enabled,obsess_over_host,check_freshness,';
            $strNoTwo .= 'event_handler_enabled,flap_detection_enabled,process_perf_data,retain_status_information,';
            $strNoTwo .= 'retain_nonstatus_information,notifications_enabled,parallelize_check,is_volatile,';
            $strNoTwo .= 'host_notifications_enabled,service_notifications_enabled,can_submit_commands,';
            $strNoTwo .= 'obsess_over_service';
            foreach (explode(',', $strNoTwo) as $elem) {
                if (($key === $elem) && ((string)$value === '2')) {
                    $intReturn = 1;
                }
                if (($intVersionValue < 3) && ($key === $elem) && ((string)$value === '3')) {
                    $intReturn = 1;
                }
            }
        }
        return $intReturn;
    }

    /**
     * Get related data
     * @param string $strTableName Table name
     * @param HTML_Template_IT $resTemplate Template ressource
     * @param array $arrData Dataset array
     * @param string $strDataKey Data key
     * @param string|null $strDataValue Data value
     * @return int 0 = use data / 1 = skip data
     * Status message is stored in message class variables
     */
    private function getRelationData(string $strTableName, HTML_Template_IT $resTemplate, array $arrData, string $strDataKey, string &$strDataValue = null): int
    {
        /* Define variables */
        $intReturn = 0;
        $intSkipProc = 0;
        $arrRelations = array();
        /* Pass function for tbl_command */
        if ($strTableName === 'tbl_command') {
            $intSkipProc = 1;
        }
        /* Get relation info and store the value in a class variable (speedup export) */
        if (($intSkipProc === 0) && ($this->strRelTable !== $strTableName)) {
            $intReturn = $this->myDataClass->tableRelations($strTableName, $arrRelations);
            $this->strRelTable = $strTableName;
            $this->arrRelData = $arrRelations;
        } elseif ($intSkipProc === 0) {
            $arrRelations = $this->arrRelData;
            $intReturn = 0;
        }
        if (($intSkipProc === 0) && (!is_array($arrRelations))) {
            $intSkipProc = 1;
            $intReturn = 1;
        }
        if ($intSkipProc === 0) {
            /* Common domain is enabled? */
            $this->getDomainData('enable_common', $strCommonEnable);
            $intCommonEnable = (int)$strCommonEnable;
            if ($intCommonEnable === 1) {
                $strDomainWhere1 = ' (`config_id`=' . $this->intDomainId . ' OR `config_id`=0) ';
            } else {
                $strDomainWhere1 = ' `config_id`=' . $this->intDomainId . ' ';
            }
            /* Process relations */
            foreach ($arrRelations as $elem) {
                if ($elem['fieldName'] === $strDataKey) {
                    /* Process normal 1:n relations (1 = only data / 2 = including a * value) */
                    if (((int)$elem['type'] === 2) && (((int)$strDataValue === 1) || ((int)$strDataValue === 2))) {
                        $intReturn = $this->processRelation1($arrData, $strDataValue, $elem, $strDomainWhere1);
                        /* Process normal 1:1 relations */
                    } elseif ((int)$elem['type'] === 1) {
                        $intReturn = $this->processRelation2($arrData, $strDataValue, $elem, $strDomainWhere1);
                        /* Process normal 1:n relations with special table and idSort (template tables) */
                    } elseif (((int)$elem['type'] === 3) && ((int)$strDataValue === 1)) {
                        $intReturn = $this->processRelation3($arrData, $strDataValue, $elem, $strDomainWhere1);
                        /* Process special 1:n:str relations with string values (servicedependencies) */
                    } elseif (((int)$elem['type'] === 6) && (((int)$strDataValue === 1) || ((int)$strDataValue === 2))) {
                        $intReturn = $this->processRelation4($arrData, $strDataValue, $elem, $strDomainWhere1);
                        /* Process special relations for free variables */
                    } elseif (((int)$elem['type'] === 4) && ((int)$strDataValue === 1) && ($this->intNagVersion >= 3)) {
                        $intReturn = $this->processRelation5($resTemplate, $arrData, $elem);
                        /* Process special relations for service groups */
                    } elseif (((int)$elem['type'] === 5) && ((int)$strDataValue === 1)) {
                        $intReturn = $this->processRelation6($arrData, $strDataValue, $elem, $strDomainWhere1);
                        /* Process special relations for service parents */
                    } elseif (((int)$elem['type'] === 7) && ((int)$strDataValue === 1)) {
                        $intReturn = $this->processRelation7($arrData, $strDataValue, $elem);
                        /* Process "*" */
                    } elseif ((int)$strDataValue === 2) {
                        $strDataValue = '*';
                    } else {
                        $intReturn = 1;
                    }
                }
            }
        }
        return $intReturn;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @param string $strDomainWhere1 SQL WHERE add-in
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation1(array $arrData, string &$strDataValue, array $elem, string $strDomainWhere1): int
    {
        /* Define variables */
        $arrDataRel = array();
        $intDataCountRel = 0;
        $intReturn = 0;
        /* Get relation data */
        $strSQLRel = 'SELECT `' . $elem['tableName1'] . '`.`' . $elem['target1'] . '`, `' . $elem['linkTable'] .
            '`.`exclude` FROM `' . $elem['linkTable'] . '` LEFT JOIN `' . $elem['tableName1'] .
            '` ON `' . $elem['linkTable'] . '`.`idSlave` = `' . $elem['tableName1'] . '`.`id`' .
            'WHERE `idMaster`=' . $arrData['id'] . " AND `active`='1' AND $strDomainWhere1" .
            'ORDER BY `' . $elem['tableName1'] . '`.`' . $elem['target1'] . '`';
        $booReturn = $this->myDBClass->hasDataArray($strSQLRel, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            if ((int)$strDataValue === 2) {
                $strDataValue = '*,';
            } else {
                $strDataValue = '';
            }
            foreach ($arrDataRel as $data) {
                if ((int)$data['exclude'] === 0) {
                    $strDataValue .= $data[$elem['target1']] . ',';
                } elseif ($this->intNagVersion >= 3) {
                    $strDataValue .= '!' . $data[$elem['target1']] . ',';
                }
            }
            $strDataValue = substr($strDataValue, 0, -1);
            if ($strDataValue === '') {
                $intReturn = 1;
            }
        } else if ((int)$strDataValue === 2) {
            $strDataValue = '*';
        } else {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @param string $strDomainWhere1 SQL WHERE add-in
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation2(array $arrData, string &$strDataValue, array $elem, string $strDomainWhere1): int
    {
        /* Define variables */
        $arrDataRel = array();
        $arrField = array();
        $intDataCountRel = 0;
        $intReturn = 0;
        $strCommand = '';
        /* Get relation data */
        if (((string)$elem['tableName1'] === 'tbl_command') &&
            (substr_count($arrData[$elem['fieldName']], '!') !== 0)) {
            $arrField = explode('!', $arrData[$elem['fieldName']]);
            $strCommand = strstr($arrData[$elem['fieldName']], '!');
            $strSQLRel = 'SELECT `' . $elem['target1'] . '` FROM `' . $elem['tableName1'] . '`' .
                'WHERE `id`=' . $arrField[0] . "  AND `active`='1' AND $strDomainWhere1";
        } else {
            $strSQLRel = 'SELECT `' . $elem['target1'] . '` FROM `' . $elem['tableName1'] . '`' .
                'WHERE `id`=' . $arrData[$elem['fieldName']] . "  AND `active`='1' AND $strDomainWhere1";
        }
        $booReturn = $this->myDBClass->hasDataArray($strSQLRel, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            if (((string)$elem['tableName1'] === 'tbl_command') && (substr_count($strDataValue, '!') !== 0)) {
                $strDataValue = $arrDataRel[0][$elem['target1']] . $strCommand;
            } else {
                $strDataValue = $arrDataRel[0][$elem['target1']];
            }
        } else if (((string)$elem['tableName1'] === 'tbl_command') && (substr_count($strDataValue, '!') !== 0) &&
            ((int)$arrField[0] === -1)) {
            $strDataValue = 'null';
        } else {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @param string $strDomainWhere1 SQL WHERE add-in
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation3(array $arrData, string &$strDataValue, array $elem, string $strDomainWhere1): int
    {
        /* Define variables */
        $arrDataRel = array();
        $intDataCountRel = 0;
        $intReturn = 0;
        /* Get relation data */
        /** @noinspection SqlResolve */
        $strSQLRel = 'SELECT * FROM `' . $elem['linkTable'] . '` WHERE `idMaster`=' . $arrData['id'] . ' ORDER BY idSort';
        $booReturn = $this->myDBClass->hasDataArray($strSQLRel, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            $strDataValue = '';
            foreach ($arrDataRel as $data) {
                if ((int)$data['idTable'] === 1) {
                    $strSQLName = 'SELECT `' . $elem['target1'] . '` FROM `' . $elem['tableName1'] . '`' .
                        "WHERE `active`='1' AND $strDomainWhere1 AND `id`=" . $data['idSlave'];
                } else {
                    $strSQLName = 'SELECT `' . $elem['target2'] . '` FROM `' . $elem['tableName2'] . '`' .
                        "WHERE `active`='1' AND $strDomainWhere1 AND `id`=" . $data['idSlave'];
                }
                $strDataValue .= $this->myDBClass->getFieldData($strSQLName) . ',';
            }
            $strDataValue = substr($strDataValue, 0, -1);
        } else {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @param string $strDomainWhere1 SQL WHERE add-in
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation4(array $arrData, string &$strDataValue, array $elem, string $strDomainWhere1): int
    {
        /* Define variables */
        $arrDataRel = array();
        $intDataCountRel = 0;
        $intReturn = 0;
        /* Get relation data */
        $strSQLRel = 'SELECT `' . $elem['linkTable'] . '`.`strSlave`, `' . $elem['linkTable'] . '`.`exclude` ' .
            'FROM `' . $elem['linkTable'] . '` ' .
            'LEFT JOIN `tbl_service` ON `' . $elem['linkTable'] . '`.`idSlave`=`tbl_service`.`id` ' .
            'WHERE `' . $elem['linkTable'] . '`.`idMaster`=' . $arrData['id'] . " AND `active`='1' AND " .
            $strDomainWhere1 . ' ' .
            'ORDER BY `' . $elem['linkTable'] . '`.`strSlave`';
        $booReturn = $this->myDBClass->hasDataArray($strSQLRel, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            if ((int)$strDataValue === 2) {
                $strDataValue = '*,';
            } else {
                $strDataValue = '';
            }
            foreach ($arrDataRel as $data) {
                if ((int)$data['exclude'] === 0) {
                    $strDataValue .= $data['strSlave'] . ',';
                } elseif ($this->intNagVersion >= 3) {
                    $strDataValue .= '!' . $data['strSlave'] . ',';
                }
            }
            $strDataValue = substr($strDataValue, 0, -1);
            if ($strDataValue === '') {
                $intReturn = 1;
            }
        } else if ((int)$strDataValue === 2) {
            $strDataValue = '*';
        } else {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * @param HTML_Template_IT $resTemplate Template object
     * @param array $arrData Dataset array
     * @param array $elem Relation data array
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation5(HTML_Template_IT $resTemplate, array $arrData, array $elem): int
    {
        /* Define variables */
        $arrDataRel = array();
        $intDataCountRel = 0;
        /** @noinspection SqlResolve */
        $strSQLRel = 'SELECT * FROM `tbl_variabledefinition` LEFT JOIN `' . $elem['linkTable'] . '` ' .
            'ON `id`=`idSlave` WHERE `idMaster`=' . $arrData['id'] . ' ORDER BY `name`';
        $booReturn = $this->myDBClass->hasDataArray($strSQLRel, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            foreach ($arrDataRel as $vardata) {
                /* Insert fill spaces */
                $strFillLen = (30 - strlen($vardata['name']));
                $strSpace = ' ';
                $strSpace .= str_repeat(' ', $strFillLen);
                $resTemplate->setVariable('ITEM_TITLE', $vardata['name'] . $strSpace);
                $resTemplate->setVariable('ITEM_VALUE', html_entity_decode($vardata['value'], ENT_QUOTES | ENT_XML1, 'UTF-8'));
                /** @noinspection DisconnectedForeachInstructionInspection */
                $resTemplate->parse('configline');
            }
        }
        return 1;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @param string $strDomainWhere1 SQL WHERE add-in
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation6(array $arrData, string &$strDataValue, array $elem, string $strDomainWhere1): int
    {
        /* Define variables */
        $arrDataRel = array();
        $arrHG1 = array();
        $arrHG2 = array();
        $intDataCountRel = 0;
        $intHG1 = 0;
        $intHG2 = 0;
        $intReturn = 0;
        /* Get relation data */
        /** @noinspection SqlResolve */
        $strSQLMaster = 'SELECT * FROM `' . $elem['linkTable'] . '` WHERE `idMaster`=' . $arrData['id'];
        $booReturn = $this->myDBClass->hasDataArray($strSQLMaster, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            $strDataValue = '';
            foreach ($arrDataRel as $data) {
                /* Get excluded hosts */
                $arrExclude = array();
                $strSQLEx = 'SELECT `idSlave` FROM `tbl_lnkServiceToHost` WHERE `exclude`=1 AND `idMaster`=' .
                    $data['idSlaveS'];
                $booReturn = $this->myDBClass->hasDataArray($strSQLEx, $arrEx, $intEx);
                if ($booReturn && ($intEx !== 0)) {
                    foreach ($arrEx as $elemEx) {
                        $arrExclude[] = $elemEx['idSlave'];
                    }
                }
                if ((int)$data['idSlaveHG'] !== 0) {
                    /* Get Sevices */
                    $strSQLSrv = 'SELECT `' . $elem['target2'] . '` FROM `' . $elem['tableName2'] .
                        '` WHERE `id`=' . $data['idSlaveS'];
                    $strService = $this->myDBClass->getFieldData($strSQLSrv);
                    $strSQLHG1 = 'SELECT `host_name`, `idSlave` FROM `tbl_host` ' .
                        'LEFT JOIN `tbl_lnkHostgroupToHost` ON `id`=`idSlave` ' .
                        'WHERE `idMaster`=' . $data['idSlaveHG'] . "  AND `active`='1' AND `exclude`=0 " .
                        "AND $strDomainWhere1";
                    $booReturn = $this->myDBClass->hasDataArray($strSQLHG1, $arrHG1, $intHG1);
                    if ($booReturn && ($intHG1 !== 0)) {
                        foreach ($arrHG1 as $elemHG1) {
                            if (!in_array($elemHG1['idSlave'], $arrExclude, true) &&
                                substr_count($strDataValue, $elemHG1['host_name'] . ',' . $strService) === 0) {
                                $strDataValue .= $elemHG1['host_name'] . ',' . $strService . ',';
                            }
                        }
                    }
                    $strSQLHG2 = 'SELECT `host_name`, `idMaster` FROM `tbl_host` ' .
                        'LEFT JOIN `tbl_lnkHostToHostgroup` ON `id`=`idMaster` ' .
                        'WHERE `idSlave`=' . $data['idSlaveHG'] . " AND `active`='1' AND  `exclude`=0 " .
                        "AND $strDomainWhere1";
                    $booReturn = $this->myDBClass->hasDataArray($strSQLHG2, $arrHG2, $intHG2);
                    if ($booReturn && ($intHG2 !== 0)) {
                        foreach ($arrHG2 as $elemHG2) {
                            if (!in_array($elemHG2['idMaster'], $arrExclude, true) &&
                                substr_count($strDataValue, $elemHG2['host_name'] . ',' . $strService) === 0) {
                                $strDataValue .= $elemHG2['host_name'] . ',' . $strService . ',';
                            }
                        }
                    }
                } else {
                    $strSQLHost = 'SELECT `' . $elem['target1'] . '` FROM `' . $elem['tableName1'] . '` ' .
                        'WHERE `id`=' . $data['idSlaveH'] . "  AND `active`='1' AND $strDomainWhere1";
                    $strHost = $this->myDBClass->getFieldData($strSQLHost);
                    $strSQLSrv = 'SELECT `' . $elem['target2'] . '` FROM `' . $elem['tableName2'] . '` ' .
                        'WHERE `id`=' . $data['idSlaveS'] . "  AND `active`='1' AND $strDomainWhere1";
                    $strService = $this->myDBClass->getFieldData($strSQLSrv);
                    if (($strHost !== '') && ($strService !== '') &&
                        substr_count($strDataValue, $strHost . ',' . $strService) === 0 &&
                        !in_array($data['idSlaveH'], $arrExclude, true)) {
                        $strDataValue .= $strHost . ',' . $strService . ',';
                    }
                }
            }
            $strDataValue = substr($strDataValue, 0, -1);
            if ($strDataValue === '') {
                $intReturn = 1;
            }
        } else {
            $intReturn = 1;
        }
        return $intReturn;
    }

    /**
     * @param array $arrData Dataset array
     * @param string $strDataValue Data value
     * @param array $elem Relation data array
     * @return int 0 = use data / 1 = skip data
     */
    private function processRelation7(array $arrData, string &$strDataValue, array $elem): int
    {
        $intReturn = 1;
        /* Get relation data */
        /** @noinspection SqlResolve */
        $strSQLMaster = 'SELECT * FROM `' . $elem['linkTable'] . '` WHERE `idMaster`=' . $arrData['id'];
        $booReturn = $this->myDBClass->hasDataArray($strSQLMaster, $arrDataRel, $intDataCountRel);
        if ($booReturn && ($intDataCountRel !== 0)) {
            /* Rewrite $strDataValue with returned relation data */
            $strDataValue = '';
            /** @var array $arrDataRel */
            foreach ($arrDataRel as $data) {
                $strSQL = 'SELECT host_name FROM tbl_host WHERE id=' . $data['idHost'];
                $strHost = $this->myDBClass->getFieldData($strSQL);
                $strSQL = 'SELECT service_description FROM tbl_service WHERE id=' . $data['idSlave'];
                $strService = $this->myDBClass->getFieldData($strSQL);
                $strDataValue .= $strHost . ',' . $strService . ',';
                $intReturn = 0;
            }
            $strDataValue = substr($strDataValue, 0, -1);
        }
        return $intReturn;
    }

    /**
     * Rename field names
     * @param string $strTableName Table name
     * @param int $intConfigID Configuration target ID
     * @param int $intDataId Data ID
     * @param string $key Data key (by reference)
     * @param string $value Data value (by reference)
     * @param int $intSkip Skip value (by reference) 1 = skip / 0 = pass
     */
    private function renameFields(string $strTableName, int $intConfigID, int $intDataId, string &$key, string &$value, int &$intSkip): void
    {
        if ($this->intNagVersion === 0) {
            $this->getConfigData($intConfigID, 'version', $this->intNagVersion);
        }
        /* Picture path */
        if ($this->strPicPath === 'none') {
            $this->getConfigData($intConfigID, 'picturedir', $this->strPicPath);
        }
        if ($key === 'icon_image') {
            $value = $this->strPicPath . $value;
        }
        if ($key === 'vrml_image') {
            $value = $this->strPicPath . $value;
        }
        if ($key === 'statusmap_image') {
            $value = $this->strPicPath . $value;
        }
        /* Tables */
        if ($strTableName === 'tbl_host') {
            if ($key === 'use_template') {
                $key = 'use';
            }
            $strVIValues = 'active_checks_enabled,passive_checks_enabled,check_freshness,obsess_over_host,';
            $strVIValues .= 'event_handler_enabled,flap_detection_enabled,process_perf_data,retain_status_information,';
            $strVIValues .= 'retain_nonstatus_information,notifications_enabled';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'parents') {
                $value = $this->checkTpl($value, 'parents_tploptions', 'tbl_host', $intDataId, $intSkip);
            }
            if ($key === 'hostgroups') {
                $value = $this->checkTpl($value, 'hostgroups_tploptions', 'tbl_host', $intDataId, $intSkip);
            }
            if ($key === 'contacts') {
                $value = $this->checkTpl($value, 'contacts_tploptions', 'tbl_host', $intDataId, $intSkip);
            }
            if ($key === 'contact_groups') {
                $value = $this->checkTpl($value, 'contact_groups_tploptions', 'tbl_host', $intDataId, $intSkip);
            }
            if ($key === 'use') {
                $value = $this->checkTpl($value, 'use_template_tploptions', 'tbl_host', $intDataId, $intSkip);
            }
            if ($key === 'check_command') {
                $value = str_replace("\::bang::", "\!", $value);
            }
            if ($key === 'check_command') {
                $value = str_replace('::bang::', "\!", $value);
            }
        }
        if ($strTableName === 'tbl_service') {
            if ($key === 'use_template') {
                $key = 'use';
            }
            if ($this->intNagVersion < 2) {
                if ($key === 'check_interval') {
                    $key = 'normal_check_interval';
                }
                if ($key === 'retry_interval') {
                    $key = 'retry_check_interval';
                }
            }
            $strVIValues = 'is_volatile,active_checks_enabled,passive_checks_enabled,parallelize_check,';
            $strVIValues .= 'obsess_over_service,check_freshness,event_handler_enabled,flap_detection_enabled,';
            $strVIValues .= 'process_perf_data,retain_status_information,retain_nonstatus_information,';
            $strVIValues .= 'notifications_enabled';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'host_name') {
                $value = $this->checkTpl($value, 'host_name_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'hostgroup_name') {
                $value = $this->checkTpl($value, 'hostgroup_name_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'parents') {
                $value = $this->checkTpl($value, 'parents_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'servicegroups') {
                $value = $this->checkTpl($value, 'servicegroups_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'contacts') {
                $value = $this->checkTpl($value, 'contacts_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'contact_groups') {
                $value = $this->checkTpl($value, 'contact_groups_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'use') {
                $value = $this->checkTpl($value, 'use_template_tploptions', 'tbl_service', $intDataId, $intSkip);
            }
            if ($key === 'check_command') {
                $value = str_replace("\::bang::", "\!", $value);
            }
            if ($key === 'check_command') {
                $value = str_replace('::bang::', "\!", $value);
            }
        }
        if ($strTableName === 'tbl_hosttemplate') {
            if ($key === 'template_name') {
                $key = 'name';
            }
            if ($key === 'use_template') {
                $key = 'use';
            }
            $strVIValues = 'active_checks_enabled,passive_checks_enabled,check_freshness,obsess_over_host,';
            $strVIValues .= 'event_handler_enabled,flap_detection_enabled,process_perf_data,retain_status_information,';
            $strVIValues .= 'retain_nonstatus_information,notifications_enabled';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'parents') {
                $value = $this->checkTpl($value, 'parents_tploptions', 'tbl_hosttemplate', $intDataId, $intSkip);
            }
            if ($key === 'hostgroups') {
                $value = $this->checkTpl($value, 'hostgroups_tploptions', 'tbl_hosttemplate', $intDataId, $intSkip);
            }
            if ($key === 'contacts') {
                $value = $this->checkTpl($value, 'contacts_tploptions', 'tbl_hosttemplate', $intDataId, $intSkip);
            }
            if ($key === 'contact_groups') {
                $value = $this->checkTpl($value, 'contact_groups_tploptions', 'tbl_hosttemplate', $intDataId, $intSkip);
            }
            if ($key === 'use') {
                $value = $this->checkTpl($value, 'use_template_tploptions', 'tbl_hosttemplate', $intDataId, $intSkip);
            }
        }
        if ($strTableName === 'tbl_servicetemplate') {
            if ($key === 'template_name') {
                $key = 'name';
            }
            if ($key === 'use_template') {
                $key = 'use';
            }
            if ($this->intNagVersion < 2) {
                if ($key === 'check_interval') {
                    $key = 'normal_check_interval';
                }
                if ($key === 'retry_interval') {
                    $key = 'retry_check_interval';
                }
            }
            $strVIValues = 'is_volatile,active_checks_enabled,passive_checks_enabled,parallelize_check,';
            $strVIValues .= 'obsess_over_service,check_freshness,event_handler_enabled,flap_detection_enabled,';
            $strVIValues .= 'process_perf_data,retain_status_information,retain_nonstatus_information,';
            $strVIValues .= 'notifications_enabled';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'host_name') {
                $value = $this->checkTpl($value, 'host_name_tploptions', 'tbl_servicetemplate', $intDataId, $intSkip);
            }
            if ($key === 'hostgroup_name') {
                $value = $this->checkTpl(
                    $value,
                    'hostgroup_name_tploptions',
                    'tbl_servicetemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'parents') {
                $value = $this->checkTpl($value, 'parents_tploptions', 'tbl_servicetemplate', $intDataId, $intSkip);
            }
            if ($key === 'servicegroups') {
                $value = $this->checkTpl(
                    $value,
                    'servicegroups_tploptions',
                    'tbl_servicetemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'contacts') {
                $value = $this->checkTpl($value, 'contacts_tploptions', 'tbl_servicetemplate', $intDataId, $intSkip);
            }
            if ($key === 'contact_groups') {
                $value = $this->checkTpl(
                    $value,
                    'contact_groups_tploptions',
                    'tbl_servicetemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'use') {
                $value = $this->checkTpl(
                    $value,
                    'use_template_tploptions',
                    'tbl_servicetemplate',
                    $intDataId,
                    $intSkip
                );
            }
        }
        if ($strTableName === 'tbl_contact') {
            if ($key === 'use_template') {
                $key = 'use';
            }
            $strVIValues = 'host_notifications_enabled,service_notifications_enabled,can_submit_commands,';
            $strVIValues .= 'retain_status_information,retain_nonstatus_information';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'contactgroups') {
                $value = $this->checkTpl($value, 'contactgroups_tploptions', 'tbl_contact', $intDataId, $intSkip);
            }
            if ($key === 'host_notification_commands') {
                $value = $this->checkTpl(
                    $value,
                    'host_notification_commands_tploptions',
                    'tbl_contact',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'service_notification_commands') {
                $value = $this->checkTpl(
                    $value,
                    'service_notification_commands_tploptions',
                    'tbl_contact',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'use') {
                $value = $this->checkTpl($value, 'use_template_tploptions', 'tbl_contact', $intDataId, $intSkip);
            }
        }
        if ($strTableName === 'tbl_contacttemplate') {
            if ($key === 'template_name') {
                $key = 'name';
            }
            if ($key === 'use_template') {
                $key = 'use';
            }
            $strVIValues = 'host_notifications_enabled,service_notifications_enabled,can_submit_commands,';
            $strVIValues .= 'retain_status_information,retain_nonstatus_information';
            if (in_array($key, explode(',', $strVIValues), true)) {
                if ((int)$value === -1) {
                    $value = 'null';
                }
                if ((int)$value === 3) {
                    $value = 'null';
                }
            }
            if ($key === 'contactgroups') {
                $value = $this->checkTpl(
                    $value,
                    'contactgroups_tploptions',
                    'tbl_contacttemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'host_notification_commands') {
                $value = $this->checkTpl(
                    $value,
                    'host_notification_commands_tploptions',
                    'tbl_contacttemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'service_notification_commands') {
                $value = $this->checkTpl(
                    $value,
                    'service_notification_commands_tploptions',
                    'tbl_contacttemplate',
                    $intDataId,
                    $intSkip
                );
            }
            if ($key === 'use') {
                $value = $this->checkTpl(
                    $value,
                    'use_template_tploptions',
                    'tbl_contacttemplate',
                    $intDataId,
                    $intSkip
                );
            }
        }
        if ((($strTableName === 'tbl_hosttemplate') || ($strTableName === 'tbl_servicetemplate') ||
                ($strTableName === 'tbl_contacttemplate')) && $key === 'register') {
            $value = '0';
        }
        if ($strTableName === 'tbl_timeperiod' && $key === 'use_template') {
            $key = 'use';
        }
    }

    /**
     * Process special settings based on template option
     * @param string $strValue Original data value
     * @param string $strKeyField Template option field name
     * @param string $strTable Table name
     * @param int $intId Dataset ID
     * @param int $intSkip Skip value (by reference)
     * @return string Manipulated data value
     */
    public function checkTpl(string $strValue, string $strKeyField, string $strTable, int $intId, int &$intSkip): string
    {
        if ($this->intNagVersion < 3) {
            return $strValue;
        }
        $strSQL = 'SELECT `' . $strKeyField . '` FROM `' . $strTable . "` WHERE `id` = $intId";
        $intValue = $this->myDBClass->getFieldData($strSQL);
        if ((int)$intValue === 0) {
            return ('+' . $strValue);
        }
        if ((int)$intValue === 1) {
            $intSkip = 0;
            return 'null';
        }
        return $strValue;
    }

    /**
     * Open configuration file
     * @param string $strFile File name
     * @param int $intConfigID Configuration ID
     * @param int $intType Type ID
     * @param resource $resConfigFile Temporary or configuration file ressource (by reference)
     * @param string $strConfigFile Configuration file name (by reference)
     * @return int 0 = successful / 1 = error
     */
    private function getConfigFile(string $strFile, int $intConfigID, int $intType, &$resConfigFile, string &$strConfigFile): int
    {
        /* Variable definitions */
        $strBaseDir = '';
        $intMethod = 1;
        $intReturn = 0;
        $strConfigValue = '';
        /* Get config data */
        if ($intType === 1) {
            $this->getConfigData($intConfigID, 'hostconfig', $strBaseDir);
            $strType = 'host';
        } elseif ($intType === 2) {
            $this->getConfigData($intConfigID, 'serviceconfig', $strBaseDir);
            $strType = 'service';
        } else {
            $this->getConfigData($intConfigID, 'basedir', $strBaseDir);
            $strType = 'basic';
        }
        if ($this->getConfigData($intConfigID, 'method', $strConfigValue) === 0) {
            $intMethod = (int)$strConfigValue;
        }
        /* Backup config file */
        $this->moveFile($strType, $strFile, $intConfigID);
        /* Variable definition */
        $strConfigFile = $strBaseDir . '/' . $strFile;
        /* Local file system */
        if ($intMethod === 1) {
            /* Save configuration file */
            if (is_writable($strConfigFile) || (!file_exists($strConfigFile) && is_writable($strBaseDir))) {
                /** @noinspection CallableParameterUseCaseInTypeContextInspection */
                $resConfigFile = fopen($strConfigFile, 'wb');
                chmod($strConfigFile, 0644);
            } else {
                $this->myDataClass->writeLog(translate('Configuration write failed:') . ' ' . $strFile);
                $this->processClassMessage(translate('Cannot open/overwrite the configuration file (check the '
                        . 'permissions)!') . '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        } elseif ($intMethod === 2) { /* Remote file (FTP) */
            /* Check connection */
            if (empty($this->conFTPConId) || !is_resource($this->conFTPConId) ||
                ($this->resConnectType !== 'FTP')) {
                $intReturn = $this->getFTPConnection($intConfigID);
            }
            if ($intReturn === 0) {
                /* Open the config file */
                if (isset($this->arrSettings['path']['tempdir'])) {
                    $strConfigFile = tempnam($this->arrSettings['path']['tempdir'], 'nagiosql');
                } else {
                    $strConfigFile = tempnam(sys_get_temp_dir(), 'nagiosql');
                }
                /** @noinspection CallableParameterUseCaseInTypeContextInspection */
                $resConfigFile = fopen($strConfigFile, 'wb');
            }
        } elseif ($intMethod === 3) { /* Remote file (SFTP) */
            /* Check connection */
            if (empty($this->resSSHConId) || !is_resource($this->resSSHConId) ||
                ($this->resConnectType !== 'SSH')) {
                $intReturn = $this->getSSHConnection($intConfigID);
            }
            if ($intReturn === 0) {
                if (isset($this->arrSettings['path']['tempdir'])) {
                    $strConfigFile = tempnam($this->arrSettings['path']['tempdir'], 'nagiosql');
                } else {
                    $strConfigFile = tempnam(sys_get_temp_dir(), 'nagiosql');
                }
                /** @noinspection CallableParameterUseCaseInTypeContextInspection */
                $resConfigFile = fopen($strConfigFile, 'wb');
            }
        }
        return $intReturn;
    }

    /**
     * Moves an existing configuration file to the backup directory and removes then the original file
     * @param string $strType Type of the configuration file
     * @param string $strName Name of the configuration file
     * @param int $intConfigID Configuration target ID
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function moveFile(string $strType, string $strName, int $intConfigID): int
    {
        /* Variable definitions */
        $strConfigDir = '';
        $strBackupDir = '';
        $intReturn = 0;
        /* Get directories */
        switch ($strType) {
            case 'host':
                $this->getConfigData($intConfigID, 'hostconfig', $strConfigDir);
                $this->getConfigData($intConfigID, 'hostbackup', $strBackupDir);
                break;
            case 'service':
                $this->getConfigData($intConfigID, 'serviceconfig', $strConfigDir);
                $this->getConfigData($intConfigID, 'servicebackup', $strBackupDir);
                break;
            case 'basic':
                $this->getConfigData($intConfigID, 'basedir', $strConfigDir);
                $this->getConfigData($intConfigID, 'backupdir', $strBackupDir);
                break;
            case 'nagiosbasic':
                $this->getConfigData($intConfigID, 'nagiosbasedir', $strConfigDir);
                $this->getConfigData($intConfigID, 'backupdir', $strBackupDir);
                break;
            default:
                $intReturn = 1;
        }
        if ($intReturn === 0) {
            /* Variable definition */
            $intMethod = 1;
            $strDate = date('YmdHis');
            $strSourceFile = $strConfigDir . '/' . $strName;
            $strDestinationFile = $strBackupDir . '/' . $strName . '_old_' . $strDate;
            $booRetVal = false;
            /* Get connection method */
            if ($this->getConfigValues($intConfigID, 'method', $strMethod) === 0) {
                $intMethod = (int)$strMethod;
            }
            /* Local file system */
            if ($intMethod === 1) {
                /* Save configuration file */
                if (file_exists($strSourceFile)) {
                    if (is_writable($strBackupDir) && is_writable($strConfigDir)) {
                        copy($strSourceFile, $strDestinationFile);
                        unlink($strSourceFile);
                    } else {
                        $this->processClassMessage(translate('Cannot backup the old file because the permissions are '
                                . 'wrong - destination file: ') . $strDestinationFile . '::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                } else {
                    $this->processClassMessage(translate('Cannot backup the old file because the source file is '
                            . 'missing - source file: ') . $strSourceFile . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            } elseif ($intMethod === 2) { /* Remote file (FTP) */
                /* Check connection */
                $intReturn = $this->getFTPConnection($intConfigID);
                if ($intReturn === 0) {
                    $strSourceFile = str_replace('//', '/', $strSourceFile);
                    $strDestinationFile = str_replace('//', '/', $strDestinationFile);
                    /* Save configuration file */
                    $intFileStamp = ftp_mdtm($this->conFTPConId, $strSourceFile);
                    if ($intFileStamp > -1) {
                        $intErrorReporting = error_reporting();
                        error_reporting(0);
                        $booRetVal = ftp_rename($this->conFTPConId, $strSourceFile, $strDestinationFile);
                        error_reporting($intErrorReporting);
                    } else {
                        $this->processClassMessage(translate('Cannot backup the old file because the source file is '
                                . 'missing (remote FTP) - source file: ') . $strSourceFile . '::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                }
                if (($booRetVal === false) && ($intReturn === 0)) {
                    $this->processClassMessage(translate('Cannot backup the old file because the permissions are '
                            . 'wrong (remote FTP) - destination file: ') . $strDestinationFile . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            } elseif ($intMethod === 3) { /* Remote file (SFTP) */
                /* Check connection */
                $intReturn = $this->getSSHConnection($intConfigID);
                /* Save configuration file */
                $arrResult = array();
                $strSourceFile = str_replace('//', '/', $strSourceFile);
                $strDestinationFile = str_replace('//', '/', $strDestinationFile);
                $strCommand = 'ls ' . $strSourceFile;
                if (($intReturn === 0) && ($this->sendSSHCommand($strCommand, $arrResult) === 0)) {
                    if (isset($arrResult[0]) && $arrResult[0] === $strSourceFile) {
                        $arrInfo = ssh2_sftp_stat($this->resSFTP, $strSourceFile);
                        if ($arrInfo['mtime'] > -1) {
                            $booRetVal = ssh2_sftp_rename($this->resSFTP, $strSourceFile, $strDestinationFile);
                        }
                    } else {
                        $this->processClassMessage(translate('Cannot backup the old file because the source file is '
                                . 'missing (remote SFTP) - source file: ') . $strSourceFile . '::', $this->strErrorMessage);
                        $intReturn = 1;
                    }
                }
                if (($booRetVal === false) && ($intReturn === 0)) {
                    $this->processClassMessage(translate('Cannot backup the old file because the permissions are '
                            . 'wrong (remote SFTP) - destination file: ') . $strDestinationFile . '::', $this->strErrorMessage);
                    $intReturn = 1;
                }
            }
        }
        return $intReturn;
    }

    /**
     * Write configuration file
     * @param string $strData Data string
     * @param string $strFile File name
     * @param int $intType Type ID
     * @param int $intConfigID Configuration target ID
     * @param resource $resConfigFile Temporary or configuration file ressource
     * @param string $strConfigFile Configuration file name
     * @return int 0 = successful / 1 = error
     */
    private function writeConfigFile(string $strData, string $strFile, int $intType, int $intConfigID, $resConfigFile, string $strConfigFile): int
    {
        /* Variable definitions */
        $intReturn = 0;
        $intMethod = 1;
        $strBaseDir = '';
        $strConfigValue = '';
        /* Get config data */
        if ($intType === 1) {
            $this->getConfigData($intConfigID, 'hostconfig', $strBaseDir);
        } elseif ($intType === 2) {
            $this->getConfigData($intConfigID, 'serviceconfig', $strBaseDir);
        } else {
            $this->getConfigData($intConfigID, 'basedir', $strBaseDir);
        }
        if ($this->getConfigData($intConfigID, 'method', $strConfigValue) === 0) {
            $intMethod = (int)$strConfigValue;
        }
        $strData = str_replace("\r\n", "\n", $strData);
        fwrite($resConfigFile, $strData);
        /* Local filesystem */
        if ($intMethod === 1) {
            fclose($resConfigFile);
        } elseif ($intMethod === 2) { /* FTP access */
            /* SSH Possible */
            if (!function_exists('ftp_put')) {
                $this->processClassMessage(translate('FTP module not loaded!') . '::', $this->strErrorMessage);
                $intReturn = 1;
            } else {
                $intErrorReporting = error_reporting();
                error_reporting(0);
                if (!ftp_put($this->conFTPConId, $strBaseDir . '/' . $strFile, $strConfigFile, FTP_ASCII)) {
                    $arrError = error_get_last();
                    error_reporting($intErrorReporting);
                    $this->processClassMessage(translate('Cannot open/overwrite the configuration file (FTP connection '
                            . 'failed)!') . '::', $this->strErrorMessage);
                    if ((string)$arrError['message'] !== '') {
                        $this->processClassMessage($arrError['message'] . '::', $this->strErrorMessage);
                    }
                    $intReturn = 1;
                }
                error_reporting($intErrorReporting);
                ftp_close($this->conFTPConId);
                fclose($resConfigFile);
            }
        } elseif ($intMethod === 3) { /* SSH access */
            /* SSH Possible */
            if (!function_exists('ssh2_scp_send')) {
                $this->processClassMessage(translate('SSH module not loaded!') . '::', $this->strErrorMessage);
                $intReturn = 1;
            } else {
                $intErrorReporting = error_reporting();
                error_reporting(0);
                if (!ssh2_scp_send($this->resSSHConId, $strConfigFile, $strBaseDir . '/' . $strFile, 0644)) {
                    $arrError = error_get_last();
                    error_reporting($intErrorReporting);
                    $this->processClassMessage(translate('Cannot open/overwrite the configuration file (remote SFTP)!') .
                        '::', $this->strErrorMessage);
                    if ((string)$arrError['message'] !== '') {
                        $this->processClassMessage($arrError['message'] . '::', $this->strErrorMessage);
                    }
                    $intReturn = 1;
                }
                error_reporting($intErrorReporting);
                fclose($resConfigFile);
                unlink($strConfigFile);
                $this->resSSHConId = null;
            }
        }
        if ($intReturn === 0) {
            $this->myDataClass->writeLog(translate('Configuration successfully written:') . ' ' . $strFile);
            $this->processClassMessage(translate('Configuration file successfully written!') .
                '::', $this->strInfoMessage);
        }
        return $intReturn;
    }

    /**
     * Writes a configuration file including one single datasets of a configuration table or returns the output as
     * a text file for download.
     * @param string $strTableName Table name
     * @param int $intDbId Data ID
     * @param int $intMode 0 = Write file to filesystem
     *                     1 = Return Textfile for download test
     * @return int 0 = successful / 1 = error
     * Status message is stored in message class variables
     */
    public function createConfigSingle(string $strTableName, int $intDbId = 0, int $intMode = 0): int
    {
        /* Define variables */
        $arrData = array();
        $intDataCount = 0;
        $setEnableCommon = 0;
        $intReturn = 0;
        $strDomainWhere = ' (`config_id`=' . $this->intDomainId . ') ';
        /* Read some settings and information */
        if ($this->getDomainData('enable_common', $strEnableCommon) === 0) {
            $setEnableCommon = (int)$strEnableCommon;
        }
        /* Variable rewriting */
        if ($setEnableCommon !== 0) {
            $strDomainWhere = str_replace(')', ' OR `config_id`=0)', $strDomainWhere);
        }
        /* Do not create configs in common domain */
        if ($this->intDomainId === 0) {
            $this->processClassMessage(translate('It is not possible to write config files directly from the common '
                    . 'domain!') . '::', $this->strErrorMessage);
            $intReturn = 1;
        }
        if ($intReturn === 0) {
            if ($intDbId === 0) {
                /** @noinspection SqlResolve */
                $strSQL = 'SELECT * FROM `' . $strTableName . "` WHERE $strDomainWhere AND `active`='1' ORDER BY `id`";
            } else {
                /** @noinspection SqlResolve */
                $strSQL = 'SELECT * FROM `' . $strTableName . "` WHERE $strDomainWhere AND `active`='1' AND `id`=$intDbId";
            }
            $booReturn = $this->myDBClass->hasDataArray($strSQL, $arrData, $intDataCount);
            if (($booReturn !== false) && ($intDataCount !== 0)) {
                for ($i = 0; $i < $intDataCount; $i++) {
                    /* Process form POST variable */
                    $strChbName = 'chbId_' . $arrData[$i]['id'];
                    /* Check if this POST variable exists or the data ID parameter matches */
                    if ((($intDbId !== 0) && ($intDbId === (int)$arrData[$i]['id'])) ||
                        (filter_input(INPUT_POST, $strChbName) !== null)) {
                        /* Get configuration targets */
                        $this->getConfigSets($arrConfigID);
                        if (($arrConfigID !== 1) && is_array($arrConfigID)) {
                            foreach ($arrConfigID as $intConfigID) {
                                $intReturn = $this->writeConfTemplate(
                                    $intConfigID,
                                    $strTableName,
                                    $intMode,
                                    $arrData,
                                    $i
                                );
                            }
                        } else {
                            $this->processClassMessage(translate('Warning: no configuration target defined!') .
                                '::', $this->strErrorMessage);
                            $intReturn = 1;
                        }
                    }
                }
            } else {
                $this->myDataClass->writeLog(translate('Writing of the configuration failed - no dataset or not '
                    . 'activated dataset found'));
                $this->processClassMessage(translate('Writing of the configuration failed - no dataset or not '
                        . 'activated dataset found') . '::', $this->strErrorMessage);
                $intReturn = 1;
            }
        }
        return $intReturn;
    }
}