fix(sync-php): read Authorization via getallheaders and tolerate ETag forms

Apache commonly strips Authorization before PHP sees it, so every request looked unauthenticated on a stock XAMPP and answered 404.

If-None-Match is now compared on the bare hash: a validator that lost its quotes, or a weak W/ tag, would never match and each request would silently re-download the whole snapshot - expensive on mobile data and invisible in the logs, a 200 looking perfectly normal.
This commit is contained in:
r-zakarya
2026-08-14 17:58:48 +01:00
parent ec4fa433a9
commit 90ac75525a
+20 -3
View File
@@ -41,10 +41,22 @@ header('X-Content-Type-Options: nosniff');
// Authentication
// ---------------------------------------------------------------------------
/** Bearer token, compared in constant time so a wrong guess leaks no timing. */
/**
* Bearer token, compared in constant time so a wrong guess leaks no timing.
*
* Three sources on purpose: Apache commonly strips Authorization before PHP
* sees it (it is only forwarded with CGIPassAuth on, or through a rewrite), so
* $_SERVER alone answers empty on a stock XAMPP and every request looks
* unauthenticated. getallheaders() reads what the client actually sent.
*/
function client_token(): string {
$h = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
if (stripos($h, 'Bearer ') === 0) return substr($h, 7);
if ($h === '' && function_exists('getallheaders')) {
foreach (getallheaders() as $name => $value) {
if (strcasecmp($name, 'Authorization') === 0) { $h = $value; break; }
}
}
if (stripos($h, 'Bearer ') === 0) return trim(substr($h, 7));
return '';
}
@@ -85,7 +97,12 @@ if ($method === 'HEAD' || $method === 'GET') {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', (int)filemtime(SYNC_FILE)) . ' GMT');
// Save the round trip when the client already holds this version.
if (trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etag) {
// Compared on the bare hash: a validator that loses its quotes or arrives
// as a weak tag (W/"…") would otherwise never match, and every request
// would silently download the whole snapshot again — costly on mobile data
// and invisible in the logs, since a 200 looks perfectly normal.
$sent = trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? '');
if ($sent !== '' && trim($sent, 'W/" ') === trim($etag, '" ')) {
http_response_code(304);
exit;
}