<?php
//------------------------------------------------------------------------------
//  Pi.Alert
//  Open Source Network Guard / WIFI & LAN intrusion detector
//
//  file.php - Back module. Server side. FileSystem Operations
//------------------------------------------------------------------------------
//  leiweibau  2023+        https://github.com/leiweibau     GNU GPLv3
//------------------------------------------------------------------------------

require_once __DIR__ . "/session.php";
pialert_start_session();
require_once __DIR__ . '/csrf.php';

if ($_SESSION["login"] != 1) {
	header('Location: ../../index.php');
	exit;
}

require 'timezone.php';
require 'db.php';
require 'auth.php';
require 'util.php';
require 'journal.php';
require 'language_switch.php';
require '../templates/language/' . $pia_lang_selected . '.php';

// Action selector
// Set maximum execution time to 15 seconds
ini_set('max_execution_time', '30');
$maskKeys = [
    'PIALERT_APIKEY',
    'PIALERT_WEB_PASSWORD',
    'FRITZBOX_PASS',
    'PUSHSAFER_TOKEN',
    'NTFY_PASSWORD',
    'MIKROTIK_PASS',
    'UNIFI_PASS',
    'OPENWRT_PASS',
    'ASUSWRT_PASS',
    'SMTP_PASS',
    'REPORT_MQTT_PASSWORD',
    'PUSHOVER_TOKEN',
    'PUSHOVER_USER',
    'PFSENSE_APIKEY',
    'OPNSENSE_APIKEY',
    'OPNSENSE_APISECRET',
    'ADGUARD_PASSWORD',
    'PIHOLE6_PASSWORD',
    'DDNS_PASSWORD',
    'TELEGRAM_BOT_TOKEN',
    'TELEGRAM_BOT_TOKEN_URL'
];

// Open DB
OpenDB();

pialert_dispatch_action([
    'getReportTotals', 'GetLogfiles', 'GetServerTime', 'GetUpdateStatus',
    'GetARPStatus', 'GetAutoBackupStatus', 'GetConfigFile'
], [
    'RestoreDBfromArchive', 'PurgeDBBackups', 'EnableDarkmode',
    'EnableOnlineHistoryGraph', 'SetAPIKey', 'LoginEnable', 'LoginDisable',
    'deleteAllNotifications', 'deleteAllNotificationsArchive', 'setTheme',
    'setLanguage', 'setArpTimer', 'setDeviceListCol', 'setListHeaderConfig',
    'RestoreConfigFile', 'BackupConfigFile', 'BackupDBtoArchive',
    'BackupDBtoCSV', 'SaveConfigFile', 'setFavIconURL', 'setPiholeURL',
    'ToggleImport', 'ToggleExtLogging', 'ToggleRogueDHCP', 'BlockDeviceMAC',
    'DeleteBlockDeviceMAC', 'BlockDeviceIP', 'DeleteBlockDeviceIP'
]);
// Action functions
if (isset($GLOBALS["pialert_request"]['action']) && !empty($GLOBALS["pialert_request"]['action'])) {
	$action = $GLOBALS["pialert_request"]['action'];
	switch ($action) {
	case 'RestoreDBfromArchive':RestoreDBfromArchive();
		break;
	case 'PurgeDBBackups':PurgeDBBackups();
		break;
	case 'EnableDarkmode':EnableDarkmode();
		break;
	case 'EnableOnlineHistoryGraph':EnableOnlineHistoryGraph();
		break;
	case 'SetAPIKey':SetAPIKey();
		break;
	case 'LoginEnable':LoginEnable();
		break;
	case 'LoginDisable':LoginDisable();
		break;
	case 'deleteAllNotifications':deleteAllNotifications();
		break;
	case 'deleteAllNotificationsArchive':deleteAllNotificationsArchive();
		break;
	case 'setTheme':setTheme();
		break;
	case 'setLanguage':setLanguage();
		break;
	case 'setArpTimer':setArpTimer();
		break;
	case 'setDeviceListCol':setDeviceListCol();
		break;
	case 'setListHeaderConfig':setListHeaderConfig();
		break;
	case 'RestoreConfigFile':RestoreConfigFile();
		break;
	case 'BackupConfigFile':BackupConfigFile();
		break;
	case 'BackupDBtoArchive':BackupDBtoArchive();
		break;
	case 'BackupDBtoCSV':BackupDBtoCSV();
		break;
	case 'SaveConfigFile':SaveConfigFile();
		break;
	case 'getReportTotals':getReportTotals();
		break;
	case 'setFavIconURL':setFavIconURL();
		break;
	case 'setPiholeURL':setPiholeURL();
		break;
	case 'GetLogfiles':GetLogfiles();
		break;
	case 'GetServerTime':GetServerTime();
		break;
	case 'GetUpdateStatus':GetUpdateStatus();
		break;
	case 'GetARPStatus':GetARPStatus();
		break;
	case 'GetAutoBackupStatus':GetAutoBackupStatus();
		break;
	case 'ToggleImport':ToggleImport();
		break;
	case 'ToggleExtLogging':ToggleExtLogging();
		break;
	case 'ToggleRogueDHCP':ToggleRogueDHCP();
		break;
	case 'BlockDeviceMAC':BlockDeviceMAC();
		break;
	case 'DeleteBlockDeviceMAC':DeleteBlockDeviceMAC();
		break;
	case 'BlockDeviceIP':BlockDeviceIP();
		break;
	case 'DeleteBlockDeviceIP':DeleteBlockDeviceIP();
		break;
	case 'GetConfigFile':GetConfigFile();
		break;
	default:logServerConsole('Action: ' . $action);
		break;
	}
}

function GetConfigFile() {
	global $maskKeys;
	global $db;

    $configFile = __DIR__ . '/../../../config/pialert.conf';

    if (!file_exists($configFile) || !is_readable($configFile)) {
        http_response_code(500);
        echo 'ERROR: Config file not accessible';
        exit;
    }

    $lines = file($configFile, FILE_IGNORE_NEW_LINES);

    if ($lines === false) {
        http_response_code(500);
        echo 'ERROR: Unable to read config file';
        exit;
    }

    foreach ($lines as &$line) {

        // skipp comments
        if (preg_match('/^\s*#/', $line) || trim($line) === '') {
            continue;
        }

        foreach ($maskKeys as $key) {
            // Match: KEY = VALUE (mit optionalen Quotes)
            if (preg_match(
                '/^(\s*' . preg_quote($key, '/') . '\s*=\s*)([\'"]?)(.*?)(\2)\s*$/',
                $line,
                $matches
            )) {
                $prefix = $matches[1];
                $quote  = $matches[2]; // ' oder "
                $value  = decode_python_config_string($matches[3], $quote);

                $trimmedValue = trim($value);

                // not mask: empty, "password"
                if ($trimmedValue === '' || strtolower($trimmedValue) === 'password') {
                    break;
                }

                $len = strlen($trimmedValue);

                // mask
                if ($len > 2) {
                    $maskedValue =
                        $trimmedValue[0] .
                        str_repeat('*', $len - 2) .
                        $trimmedValue[$len - 1];

                    $line = $prefix . $quote . $maskedValue . $quote;
                }
                break; // Schlüssel gefunden, nächster Line
            }
        }
    }

    header('Content-Type: text/plain; charset=utf-8');
    echo implode(PHP_EOL, $lines);
    exit;
}

function DeleteBlockDeviceIP() {
	global $pia_lang;
    $configfile = '../../../config/pialert.conf';

    if (!isset($GLOBALS["pialert_request"]['ip'])) {
        echo $pia_lang['BE_Dev_Ignore_a'];
        return;
    }

    $removeIP = trim($GLOBALS["pialert_request"]['ip']);

    if ($removeIP === '') {
        echo $pia_lang['BE_Dev_Ignore_b'];
        return;
    }

    if (!file_exists($configfile) || !is_readable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_c'];
        return;
    }

    $configContent = file_get_contents($configfile);

    if (!preg_match('/^(\s*IP_IGNORE_LIST)(\s*=\s*)\[(.*?)\]\s*$/m', $configContent, $matches)) {
        echo $pia_lang['BE_Dev_Ignore_d'];
        return;
    }

    $ipListRaw = $matches[3];
    $ipArray = array_filter(array_map(function($item) {
        return trim(str_replace("'", '', $item));
    }, explode(',', $ipListRaw)));

    $updatedArray = array_filter($ipArray, function($ip) use ($removeIP) {
        return $ip !== $removeIP;
    });

    $newListLine = $matches[1] . $matches[2] .
        (empty($updatedArray) ? '[]' : "['" . implode("','", $updatedArray) . "']");

    $newConfigContent = preg_replace(
        '/^\s*IP_IGNORE_LIST\s*=\s*\[.*?\]\s*$/m',
        $newListLine,
        $configContent
    );

    if (!is_writable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_e'];
        return;
    }

    validate_and_replace_pialert_config($configfile, $newConfigContent);
    echo $pia_lang['BE_Dev_Ignore_f'];
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $removeIP.' '.$pia_lang['BE_Files_Ignore_a']);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php'>";
}

function BlockDeviceIP() {
	global $pia_lang;
    $configfile = '../../../config/pialert.conf';

    if (!isset($GLOBALS["pialert_request"]['ip'])) {
        echo $pia_lang['BE_Dev_Ignore_a'] ;
        return;
    }

    $newIP = trim($GLOBALS["pialert_request"]['ip']);

    if ($newIP === '') {
        echo $pia_lang['BE_Dev_Ignore_b'];
        return;
    }

    if (!file_exists($configfile) || !is_readable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_c'];
        return;
    }

    $configContent = file_get_contents($configfile);

    if (!preg_match('/^(\s*IP_IGNORE_LIST)(\s*=\s*)\[(.*?)\]\s*$/m', $configContent, $matches)) {
        echo $pia_lang['BE_Dev_Ignore_d'];
        return;
    }

    $ipListRaw = $matches[3];
    $ipArray = array_filter(array_map(function($item) {
        return trim(str_replace("'", '', $item));
    }, explode(',', $ipListRaw)));

    if (!in_array($newIP, $ipArray)) {
        $ipArray[] = $newIP;
    }

    $newListLine = $matches[1] . $matches[2] . "['" . implode("','", $ipArray) . "']";

    $newConfigContent = preg_replace(
        '/^\s*IP_IGNORE_LIST\s*=\s*\[.*?\]\s*$/m',
        $newListLine,
        $configContent
    );

    if (!is_writable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_e'];
        return;
    }

    validate_and_replace_pialert_config($configfile, $newConfigContent);
    echo $pia_lang['BE_Dev_Ignore_g'];
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $newIP.' '.$pia_lang['BE_Files_Ignore_b']);
}

