Merge pull request #1704 from RaspAP/feat/adguard-provider

Add VPN provider support for AdGuard
This commit is contained in:
Bill Zimmerman 2024-11-28 10:28:04 +01:00 committed by GitHub
commit dc922bab30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 125 additions and 38 deletions

View File

@ -50,6 +50,26 @@
"pattern": "(\\w+)\\s+", "pattern": "(\\w+)\\s+",
"replace": "$1,$1\\n" "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": {
"countries": "list-locations",
"connect": "connect -y -l",
"log": "status",
"account": "license",
"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
}
} }
] ]
} }

View File

@ -16,37 +16,11 @@ function DisplayProviderConfig()
$providerName = getProviderValue($id, "name"); $providerName = getProviderValue($id, "name");
$providerVersion = getProviderVersion($id, $binPath); $providerVersion = getProviderVersion($id, $binPath);
$installPage = getProviderValue($id, "install_page"); $installPage = getProviderValue($id, "install_page");
$publicIP = get_public_ip();
$serviceStatus = 'down'; $serviceStatus = 'down';
$statusDisplay = 'down'; $statusDisplay = 'down';
$ctlState = ''; $ctlState = '';
if (!file_exists($binPath)) { // handle page actions
$status->addMessage(sprintf(_('Expected %s binary not found at: %s'), $providerName, $binPath), 'warning');
$status->addMessage(sprintf(_('Visit the <a href="%s" target="_blank">installation instructions</a> 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 (!RASPI_MONITOR_ENABLED) {
if (isset($_POST['SaveProviderSettings'])) { if (isset($_POST['SaveProviderSettings'])) {
if (isset($_POST['country'])) { if (isset($_POST['country'])) {
@ -60,7 +34,11 @@ function DisplayProviderConfig()
} elseif (isset($_POST['StartProviderVPN'])) { } elseif (isset($_POST['StartProviderVPN'])) {
$status->addMessage('Attempting to connect VPN provider', 'info'); $status->addMessage('Attempting to connect VPN provider', 'info');
$cmd = getCliOverride($id, 'cmd_overrides', 'connect'); $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); $return = stripArtifacts($return);
foreach ($return as $line) { foreach ($return as $line) {
if (strlen(trim($line)) > 0) { if (strlen(trim($line)) > 0) {
@ -83,6 +61,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 <a href="%s" target="_blank">installation instructions</a> 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( echo renderTemplate(
"provider", compact( "provider", compact(
"status", "status",
@ -129,16 +134,45 @@ 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|array $output
* @param string $pattern * @param string|null $pattern
* @return string $result * @return string|array $result
*/ */
function stripArtifacts($output, $pattern = null) function stripArtifacts($output, $pattern = null)
{ {
$result = preg_replace('/[-\/\n\t\\\\'.$pattern.'|]/', '', $output); if (is_array($output)) {
return $result; 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);
}
/**
* 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);
} }
/** /**
@ -176,8 +210,7 @@ function getProviderStatus($id, $binPath)
$cmd = getCliOverride($id, 'cmd_overrides', 'status'); $cmd = getCliOverride($id, 'cmd_overrides', 'status');
$pattern = getCliOverride($id, 'regex', 'status'); $pattern = getCliOverride($id, 'regex', 'status');
exec("sudo $binPath $cmd", $cmd_raw); 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 (!empty($cmd_raw[0])) {
if (preg_match($pattern, $cmd_raw, $match)) { if (preg_match($pattern, $cmd_raw, $match)) {
$status = "down"; $status = "down";
@ -245,6 +278,40 @@ function getCountries($id, $binPath)
$countries[$value] = str_replace("_", " ", $value); $countries[$value] = str_replace("_", " ", $value);
} }
break; 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;
}
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
}
// sort results 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: default:
break; break;
} }
@ -266,7 +333,7 @@ function getProviderLog($id, $binPath, &$country)
$providerLog = ''; $providerLog = '';
$cmd = getCliOverride($id, 'cmd_overrides', 'log'); $cmd = getCliOverride($id, 'cmd_overrides', 'log');
exec("sudo $binPath $cmd", $cmd_raw); exec("sudo $binPath $cmd", $cmd_raw);
$output = stripArtifacts($cmd_raw); $output = stripAnsiSequence($cmd_raw);
foreach ($output as $item) { foreach ($output as $item) {
if (preg_match('/Country: (\w+)/', $item, $match)) { if (preg_match('/Country: (\w+)/', $item, $match)) {
$country = $match[1]; $country = $match[1];
@ -303,7 +370,7 @@ function getAccountInfo($id, $binPath, $providerName)
{ {
$cmd = getCliOverride($id, 'cmd_overrides', 'account'); $cmd = getCliOverride($id, 'cmd_overrides', 'account');
exec("sudo $binPath $cmd", $acct); exec("sudo $binPath $cmd", $acct);
$acct = stripAnsiSequence($acct);
foreach ($acct as &$item) { foreach ($acct as &$item) {
$item = preg_replace('/^[^\w]+\s*/', '', $item); $item = preg_replace('/^[^\w]+\s*/', '', $item);
} }