mirror of
				https://github.com/billz/raspap-webgui.git
				synced 2025-03-01 10:31:47 +00:00 
			
		
		
		
	Merge pull request #1704 from RaspAP/feat/adguard-provider
Add VPN provider support for AdGuard
This commit is contained in:
		| @@ -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 | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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); | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user