function DeleteBlockDeviceMAC() {
	global $pia_lang;
    $configfile = '../../../config/pialert.conf';

    if (!isset($GLOBALS["pialert_request"]['mac'])) {
        echo $pia_lang['BE_Dev_Ignore_h'];
        return;
    }

    $removeMac = strtolower(trim($GLOBALS["pialert_request"]['mac']));

    if (!file_exists($configfile) || !is_readable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_c'];
        return;
    }

    $configContent = file_get_contents($configfile);

    // Zeile mit MAC_IGNORE_LIST extrahieren
    if (!preg_match('/^(\s*MAC_IGNORE_LIST)(\s*=\s*)\[(.*?)\]\s*$/m', $configContent, $matches)) {
        echo $pia_lang['BE_Dev_Ignore_i'];
        return;
    }

    $macListRaw = $matches[3]; // Inhalt zwischen den Klammern

    // Liste bereinigen und als Array aufbereiten
    $macArray = array_filter(array_map(function($item) {
        return strtolower(trim(str_replace("'", '', $item)));
    }, explode(',', $macListRaw)));

    // MAC entfernen, wenn vorhanden
    $updatedArray = array_filter($macArray, function($mac) use ($removeMac) {
        return $mac !== $removeMac;
    });

    // Neue Zeile erzeugen
    $newListLine = $matches[1] . $matches[2] .
        (empty($updatedArray) ? '[]' : "['" . implode("','", $updatedArray) . "']");

    // Alte Zeile ersetzen
    $newConfigContent = preg_replace(
        '/^\s*MAC_IGNORE_LIST\s*=\s*\[.*?\]\s*$/m',
        $newListLine,
        $configContent
    );

    // Datei schreiben
    if (!is_writable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_e'];
        return;
    }

    validate_and_replace_pialert_config($configfile, $newConfigContent);
    echo $pia_lang['BE_Dev_Ignore_j'];

	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $removeMac.' '.$pia_lang['BE_Files_Ignore_a']);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php'>";
}

function BlockDeviceMAC() {
	global $pia_lang;
	$laststate = '../../../config/pialert-prev.bak';
	$configfile = '../../../config/pialert.conf';

	copy($configfile, $laststate);

    if (!isset($GLOBALS["pialert_request"]['mac'])) {
        echo $pia_lang['BE_Dev_Ignore_h'];
        return;
    }

    $newMac = strtolower(trim($GLOBALS["pialert_request"]['mac']));

    if (!file_exists($configfile) || !is_readable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_c'];
        return;
    }

    $configContent = file_get_contents($configfile);

    // Original-Zeile parsen: Key, Gleichheitszeichen mit Leerzeichen, Inhalt
    if (!preg_match('/^(\s*MAC_IGNORE_LIST)(\s*=\s*)\[(.*?)\]\s*$/m', $configContent, $matches)) {
        echo $pia_lang['BE_Dev_Ignore_i'];
        return;
    }

    $macListRaw = $matches[3]; // Inhalt zwischen den Klammern

    // In Array umwandeln
    $macArray = array_filter(array_map(function($item) {
        return strtolower(trim(str_replace("'", '', $item)));
    }, explode(',', $macListRaw)));

    // Neuen MAC hinzufügen, falls nicht vorhanden
    if (!in_array($newMac, $macArray)) {
        $macArray[] = $newMac;
    }

    // Neue MAC_IGNORE_LIST-Zeile im ursprünglichen Format bauen
    $newListLine = $matches[1] . $matches[2] . "['" . implode("','", $macArray) . "']";

    // Ersetzen der alten Zeile im Dateitext
    $newConfigContent = preg_replace(
        '/^\s*MAC_IGNORE_LIST\s*=\s*\[.*?\]\s*$/m',
        $newListLine,
        $configContent
    );

    // Schreiben in Datei
    if (!is_writable($configfile)) {
        echo $pia_lang['BE_Dev_Ignore_e'];
        return;
    }

    validate_and_replace_pialert_config($configfile, $newConfigContent);
    echo $pia_lang['BE_Dev_Ignore_k'];

	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $newMac.' '.$pia_lang['BE_Files_Ignore_b']);
}

function GetAutoBackupStatus() {
	global $pia_lang;
	if (file_exists("../../../back/.backup")) {$result = array($pia_lang['BE_Files_autobkp_pending']);} else {$result = array($pia_lang['BE_Files_autobkp_pause']);}
	//Count db backups
	$backupdir = "../../../db";
	$backupfiles = glob($backupdir . "/pialertdb_20*.zip");
	$result[] = count($backupfiles);
	//Calc Backup size
	$backupfile_size = 0;
	foreach ($backupfiles as $file) {
		$backupfile_size = $backupfile_size + filesize($file);
	}
	//Count config backups
	$backupdir = "../../../config";
	$backupfiles = glob($backupdir . "/pialert-20*.bak");
	$result[] = count($backupfiles);

	$result[] = number_format(($backupfile_size / 1000000), 2, ",", ".") . ' MB';
	echo json_encode($result);
}

function GetARPStatus() {
	global $pia_lang;
	if (file_exists("../../../back/.scanning")) {$result = array('');} else {$result = array($pia_lang['MT_arpscancout_norun']);}
	echo json_encode($result);
}

function GetUpdateStatus() {
	$updatenotification = '../../auto_Update.info';
	if (file_exists($updatenotification)) {
		$answer[0] = "i";
		echo (json_encode($answer));
	} else {
		$answer[0] = "";
		echo (json_encode($answer));
	}
}

function GetServerTime() {
	echo date("Y,n,j,G,i,s");
}

// Read logfiles --------------------------------------------------------------
function GetLogfiles() {
	global $pia_lang;

	$logDirectory = dirname(__DIR__, 3) . '/log';
	$logfiles = array(
		$logDirectory . '/pialert.1.log',
		$logDirectory . '/pialert.IP.log',
		$logDirectory . '/pialert.vendors.log',
		$logDirectory . '/pialert.cleanup.log',
		$logDirectory . '/pialert.webservices.log',
		$logDirectory . '/pialert.speedtest.log',
		$logDirectory . '/pialert.nmap.log'
	);
	$logmessages = array(
		$pia_lang['MT_Tools_Logviewer_Scan_empty'],
		$pia_lang['MT_Tools_Logviewer_IPLog_empty'],
		'',
		$pia_lang['MT_Tools_Logviewer_Cleanup_empty'],
		$pia_lang['MT_Tools_Logviewer_WebServices_empty'],
		'',
		$pia_lang['MT_Tools_Logviewer_Nmap_empty']
	);

	$logs = array();
	foreach ($logfiles as $index => $logfile) {
		$file = file_get_contents($logfile, true);
		$logs[] = $file === false || $file === '' ? $logmessages[$index] : $file;
	}

	header('Content-Type: application/json; charset=UTF-8');
	echo json_encode($logs);
}

function convert_bool($val) {
    if (is_bool($val)) return $val ? 'True' : 'False';
    if (!is_string($val)) {
        throw new InvalidArgumentException('Invalid boolean value');
    }
    $val_lower = strtolower(trim($val));
    if ($val_lower === 'true') return 'True';
    if ($val_lower === 'false') return 'False';
    throw new InvalidArgumentException('Invalid boolean value');
}

function validate_and_replace_pialert_config($configfile, $content) {
    $directory = realpath(dirname($configfile));
    $validator = realpath(__DIR__ . '/../../../back/validate_pialert_config.py');
    if ($directory === false || $validator === false || strlen($content) > 262144) {
        throw new RuntimeException('Unable to prepare configuration update');
    }
    $temporary = tempnam($directory, '.pialert.conf.');
    if ($temporary === false) {
        throw new RuntimeException('Unable to create temporary configuration');
    }
    try {
        if (file_put_contents($temporary, $content, LOCK_EX) !== strlen($content)) {
            throw new RuntimeException('Unable to write temporary configuration');
        }
        $command = 'python3 ' . escapeshellarg($validator) . ' ' . escapeshellarg($temporary) .
            ' --expected-pialert-path ' . escapeshellarg(dirname($directory));
        exec($command, $output, $status);
        if ($status !== 0) {
            throw new InvalidArgumentException('Invalid configuration');
        }
        if (file_exists($configfile) && !copy($configfile, $directory . '/pialert-prev.bak')) {
            throw new RuntimeException('Unable to back up configuration');
        }
        if (!rename($temporary, $configfile)) {
            throw new RuntimeException('Unable to replace configuration');
        }
        $temporary = null;
    } finally {
        if ($temporary !== null && file_exists($temporary)) {
            unlink($temporary);
        }
    }
}

function get_config_schema() {
    static $schema = null;
    if ($schema !== null) return $schema;
    $groups = [
        'bool' => 'PRINT_LOG PIALERT_WEB_PROTECTION AUTO_UPDATE_CHECK AUTO_DB_BACKUP REPORT_NEW_CONTINUOUS NEW_DEVICE_PRESET_EVENTS NEW_DEVICE_PRESET_DOWN OFFLINE_MODE SCAN_WEBSERVICES ICMPSCAN_ACTIVE SATELLITES_ACTIVE SCAN_ROGUE_DHCP SMTP_SSL SMTP_SKIP_TLS SMTP_SKIP_LOGIN REPORT_WEBGUI REPORT_WEBGUI_WEBMON REPORT_TO_MQTT REPORT_MQTT_TLS PUBLISH_MQTT_STATUS REPORT_MAIL REPORT_MAIL_WEBMON REPORT_PUSHSAFER REPORT_PUSHSAFER_WEBMON REPORT_PUSHOVER REPORT_PUSHOVER_WEBMON REPORT_NTFY REPORT_NTFY_WEBMON NTFY_CLICKABLE REPORT_DISCORD REPORT_DISCORD_WEBMON REPORT_TELEGRAM REPORT_TELEGRAM_WEBMON DDNS_ACTIVE SPEEDTEST_TASK_ACTIVE ARPSCAN_ACTIVE PIHOLE_ACTIVE DHCP_ACTIVE DHCP_INCL_SELF_TO_LEASES FRITZBOX_ACTIVE MIKROTIK_ACTIVE UNIFI_ACTIVE OPENWRT_ACTIVE ASUSWRT_ACTIVE ASUSWRT_SSL PFSENSE_ACTIVE PFSENSE_SSL OPNSENSE_ACTIVE OPNSENSE_SSL ADGUARD_ACTIVE ADGUARD_SSL SATELLITE_PROXY_MODE',
        'int' => 'AUTO_DB_BACKUP_KEEP REPORT_TO_ARCHIVE SMTP_PORT REPORT_MQTT_PORT PUSHSAFER_PRIO PUSHSAFER_SOUND PUSHOVER_PRIO ICMP_ONLINE_TEST ICMP_GET_AVG_RTT PIHOLE_VERSION PIHOLE6_API_MAXCLIENTS PFSENSE_PORT OPNSENSE_PORT ADGUARD_PORT ADGUARD_QUERY_MINUTES ADGUARD_ACTIVITY_MINUTES ADGUARD_QUERY_LIMIT DAYS_TO_KEEP_ONLINEHISTORY DAYS_TO_KEEP_EVENTS',
        'string' => 'PIALERT_PATH VENDORS_DB PIALERT_APIKEY PIALERT_WEB_PASSWORD NETWORK_DNS_SERVER SYSTEM_TIMEZONE AUTO_UPDATE_CHECK_CRON AUTO_DB_BACKUP_CRON REPORT_NEW_CONTINUOUS_CRON SPEEDTEST_TASK_CRON SMTP_SERVER SMTP_USER SMTP_PASS REPORT_MQTT_BROKER REPORT_MQTT_USERNAME REPORT_MQTT_PASSWORD REPORT_FROM REPORT_TO REPORT_DEVICE_URL REPORT_DASHBOARD_URL PUSHSAFER_TOKEN PUSHSAFER_DEVICE PUSHOVER_TOKEN PUSHOVER_USER PUSHOVER_SOUND NTFY_HOST NTFY_TOPIC NTFY_USER NTFY_PASSWORD NTFY_PRIORITY DISCORD_BOT_TOKEN_URL TELEGRAM_BOT_TOKEN_URL TELEGRAM_BOT_TOKEN QUERY_MYIP_SERVER QUERY_MYIP_SERVER_FALLBACK DDNS_DOMAIN DDNS_USER DDNS_PASSWORD DDNS_UPDATE_URL PIHOLE_DB PIHOLE6_URL PIHOLE6_PASSWORD DHCP_LEASES FRITZBOX_IP FRITZBOX_USER FRITZBOX_PASS MIKROTIK_IP MIKROTIK_USER MIKROTIK_PASS UNIFI_IP UNIFI_API UNIFI_USER UNIFI_PASS OPENWRT_IP OPENWRT_USER OPENWRT_PASS ASUSWRT_IP ASUSWRT_USER ASUSWRT_PASS PFSENSE_IP PFSENSE_APIKEY OPNSENSE_IP OPNSENSE_APIKEY OPNSENSE_APISECRET ADGUARD_IP ADGUARD_USER ADGUARD_PASSWORD SATELLITE_PROXY_URL',
        'list' => 'MAC_IGNORE_LIST IP_IGNORE_LIST HOSTNAME_IGNORE_LIST PFSENSE_EXCLUDE_INT OPNSENSE_EXCLUDE_INT TELEGRAM_CHAT_IDS',
        'special' => 'DB_PATH LOG_PATH DHCP_SERVER_ADDRESS SCAN_SUBNETS',
    ];
    $schema = [];
    foreach ($groups as $type => $keys) {
        foreach (explode(' ', $keys) as $key) {
            $schema[$key] = ['type' => $type, 'required' => true];
        }
    }
    $schema['SMTP_SSL']['required'] = false;
    $schema['SMTP_SSL']['default'] = false;
    $schema['TELEGRAM_BOT_TOKEN']['required'] = false;
    $schema['TELEGRAM_BOT_TOKEN']['default'] = '';
    $schema['TELEGRAM_CHAT_IDS']['required'] = false;
    $schema['TELEGRAM_CHAT_IDS']['default'] = [];
    return $schema;
}

