'video/mp4', 'webm' => 'video/webm', 'mp3' => 'audio/mpeg', 'm4a' => 'audio/mp4', 'ogg' => 'audio/ogg', 'wav' => 'audio/wav', ]; if (!isset($mimeTypes[$extension])) return false; $size = filesize($filePath); if ($size === false || $size < 1) return false; $start = 0; $end = $size - 1; $status = 200; $range = trim((string)($_SERVER['HTTP_RANGE'] ?? '')); if ($range !== '') { if (!preg_match('/^bytes=(\d*)-(\d*)$/', $range, $matches) || ($matches[1] === '' && $matches[2] === '')) { header('Content-Range: bytes */' . $size); http_response_code(416); return true; } if ($matches[1] === '' && $matches[2] !== '') { $suffixLength = min((int)$matches[2], $size); $start = $size - $suffixLength; } else { $start = (int)$matches[1]; if ($matches[2] !== '') $end = min((int)$matches[2], $end); } if ($start < 0 || $start > $end || $start >= $size) { header('Content-Range: bytes */' . $size); http_response_code(416); return true; } $status = 206; } $length = $end - $start + 1; http_response_code($status); header('Content-Type: ' . $mimeTypes[$extension]); header('Accept-Ranges: bytes'); header('Content-Length: ' . $length); if ($status === 206) header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size); if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'HEAD') return true; $handle = fopen($filePath, 'rb'); if ($handle === false) { http_response_code(500); return true; } fseek($handle, $start); $remaining = $length; while ($remaining > 0 && !feof($handle)) { $chunk = fread($handle, min(8192, $remaining)); if ($chunk === false || $chunk === '') break; echo $chunk; $remaining -= strlen($chunk); } fclose($handle); return true; } // Block access to sensitive paths BEFORE serving any files if (preg_match('#^/(content|backups|tests|cli|not-for-git)(/|$)|-trash(/|$)|(^|/)\.(?!well-known(?:/|$))#i', $uri)) { http_response_code(403); echo '403 Forbidden'; return true; } if (preg_match('#/(config|smtp-config)\.php$#i', $uri)) { http_response_code(403); echo '403 Forbidden'; return true; } // Uploaded assets are static only; the built-in server would otherwise execute // a .php file placed under assets/. Mirrors assets/.htaccess for production. if (preg_match('#^/assets/.+\.(php[0-9]?|phtml|phtm|pht|phar|phps|cgi|pl|py|shtml)$#i', $uri)) { http_response_code(403); echo '403 Forbidden'; return true; } // Run the same route-specific normalization as Apache before directory lookup. require_once $root . '/includes/routing-helper.php'; raijuRoutingCanonicalize($root); // Serve existing files directly (CSS, JS, images, etc.) if ($uri !== '/' && is_file($filePath)) { if (_routerServeSeekableMedia($filePath)) return true; return false; } if ($uri === '/sitemap.xml' || $uri === '/robots.txt') { if (!is_file($root . '/admin/config.php')) { http_response_code(404); return true; } require_once $root . '/admin/config.php'; require_once $root . '/includes/seo-helper.php'; if ($uri === '/sitemap.xml') { raijuSeoServeSitemap(); } raijuSeoServeRobots(); } // Apply migration redirects before slug-based routing. require_once $root . '/includes/redirect-helper.php'; applyRedirects($_SERVER['REQUEST_URI'] ?? '/'); require_once $root . '/includes/access-guard.php'; raijuAccessEnforceMaintenance(); // Serve existing directories with index.php if (is_dir($filePath)) { $index = rtrim($filePath, '/') . '/index.php'; if (is_file($index)) { $_SERVER['SCRIPT_FILENAME'] = $index; $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'] = rtrim($uri, '/') . '/index.php'; require_once $root . '/includes/analytics-helper.php'; raijuAnalyticsScheduleTrack(null, null, basename(rtrim($uri, '/')) ?: 'home'); include $index; return true; } // Don't return false yet — a directory name might collide with a // primary-language page slug (e.g. /docs dir vs en/docs.php). // Fall through to the slug-based routing below. } // Clean URLs: try appending .php $phpFile = $filePath . '.php'; if (is_file($phpFile)) { require_once $root . '/includes/analytics-helper.php'; raijuAnalyticsScheduleTrack(null, null, basename($filePath)); include $phpFile; return true; } // Share dynamic routing with Apache so language and page behavior stays identical. require $root . '/route.php'; return true;