From 4bba3384e4bfec31bb8d917e153d6eec312b1889 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 26 Nov 2024 01:22:42 -0800 Subject: [PATCH 1/7] Update w/ Adguard VPN --- config/vpn-providers.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/config/vpn-providers.json b/config/vpn-providers.json index 3108295f2..41cb355c6 100644 --- a/config/vpn-providers.json +++ b/config/vpn-providers.json @@ -50,6 +50,25 @@ "pattern": "(\\w+)\\s+", "replace": "$1,$1\\n" } + }, + { + "id": 4, + "name": "AdGuard VPN", + "bin_path": "/usr/local/bin/adguardvpn-cli", + "install_page": "https://adguard-vpn.com/kb/adguard-vpn-for-linux/installation/", + "account_page": "https://my.adguard-vpn.com/en/account/product/vpn", + "cmd_overrides": { + "account": "account get", + "countries": "list-locations", + "log": "status", + "version": "--version" + }, + "regex": { + "status": "\/VPN is disconnected\/", + "pattern": "/^([A-Z]{2})\\s+.*?\\s([A-Za-z]+(?:\\s[A-Za-z]+)?)\\s+\\d+$/m", + "replace": "$2", + "slice": 3 + } } ] } From 68d47f9a417b5300dfa39238caeb6d275f3bab0c Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 26 Nov 2024 01:23:20 -0800 Subject: [PATCH 2/7] Process Adguard cmd output --- includes/provider.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/includes/provider.php b/includes/provider.php index c2ceb1a3c..38292e782 100755 --- a/includes/provider.php +++ b/includes/provider.php @@ -245,6 +245,41 @@ function getCountries($id, $binPath) $countries[$value] = str_replace("_", " ", $value); } break; + case 4: // AdGuard + $raw_countries = []; + $totalLines = count($output); + foreach ($output as $index => $item) { + if ($index === 0 || $index === $totalLines - 1) { + // exclude first and last lines + continue; + } + // $item = preg_replace($pattern, $replace, $item); + preg_match($pattern, $item, $matches); + $item_country = trim($matches[1]); + $item_city = trim($matches[2]); + $item_key = str_replace(" ", "_", $item_city); + if ( strlen($item_key) > 0 ){ + $countries[$item_key] = "{$item_country} {$item_city}"; + if (!isset($raw_countries[$item_country])) { + $raw_countries[$item_country] = []; + } + $raw_countries[$item_country][] = $item_city; + } + } + // sort countries alphabetically + ksort($raw_countries); + // sort cities within each country + foreach ($raw_countries as $country => $cities) { + sort($raw_countries[$country]); // Trier les villes par ordre alphabétique + } + // display results sorted by country, then by city + foreach ($raw_countries as $country => $cities) { + foreach ($cities as $city) { + $item_key = str_replace(" ", "_", $city); + $countries[$item_key] = "{$country} {$city}"; + } + } + break; default: break; } From 12b0d9f6c86c63ea84ff13218d6d9b1736abf0cd Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 26 Nov 2024 01:43:46 -0800 Subject: [PATCH 3/7] Append automatic yes answer to questions on connect --- config/vpn-providers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/vpn-providers.json b/config/vpn-providers.json index 41cb355c6..5f2f3bcfb 100644 --- a/config/vpn-providers.json +++ b/config/vpn-providers.json @@ -58,8 +58,8 @@ "install_page": "https://adguard-vpn.com/kb/adguard-vpn-for-linux/installation/", "account_page": "https://my.adguard-vpn.com/en/account/product/vpn", "cmd_overrides": { - "account": "account get", "countries": "list-locations", + "connect": "connect -y -l", "log": "status", "version": "--version" }, From 72475b16ef2221986acf72c82b539d9b8c95ba6c Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 26 Nov 2024 14:30:14 -0800 Subject: [PATCH 4/7] Remove ANSI escape sequences w/ stripArtifacts() --- includes/provider.php | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/includes/provider.php b/includes/provider.php index 38292e782..e7bc59052 100755 --- a/includes/provider.php +++ b/includes/provider.php @@ -129,16 +129,33 @@ function saveProviderConfig($status, $binPath, $country, $id = null) } /** - * Removes artifacts from shell_exec string values + * Removes artifacts from shell_exec string values and lines with ANSI escape sequences * - * @param string $output - * @param string $pattern - * @return string $result + * @param string|array $output + * @param string|null $pattern + * @return string|array $result */ function stripArtifacts($output, $pattern = null) { - $result = preg_replace('/[-\/\n\t\\\\'.$pattern.'|]/', '', $output); - return $result; + if (is_array($output)) { + return array_map(function ($line) use ($pattern) { + return stripArtifacts($line, $pattern); + }, $output); + } + if (!is_string($output)) { + return $output; + } + + $lines = explode("\n", $output); + $lines = array_filter($lines, function ($line) use ($pattern) { + // remove ANSI escape sequences + if (preg_match('/\x1b\[[0-9;]*[a-zA-Z]/', $line)) { + return false; + } + $line = preg_replace('/[-\/\t\\\\' . preg_quote($pattern, '/') . '|]/', '', $line); + return trim($line) !== ''; + }); + return implode("\n", $lines); } /** From 83d924824972d7e1c7efc632b1786fd9fbf11958 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 27 Nov 2024 00:36:32 -0800 Subject: [PATCH 5/7] Update Adguard regex + cmd_overrides --- config/vpn-providers.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/vpn-providers.json b/config/vpn-providers.json index 5f2f3bcfb..c2a6afe19 100644 --- a/config/vpn-providers.json +++ b/config/vpn-providers.json @@ -61,10 +61,11 @@ "countries": "list-locations", "connect": "connect -y -l", "log": "status", + "account": "license", "version": "--version" }, "regex": { - "status": "\/VPN is disconnected\/", + "status": "\/vpn is disconnected\/", "pattern": "/^([A-Z]{2})\\s+.*?\\s([A-Za-z]+(?:\\s[A-Za-z]+)?)\\s+\\d+$/m", "replace": "$2", "slice": 3 From 50d0e0be824af0be4d88c4e7f76c7af46d4f45f0 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 27 Nov 2024 00:41:24 -0800 Subject: [PATCH 6/7] Sanitize CLI output, fetch provider details after page functions --- includes/provider.php | 70 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/includes/provider.php b/includes/provider.php index e7bc59052..d2a397ea7 100755 --- a/includes/provider.php +++ b/includes/provider.php @@ -16,37 +16,10 @@ function DisplayProviderConfig() $providerName = getProviderValue($id, "name"); $providerVersion = getProviderVersion($id, $binPath); $installPage = getProviderValue($id, "install_page"); - $publicIP = get_public_ip(); $serviceStatus = 'down'; $statusDisplay = 'down'; $ctlState = ''; - if (!file_exists($binPath)) { - $status->addMessage(sprintf(_('Expected %s binary not found at: %s'), $providerName, $binPath), 'warning'); - $status->addMessage(sprintf(_('Visit the installation instructions for %s\'s Linux CLI.'), $installPage, $providerName), 'warning'); - $ctlState = 'disabled'; - $providerVersion = 'not found'; - } elseif (empty($providerVersion)) { - $status->addMessage(sprintf(_('Unable to execute %s binary found at: %s'), $providerName, $binPath), 'warning'); - $status->addMessage(_('Check that binary is executable and permissions exist in raspap.sudoers'), 'warning'); - $ctlState = 'disabled'; - $providerVersion = 'not found'; - } else { - // fetch provider status - $serviceStatus = getProviderStatus($id, $binPath); - $statusDisplay = $serviceStatus == "down" ? "inactive" : "active"; - - // fetch provider log - $providerLog = getProviderLog($id, $binPath, $country); - - // fetch account info - $accountInfo = getAccountInfo($id, $binPath, $providerName); - $accountLink = getProviderValue($id, "account_page"); - - // fetch available countries - $countries = getCountries($id, $binPath); - } - if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['SaveProviderSettings'])) { if (isset($_POST['country'])) { @@ -60,7 +33,11 @@ function DisplayProviderConfig() } elseif (isset($_POST['StartProviderVPN'])) { $status->addMessage('Attempting to connect VPN provider', 'info'); $cmd = getCliOverride($id, 'cmd_overrides', 'connect'); - exec("sudo $binPath $cmd", $return); + $country = escapeshellarg(trim($_POST['country'])); + if ($id = 4) { // AdGuard requires country argument on connect + $arg = escapeshellarg(trim($_POST['country'])); + } + exec("sudo $binPath $cmd $arg", $return); $return = stripArtifacts($return); foreach ($return as $line) { if (strlen(trim($line)) > 0) { @@ -83,6 +60,33 @@ function DisplayProviderConfig() } } + if (!file_exists($binPath)) { + $status->addMessage(sprintf(_('Expected %s binary not found at: %s'), $providerName, $binPath), 'warning'); + $status->addMessage(sprintf(_('Visit the installation instructions for %s\'s Linux CLI.'), $installPage, $providerName), 'warning'); + $ctlState = 'disabled'; + $providerVersion = 'not found'; + } elseif (empty($providerVersion)) { + $status->addMessage(sprintf(_('Unable to execute %s binary found at: %s'), $providerName, $binPath), 'warning'); + $status->addMessage(_('Check that binary is executable and permissions exist in raspap.sudoers'), 'warning'); + $ctlState = 'disabled'; + $providerVersion = 'not found'; + } else { + // fetch provider status + $serviceStatus = getProviderStatus($id, $binPath); + $statusDisplay = $serviceStatus == "down" ? "inactive" : "active"; + + // fetch account info + $accountInfo = getAccountInfo($id, $binPath, $providerName); + $accountLink = getProviderValue($id, "account_page"); + + // fetch available countries + $countries = getCountries($id, $binPath); + + // fetch provider log + $providerLog = getProviderLog($id, $binPath, $country); + } + $publicIP = get_public_ip(); + echo renderTemplate( "provider", compact( "status", @@ -193,8 +197,7 @@ function getProviderStatus($id, $binPath) $cmd = getCliOverride($id, 'cmd_overrides', 'status'); $pattern = getCliOverride($id, 'regex', 'status'); exec("sudo $binPath $cmd", $cmd_raw); - $cmd_raw = strtolower(stripArtifacts($cmd_raw[0])); - + $cmd_raw = strtolower(($cmd_raw[0])); if (!empty($cmd_raw[0])) { if (preg_match($pattern, $cmd_raw, $match)) { $status = "down"; @@ -318,7 +321,9 @@ function getProviderLog($id, $binPath, &$country) $providerLog = ''; $cmd = getCliOverride($id, 'cmd_overrides', 'log'); exec("sudo $binPath $cmd", $cmd_raw); - $output = stripArtifacts($cmd_raw); + $output = array_map(function ($line) { + return preg_replace('/\x1b\[[0-9;]*[a-zA-Z]/', '', $line); + }, $cmd_raw); foreach ($output as $item) { if (preg_match('/Country: (\w+)/', $item, $match)) { $country = $match[1]; @@ -356,6 +361,9 @@ function getAccountInfo($id, $binPath, $providerName) $cmd = getCliOverride($id, 'cmd_overrides', 'account'); exec("sudo $binPath $cmd", $acct); + $acct = array_map(function ($line) { + return preg_replace('/\x1b\[[0-9;]*[a-zA-Z]/', '', $line); + }, $acct); foreach ($acct as &$item) { $item = preg_replace('/^[^\w]+\s*/', '', $item); } From 66f5cd4183031145c1ffe6482a73c85f162a8aea Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 27 Nov 2024 23:19:41 -0800 Subject: [PATCH 7/7] Create stripAnsiSequence() --- includes/provider.php | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/includes/provider.php b/includes/provider.php index d2a397ea7..1e5bf8a33 100755 --- a/includes/provider.php +++ b/includes/provider.php @@ -20,6 +20,7 @@ function DisplayProviderConfig() $statusDisplay = 'down'; $ctlState = ''; + // handle page actions if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['SaveProviderSettings'])) { if (isset($_POST['country'])) { @@ -162,6 +163,18 @@ function stripArtifacts($output, $pattern = null) return implode("\n", $lines); } +/** + * Removes ANSI escape sequences and preserves CLI return values + * + * @param array $output + */ +function stripAnsiSequence($output) +{ + return array_map(function ($line) { + return preg_replace('/\x1b\[[0-9;]*[a-zA-Z]/', '', $line); + }, $output); +} + /** * Retrieves an override for provider CLI * @@ -265,7 +278,7 @@ function getCountries($id, $binPath) $countries[$value] = str_replace("_", " ", $value); } break; - case 4: // AdGuard + case 4: // adguard $raw_countries = []; $totalLines = count($output); foreach ($output as $index => $item) { @@ -273,7 +286,6 @@ function getCountries($id, $binPath) // exclude first and last lines continue; } - // $item = preg_replace($pattern, $replace, $item); preg_match($pattern, $item, $matches); $item_country = trim($matches[1]); $item_city = trim($matches[2]); @@ -292,7 +304,7 @@ function getCountries($id, $binPath) foreach ($raw_countries as $country => $cities) { sort($raw_countries[$country]); // Trier les villes par ordre alphabétique } - // display results sorted by country, then by city + // sort results by country, then by city foreach ($raw_countries as $country => $cities) { foreach ($cities as $city) { $item_key = str_replace(" ", "_", $city); @@ -321,9 +333,7 @@ function getProviderLog($id, $binPath, &$country) $providerLog = ''; $cmd = getCliOverride($id, 'cmd_overrides', 'log'); exec("sudo $binPath $cmd", $cmd_raw); - $output = array_map(function ($line) { - return preg_replace('/\x1b\[[0-9;]*[a-zA-Z]/', '', $line); - }, $cmd_raw); + $output = stripAnsiSequence($cmd_raw); foreach ($output as $item) { if (preg_match('/Country: (\w+)/', $item, $match)) { $country = $match[1]; @@ -360,10 +370,7 @@ function getAccountInfo($id, $binPath, $providerName) { $cmd = getCliOverride($id, 'cmd_overrides', 'account'); exec("sudo $binPath $cmd", $acct); - - $acct = array_map(function ($line) { - return preg_replace('/\x1b\[[0-9;]*[a-zA-Z]/', '', $line); - }, $acct); + $acct = stripAnsiSequence($acct); foreach ($acct as &$item) { $item = preg_replace('/^[^\w]+\s*/', '', $item); }