function assert_config_editor_keys($content) {
    if (!is_string($content) || strlen($content) > 262144) {
        throw new InvalidArgumentException('Invalid configuration input');
    }
    $schema = get_config_schema();
    $deprecatedKeys = ['SHOUTRRR_BINARY'];
    $seen = [];
    foreach (preg_split('/\R/', $content) as $line) {
        $line = trim($line);
        if ($line === '' || strpos($line, '#') === 0) continue;
        if (!preg_match('/^([A-Z][A-Z0-9_]*)\s*=/', $line, $matches)) {
            throw new InvalidArgumentException('Invalid configuration statement');
        }
        $key = $matches[1];
        if ((!isset($schema[$key]) && !in_array($key, $deprecatedKeys, true)) || isset($seen[$key])) {
            throw new InvalidArgumentException('Unknown or duplicate configuration key');
        }
        $seen[$key] = true;
    }
    foreach ($schema as $key => $rule) {
        if ($rule['required'] && !isset($seen[$key])) {
            throw new InvalidArgumentException('Missing required configuration key');
        }
    }
}
function escape_python_config_string($val) {
    return str_replace(
        ["\\", "'"],
        ["\\\\", "\\'"],
        (string)$val
    );
}

function decode_python_config_string($val, $quote = "'") {
    $decoded = str_replace("\\\\", "\\", (string)$val);

    if ($quote === "'") {
        $decoded = str_replace("\\'", "'", $decoded);
    } elseif ($quote === '"') {
        $decoded = str_replace('\\"', '"', $decoded);
    }

    return $decoded;
}

function serializeList($listString) {
    $listString = trim($listString, " \t\n\r\0\x0B[]");

    if ($listString === '') { return '[]'; }

    $items = preg_split('/\s*,\s*/', $listString);
    $result = [];

    foreach ($items as $item) {
        $item = trim($item, " \t\n\r\0\x0B'\"");
        if ($item === '') {
            continue;
        }

        $item = preg_replace('/\s+/', ' ', $item);
        $result[] = "'" . escape_python_config_string($item) . "'";
    }

    return '[' . implode(',', $result) . ']';
}

// Get pialert root
function find_pialert_root($startDir = __DIR__) {
    $dir = realpath($startDir);

    while ($dir !== '/' && $dir !== false) {

        if (
            file_exists($dir . '/config') &&
            file_exists($dir . '/front') &&
            file_exists($dir . '/back')
        ) {
            return $dir;
        }

        $dir = dirname($dir);
    }

    throw new Exception("Pi.Alert Root not found");
}

// Secure the path specification for the configuration file
// Layer 1: Basic sanitization (removes obvious injection vectors)
function pialert_sanitize_input($input) {

    if (!is_string($input) || trim($input) === '') {
        throw new Exception("Invalid input");
    }

    // Decode common encodings
    $input = urldecode($input);
    $input = html_entity_decode($input, ENT_QUOTES | ENT_HTML5);

    // Split injection chains early
    $input = preg_split('/[;\r\n]+/', $input)[0];

    // Remove dangerous control characters
    $input = str_replace(["\r", "\n", "\0"], '', $input);

    return trim($input);
}

// Layer 2: Structural validation (must LOOK like a path)
function pialert_validate_path_shape($path) {

    // Must contain at least one slash (absolute or relative path)
    if (strpos($path, '/') === false) {
        return false;
    }

    // Reject obvious command patterns (ls, rm, etc.)
    if (preg_match('#(^|\s)(ls|rm|cat|echo|bash|sh|wget|curl)\b#i', $path)) {
        return false;
    }

    // Only allow safe filesystem characters
    if (!preg_match('#^[a-zA-Z0-9_./\-\s\'"]+$#', $path)) {
        return false;
    }

    return true;
}

// Layer 3: Filesystem resolution + type validation
function pialert_resolve_path($path, $mustExist = true, $mustBeFile = true) {

    $realPath = realpath($path);

    if ($mustExist) {

        if ($realPath === false) {
            return false;
        }

        if ($mustBeFile && !is_file($realPath)) {
            return false;
        }

        if (!$mustBeFile && !is_dir($realPath)) {
            return false;
        }

        return $realPath;
    }

    return $path;
}

// MAIN FUNCTION: Secure path resolver
function safeConfigPath($input, $mustExist = true, $mustBeFile = true) {

    // 1. sanitize raw input
    $clean = pialert_sanitize_input($input);

    // 2. split into segments (injection chain support)
    $segments = preg_split('/[;\r\n]+/', $clean);

    foreach ($segments as $segment) {

        $segment = trim($segment);

        if ($segment === '') {
            continue;
        }

        // 3. structural validation
        if (!pialert_validate_path_shape($segment)) {
            continue;
        }

        // 4. filesystem validation
        $resolved = pialert_resolve_path($segment, $mustExist, $mustBeFile);

        if ($resolved === false) {
            continue;
        }

        // 5. FINAL NORMALIZATION STEP (IMPORTANT)
        // Removes leftover quotes from INI or earlier processing layers
        $resolved = trim($resolved, " \t\n\r\0\x0B'\"");

        return $resolved;
    }

    throw new Exception("No valid path found in input");
}

