diff --git a/includes/UniFiController.php b/includes/UniFiController.php index 58ed54f..90dce55 100644 --- a/includes/UniFiController.php +++ b/includes/UniFiController.php @@ -6,6 +6,7 @@ class UniFiController { private $siteId; private $cookieFile; private $csrfToken = null; + private $sessionCookie = null; public function __construct($controllerUrl, $username, $password, $siteId) { $this->controllerUrl = rtrim($controllerUrl, '/'); @@ -46,10 +47,18 @@ class UniFiController { CURLOPT_HEADERFUNCTION => function($ch, $header) { $parts = explode(':', $header, 2); if (count($parts) === 2) { - $name = trim($parts[0]); + $name = strtolower(trim($parts[0])); $value = trim($parts[1]); - if (strtolower($name) === 'x-csrf-token') { + if ($name === 'x-csrf-token') { $this->csrfToken = $value; + } elseif ($name === 'set-cookie') { + // Extract TOKEN value directly — cookie jar may not persist + // cookies with the 'Partitioned' attribute on some libcurl versions + $cookieParts = explode(';', $value); + $first = trim($cookieParts[0]); + if (strpos($first, 'TOKEN=') === 0) { + $this->sessionCookie = $first; + } } } return strlen($header); @@ -100,12 +109,19 @@ class UniFiController { CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_COOKIEFILE => $this->cookieFile, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_HTTPHEADER => $headers ]; + // Prefer manually extracted cookie over file-based jar — the Partitioned + // attribute on the TOKEN cookie prevents some libcurl versions from writing it + if ($this->sessionCookie !== null) { + $options[CURLOPT_COOKIE] = $this->sessionCookie; + } else { + $options[CURLOPT_COOKIEFILE] = $this->cookieFile; + } + if ($method === 'GET') { $options[CURLOPT_HTTPGET] = true; } elseif ($method === 'POST') { diff --git a/test.php b/test.php index 6beca36..485c158 100644 --- a/test.php +++ b/test.php @@ -190,29 +190,52 @@ try { if (file_exists($cookieFile)) { $cookieContents = file_get_contents($cookieFile); echo "Cookie-Datei:
";
-            echo htmlspecialchars($cookieContents ?: '(leer)');
+            echo htmlspecialchars($cookieContents ?: '(leer — TOKEN hat Partitioned-Attribut, libcurl schreibt es nicht in die Jar-Datei)');
             echo "
"; } + // Extract TOKEN from Set-Cookie header (the fix for Partitioned cookie issue) + $tokenCookie = null; + foreach ($responseHeaders as $h) { + if (stripos($h, 'set-cookie:') === 0) { + $cookieVal = trim(substr($h, strlen('set-cookie:'))); + $cookieParts = explode(';', $cookieVal); + $first = trim($cookieParts[0]); + if (strpos($first, 'TOKEN=') === 0) { + $tokenCookie = $first; + } + } + } + if ($tokenCookie) { + echo "✓ TOKEN aus Set-Cookie-Header extrahiert: " . htmlspecialchars(substr($tokenCookie, 0, 40)) . "…
"; + } else { + echo "✗ TOKEN nicht in Set-Cookie-Header gefunden
"; + } + // --- API Test (only if login succeeded) --- if ($httpCode === 200) { - echo "API-Test (stat/voucher GET):
"; + echo "
API-Test (stat/voucher GET) — mit extrahiertem Cookie:
"; $apiHeaders = ['Content-Type: application/json']; if ($csrfToken !== null) { $apiHeaders[] = 'X-CSRF-Token: ' . $csrfToken; } $ch2 = curl_init(); - curl_setopt_array($ch2, [ + $apiOpts = [ CURLOPT_URL => $controllerUrl . "/proxy/network/api/s/" . $site['site_id'] . "/stat/voucher", CURLOPT_HTTPGET => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_COOKIEFILE => $cookieFile, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_HTTPHEADER => $apiHeaders, - ]); + ]; + if ($tokenCookie !== null) { + $apiOpts[CURLOPT_COOKIE] = $tokenCookie; + } else { + $apiOpts[CURLOPT_COOKIEFILE] = $cookieFile; + } + curl_setopt_array($ch2, $apiOpts); $apiBody = curl_exec($ch2); $apiCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE); $apiErr = curl_error($ch2);