//  Save Config
function SaveConfigFile() {
	global $pia_lang;
	global $maskKeys;
	global $db;

	$laststate = '../../../config/pialert-prev.bak';
	$configfile = '../../../config/pialert.conf';

	try {
		assert_config_editor_keys($GLOBALS["pialert_request"]['configfile']);
	} catch (Throwable $exception) {
		pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9998', '1', '');
		echo 'ERROR: Invalid configuration';
		return;
	}

	// Always preserve the current configuration before processing a save.
	// copy() intentionally replaces an existing pialert-prev.bak.
	if (!copy($configfile, $laststate)) {
		pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9998', '1', '');
		echo 'ERROR: Unable to back up current configuration';
		return;
	}

	$configContent = preg_replace('/^\s*#.*$/m', '', $GLOBALS["pialert_request"]['configfile']);
	$configArray = parse_ini_string($configContent);

    // Get old Values from Backup
    $oldLines = file($laststate, FILE_IGNORE_NEW_LINES) ?: [];
    $oldValues = [];
    foreach ($oldLines as $line) {
        foreach ($maskKeys as $key) {
            if (preg_match('/^\s*' . preg_quote($key, '/') . '\s*=\s*([\'"]?)(.*)\1\s*$/', $line, $matches)) {
                $oldValues[$key] = decode_python_config_string($matches[2], $matches[1]);
            }
        }
    }

    // Read Frontend Input
    $inputLines = explode("\n", $GLOBALS["pialert_request"]['configfile']);
    $configArray = [];

    foreach ($inputLines as $line) {
        if (preg_match('/^\s*([A-Z0-9_]+)\s*=\s*([\'"]?)(.*)\2\s*$/', trim($line), $matches)) {
            $key = $matches[1];
            $value = $matches[3];

            // Masked values
            if (in_array($key, $maskKeys) && isset($oldValues[$key])) {
                $lenOld = strlen($oldValues[$key]);
                $lenFront = strlen($value);
                $firstOld = substr($oldValues[$key], 0, 1);
                $lastOld = substr($oldValues[$key], -1);
                $firstFront = substr($value, 0, 1);
                $lastFront = substr($value, -1);
                $middleFront = substr($value, 1, -1);

                if ($lenOld == $lenFront && $firstOld == $firstFront && $lastOld == $lastFront && preg_match('/^\*+$/', $middleFront)) {
                    $value = $oldValues[$key];
                }
            }

            $configArray[$key] = $value;
        }
    }

	// Handle some special entries
	$Mail_Reort = str_replace(" ", "", $configArray['REPORT_FROM']);
	if (stristr($Mail_Reort, "<+SMTP_USER+>")) {
		$mail_parts = array();
		$mail_parts = explode("<", $configArray['REPORT_FROM']);
		$mail_parts[1] = '<' . $mail_parts[1];
		$mail_parts[1] = str_replace(" ", "", $mail_parts[1]);
		$mail_parts[1] = str_replace("<+SMTP_USER+>", "<' + SMTP_USER + '>", $mail_parts[1]);
		$configArray['REPORT_FROM'] = $mail_parts[0] . $mail_parts[1];
	}
	
    if (strpos($configArray['DHCP_SERVER_ADDRESS'], '[') === false && strpos($configArray['DHCP_SERVER_ADDRESS'], ']') === false) {
        $configArray['DHCP_SERVER_ADDRESS'] = "'" . $configArray['DHCP_SERVER_ADDRESS'] . "'";
    }

	// Ignore List Syntax handling start
	if ($configArray['MAC_IGNORE_LIST'] == "") {$configArray['MAC_IGNORE_LIST'] = "[]";}
	if ($configArray['IP_IGNORE_LIST'] == "") {$configArray['IP_IGNORE_LIST'] = "[]";}
	if ($configArray['HOSTNAME_IGNORE_LIST'] == "") {$configArray['HOSTNAME_IGNORE_LIST'] = "[]";}
	if ($configArray['PFSENSE_EXCLUDE_INT'] == "") {$configArray['PFSENSE_EXCLUDE_INT'] = "[]";}
    if ($configArray['OPNSENSE_EXCLUDE_INT'] == "") {$configArray['OPNSENSE_EXCLUDE_INT'] = "[]";}

    // Ignore List Syntax handling stop
    $scanSubnets = trim($configArray['SCAN_SUBNETS']);
    if (substr($scanSubnets, 0, 2) === '--') {
        $scanSubnets = preg_replace('/\s+/', ' ', $scanSubnets);
        $configArray['SCAN_SUBNETS'] = "'" . trim($scanSubnets, "'\" ") . "'";
    } else {
        $configArray['SCAN_SUBNETS'] = serializeList($scanSubnets);
    }
	
	$configArray['NETWORK_DNS_SERVER'] = str_replace(" ", "", $configArray['NETWORK_DNS_SERVER']);
	if ($configArray['NETWORK_DNS_SERVER'] == "") {
		$configArray['NETWORK_DNS_SERVER'] = "localhost";
	} else {
		if (filter_var(gethostbyname($configArray['NETWORK_DNS_SERVER']), FILTER_VALIDATE_IP)) {
		    $configArray['NETWORK_DNS_SERVER'] = $configArray['NETWORK_DNS_SERVER'];
		} else {$configArray['NETWORK_DNS_SERVER'] = "localhost";}
	}

    $PIALERT_PATH = find_pialert_root();
    $VENDORS_DB = safeConfigPath($configArray['VENDORS_DB'], true, true);
    $PIHOLE_DB = safeConfigPath($configArray['PIHOLE_DB'], false, true);
    $DHCP_LEASES = safeConfigPath($configArray['DHCP_LEASES'], false, true);

    $smtpSsl = isset($configArray['SMTP_SSL']) ? $configArray['SMTP_SSL'] : false;
    $telegramBotToken = isset($configArray['TELEGRAM_BOT_TOKEN'])
        ? $configArray['TELEGRAM_BOT_TOKEN'] : '';
    $telegramChatIds = isset($configArray['TELEGRAM_CHAT_IDS'])
        ? serializeList($configArray['TELEGRAM_CHAT_IDS']) : '[]';
	$config_template = "# General Settings
# ----------------------
PIALERT_PATH               = '" . $PIALERT_PATH . "'
DB_PATH                    = PIALERT_PATH + '/db/pialert.db'
LOG_PATH                   = PIALERT_PATH + '/log'
PRINT_LOG                  = " . convert_bool($configArray['PRINT_LOG']) . "
VENDORS_DB                 = '" . $VENDORS_DB . "'
PIALERT_APIKEY             = '" . escape_python_config_string($configArray['PIALERT_APIKEY']) . "'
PIALERT_WEB_PROTECTION     = " . convert_bool($configArray['PIALERT_WEB_PROTECTION']) . "
PIALERT_WEB_PASSWORD       = '" . escape_python_config_string($configArray['PIALERT_WEB_PASSWORD']) . "'
NETWORK_DNS_SERVER         = '" . $configArray['NETWORK_DNS_SERVER'] . "'
AUTO_UPDATE_CHECK          = " . convert_bool($configArray['AUTO_UPDATE_CHECK']) . "
AUTO_DB_BACKUP             = " . convert_bool($configArray['AUTO_DB_BACKUP']) . "
AUTO_DB_BACKUP_KEEP        = " . ((isset($configArray['AUTO_DB_BACKUP_KEEP']) && is_numeric($configArray['AUTO_DB_BACKUP_KEEP'])) ? $configArray['AUTO_DB_BACKUP_KEEP'] : 5) . "
REPORT_NEW_CONTINUOUS      = " . convert_bool($configArray['REPORT_NEW_CONTINUOUS']) . "
NEW_DEVICE_PRESET_EVENTS   = " . convert_bool($configArray['NEW_DEVICE_PRESET_EVENTS']) . "
NEW_DEVICE_PRESET_DOWN     = " . convert_bool($configArray['NEW_DEVICE_PRESET_DOWN']) . "
SYSTEM_TIMEZONE            = '" . $configArray['SYSTEM_TIMEZONE'] . "'
OFFLINE_MODE               = " . convert_bool($configArray['OFFLINE_MODE']) . "

# Other Modules
# ----------------------
SCAN_WEBSERVICES           = " . convert_bool($configArray['SCAN_WEBSERVICES']) . "
ICMPSCAN_ACTIVE            = " . convert_bool($configArray['ICMPSCAN_ACTIVE']) . "
SATELLITES_ACTIVE          = " . convert_bool($configArray['SATELLITES_ACTIVE']) . "

# Special Protocol Scanning
# ----------------------
SCAN_ROGUE_DHCP            = " . convert_bool($configArray['SCAN_ROGUE_DHCP']) . "
DHCP_SERVER_ADDRESS        = " . $configArray['DHCP_SERVER_ADDRESS'] . "
# DHCP_SERVER_ADDRESS        = '192.168.1.1'
# DHCP_SERVER_ADDRESS        = ['192.168.1.1','10.0.0.1']

# Custom Cronjobs
# ----------------------
# The shortest interval is 3 minutes. All larger intervals must be integer multiples of 3 minutes.
AUTO_UPDATE_CHECK_CRON     = '" . $configArray['AUTO_UPDATE_CHECK_CRON'] . "'
AUTO_DB_BACKUP_CRON        = '" . $configArray['AUTO_DB_BACKUP_CRON'] . "'
REPORT_NEW_CONTINUOUS_CRON = '" . $configArray['REPORT_NEW_CONTINUOUS_CRON'] . "'
SPEEDTEST_TASK_CRON        = '" . $configArray['SPEEDTEST_TASK_CRON'] . "'

# Mail-Account Settings
# ----------------------
SMTP_SERVER                = '" . $configArray['SMTP_SERVER'] . "'
SMTP_PORT                  = " . ((isset($configArray['SMTP_PORT']) && is_numeric($configArray['SMTP_PORT'])) ? $configArray['SMTP_PORT'] : 587) . "
SMTP_USER                  = '" . $configArray['SMTP_USER'] . "'
SMTP_PASS                  = '" . escape_python_config_string($configArray['SMTP_PASS']) . "'
SMTP_SSL                   = " . convert_bool($smtpSsl) . "
SMTP_SKIP_TLS	           = " . convert_bool($configArray['SMTP_SKIP_TLS']) . "
SMTP_SKIP_LOGIN	           = " . convert_bool($configArray['SMTP_SKIP_LOGIN']) . "

# WebGUI Reporting
# ----------------------
REPORT_WEBGUI              = " . convert_bool($configArray['REPORT_WEBGUI']) . "
REPORT_WEBGUI_WEBMON       = " . convert_bool($configArray['REPORT_WEBGUI_WEBMON']) . "
REPORT_TO_ARCHIVE          = " . ((isset($configArray['REPORT_TO_ARCHIVE']) && is_numeric($configArray['REPORT_TO_ARCHIVE'])) ? $configArray['REPORT_TO_ARCHIVE'] : 12) . "
# Number of hours after which a report is moved to the archive. The value 0 disables the feature

# MQTT Reporting
# ----------------------
REPORT_TO_MQTT             = " . convert_bool($configArray['REPORT_TO_MQTT']) . "
REPORT_MQTT_BROKER         = '" . $configArray['REPORT_MQTT_BROKER'] . "'
REPORT_MQTT_PORT           = " . ((isset($configArray['REPORT_MQTT_PORT']) && is_numeric($configArray['REPORT_MQTT_PORT'])) ? $configArray['REPORT_MQTT_PORT'] : 1883) . "
REPORT_MQTT_USERNAME       = '" . $configArray['REPORT_MQTT_USERNAME'] . "'
REPORT_MQTT_PASSWORD       = '" . escape_python_config_string($configArray['REPORT_MQTT_PASSWORD']) . "'
REPORT_MQTT_TLS            = " . convert_bool($configArray['REPORT_MQTT_TLS']) . "
PUBLISH_MQTT_STATUS        = " . convert_bool($configArray['PUBLISH_MQTT_STATUS']) . "

# Mail Reporting
# ----------------------
REPORT_MAIL                = " . convert_bool($configArray['REPORT_MAIL']) . "
REPORT_MAIL_WEBMON         = " . convert_bool($configArray['REPORT_MAIL_WEBMON']) . "
REPORT_FROM                = '" . $configArray['REPORT_FROM'] . "'
REPORT_TO                  = '" . $configArray['REPORT_TO'] . "'
REPORT_DEVICE_URL          = '" . $configArray['REPORT_DEVICE_URL'] . "'
REPORT_DASHBOARD_URL       = '" . $configArray['REPORT_DASHBOARD_URL'] . "'

# Pushsafer
# ----------------------
REPORT_PUSHSAFER           = " . convert_bool($configArray['REPORT_PUSHSAFER']) . "
REPORT_PUSHSAFER_WEBMON    = " . convert_bool($configArray['REPORT_PUSHSAFER_WEBMON']) . "
PUSHSAFER_TOKEN            = '" . escape_python_config_string($configArray['PUSHSAFER_TOKEN']) . "'
PUSHSAFER_DEVICE           = '" . $configArray['PUSHSAFER_DEVICE'] . "'
PUSHSAFER_PRIO             = " . ((isset($configArray['PUSHSAFER_PRIO']) && is_numeric($configArray['PUSHSAFER_PRIO'])) ? $configArray['PUSHSAFER_PRIO'] : 0) . "
PUSHSAFER_SOUND            = " . ((isset($configArray['PUSHSAFER_SOUND']) && is_numeric($configArray['PUSHSAFER_SOUND'])) ? $configArray['PUSHSAFER_SOUND'] : 22) . "

# Pushover
# ----------------------
REPORT_PUSHOVER            = " . convert_bool($configArray['REPORT_PUSHOVER']) . "
REPORT_PUSHOVER_WEBMON     = " . convert_bool($configArray['REPORT_PUSHOVER_WEBMON']) . "
PUSHOVER_TOKEN             = '" . escape_python_config_string($configArray['PUSHOVER_TOKEN']) . "'
PUSHOVER_USER              = '" . escape_python_config_string($configArray['PUSHOVER_USER']) . "'
PUSHOVER_PRIO              = " . ((isset($configArray['PUSHOVER_PRIO']) && is_numeric($configArray['PUSHOVER_PRIO'])) ? $configArray['PUSHOVER_PRIO'] : 0) . "
PUSHOVER_SOUND             = '" . $configArray['PUSHOVER_SOUND'] . "'

# NTFY
#---------------------------
REPORT_NTFY                = " . convert_bool($configArray['REPORT_NTFY']) . "
REPORT_NTFY_WEBMON         = " . convert_bool($configArray['REPORT_NTFY_WEBMON']) . "
NTFY_HOST                  = '" . $configArray['NTFY_HOST'] . "'
NTFY_TOPIC                 = '" . $configArray['NTFY_TOPIC'] . "'
NTFY_USER                  = '" . $configArray['NTFY_USER'] . "'
NTFY_PASSWORD	           = '" . escape_python_config_string($configArray['NTFY_PASSWORD']) . "'
NTFY_PRIORITY 	           = '" . $configArray['NTFY_PRIORITY'] . "'
NTFY_CLICKABLE 	           = " . convert_bool($configArray['NTFY_CLICKABLE']) . "

# Discord
# ----------------------
REPORT_DISCORD             = " . convert_bool($configArray['REPORT_DISCORD']) . "
REPORT_DISCORD_WEBMON      = " . convert_bool($configArray['REPORT_DISCORD_WEBMON']) . "
DISCORD_BOT_TOKEN_URL      = '" . $configArray['DISCORD_BOT_TOKEN_URL'] . "'

# Telegram
# ----------------------
REPORT_TELEGRAM            = " . convert_bool($configArray['REPORT_TELEGRAM']) . "
REPORT_TELEGRAM_WEBMON     = " . convert_bool($configArray['REPORT_TELEGRAM_WEBMON']) . "
TELEGRAM_BOT_TOKEN         = '" . escape_python_config_string($telegramBotToken) . "'
TELEGRAM_CHAT_IDS          = " . $telegramChatIds . "
# Deprecated compatibility input. Shoutrrr is not used to send notifications.
TELEGRAM_BOT_TOKEN_URL     = '" . escape_python_config_string($configArray['TELEGRAM_BOT_TOKEN_URL']) . "'

# DynDNS and IP
# ----------------------
QUERY_MYIP_SERVER          = '" . $configArray['QUERY_MYIP_SERVER'] . "'
QUERY_MYIP_SERVER_FALLBACK = '" . $configArray['QUERY_MYIP_SERVER_FALLBACK'] . "'
DDNS_ACTIVE                = " . convert_bool($configArray['DDNS_ACTIVE']) . "
DDNS_DOMAIN                = '" . $configArray['DDNS_DOMAIN'] . "'
DDNS_USER                  = '" . $configArray['DDNS_USER'] . "'
DDNS_PASSWORD              = '" . escape_python_config_string($configArray['DDNS_PASSWORD']) . "'
DDNS_UPDATE_URL            = '" . $configArray['DDNS_UPDATE_URL'] . "'

# Automatic Speedtest
# ----------------------
SPEEDTEST_TASK_ACTIVE      = " . convert_bool($configArray['SPEEDTEST_TASK_ACTIVE']) . "

# Arp-scan Options & Samples
# ----------------------
ARPSCAN_ACTIVE             = " . convert_bool($configArray['ARPSCAN_ACTIVE']) . "
MAC_IGNORE_LIST            = " . $configArray['MAC_IGNORE_LIST'] . "
IP_IGNORE_LIST             = " . $configArray['IP_IGNORE_LIST'] . "
HOSTNAME_IGNORE_LIST       = " . $configArray['HOSTNAME_IGNORE_LIST'] . "
SCAN_SUBNETS               = " . $configArray['SCAN_SUBNETS'] . "
# SCAN_SUBNETS               = '--localnet'
# SCAN_SUBNETS               = '--localnet --interface=eth0'
# SCAN_SUBNETS               = ['192.168.1.0/24 --interface=eth0','192.168.2.0/24 --interface=eth1']

# ICMP Monitoring Options
# ----------------------
ICMP_ONLINE_TEST           = " . ((isset($configArray['ICMP_ONLINE_TEST']) && is_numeric($configArray['ICMP_ONLINE_TEST'])) ? $configArray['ICMP_ONLINE_TEST'] : 2) . "
ICMP_GET_AVG_RTT           = " . ((isset($configArray['ICMP_GET_AVG_RTT']) && is_numeric($configArray['ICMP_GET_AVG_RTT'])) ? $configArray['ICMP_GET_AVG_RTT'] : 3) . "

# Pi-hole Configuration
# ----------------------
PIHOLE_ACTIVE              = " . convert_bool($configArray['PIHOLE_ACTIVE']) . "
PIHOLE_VERSION             = " . ((isset($configArray['PIHOLE_VERSION']) && is_numeric($configArray['PIHOLE_VERSION'])) ? $configArray['PIHOLE_VERSION'] : 6) . "
PIHOLE_DB                  = '" . $PIHOLE_DB . "'
PIHOLE6_URL                = '" . $configArray['PIHOLE6_URL'] . "'
PIHOLE6_PASSWORD           = '" . escape_python_config_string($configArray['PIHOLE6_PASSWORD']) . "'
PIHOLE6_API_MAXCLIENTS     = " . ((isset($configArray['PIHOLE6_API_MAXCLIENTS']) && is_numeric($configArray['PIHOLE6_API_MAXCLIENTS'])) ? $configArray['PIHOLE6_API_MAXCLIENTS'] : 150) . "
DHCP_ACTIVE                = " . convert_bool($configArray['DHCP_ACTIVE']) . "
DHCP_LEASES                = '" . $DHCP_LEASES . "'
DHCP_INCL_SELF_TO_LEASES   = " . convert_bool($configArray['DHCP_INCL_SELF_TO_LEASES']) . "

# Fritzbox Configuration
# ----------------------
FRITZBOX_ACTIVE            = " . convert_bool($configArray['FRITZBOX_ACTIVE']) . "
FRITZBOX_IP                = '" . $configArray['FRITZBOX_IP'] . "'
FRITZBOX_USER              = '" . $configArray['FRITZBOX_USER'] . "'
FRITZBOX_PASS              = '" . escape_python_config_string($configArray['FRITZBOX_PASS']) . "'

# Mikrotik Configuration
# ----------------------
MIKROTIK_ACTIVE            = " . convert_bool($configArray['MIKROTIK_ACTIVE']) . "
MIKROTIK_IP                = '" . $configArray['MIKROTIK_IP'] . "'
MIKROTIK_USER              = '" . $configArray['MIKROTIK_USER'] . "'
MIKROTIK_PASS              = '" . escape_python_config_string($configArray['MIKROTIK_PASS']) . "'

# UniFi Configuration
# -------------------
UNIFI_ACTIVE               = " . convert_bool($configArray['UNIFI_ACTIVE']) . "
UNIFI_IP                   = '" . $configArray['UNIFI_IP'] . "'
UNIFI_API                  = '" . $configArray['UNIFI_API'] . "'
UNIFI_USER                 = '" . $configArray['UNIFI_USER'] . "'
UNIFI_PASS                 = '" . escape_python_config_string($configArray['UNIFI_PASS']) . "'
# Possible UNIFI APIs are v4, v5, unifiOS, UDMP-unifiOS, default

# OpenWRT Configuration
# ----------------------
OPENWRT_ACTIVE            = " . convert_bool($configArray['OPENWRT_ACTIVE']) . "
OPENWRT_IP                = '" . $configArray['OPENWRT_IP'] . "'
OPENWRT_USER              = '" . $configArray['OPENWRT_USER'] . "'
OPENWRT_PASS              = '" . escape_python_config_string($configArray['OPENWRT_PASS']) . "'

# AsusWRT Configuration
# ----------------------
ASUSWRT_ACTIVE            = " . convert_bool($configArray['ASUSWRT_ACTIVE']) . "
ASUSWRT_IP                = '" . $configArray['ASUSWRT_IP'] . "'
ASUSWRT_USER              = '" . $configArray['ASUSWRT_USER'] . "'
ASUSWRT_PASS              = '" . escape_python_config_string($configArray['ASUSWRT_PASS']) . "'
ASUSWRT_SSL               = " . convert_bool($configArray['ASUSWRT_SSL']) . "

# pfsense Configuration
# ----------------------
PFSENSE_ACTIVE            = " . convert_bool($configArray['PFSENSE_ACTIVE']) . "
PFSENSE_IP                = '" . $configArray['PFSENSE_IP'] . "'
PFSENSE_PORT              = " . ((isset($configArray['PFSENSE_PORT']) && is_numeric($configArray['PFSENSE_PORT'])) ? $configArray['PFSENSE_PORT'] : 443) . "
PFSENSE_APIKEY            = '" . escape_python_config_string($configArray['PFSENSE_APIKEY']) . "'
PFSENSE_SSL               = " . convert_bool($configArray['PFSENSE_SSL']) . "
PFSENSE_EXCLUDE_INT       = " . $configArray['PFSENSE_EXCLUDE_INT'] . "

# OPNsense Configuration
# ----------------------
OPNSENSE_ACTIVE            = " . convert_bool($configArray['OPNSENSE_ACTIVE']) . "
OPNSENSE_IP                = '" . $configArray['OPNSENSE_IP'] . "'
OPNSENSE_PORT              = " . ((isset($configArray['OPNSENSE_PORT']) && is_numeric($configArray['OPNSENSE_PORT'])) ? $configArray['OPNSENSE_PORT'] : 443) . "
OPNSENSE_APIKEY            = '" . escape_python_config_string($configArray['OPNSENSE_APIKEY']) . "'
OPNSENSE_APISECRET         = '" . escape_python_config_string($configArray['OPNSENSE_APISECRET']) . "'
OPNSENSE_SSL               = " . convert_bool($configArray['OPNSENSE_SSL']) . "
OPNSENSE_EXCLUDE_INT       = " . $configArray['OPNSENSE_EXCLUDE_INT'] . "

# AdGuard Configuration
# ---------------------
ADGUARD_ACTIVE            = " . convert_bool($configArray['ADGUARD_ACTIVE']) . "
ADGUARD_IP                = '" . escape_python_config_string($configArray['ADGUARD_IP']) . "'
ADGUARD_PORT              = " . ((isset($configArray['ADGUARD_PORT']) && is_numeric($configArray['ADGUARD_PORT'])) ? $configArray['ADGUARD_PORT'] : 80) . "
ADGUARD_USER              = '" . escape_python_config_string($configArray['ADGUARD_USER']) . "'
ADGUARD_PASSWORD          = '" . escape_python_config_string($configArray['ADGUARD_PASSWORD']) . "'
ADGUARD_SSL               = " . convert_bool($configArray['ADGUARD_SSL']) . "
ADGUARD_QUERY_MINUTES     = " . ((isset($configArray['ADGUARD_QUERY_MINUTES']) && is_numeric($configArray['ADGUARD_QUERY_MINUTES'])) ? $configArray['ADGUARD_QUERY_MINUTES'] : 5) . "
ADGUARD_ACTIVITY_MINUTES  = " . ((isset($configArray['ADGUARD_ACTIVITY_MINUTES']) && is_numeric($configArray['ADGUARD_ACTIVITY_MINUTES'])) ? $configArray['ADGUARD_ACTIVITY_MINUTES'] : 10) . "
ADGUARD_QUERY_LIMIT       = " . ((isset($configArray['ADGUARD_QUERY_LIMIT']) && is_numeric($configArray['ADGUARD_QUERY_LIMIT'])) ? $configArray['ADGUARD_QUERY_LIMIT'] : 1000) . "

# Satellite Configuration
# -----------------------
SATELLITE_PROXY_MODE       = " . convert_bool($configArray['SATELLITE_PROXY_MODE']) . "
SATELLITE_PROXY_URL        = '" . $configArray['SATELLITE_PROXY_URL'] . "'

# Maintenance Tasks Cron
# ----------------------
DAYS_TO_KEEP_ONLINEHISTORY = " . ((isset($configArray['DAYS_TO_KEEP_ONLINEHISTORY']) && is_numeric($configArray['DAYS_TO_KEEP_ONLINEHISTORY'])) ? $configArray['DAYS_TO_KEEP_ONLINEHISTORY'] : 60) . "
DAYS_TO_KEEP_EVENTS        = " . ((isset($configArray['DAYS_TO_KEEP_EVENTS']) && is_numeric($configArray['DAYS_TO_KEEP_EVENTS'])) ? $configArray['DAYS_TO_KEEP_EVENTS'] : 0) . "
";

	try {
		validate_and_replace_pialert_config($configfile, $config_template);
	} catch (Throwable $exception) {
		pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9998', '1', '');
		echo 'ERROR: Invalid configuration';
		echo "<meta http-equiv='refresh' content='2; URL=./index.php'>";
		return;
	}

	if (($oldValues["PIALERT_WEB_PASSWORD"] ?? null) !== ($configArray["PIALERT_WEB_PASSWORD"] ?? null)) {
		pialert_revoke_all_remember_tokens($db);
	}
	echo $pia_lang['BE_Dev_ConfEditor_CopOkay'];

	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', '');
	echo "<meta http-equiv='refresh' content='2; URL=./index.php'>";
}

//  Backup DB to Archiv
function BackupDBtoArchive() {

	$db_file_path = '../../../db';
	$db_file_name_org = 'pialert.db';
	$dbtools_file_name_org = 'pialert_tools.db';
	$db_file_path_temp = '../../../db/temp';

	$db_file_org_full = $db_file_path . '/' . $db_file_name_org; # ../../../db/pialert.db
	$db_file_new_full = $db_file_path_temp . '/' . $db_file_name_org; # ../../../db/temp/pialert.db
	$dbtools_file_org_full = $db_file_path . '/' . $dbtools_file_name_org; # ../../../db/pialert.db
	$dbtools_file_new_full = $db_file_path_temp . '/' . $dbtools_file_name_org; # ../../../db/temp/pialert.db

	$Pia_Archive_Name = 'pialertdb_' . date("Ymd_His") . '.zip';
	$Pia_Archive_Path = '../../../db/';

	global $pia_lang;

	// Check if DB has open transactions
	if ((filesize($db_file_org_full . '-wal') != "0") && (filesize($dbtools_file_org_full . '-wal') != "0")) {
		//DEBUG
		//echo filesize($db_file_org_full.'-shm').'-'.filesize($db_file_org_full.'-wal').' - ';
		echo $pia_lang['BE_Dev_Backup_WALError'];exit;
	}

	// copy database
	exec('sqlite3 "' . $db_file_org_full . '" ".backup ' . $db_file_new_full . '"', $output);
	exec('sqlite3 "' . $dbtools_file_org_full . '" ".backup ' . $dbtools_file_new_full . '"', $output);

	if (file_exists($db_file_new_full) && file_exists($dbtools_file_new_full)) {
		// Integrity Check if file copy exists
		// $sql1 = "PRAGMA integrity_check";
		// $sql2 = "PRAGMA foreign_key_check";
		// exec('sqlite3 ' . $db_file_temp_full . ' "' . $sql1 . '"', $output_a);
		// exec('sqlite3 ' . $db_file_temp_full . ' "' . $sql2 . '"', $output_b);

		// Create archive with actual date
		exec('zip -j ' . $Pia_Archive_Path . $Pia_Archive_Name . ' ' . $db_file_new_full . ' ' . $dbtools_file_org_full, $output);
		// check if archive exists
		if (file_exists($Pia_Archive_Path . $Pia_Archive_Name) && filesize($Pia_Archive_Path . $Pia_Archive_Name) > 0) {
			// if archive exists
			echo $pia_lang['BE_Dev_Backup_okay'] . ': (' . $Pia_Archive_Name . ')';
			unlink($db_file_new_full);
			echo ("<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=3'>");
		} else {
			// if archive not exists
			echo $pia_lang['BE_Dev_Backup_Failed'] . ' / Integrity Checked (pialert-latestbackup.db)';
		}
	} else {
		// File does not exists
		echo $pia_lang['BE_Dev_Backup_CopError'];exit;
	}
	// Logging
	pialert_logging('a_010', $_SERVER['REMOTE_ADDR'], 'LogStr_0011', '', '');
}

//  Backup DB to CSV
function BackupDBtoCSV() {
	global $pia_lang;

	$Pia_Archive_Name = 'pialertcsv.zip';
	$Pia_Archive_Path = '../../../db/';

	$db_file_path = '../../../db';
	$db_file_name_org = 'pialert.db';
	$db_file_path_temp = '../../../db/temp';

	$db_file_org_full = $db_file_path . '/' . $db_file_name_org; # ../../../db/pialert.db
	$csv_file_devices = $db_file_path_temp . '/devices.csv'; # ../../../db/temp/devices.csv
	$csv_file_services = $db_file_path_temp . '/services.csv'; # ../../../db/temp/services.csv
	$csv_file_icmphosts = $db_file_path_temp . '/icmphosts.csv'; # ../../../db/temp/icmphosts.csv

	// delete old archive
	unlink($Pia_Archive_Path . $Pia_Archive_Name);

	exec('sqlite3 -header -csv "' . $db_file_org_full . '" "select * from devices;" > ' . $csv_file_devices, $output);
	exec('sqlite3 -header -csv "' . $db_file_org_full . '" "select * from services;" > ' . $csv_file_services, $output);
	exec('sqlite3 -header -csv "' . $db_file_org_full . '" "select * from ICMP_Mon;" > ' . $csv_file_icmphosts, $output);

	if (!file_exists($csv_file_devices) || !file_exists($csv_file_services) || !file_exists($csv_file_icmphosts)) {
		echo $pia_lang['BE_Dev_BackupCSV_FailedExport'];
		// delete csv files
		unlink($csv_file_devices);
		unlink($csv_file_services);
		unlink($csv_file_icmphosts);
		exit;
	}

	// create new archive
	exec('zip -j ' . $Pia_Archive_Path . $Pia_Archive_Name . ' ' . $db_file_path_temp . '/*.csv', $output);
	// delete csv files
	unlink($csv_file_devices);
	unlink($csv_file_services);
	unlink($csv_file_icmphosts);

	if (!file_exists($Pia_Archive_Path . $Pia_Archive_Name)) {
		echo $pia_lang['BE_Dev_BackupCSV_FailedZip'];
		exit;
	}

	echo $pia_lang['BE_Dev_BackupCSV_okay'];
	// Logging
	pialert_logging('a_010', $_SERVER['REMOTE_ADDR'], 'LogStr_0036', '', '');
}

//  Restore DB from Archiv
function RestoreDBfromArchive() {
	// prepare fast Backup
	$file = '../../../db/pialert.db';
	
	global $pia_lang;

	$Pia_Archive_Path = '../../../db/';
	exec('/bin/ls -Art ' . $Pia_Archive_Path . '*.zip | /bin/tail -n 1 | /usr/bin/xargs -n1 /bin/unzip -o -d ../../../db/', $output);
	// check if the pialert.db exists
	if (file_exists($file)) {
		echo $pia_lang['BE_Dev_Restore_okay'];
		// unlink($oldfile);
		echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=3'>";
	} else {
		echo $pia_lang['BE_Dev_Restore_Failed'];
	}
	// }
}

//  Enable Login
function LoginEnable() {
	global $pia_lang;
	global $db;

	pialert_revoke_all_remember_tokens($db);
	$sessionCookieName = session_name();
	$_SESSION = array();
	session_destroy();
	pialert_delete_auth_cookie($sessionCookieName);
	exec("../../../back/pialert-cli set_login", $output);
	pialert_logging("a_005", $_SERVER["REMOTE_ADDR"], "LogStr_0050", "", "");
	echo $pia_lang["BE_Dev_Login_enabled"];
	echo "<meta http-equiv='refresh' content='1; ./index.php'>";
}

//  Disable Login
function LoginDisable() {
	global $pia_lang;
	global $db;

	pialert_logging("a_005", $_SERVER["REMOTE_ADDR"], "LogStr_0051", "", "");
	pialert_revoke_all_remember_tokens($db);
	$sessionCookieName = session_name();
	$_SESSION = array();
	session_destroy();
	pialert_delete_auth_cookie($sessionCookieName);
	exec("../../../back/pialert-cli unset_login", $output);
	echo $pia_lang["BE_Dev_Login_disabled"];
	echo "<meta http-equiv='refresh' content='1; ./index.php'>";
}

//  Set Device List Columns
function setDeviceListCol() {
	global $pia_lang;

	$param_map = [
	    'connectiontype' => 'Set_ConnectionType',
	    'favorite'       => 'Set_Favorites',
	    'group'          => 'Set_Group',
	    'owner'          => 'Set_Owner',
	    'type'           => 'Set_Type',
	    'firstsess'      => 'Set_First_Session',
	    'lastsess'       => 'Set_Last_Session',
	    'lastip'         => 'Set_LastIP',
	    'mactype'        => 'Set_MACType',
	    'macaddress'     => 'Set_MACAddress',
	    'macvendor'      => 'Set_MACVendor',
	    'location'       => 'Set_Location',
	    'wakeonlan'      => 'Set_WakeOnLAN'
	];

	foreach ($param_map as $request_key => $var_name) {
	    if (!isset($GLOBALS["pialert_request"][$request_key]) || !in_array($GLOBALS["pialert_request"][$request_key], ['0', '1'], true)) {
	        exit("Error. Wrong variable value for $request_key!");
	    }
	    $$var_name = $GLOBALS["pialert_request"][$request_key]; // dynamische Variable
	}

	echo $pia_lang['BE_Dev_DevListCol_noti_text'];
	$config_array = array('ConnectionType' => $Set_ConnectionType, 'Favorites' => $Set_Favorites, 'Group' => $Set_Group, 'Owner' => $Set_Owner, 'Type' => $Set_Type, 'FirstSession' => $Set_First_Session, 'LastSession' => $Set_Last_Session, 'LastIP' => $Set_LastIP, 'MACType' => $Set_MACType, 'MACAddress' => $Set_MACAddress, 'MACVendor' => $Set_MACVendor, 'Location' => $Set_Location, 'WakeOnLAN' => $Set_WakeOnLAN);
	$DevListCol_file = '../../../config/setting_devicelist';
	$DevListCol_new = fopen($DevListCol_file, 'w');
	fwrite($DevListCol_new, json_encode($config_array));
	fclose($DevListCol_new);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0052', '', '');
}

//  Set Device List Columns
function setListHeaderConfig() {
	global $pia_lang;

	$valid_keys = [
	    'devices' => ['hc_devall' => 'all', 'hc_devcon' => 'con', 'hc_devfav' => 'fav', 'hc_devdnw' => 'dnw', 'hc_devarc' => 'arc', 'hc_devnew' => 'new'],
	    'icmp' =>    ['hc_icmpall' => 'all', 'hc_icmpcon' => 'con', 'hc_icmpfav' => 'fav', 'hc_icmpdnw' => 'dnw', 'hc_icmparc' => 'arc'],
	    'presence' => ['hc_presall' => 'all', 'hc_prescon' => 'con', 'hc_presfav' => 'fav', 'hc_presdnw' => 'dnw', 'hc_presarc' => 'arc', 'hc_presnew' => 'new']
	];

	$list = [];
	foreach ($valid_keys as $category => $keys) {
	    foreach ($keys as $request_key => $subkey) {
	        if (!isset($GLOBALS["pialert_request"][$request_key]) || !in_array($GLOBALS["pialert_request"][$request_key], ['0', '1'], true)) {
	            exit("Error. Wrong variable value for $request_key!");
	        }
	        $list[$category][$subkey] = (int) $GLOBALS["pialert_request"][$request_key];
	    }
	}

	echo $pia_lang['BE_Files_HeaderConfig_noti_text'];
	$ListHeaderConfig_file = '../../../config/setting_listheaders';
	$ListHeaderConfig_new = fopen($ListHeaderConfig_file, 'w');
	fwrite($ListHeaderConfig_new, json_encode($list));
	fclose($ListHeaderConfig_new);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0076', '', '');
}

//  Purge Backups
function PurgeDBBackups() {
	global $pia_lang;

	// Clean DB Backups
	$Pia_Archive_Path = '../../../db';
	$Pia_Backupfiles = array();
	$files = array_diff(scandir($Pia_Archive_Path, SCANDIR_SORT_DESCENDING), array('.', '..', 'pialert.db', 'temp', 'GeoLite2-Country.mmdb', 'pialert.db-shm', 'pialert.db-wal', 'pialertcsv.zip', 'user_vendors.txt', 'pialert_tools.db', 'pialert_tools.db-shm', 'pialert_tools.db-wal'));
    foreach ($files as &$item) {
        $item = $Pia_Archive_Path . '/' . $item;
        array_push($Pia_Backupfiles, $item);
    }
	if (sizeof($Pia_Backupfiles) > 3) {
		rsort($Pia_Backupfiles);
		unset($Pia_Backupfiles[0], $Pia_Backupfiles[1], $Pia_Backupfiles[2]);
		$Pia_Backupfiles_Purge = array_values($Pia_Backupfiles);
		for ($i = 0; $i < sizeof($Pia_Backupfiles_Purge); $i++) {
			unlink($Pia_Backupfiles_Purge[$i]);
		}
	}
	// Clean Config Backups
	unset($Pia_Backupfiles);
	$Pia_Archive_Path = '../../../config';
	$Pia_Backupfiles = array();
	$files = array_diff(scandir($Pia_Archive_Path, SCANDIR_SORT_DESCENDING), array('.', '..', 'pialert.conf', 'pialert.example.conf', 'version.conf', 'pialert-prev.bak', 'pialert.conf.back'));
	foreach ($files as &$item) {
		$item = $Pia_Archive_Path . '/' . $item;
		if (stristr($item, 'setting_') == '') {array_push($Pia_Backupfiles, $item);}
	}
	if (sizeof($Pia_Backupfiles) > 3) {
		rsort($Pia_Backupfiles);
		unset($Pia_Backupfiles[0], $Pia_Backupfiles[1], $Pia_Backupfiles[2]);
		$Pia_Backupfiles_Purge = array_values($Pia_Backupfiles);
		for ($i = 0; $i < sizeof($Pia_Backupfiles_Purge); $i++) {
			unlink($Pia_Backupfiles_Purge[$i]);
		}
	}
	// Logging
	pialert_logging('a_010', $_SERVER['REMOTE_ADDR'], 'LogStr_0013', '', '');

	echo $pia_lang['BE_Dev_DBTools_Purge'];
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=3'>";
}

//  Toggle Dark/Light Themes
function EnableDarkmode() {
	$file = '../../../config/setting_darkmode';
	global $pia_lang;

	if (file_exists($file)) {
		echo $pia_lang['BE_Dev_darkmode_disabled'];
		unlink($file);
		// Logging
		pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0055', '', '');

		echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
	} else {
		echo $pia_lang['BE_Dev_darkmode_enabled'];
		$darkmode = fopen($file, 'w');
		// Logging
		pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0056', '', '');
		echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
	}
}

//  Toggle History Graph Themes
function EnableOnlineHistoryGraph() {
	$file = '../../../config/setting_noonlinehistorygraph';
	global $pia_lang;

	if (file_exists($file)) {
		echo $pia_lang['BE_Dev_onlinehistorygraph_enabled'];
		unlink($file);
		// Logging
		pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0058', '', '');
		echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
	} else {
		echo $pia_lang['BE_Dev_onlinehistorygraph_disabled'];
		$history = fopen($file, 'w');
		fclose($history);
		// Logging
		pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0057', '', '');
		echo "<meta http-equiv='refresh'content='2; URL=./maintenance.php?tab=4'>";
	}
}

//  Set API-Key
function SetAPIKey() {
	//$file = '../../../db/setting_noonlinehistorygraph';
	global $pia_lang;

	exec('../../../back/pialert-cli set_apikey', $output);
	// Logging
	pialert_logging('a_070', $_SERVER['REMOTE_ADDR'], 'LogStr_0700', '', '');
	echo $pia_lang['BE_Dev_setapikey'];
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=1'>";
}

//  Set Theme
function setTheme() {
	global $pia_lang;

	$installed_skins = array('skin-black-light',
		'skin-black',
		'skin-blue-light',
		'skin-blue',
		'skin-green-light',
		'skin-green',
		'skin-purple-light',
		'skin-purple',
		'skin-red-light',
		'skin-red',
		'skin-yellow-light',
		'skin-yellow');

	$installed_themes = array('leiweibau_dark',
		'leiweibau_light');

	if (isset($GLOBALS["pialert_request"]['SkinSelection'])) {
		$skin_set_dir = '../../../config/';
		// echo "Enter Level 1";
		$skin_selector = htmlspecialchars($GLOBALS["pialert_request"]['SkinSelection']);
		if (in_array($skin_selector, $installed_skins)) {
			// lösche alle vorherigen skins
			foreach ($installed_skins as $file) {
				unlink($skin_set_dir . 'setting_' . $file);
			}
			// lösche alle vorherigen themes
			foreach ($installed_themes as $file) {
				unlink($skin_set_dir . 'setting_theme_' . $file);
			}
			foreach ($installed_skins as $file) {
				if (file_exists($skin_set_dir . 'setting_' . $file)) {
					$skin_error = True;
					break;
				} else {
					$skin_error = False;
				}
			}
			if ($skin_error == False) {
				$testskin = fopen($skin_set_dir . 'setting_' . $skin_selector, 'w');
				echo $pia_lang['BE_Dev_Theme_set'] . ': ' . $GLOBALS["pialert_request"]['SkinSelection'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			} else {
				echo $pia_lang['BE_Dev_Theme_notset'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			}
		} elseif (in_array($skin_selector, $installed_themes)) {
			// lösche alle vorherigen skins
			foreach ($installed_skins as $file) {
				unlink($skin_set_dir . 'setting_' . $file);
			}
			// lösche alle vorherigen themes
			foreach ($installed_themes as $file) {
				unlink($skin_set_dir . 'setting_theme_' . $file);
			}
			foreach ($installed_skins as $file) {
				if (file_exists($skin_set_dir . 'setting_theme_' . $file)) {
					$skin_error = True;
					break;
				} else {
					$skin_error = False;
				}
			}
			if ($skin_error == False) {
				$testskin = fopen($skin_set_dir . 'setting_theme_' . $skin_selector, 'w');
				echo $pia_lang['BE_Dev_Theme_set'] . ': ' . $GLOBALS["pialert_request"]['SkinSelection'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			} else {
				echo $pia_lang['BE_Dev_Theme_notset'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			}
		} else {echo $pia_lang['BE_Dev_Theme_invalid'];}
	}
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0053', '', $skin_selector);
}

//  Set Language
function setLanguage() {
	global $pia_lang;

	$pia_installed_langs = array('en_us',
		'de_de',
		'es_es',
		'fr_fr',
		'it_it',
		'pl_pl',
		'cz_cs',
		'dk_da',
		'nl_nl',
	    'fi_fi',
	    'lt_lt',
	    'no_no',
	    'ru_ru',
	    'se_sv',
	    'ua_uk');

	if (isset($GLOBALS["pialert_request"]['LangSelection'])) {
		$pia_lang_set_dir = '../../../config/';
		$pia_lang_selector = htmlspecialchars($GLOBALS["pialert_request"]['LangSelection']);
		if (in_array($pia_lang_selector, $pia_installed_langs)) {
			foreach ($pia_installed_langs as $file) {
				unlink($pia_lang_set_dir . 'setting_language_' . $file);
			}
			foreach ($pia_installed_langs as $file) {
				if (file_exists($pia_lang_set_dir . 'setting_language_' . $file)) {
					$pia_lang_error = True;
					break;
				} else {
					$pia_lang_error = False;
				}
			}
			if ($pia_lang_error == False) {
				$testlang = fopen($pia_lang_set_dir . 'setting_language_' . $pia_lang_selector, 'w');
				echo $pia_lang['BE_Dev_Language_set'] . ': ' . $GLOBALS["pialert_request"]['LangSelection'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			} else {
				echo $pia_lang['BE_Dev_Language_notset'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			}
		} else {echo $pia_lang['BE_Dev_Language_invalid'];}
	}
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0054', '', $pia_lang_selector);
}

//  Set Timer
function setArpTimer() {
	global $pia_lang;

	if (isset($GLOBALS["pialert_request"]['ArpTimer'])) {
		#$pia_lang_set_dir = '../../../config/';
		$file = '../../../config/setting_stoppialert';
		if (file_exists($file)) {
			echo $pia_lang['BE_Dev_Arpscan_enabled'];
			// Logging
			pialert_logging('a_002', $_SERVER['REMOTE_ADDR'], 'LogStr_0510', '', '');
			exec('../../../back/pialert-cli enable_scan', $output);
			echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php'>";
		} else {
			if (is_numeric($GLOBALS["pialert_request"]['ArpTimer'])) {
				// Logging
				pialert_logging('a_002', $_SERVER['REMOTE_ADDR'], 'LogStr_0511', '', $GLOBALS["pialert_request"]['ArpTimer'] . ' min');
				exec('../../../back/pialert-cli disable_scan ' . $GLOBALS["pialert_request"]['ArpTimer'], $output);
			} else {
				// Logging
				pialert_logging('a_002', $_SERVER['REMOTE_ADDR'], 'LogStr_0512', '', '');
				exec('../../../back/pialert-cli disable_scan', $output);
			}
			echo $pia_lang['BE_Dev_Arpscan_disabled'];
			echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php'>";
		}
	}
}

//  Restore Config File
function RestoreConfigFile() {
	global $pia_lang;

	$file = '../../../config/pialert.conf';
	$laststate = '../../../config/pialert-prev.bak';
	// Restore only a structurally valid backup through the atomic writer.
	try {
		$content = file_get_contents($laststate);
		if ($content === false) {
			throw new RuntimeException('Unable to read configuration backup');
		}
		validate_and_replace_pialert_config($file, $content);
		echo $pia_lang['BE_Dev_ConfEditor_RestoreOkay'];
	} catch (Throwable $exception) {
		echo $pia_lang['BE_Dev_ConfEditor_RestoreError'];
	}
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_0006', '1', '');
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php'>";
}

//  Backup Config File
function BackupConfigFile() {
	global $pia_lang;

	// prepare fast Backup
	$file = '../../../config/pialert.conf';
	$newfile = '../../../config/pialert-' . date("Ymd_His") . '.bak';
	$laststate = '../../../config/pialert-prev.bak';
	if (!copy($file, $newfile)) {
		echo $pia_lang['BE_Dev_ConfEditor_CopError'];
	} else {
		echo $pia_lang['BE_Dev_ConfEditor_CopOkay'];
	}
	// copy files as a fast Backup
	copy($file, $laststate);

	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_0007', '1', '');
	if ($GLOBALS["pialert_request"]['reload'] == 'yes') {
		echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=3'>";
	}
}

//  Delete All Notification in WebGUI
function deleteAllNotifications() {
	global $pia_lang;

	$regex = '/[0-9]+-[0-9]+_.*\\.txt/i';
	$reports_path = '../../reports/';
	$files = array_diff(scandir($reports_path, SCANDIR_SORT_DESCENDING), array('.', '..', 'archived'));
	$count_all_reports = sizeof($files);
	foreach ($files as &$item) {
		if (preg_match($regex, $item) == True) {
			unlink($reports_path . $item);
		}
	}
	echo $count_all_reports . ' ' . $pia_lang['BE_Dev_Report_Delete'];
	echo "<meta http-equiv='refresh' content='2; URL=./reports.php'>";
	// Logging
	pialert_logging('a_050', $_SERVER['REMOTE_ADDR'], 'LogStr_0504', '', '');
}

//  Delete All Notification in WebGUI
function deleteAllNotificationsArchive() {
	global $pia_lang;

	$regex = '/[0-9]+-[0-9]+_.*\\.txt/i';
	$reports_path = '../../reports/archived/';
	$files = array_diff(scandir($reports_path, SCANDIR_SORT_DESCENDING), array('.', '..', 'archived'));
	$count_all_reports = sizeof($files);
	foreach ($files as &$item) {
		if (preg_match($regex, $item) == True) {
			unlink($reports_path . $item);
		}
	}
	echo $count_all_reports . ' ' . $pia_lang['BE_Dev_Report_Delete'];
	echo "<meta http-equiv='refresh' content='2; URL=./reports.php?report_source=archive'>";
	// Logging
	pialert_logging('a_050', $_SERVER['REMOTE_ADDR'], 'LogStr_0506', '', '');
}

// Get Report Counter
function getReportTotals() {
	$files = array_diff(scandir('../../reports'), array('..', '.', 'archived'));
	$report_counter = count($files);
	$totals = array($report_counter);
	echo (json_encode($totals));
}

//  Set FavIcon
function setFavIconURL() {
	global $pia_lang;

	if (isset($GLOBALS["pialert_request"]['FavIconURL'])) {
		$iconlist = array();
		$iconlist['redglass_w_local'] = 'img/favicons/glass_red_white.png';
		$iconlist['redflat_w_local'] = 'img/favicons/flat_red_white.png';
		$iconlist['redglass_b_local'] = 'img/favicons/glass_red_black.png';
		$iconlist['redflat_b_local'] = 'img/favicons/flat_red_black.png';
		$iconlist['blueglass_w_local'] = 'img/favicons/glass_blue_white.png';
		$iconlist['blueflat_w_local'] = 'img/favicons/flat_blue_white.png';
		$iconlist['blueglass_b_local'] = 'img/favicons/glass_blue_black.png';
		$iconlist['blueflat_b_local'] = 'img/favicons/flat_blue_black.png';
		$iconlist['greenglass_w_local'] = 'img/favicons/glass_green_white.png';
		$iconlist['greenflat_w_local'] = 'img/favicons/flat_green_white.png';
		$iconlist['greenglass_b_local'] = 'img/favicons/glass_green_black.png';
		$iconlist['greenflat_b_local'] = 'img/favicons/flat_green_black.png';
		$iconlist['yellowglass_w_local'] = 'img/favicons/glass_yellow_white.png';
		$iconlist['yellowflat_w_local'] = 'img/favicons/flat_yellow_white.png';
		$iconlist['yellowglass_b_local'] = 'img/favicons/glass_yellow_black.png';
		$iconlist['yellowflat_b_local'] = 'img/favicons/flat_yellow_black.png';
		$iconlist['purpleglass_w_local'] = 'img/favicons/glass_purple_white.png';
		$iconlist['purpleflat_w_local'] = 'img/favicons/flat_purple_white.png';
		$iconlist['purpleglass_b_local'] = 'img/favicons/glass_purple_black.png';
		$iconlist['purpleflat_b_local'] = 'img/favicons/flat_purple_black.png';
		$iconlist['blackglass_w_local'] = 'img/favicons/glass_black_white.png';
		$iconlist['blackflat_w_local'] = 'img/favicons/flat_black_white.png';
		$iconlist['whiteglass_b_local'] = 'img/favicons/glass_white_black.png';
		$iconlist['whiteflat_b_local'] = 'img/favicons/flat_white_black.png';
		$iconlist['redglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_red_white.png';
		$iconlist['redflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_red_white.png';
		$iconlist['redglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_red_black.png';
		$iconlist['redflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_red_black.png';
		$iconlist['blueglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_blue_white.png';
		$iconlist['blueflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_blue_white.png';
		$iconlist['blueglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_blue_black.png';
		$iconlist['blueflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_blue_black.png';
		$iconlist['greenglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_green_white.png';
		$iconlist['greenflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_green_white.png';
		$iconlist['greenglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_green_black.png';
		$iconlist['greenflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_green_black.png';
		$iconlist['yellowglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_yellow_white.png';
		$iconlist['yellowflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_yellow_white.png';
		$iconlist['yellowglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_yellow_black.png';
		$iconlist['yellowflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_yellow_black.png';
		$iconlist['purpleglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_purple_white.png';
		$iconlist['purpleflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_purple_white.png';
		$iconlist['purpleglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_purple_black.png';
		$iconlist['purpleflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_purple_black.png';
		$iconlist['blackglass_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_black_white.png';
		$iconlist['blackflat_w_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_black_white.png';
		$iconlist['whiteglass_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/glass_white_black.png';
		$iconlist['whiteflat_b_remote'] = 'https://raw.githubusercontent.com/leiweibau/Pi.Alert/main/front/img/favicons/flat_white_black.png';

		$url = $GLOBALS["pialert_request"]['FavIconURL'];

		if ($iconlist[$url] != "") {
			$newfavicon_url = $iconlist[$url];
			$file_path = '../../../config/setting_favicon';
			file_put_contents($file_path, $newfavicon_url);
			echo $pia_lang['BE_Files_FavIcon_okay'];
			echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
		} else {
			$temp_favicon_url = filter_var($url, FILTER_SANITIZE_URL);
			if (filter_var($temp_favicon_url, FILTER_VALIDATE_URL) && strtolower(substr($temp_favicon_url, 0, 4)) == "http") {
				$newfavicon_url = $temp_favicon_url;
				$file_path = '../../../config/setting_favicon';
				file_put_contents($file_path, $newfavicon_url);
				echo $pia_lang['BE_Files_FavIcon_okay'];
				echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
			} else {
				echo $pia_lang['BE_Files_FavIcon_error'];
			}
		}
	}
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0059', '', $GLOBALS["pialert_request"]['FavIconURL']);
}


//  Set Pihole URL
function setPiholeURL() {
	global $pia_lang;

	if (isset($GLOBALS["pialert_request"]['PiholeURL'])) {
				$url = $GLOBALS["pialert_request"]['PiholeURL'];
		$temp_favicon_url = filter_var($url, FILTER_SANITIZE_URL);
		if (filter_var($temp_favicon_url, FILTER_VALIDATE_URL) && strtolower(substr($temp_favicon_url, 0, 4)) == "http") {
			$newfavicon_url = $temp_favicon_url;
			$file_path = '../../../config/setting_piholebutton';
			file_put_contents($file_path, $newfavicon_url);
			echo $pia_lang['BE_Files_PiholeURL_okay'] ;
			echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
		} elseif ($url == "") {
			$newfavicon_url = $temp_favicon_url;
			$file_path = '../../../config/setting_piholebutton';
			file_put_contents($file_path, $newfavicon_url);
			echo $pia_lang['BE_Files_PiholeURL_remove'];
			echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=4'>";
		} else {
			echo $pia_lang['BE_Files_PiholeURL_error'];
		}
	}
	// Logging
	pialert_logging('a_005', $_SERVER['REMOTE_ADDR'], 'LogStr_0060', '', $GLOBALS["pialert_request"]['PiholeURL']);
}


function ToggleImport() {

    $file_path = '../../../config/pialert.conf';

    if (!isset($GLOBALS["pialert_request"]['deviceType']) || !isset($GLOBALS["pialert_request"]['toggleState'])) {
        echo "Missing Parameter";
        exit;
    }

    $deviceMap = [
        'FB' => 'FRITZBOX_ACTIVE',
        'MT' => 'MIKROTIK_ACTIVE',
        'UF' => 'UNIFI_ACTIVE',
        'OW' => 'OPENWRT_ACTIVE',
        'AW' => 'ASUSWRT_ACTIVE',
        'PF' => 'PFSENSE_ACTIVE',
        'OPN' => 'OPNSENSE_ACTIVE',
        'PiN' => 'PIHOLE_ACTIVE',
        'PiD' => 'DHCP_ACTIVE',
        'AG' => 'ADGUARD_ACTIVE',
    ];

    $deviceType = $GLOBALS["pialert_request"]['deviceType'];
    $toggleState = filter_var($GLOBALS["pialert_request"]['toggleState'], FILTER_VALIDATE_BOOLEAN);

    if (!array_key_exists($deviceType, $deviceMap)) {
        echo 'Invalid device type';
        exit;
    }

    $configKey = $deviceMap[$deviceType];
    $newValue = $toggleState ? 'False' : 'True';

    if (!file_exists($file_path)) {
        echo 'Configuration file not found';
        exit;
    }

    $fileContents = file_get_contents($file_path);
    if (strpos($fileContents, $configKey) === false) {
        echo "Key '{$configKey}' not found in configuration";
        exit;
    }

    $pattern = '/^(' . preg_quote($configKey, '/') . '\s*=\s*)(True|False)$/m';
    $replacement = '${1}' . $newValue;

    $newContents = preg_replace($pattern, $replacement, $fileContents);

    if ($newContents !== null) {
        validate_and_replace_pialert_config($file_path, $newContents);
        echo "{$configKey} set to {$newValue}";
    } else {
        echo 'Failed to update configuration';
    }
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $configKey.' set to '.$newValue);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=1'>";
}

function ToggleExtLogging() {

    $file_path = '../../../config/pialert.conf';
	$toggleState = filter_var($GLOBALS["pialert_request"]['toggleState'], FILTER_VALIDATE_BOOLEAN);


    $configKey = 'PRINT_LOG';
    $newValue = $toggleState ? 'False' : 'True';

    if (!file_exists($file_path)) {
        echo 'Configuration file not found';
        exit;
    }

    $fileContents = file_get_contents($file_path);
    if (strpos($fileContents, $configKey) === false) {
        echo "Key '{$configKey}' not found in configuration";
        exit;
    }

    $pattern = '/^(' . preg_quote($configKey, '/') . '\s*=\s*)(True|False)$/m';
    $replacement = '${1}' . $newValue;

    $newContents = preg_replace($pattern, $replacement, $fileContents);

    if ($newContents !== null) {
        validate_and_replace_pialert_config($file_path, $newContents);
        echo "{$configKey} set to {$newValue}";
    } else {
        echo 'Failed to update configuration';
    }
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $configKey.' set to '.$newValue);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=1'>";
}

function ToggleRogueDHCP() {

    $file_path = '../../../config/pialert.conf';
	$toggleState = filter_var($GLOBALS["pialert_request"]['toggleState'], FILTER_VALIDATE_BOOLEAN);


    $configKey = 'SCAN_ROGUE_DHCP';
    $newValue = $toggleState ? 'False' : 'True';

    if (!file_exists($file_path)) {
        echo 'Configuration file not found';
        exit;
    }

    $fileContents = file_get_contents($file_path);
    if (strpos($fileContents, $configKey) === false) {
        echo "Key '{$configKey}' not found in configuration";
        exit;
    }

    $pattern = '/^(' . preg_quote($configKey, '/') . '\s*=\s*)(True|False)$/m';
    $replacement = '${1}' . $newValue;

    $newContents = preg_replace($pattern, $replacement, $fileContents);

    if ($newContents !== null) {
        validate_and_replace_pialert_config($file_path, $newContents);
        echo "{$configKey} set to {$newValue}";
    } else {
        echo 'Failed to update configuration';
    }
	// Logging
	pialert_logging('a_000', $_SERVER['REMOTE_ADDR'], 'LogStr_9999', '1', $configKey.' set to '.$newValue);
	echo "<meta http-equiv='refresh' content='2; URL=./maintenance.php?tab=1'>";
}
?>
