getMessage()); } } if (!$disableApi && !file_exists($localConfigReferenceFile)) { $referenceUrl = $apiBaseUrl . '/api.php?action=configReference'; $referenceResponse = @file_get_contents($referenceUrl); if ($referenceResponse !== false) { $referenceData = json_decode($referenceResponse, true); if ($referenceData !== null && isset($referenceData['content'])) { file_put_contents($localConfigReferenceFile, $referenceData['content']); } } } $cacheInstance = initializeCache(); runCacheCleanup(); handleFileDownload(); function parseDenyAllowList($listString) { if (empty(trim($listString))) { return []; } $items = array_map('trim', explode(',', $listString)); $parsedList = []; foreach ($items as $item) { if (empty($item)) continue; $rule = [ 'original' => $item, 'path' => '', 'type' => 'exact', 'target' => 'both' ]; if (substr($item, -2) === '/*') { $rule['type'] = 'folder_recursive'; $rule['path'] = substr($item, 0, -2); $rule['target'] = 'folder'; } elseif (substr($item, -1) === '*' && substr($item, -2) !== '/*') { if (strpos($item, '.') !== false && strrpos($item, '.') > strrpos($item, '/')) { $rule['type'] = 'wildcard'; $rule['path'] = $item; $rule['target'] = 'file'; } else { $rule['type'] = 'wildcard'; $rule['path'] = substr($item, 0, -1); $rule['target'] = 'folder'; } } elseif (strpos($item, '*') !== false) { $rule['type'] = 'wildcard'; $rule['path'] = $item; $rule['target'] = 'file'; } else { $rule['type'] = 'exact'; $rule['path'] = $item; if (strpos(basename($item), '.') !== false) { $rule['target'] = 'file'; } else { $rule['target'] = 'folder'; } } $parsedList[] = $rule; } return $parsedList; } function findConflictingRules($denyList, $allowList) { $conflicts = []; foreach ($denyList as $denyRule) { foreach ($allowList as $allowRule) { if ($denyRule['path'] === $allowRule['path'] && $denyRule['type'] === $allowRule['type']) { $conflicts[] = [ 'deny' => $denyRule['original'], 'allow' => $allowRule['original'] ]; } } } return $conflicts; } function pathMatchesRule($relativePath, $rule, $isFolder = false) { $rulePath = $rule['path']; if ($rule['type'] === 'exact') { return $relativePath === $rulePath; } if ($rule['type'] === 'wildcard') { if ($rule['target'] === 'file' && !$isFolder) { if (strpos($rulePath, '/') !== false) { $lastSlashPos = strrpos($rulePath, '/'); $directory = substr($rulePath, 0, $lastSlashPos); $pattern = substr($rulePath, $lastSlashPos + 1); $fileDir = dirname($relativePath); $fileName = basename($relativePath); $directory = rtrim($directory, '/'); $fileDir = rtrim($fileDir, '/'); if ($fileDir === $directory) { if (strpos($pattern, '.') === 0) { $extension = substr($pattern, 1, -1); $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); return $fileExtension === $extension; } else { $regexPattern = str_replace('*', '.*', preg_quote($pattern, '/')); return preg_match('/^' . $regexPattern . '$/i', $fileName); } } } } elseif ($rule['target'] === 'folder') { if ($isFolder) { $pathParts = explode('/', $relativePath); $topLevelFolder = $pathParts[0]; if (count($pathParts) === 1 && strpos($topLevelFolder, $rulePath) === 0) { return true; } return false; } else { $fileDir = dirname($relativePath); if ($fileDir === '.') $fileDir = ''; if (strpos($fileDir, '/') === false) { return strpos($fileDir, $rulePath) === 0; } return false; } } } if ($rule['type'] === 'folder_recursive') { if ($relativePath === $rulePath) { return true; } return strpos($relativePath, $rulePath . '/') === 0; } return false; } function pathMatchesRuleForIndexing($relativePath, $rule, $isFolder = false) { $rulePath = $rule['path']; if ($rule['type'] === 'exact') { return $relativePath === $rulePath; } if ($rule['type'] === 'wildcard') { if ($rule['target'] === 'file' && !$isFolder) { if (strpos($rulePath, '/') !== false) { $lastSlashPos = strrpos($rulePath, '/'); $directory = substr($rulePath, 0, $lastSlashPos); $pattern = substr($rulePath, $lastSlashPos + 1); $fileDir = dirname($relativePath); $fileName = basename($relativePath); $directory = rtrim($directory, '/'); $fileDir = rtrim($fileDir, '/'); if ($fileDir === $directory) { if (strpos($pattern, '.') === 0) { $extension = substr($pattern, 1, -1); $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); return $fileExtension === $extension; } else { $regexPattern = str_replace('*', '.*', preg_quote($pattern, '/')); return preg_match('/^' . $regexPattern . '$/i', $fileName); } } } } elseif ($rule['target'] === 'folder') { if ($isFolder) { $pathParts = explode('/', $relativePath); $topLevelFolder = $pathParts[0]; if (count($pathParts) === 1 && strpos($topLevelFolder, $rulePath) === 0) { return true; } return false; } else { $fileDir = dirname($relativePath); if ($fileDir === '.') $fileDir = ''; if (strpos($fileDir, '/') === false) { return strpos($fileDir, $rulePath) === 0; } return false; } } } if ($rule['type'] === 'folder_recursive') { if ($relativePath === $rulePath) { return true; } return strpos($relativePath, $rulePath . '/') === 0; } return false; } function isPathDenied($relativePath, $isFolder = false) { global $denyList, $conflictingRules; foreach ($denyList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['deny'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if (pathMatchesRule($relativePath, $rule, $isFolder)) { return true; } } return false; } function isPathDeniedForIndexing($relativePath, $isFolder = false) { global $denyList, $conflictingRules; foreach ($denyList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['deny'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if (pathMatchesRuleForIndexing($relativePath, $rule, $isFolder)) { return true; } } return false; } function isPathAllowed($relativePath, $isFolder = false) { global $allowList, $conflictingRules; foreach ($allowList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['allow'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if (pathMatchesRule($relativePath, $rule, $isFolder)) { return true; } } return false; } function isContentAllowedByWildcard($relativePath, $isFolder = false) { global $allowList, $conflictingRules; foreach ($allowList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['allow'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if ($rule['type'] === 'wildcard' && $rule['target'] === 'folder') { if (strpos($relativePath, $rule['path']) === 0) { return true; } } } return false; } function isFolderAccessible($currentPath) { global $denyList, $allowList, $conflictingRules, $config; if (isset($config['main']['index_all']) && $config['main']['index_all']) { return true; } foreach ($allowList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['allow'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if (pathMatchesRule($currentPath, $rule, true)) { return true; } } foreach ($denyList as $rule) { $isConflicting = false; foreach ($conflictingRules as $conflict) { if ($conflict['deny'] === $rule['original']) { $isConflicting = true; break; } } if ($isConflicting) continue; if ($rule['type'] === 'folder_recursive') { if ($currentPath === $rule['path'] || strpos($currentPath, $rule['path'] . '/') === 0) { return false; } } elseif ($rule['type'] === 'wildcard' && $rule['target'] === 'folder') { $pathParts = explode('/', $currentPath); $topLevelFolder = $pathParts[0]; if (strpos($topLevelFolder, $rule['path']) === 0) { if ($currentPath === $topLevelFolder) { return false; } } } elseif ($rule['type'] === 'exact') { if ($currentPath === $rule['path']) { return false; } } } return true; } function shouldIndexFile($filename, $extension) { global $config, $currentPath; $relativePath = $currentPath ? $currentPath . '/' . basename($filename) : basename($filename); if (empty($extension) || trim($extension) === '') { $indexNonDescript = isset($config['exclusions']['index_non-descript-files']) ? $config['exclusions']['index_non-descript-files'] : true; if (!$indexNonDescript) { return false; } if (isPathDeniedForIndexing($relativePath, false)) { if (isPathAllowed($relativePath, false)) { return true; } return false; } if (isContentAllowedByWildcard($relativePath, false)) { return true; } if (isPathAllowed($relativePath, false)) { return true; } if (isset($config['main']['index_all']) && $config['main']['index_all']) { return true; } if (strpos(basename($filename), '.') === 0) { if (!isset($config['main']['index_hidden']) || !$config['main']['index_hidden']) { return false; } } return true; } if (isPathDeniedForIndexing($relativePath, false)) { if (isPathAllowed($relativePath, false)) { return true; } return false; } if (isContentAllowedByWildcard($relativePath, false)) { return true; } if (isPathAllowed($relativePath, false)) { return true; } if (isset($config['main']['index_all']) && $config['main']['index_all']) { return true; } if (strpos(basename($filename), '.') === 0) { if (!isset($config['main']['index_hidden']) || !$config['main']['index_hidden']) { return false; } } $settingKey = getExtensionSetting($extension, 'indexing'); if ($settingKey !== null) { return isset($config['exclusions'][$settingKey]) ? $config['exclusions'][$settingKey] : true; } return true; } function shouldIndexFolder($foldername) { global $config, $currentPath; $relativePath = $currentPath ? $currentPath . '/' . basename($foldername) : basename($foldername); if (basename($foldername) === '.indexer_files') { if (isset($config['main']['index_all']) && $config['main']['index_all']) { return true; } return false; } if (isPathDenied($relativePath, true)) { if (isPathAllowed($relativePath, true)) { return true; } return false; } if (isContentAllowedByWildcard($relativePath, true)) { return true; } if (isPathAllowed($relativePath, true)) { return true; } if (isset($config['main']['index_all']) && $config['main']['index_all']) { return true; } if (strpos(basename($foldername), '.') === 0) { if (!isset($config['main']['index_hidden']) || !$config['main']['index_hidden']) { return false; } } return isset($config['exclusions']['index_folders']) ? $config['exclusions']['index_folders'] : true; } function isFileAccessible($filePath, $currentPath, $extension) { global $config, $disableFileDownloads; if (!isFolderAccessible($currentPath)) { return false; } $fileName = basename($filePath); $relativePath = $currentPath ? $currentPath . '/' . $fileName : $fileName; if (isPathDenied($relativePath, false)) { if (!isPathAllowed($relativePath, false)) { return false; } } if (!shouldIndexFile($filePath, $extension)) { return false; } return true; } function getFileActionMenu($file, $currentPath) { global $disableFileDownloads; $currentScript = $_SERVER['SCRIPT_NAME']; $extension = $file['extension']; $fileName = $file['name']; $isViewable = isFileViewable($extension); $showActions = isset($_GET['action']) && $_GET['action'] === $fileName; $menu = '
'; if ($showActions) { $closeParams = $_GET; unset($closeParams['action']); unset($closeParams['options']); $menu .= '×'; $menu .= '
'; if ($isViewable) { $openUrl = getFileUrl($currentPath, $fileName); $menu .= 'Open'; $menu .= 'Open in new tab'; } if (!$disableFileDownloads) { $downloadUrl = $currentScript . '?' . http_build_query(['path' => $currentPath, 'download' => '1', 'file' => $fileName]); $menu .= 'Download'; } if ($isViewable) { $shareParams = array_merge($_GET, ['share_popup' => 'view', 'share_file' => $fileName]); $menu .= 'Share'; } else { $shareParams = array_merge($_GET, ['share_popup' => 'folder', 'share_file' => $fileName]); $menu .= 'Share'; } if (!$disableFileDownloads) { $shareParams = array_merge($_GET, ['share_popup' => 'download', 'share_file' => $fileName]); $menu .= 'Share Download'; } $menu .= '
'; } else { $actionParams = $_GET; unset($actionParams['options']); if (isset($actionParams['action'])) { unset($actionParams['action']); } $actionParams['action'] = $fileName; $menu .= ''; } $menu .= '
'; return $menu; } function getFolderActionMenu($folder, $currentPath) { global $disableFolderDownloads; $currentScript = $_SERVER['SCRIPT_NAME']; $folderName = $folder['name']; $showActions = isset($_GET['action']) && $_GET['action'] === $folderName; $menu = '
'; if ($showActions) { $closeParams = $_GET; unset($closeParams['action']); unset($closeParams['options']); $menu .= '×'; $menu .= '
'; $openUrl = $currentScript . '?path=' . urlencode(($currentPath ? $currentPath . '/' : '') . $folderName); $menu .= 'Open'; $menu .= 'Open in new tab'; if (!$disableFolderDownloads) { $downloadUrl = $currentScript . '?' . http_build_query(['path' => $currentPath, 'download' => '1', 'file' => $folderName]); $menu .= 'Download'; } $shareParams = array_merge($_GET, ['share_popup' => 'view', 'share_file' => $folderName]); $menu .= 'Share'; if (!$disableFolderDownloads) { $shareParams = array_merge($_GET, ['share_popup' => 'download', 'share_file' => $folderName]); $menu .= 'Share Download'; } $menu .= '
'; } else { $actionParams = $_GET; unset($actionParams['options']); if (isset($actionParams['action'])) { unset($actionParams['action']); } $actionParams['action'] = $folderName; $menu .= ''; } $menu .= '
'; return $menu; } function getAbsoluteUrl($relativeUrl) { global $config; if (isset($config['main']['access_url']) && !empty($config['main']['access_url'])) { $baseUrl = rtrim($config['main']['access_url'], '/'); if (strpos($relativeUrl, '/') === 0) { return $baseUrl . $relativeUrl; } else { $currentDir = dirname($_SERVER['REQUEST_URI']); if ($currentDir === '/' || $currentDir === '\\') { return $baseUrl . '/' . $relativeUrl; } else { return $baseUrl . rtrim($currentDir, '/') . '/' . $relativeUrl; } } } $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; if (strpos($relativeUrl, '/') === 0) { return $protocol . '://' . $host . $relativeUrl; } else { $currentDir = dirname($_SERVER['REQUEST_URI']); return $protocol . '://' . $host . rtrim($currentDir, '/') . '/' . $relativeUrl; } } function generateSharePopup($shareType, $shareFile, $currentPath) { $currentScript = $_SERVER['SCRIPT_NAME']; if ($shareType === 'view') { if (is_dir($GLOBALS['fullPath'] . '/' . $shareFile)) { $shareUrl = $currentScript . '?path=' . urlencode(($currentPath ? $currentPath . '/' : '') . $shareFile); $popupTitle = 'Share Folder Link'; $popupText = 'Copy the following URL to share folder'; } else { $shareUrl = getFileUrl($currentPath, $shareFile); $popupTitle = 'Share File Link'; $popupText = 'Copy the following URL to view file'; } } elseif ($shareType === 'folder') { $shareUrl = $currentScript . ($currentPath ? '?path=' . urlencode($currentPath) : ''); $popupTitle = 'Share Folder Location'; $popupText = 'Copy the following URL to share the folder containing'; } elseif ($shareType === 'download') { $shareUrl = $currentScript . '?' . http_build_query(['path' => $currentPath, 'download' => '1', 'file' => $shareFile]); $popupTitle = 'Share Download Link'; $popupText = 'Copy the following URL to download'; } $absoluteShareUrl = getAbsoluteUrl($shareUrl); $closeParams = $_GET; unset($closeParams['share_popup']); unset($closeParams['share_file']); $closeUrl = $currentScript . ($closeParams ? '?' . http_build_query($closeParams) : ''); return '

' . htmlspecialchars($popupTitle) . '

' . htmlspecialchars($popupText) . ' "' . htmlspecialchars($shareFile) . '":

Select the URL above and copy it (Ctrl+C / Cmd+C)

'; } function checkAndUpdateConfig() { global $apiBaseUrl, $configFile, $config, $disableApi, $cacheInstance; if ($disableApi) { return false; } $localVersion = isset($config['version']) ? $config['version'] : '1.1.16'; try { $cacheKey = 'version_check_' . $localVersion; if ($cacheInstance !== null) { $cachedResult = $cacheInstance->get($cacheKey, 'version'); if ($cachedResult !== null) { return $cachedResult; } } $versionCheckUrl = $apiBaseUrl . '/api.php?action=versionCheck¤t_version=' . urlencode($localVersion); $response = @file_get_contents($versionCheckUrl); if ($response === false) { if ($cacheInstance !== null) { $cacheInstance->set($cacheKey, 'version', false, 900); } return false; } $data = json_decode($response, true); if (!$data || !isset($data['success']) || !$data['success']) { if ($cacheInstance !== null) { $cacheInstance->set($cacheKey, 'version', false, 900); } return false; } if ($data['update_needed'] === false) { if ($cacheInstance !== null) { $cacheInstance->set($cacheKey, 'version', false, 3600); } return false; } $latestConfigUrl = $apiBaseUrl . '/api.php?action=config'; $latestConfigResponse = @file_get_contents($latestConfigUrl); if ($latestConfigResponse === false) { return false; } $latestConfigData = json_decode($latestConfigResponse, true); if (!$latestConfigData || !isset($latestConfigData['config'])) { return false; } $latestConfig = $latestConfigData['config']; $latestVersion = isset($latestConfig['version']) ? $latestConfig['version'] : '1.1.16'; if ($localVersion === $latestVersion) { if ($cacheInstance !== null) { $cacheInstance->set($cacheKey, 'version', false, 3600); } return false; } $updateResult = mergeConfigUpdates($config, $latestConfig); if ($updateResult) { if ($cacheInstance !== null) { $cacheInstance->set($cacheKey, 'version', true, 3600); } logConfigUpdate($localVersion, $latestVersion, $updateResult['changes']); return true; } $referenceUrl = $apiBaseUrl . '/api.php?action=configReference'; $referenceResponse = @file_get_contents($referenceUrl); if ($referenceResponse !== false) { $referenceData = json_decode($referenceResponse, true); if ($referenceData !== null && isset($referenceData['content'])) { $currentReferenceContent = ''; if (file_exists($localConfigReferenceFile)) { $currentReferenceContent = file_get_contents($localConfigReferenceFile); } if ($currentReferenceContent !== $referenceData['content']) { file_put_contents($localConfigReferenceFile, $referenceData['content']); } } } return false; } catch (Exception $e) { error_log("Config update error: " . $e->getMessage()); return false; } } function mergeConfigUpdates($localConfig, $latestConfig) { global $configFile; try { $changes = []; $updated = false; $mergedConfig = $localConfig; $oldVersion = isset($localConfig['version']) ? $localConfig['version'] : '1.1.16'; $newVersion = isset($latestConfig['version']) ? $latestConfig['version'] : '1.1.16'; $mergedConfig['version'] = $newVersion; $changes[] = "Updated version from {$oldVersion} to {$newVersion}"; $updated = true; $sectionsToCheck = ['main', 'exclusions', 'viewable_files']; foreach ($sectionsToCheck as $section) { if (!isset($latestConfig[$section])) { continue; } if (!isset($mergedConfig[$section])) { $mergedConfig[$section] = []; } foreach ($latestConfig[$section] as $key => $value) { if (!array_key_exists($key, $mergedConfig[$section])) { $mergedConfig[$section][$key] = $value; $changes[] = "Added new setting: {$section}.{$key} = " . json_encode($value); $updated = true; } } ksort($mergedConfig[$section]); } if ($updated) { $backupFile = $configFile . '.backup.' . date('Y-m-d_H-i-s'); copy($configFile, $backupFile); $jsonData = json_encode($mergedConfig, JSON_PRETTY_PRINT); if (file_put_contents($configFile, $jsonData) !== false) { global $config; $config = $mergedConfig; return [ 'success' => true, 'changes' => $changes, 'backup_file' => $backupFile ]; } else { copy($backupFile, $configFile); unlink($backupFile); return false; } } return false; } catch (Exception $e) { error_log("Config merge error: " . $e->getMessage()); return false; } } function logConfigUpdate($oldVersion, $newVersion, $changes) { $logData = [ 'timestamp' => date('Y-m-d H:i:s'), 'old_version' => $oldVersion, 'new_version' => $newVersion, 'changes_count' => count($changes), 'changes' => $changes ]; $logFile = dirname(__FILE__) . '/.indexer_files/config_updates.log'; $logEntry = json_encode($logData) . "\n"; @file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX); } function ensureLocalResources() { global $apiBaseUrl, $localApiDir, $localStyleDir, $localExtensionMapFile, $localIconsFile, $iconsDir, $iconType, $localConfigReferenceFile; if (!is_dir($localApiDir)) { mkdir($localApiDir, 0755, true); } if (!is_dir($localStyleDir)) { mkdir($localStyleDir, 0755, true); } if (!is_dir($iconsDir)) { mkdir($iconsDir, 0755, true); } ensureLocalFavicons(); if (!file_exists($localExtensionMapFile)) { $extensionMapUrl = $apiBaseUrl . '/extensionMap.json'; $extensionMapData = @file_get_contents($extensionMapUrl); if ($extensionMapData !== false) { file_put_contents($localExtensionMapFile, $extensionMapData); } } if (!file_exists($localConfigReferenceFile)) { $referenceUrl = $apiBaseUrl . '/api.php?action=configReference'; $referenceResponse = @file_get_contents($referenceUrl); if ($referenceResponse !== false) { $referenceData = json_decode($referenceResponse, true); if ($referenceData !== null && isset($referenceData['content'])) { file_put_contents($localConfigReferenceFile, $referenceData['content']); } } } if (!file_exists($localIconsFile)) { $iconsJsonUrl = $apiBaseUrl . '/icons.json'; $iconsJsonData = @file_get_contents($iconsJsonUrl); if ($iconsJsonData !== false) { file_put_contents($localIconsFile, $iconsJsonData); $iconsData = json_decode($iconsJsonData, true); if ($iconsData && is_array($iconsData)) { foreach ($iconsData as $extension => $iconFile) { $localIconPath = $iconsDir . '/' . $iconFile; if (!file_exists($localIconPath)) { $iconUrl = $apiBaseUrl . '/icons/' . $iconFile; $iconData = @file_get_contents($iconUrl); if ($iconData !== false) { @file_put_contents($localIconPath, $iconData); } } } } } } if ($iconType === 'minimal' || $iconType === 'default') { $requiredIcons = ['folder.png', 'non-descript-default-file.png']; foreach ($requiredIcons as $iconFile) { $localIconPath = $iconsDir . '/' . $iconFile; if (!file_exists($localIconPath)) { $iconUrl = $apiBaseUrl . '/icons/' . $iconFile; $iconData = @file_get_contents($iconUrl); if ($iconData !== false) { @file_put_contents($localIconPath, $iconData); } } } } $stylesheetPath = $localStyleDir . '/ecf219b0e59edefbdc0124308ade7358.css'; if (!file_exists($stylesheetPath)) { $stylesheetUrl = $apiBaseUrl . '/style/ecf219b0e59edefbdc0124308ade7358.css'; $stylesheetData = @file_get_contents($stylesheetUrl); if ($stylesheetData !== false) { @file_put_contents($stylesheetPath, $stylesheetData); $fontFiles = [ 'cyrillic-ext-400.woff2', 'cyrillic-400.woff2', 'greek-400.woff2', 'vietnamese-400.woff2', 'latin-ext-400.woff2', 'latin-400.woff2', 'cyrillic-ext-500.woff2', 'cyrillic-500.woff2', 'greek-500.woff2', 'vietnamese-500.woff2', 'latin-ext-500.woff2', 'latin-500.woff2', 'cyrillic-ext-700.woff2', 'cyrillic-700.woff2', 'greek-700.woff2', 'vietnamese-700.woff2', 'latin-ext-700.woff2', 'latin-700.woff2' ]; foreach ($fontFiles as $fontFile) { $localFontPath = $localStyleDir . '/' . $fontFile; if (!file_exists($localFontPath)) { $fontUrl = $apiBaseUrl . '/style/' . $fontFile; $fontData = @file_get_contents($fontUrl); if ($fontData !== false) { @file_put_contents($localFontPath, $fontData); } } } } } } function loadConfiguration() { global $configFile, $apiBaseUrl, $cacheInstance, $disableApi; if (file_exists($configFile)) { $configData = json_decode(file_get_contents($configFile), true); if ($configData !== null) { if (isset($configData['main']['custom_exclusions']) && !isset($configData['main']['deny_list'])) { $configData['main']['deny_list'] = $configData['main']['custom_exclusions']; unset($configData['main']['custom_exclusions']); file_put_contents($configFile, json_encode($configData, JSON_PRETTY_PRINT)); } return $configData; } } if (!$disableApi) { if ($cacheInstance !== null) { $cachedConfig = $cacheInstance->get('main_config', 'api'); if ($cachedConfig !== null) { return $cachedConfig; } } $apiConfigUrl = $apiBaseUrl . '/api.php?action=config'; $configResponse = @file_get_contents($apiConfigUrl); if ($configResponse !== false) { $apiData = json_decode($configResponse, true); if ($apiData !== null && isset($apiData['config'])) { file_put_contents($configFile, json_encode($apiData['config'], JSON_PRETTY_PRINT)); if ($cacheInstance !== null) { $cacheInstance->set('main_config', 'api', $apiData['config'], 3600); } return $apiData['config']; } } checkAndUpdateConfig(); } return []; } function getExtensionSetting($extension, $type = 'indexing') { global $disableApi; if (empty($extension) || trim($extension) === '') { if ($type === 'indexing') { return 'index_non-descript-files'; } elseif ($type === 'viewing') { return 'view_non-descript-files'; } return null; } if ($disableApi) { $extensionMappings = loadLocalExtensionMappings(); } else { $extensionMappings = loadExtensionMappings(); } if ($extensionMappings === null) { $commonTypes = ['txt', 'md', 'html', 'css', 'js', 'php', 'py', 'json', 'xml']; return in_array(strtolower($extension), $commonTypes); } $extension = strtolower($extension); if ($type === 'indexing' && isset($extensionMappings['indexing'][$extension])) { return $extensionMappings['indexing'][$extension]; } elseif ($type === 'viewing' && isset($extensionMappings['viewing'][$extension])) { return $extensionMappings['viewing'][$extension]; } return null; } function loadLocalExtensionMappings() { global $localExtensionMapFile; if (!file_exists($localExtensionMapFile)) { return null; } $mappingData = file_get_contents($localExtensionMapFile); $mappings = json_decode($mappingData, true); return $mappings ?: null; } function loadLocalIconMappings() { global $localIconsFile; if (!file_exists($localIconsFile)) { return []; } $mappingData = file_get_contents($localIconsFile); $mappings = json_decode($mappingData, true); return $mappings ?: []; } function getIconPath($type, $extension = '') { global $webPath, $iconsDir, $localIcons, $disableApi, $localStyleDir, $iconType; if ($iconType === 'disabled') { return null; } if ($iconType === 'emoji') { return null; } if ($iconType === 'minimal') { $iconFilename = ($type === 'folder') ? 'folder.png' : 'non-descript-default-file.png'; if ($disableApi) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } else { if ($localIcons) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } global $apiBaseUrl; return $apiBaseUrl . '/icons/' . $iconFilename; } return null; } if ($iconType === 'default') { if ($type === 'folder') { if ($disableApi) { $iconInfo = getIconFromLocal($type, $extension); if ($iconInfo !== null) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconInfo['path']); return $webPath . $relativePath; } } else { $iconInfo = getIconFromAPI($type, $extension); if ($iconInfo === null) { return null; } if ($localIcons) { $iconPath = $iconsDir . '/' . $iconInfo['filename']; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } return $iconInfo['url']; } } if ($type === 'file') { if (empty($extension) || trim($extension) === '') { $iconFilename = 'non-descript-default-file.png'; if ($disableApi) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } else { if ($localIcons) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } global $apiBaseUrl; return $apiBaseUrl . '/icons/' . $iconFilename; } return null; } if ($disableApi) { $iconMappings = loadLocalIconMappings(); } else { global $apiBaseUrl; $cache = initializeCache(); $iconMappings = $cache->get('icon_mappings', 'api'); if ($iconMappings === null) { $apiUrl = $apiBaseUrl . '/api.php?action=iconMappings'; $response = @file_get_contents($apiUrl); if ($response !== false) { $data = json_decode($response, true); if ($data !== null && isset($data['success']) && $data['success'] && isset($data['mappings'])) { $iconMappings = $data['mappings']; $cache->set('icon_mappings', 'api', $iconMappings, 86400); } } } } if ($iconMappings && is_array($iconMappings)) { $extension = strtolower($extension); if (!isset($iconMappings[$extension])) { $iconFilename = 'non-descript-default-file.png'; if ($disableApi) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } else { if ($localIcons) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } global $apiBaseUrl; return $apiBaseUrl . '/icons/' . $iconFilename; } return null; } } if ($disableApi) { $iconInfo = getIconFromLocal($type, $extension); if ($iconInfo !== null) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconInfo['path']); return $webPath . $relativePath; } } else { $iconInfo = getIconFromAPI($type, $extension); if ($iconInfo === null) { $iconFilename = 'non-descript-default-file.png'; if ($localIcons) { $iconPath = $iconsDir . '/' . $iconFilename; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } global $apiBaseUrl; return $apiBaseUrl . '/icons/' . $iconFilename; } if ($localIcons) { $iconPath = $iconsDir . '/' . $iconInfo['filename']; if (file_exists($iconPath)) { $relativePath = str_replace($GLOBALS['scriptDir'], '', $iconPath); return $webPath . $relativePath; } } return $iconInfo['url']; } } } return null; } function getIconFromLocal($type, $extension = '') { global $iconsDir; $iconMappings = loadLocalIconMappings(); if ($type === 'folder') { $iconFile = isset($iconMappings['folder']) ? $iconMappings['folder'] : 'folder.png'; } else { $extension = strtolower($extension); $iconFile = isset($iconMappings[$extension]) ? $iconMappings[$extension] : 'non-descript-default-file.png'; } $iconPath = $iconsDir . '/' . $iconFile; if (!file_exists($iconPath)) { if ($type === 'folder') { $iconFile = 'folder.png'; } else { $iconFile = 'non-descript-default-file.png'; } $iconPath = $iconsDir . '/' . $iconFile; if (!file_exists($iconPath)) { return null; } } return [ 'filename' => $iconFile, 'path' => $iconPath, 'size' => filesize($iconPath), 'last_modified' => filemtime($iconPath) ]; } function getStylesheetUrl() { global $webPath, $disableApi, $localStyleDir, $apiBaseUrl, $scriptDir; if ($disableApi) { $relativePath = str_replace($scriptDir, '', $localStyleDir . '/ecf219b0e59edefbdc0124308ade7358.css'); return $webPath . $relativePath; } else { return $apiBaseUrl . '/style/ecf219b0e59edefbdc0124308ade7358.css'; } } function isFileViewable($extension) { global $config; if (empty($extension) || trim($extension) === '') { $viewNonDescript = isset($config['viewable_files']['view_non-descript-files']) ? $config['viewable_files']['view_non-descript-files'] : false; return $viewNonDescript; } $settingKey = getExtensionSetting($extension, 'viewing'); if ($settingKey !== null) { return isset($config['viewable_files'][$settingKey]) ? $config['viewable_files'][$settingKey] : false; } return false; } function initializeDirectories() { global $indexerFilesDir, $zipCacheDir, $indexCacheDir, $iconsDir, $localIcons, $localApiDir, $disableApi; if (!is_dir($indexerFilesDir)) { mkdir($indexerFilesDir, 0755, true); } $dirs = [$zipCacheDir, $indexCacheDir]; if ($localIcons || $disableApi) { $dirs[] = $iconsDir; } if ($disableApi) { $dirs[] = $localApiDir; } foreach ($dirs as $dir) { if (!is_dir($dir)) { mkdir($dir, 0755, true); } } } function cleanupOldTempFiles() { global $zipCacheDir; if (!is_dir($zipCacheDir)) return; $files = glob($zipCacheDir . '/*'); $fiveMinutesAgo = time() - 300; foreach ($files as $file) { if (filemtime($file) < $fiveMinutesAgo) { if (is_dir($file)) { deleteDirectory($file); } else { unlink($file); } } } } function deleteDirectory($dir) { if (!is_dir($dir)) return; $files = glob($dir . '/*'); foreach ($files as $file) { if (is_dir($file)) { deleteDirectory($file); } else { unlink($file); } } rmdir($dir); } function getDirectorySize($path) { $size = 0; if (is_dir($path)) { $files = scandir($path); foreach ($files as $file) { if ($file == '.' || $file == '..') continue; $filePath = $path . '/' . $file; if (is_dir($filePath)) { $size += getDirectorySize($filePath); } else { $size += filesize($filePath); } } } return $size; } class IndexerCache { private $cacheType; private $indexCacheDir; private $pdo = null; public function __construct($cacheType, $indexCacheDir) { $this->cacheType = $cacheType; $this->indexCacheDir = $indexCacheDir; if ($this->cacheType === 'sqlite') { $this->initializeSQLite(); } } private function initializeSQLite() { $dbPath = $this->indexCacheDir . '/cache.sqlite'; try { $this->pdo = new PDO('sqlite:' . $dbPath); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->exec("CREATE TABLE IF NOT EXISTS unified_cache ( cache_key TEXT PRIMARY KEY, cache_type TEXT NOT NULL, data TEXT NOT NULL, last_modified INTEGER NOT NULL, expires_at INTEGER DEFAULT NULL )"); $this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_cache_type ON unified_cache(cache_type)"); $this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_expires_at ON unified_cache(expires_at)"); } catch (Exception $e) { $this->cacheType = 'json'; } } public function get($key, $type = 'directory') { if ($this->cacheType === 'sqlite') { return $this->getSQLiteCache($key, $type); } else { return $this->getJSONCache($key, $type); } } public function set($key, $type, $data, $ttl = null) { if ($this->cacheType === 'sqlite') { $this->setSQLiteCache($key, $type, $data, $ttl); } else { $this->setJSONCache($key, $type, $data, $ttl); } } public function has($key, $type = 'directory') { $data = $this->get($key, $type); return $data !== null; } public function cleanup() { if ($this->cacheType === 'sqlite') { $this->cleanupSQLite(); } else { $this->cleanupJSON(); } } public function clearType($type) { if ($this->cacheType === 'sqlite') { try { $stmt = $this->pdo->prepare("DELETE FROM unified_cache WHERE cache_type = ?"); $stmt->execute([$type]); } catch (Exception $e) { } } else { if (file_exists($cacheFile)) { $cacheData = json_decode(file_get_contents($cacheFile), true) ?: []; foreach ($cacheData as $cacheKey => $item) { if ($item['cache_type'] === $type) { unset($cacheData[$cacheKey]); } } file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT)); } } } private function getSQLiteCache($key, $type) { if (!$this->pdo) return null; try { $stmt = $this->pdo->prepare("SELECT data, last_modified, expires_at FROM unified_cache WHERE cache_key = ? AND cache_type = ?"); $stmt->execute([$key, $type]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result) { if ($result['expires_at'] && time() > $result['expires_at']) { $this->pdo->prepare("DELETE FROM unified_cache WHERE cache_key = ? AND cache_type = ?")->execute([$key, $type]); return null; } if ($type === 'directory') { $actualPath = preg_replace('/_sort_.*$/', '', $key); $currentModified = $this->getPathLastModified($actualPath); if ($result['last_modified'] < $currentModified) { $this->pdo->prepare("DELETE FROM unified_cache WHERE cache_key = ? AND cache_type = ?")->execute([$key, $type]); return null; } } return json_decode($result['data'], true); } } catch (Exception $e) { error_log("Cache error: " . $e->getMessage()); } return null; } private function setSQLiteCache($key, $type, $data, $ttl = null) { if (!$this->pdo) return; try { $expiresAt = $ttl ? time() + $ttl : null; if ($type === 'directory') { $actualPath = preg_replace('/_sort_.*$/', '', $key); $lastModified = $this->getPathLastModified($actualPath); } else { $lastModified = time(); } $stmt = $this->pdo->prepare("INSERT OR REPLACE INTO unified_cache (cache_key, cache_type, data, last_modified, expires_at) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$key, $type, json_encode($data), $lastModified, $expiresAt]); } catch (Exception $e) { error_log("Cache set error: " . $e->getMessage()); } } private function getJSONCache($key, $type) { $cacheFile = $this->indexCacheDir . '/cache.json'; if (!file_exists($cacheFile)) { return null; } $cacheData = json_decode(file_get_contents($cacheFile), true) ?: []; $cacheKey = $type . ':' . $key; if (isset($cacheData[$cacheKey])) { $item = $cacheData[$cacheKey]; if (isset($item['expires_at']) && $item['expires_at'] && time() > $item['expires_at']) { unset($cacheData[$cacheKey]); file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT)); return null; } if ($type === 'directory') { $actualPath = preg_replace('/_sort_.*$/', '', $key); $currentModified = $this->getPathLastModified($actualPath); if ($item['last_modified'] < $currentModified) { unset($cacheData[$cacheKey]); file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT)); return null; } } return $item['data']; } return null; } private function setJSONCache($key, $type, $data, $ttl = null) { $cacheFile = $this->indexCacheDir . '/cache.json'; $cacheData = []; if (file_exists($cacheFile)) { $cacheData = json_decode(file_get_contents($cacheFile), true) ?: []; } $cacheKey = $type . ':' . $key; $expiresAt = $ttl ? time() + $ttl : null; if ($type === 'directory') { $actualPath = preg_replace('/_sort_.*$/', '', $key); $lastModified = $this->getPathLastModified($actualPath); } else { $lastModified = time(); } $cacheData[$cacheKey] = [ 'cache_type' => $type, 'data' => $data, 'last_modified' => $lastModified, 'expires_at' => $expiresAt ]; file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT)); } private function cleanupSQLite() { if (!$this->pdo) return; try { $this->pdo->prepare("DELETE FROM unified_cache WHERE expires_at IS NOT NULL AND expires_at < ?")->execute([time()]); } catch (Exception $e) { } } private function cleanupJSON() { $cacheFile = $this->indexCacheDir . '/cache.json'; if (!file_exists($cacheFile)) { return; } $cacheData = json_decode(file_get_contents($cacheFile), true) ?: []; $modified = false; foreach ($cacheData as $cacheKey => $item) { if (isset($item['expires_at']) && $item['expires_at'] && time() > $item['expires_at']) { unset($cacheData[$cacheKey]); $modified = true; } } if ($modified) { file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT)); } } private function getPathLastModified($path) { global $baseDir, $configFile; $fullPath = $baseDir . '/' . ltrim($path, '/'); if (!is_dir($fullPath)) return 0; $lastModified = filemtime($fullPath); $items = scandir($fullPath); foreach ($items as $item) { if ($item == '.' || $item == '..') continue; $itemPath = $fullPath . '/' . $item; if (is_readable($itemPath)) { $itemModified = filemtime($itemPath); if ($itemModified > $lastModified) { $lastModified = $itemModified; } if (is_dir($itemPath)) { $subItems = @scandir($itemPath); if ($subItems) { foreach ($subItems as $subItem) { if ($subItem == '.' || $subItem == '..') continue; $subItemPath = $itemPath . '/' . $subItem; if (is_readable($subItemPath)) { $subItemModified = filemtime($subItemPath); if ($subItemModified > $lastModified) { $lastModified = $subItemModified; } } } } } } } if (file_exists($configFile)) { $configModified = filemtime($configFile); if ($configModified > $lastModified) { $lastModified = $configModified; } } return $lastModified; } } $cacheInstance = null; function initializeCache() { global $cacheInstance, $cacheType, $indexCacheDir; if ($cacheInstance === null) { $cacheInstance = new IndexerCache($cacheType, $indexCacheDir); } return $cacheInstance; } function getCacheData($path) { $cache = initializeCache(); return $cache->get($path, 'directory'); } function setCacheData($path, $data) { $cache = initializeCache(); $cache->set($path, 'directory', $data); } function loadExtensionMappings() { global $apiBaseUrl, $disableApi; if ($disableApi) { return loadLocalExtensionMappings(); } $cache = initializeCache(); $cachedData = $cache->get('extension_mappings', 'api'); if ($cachedData !== null) { return $cachedData; } $apiUrl = $apiBaseUrl . '/api.php?action=extensionMappings&type=all'; $response = @file_get_contents($apiUrl); if ($response !== false) { $data = json_decode($response, true); if ($data !== null && isset($data['success']) && $data['success'] && isset($data['mappings'])) { $cache->set('extension_mappings', 'api', $data['mappings'], 86400); return $data['mappings']; } } return null; } function loadIconCache() { $cache = initializeCache(); return $cache->get('all_icons', 'icon') ?: []; } function saveIconCache($iconCacheData) { $cache = initializeCache(); $cache->set('all_icons', 'icon', $iconCacheData); } function getIconFromAPI($type, $extension = '') { global $apiBaseUrl; $cache = initializeCache(); $cacheKey = $type === 'folder' ? 'folder' : $extension; $cachedIcon = $cache->get($cacheKey, 'icon'); if ($cachedIcon !== null) { return $cachedIcon; } $apiUrl = $apiBaseUrl . '/api.php?action=findIcon&type=' . urlencode($type); if ($extension) { $apiUrl .= '&extension=' . urlencode($extension); } $response = @file_get_contents($apiUrl); if ($response !== false) { $data = json_decode($response, true); if ($data !== null && isset($data['success']) && $data['success'] && isset($data['icon'])) { $cache->set($cacheKey, 'icon', $data['icon'], 86400); return $data['icon']; } } return null; } function clearCache($type = null) { $cache = initializeCache(); if ($type === null) { $cache->clearType('directory'); $cache->clearType('api'); $cache->clearType('icon'); } else { $cache->clearType($type); } } function runCacheCleanup() { $cache = initializeCache(); $cache->cleanup(); } function copyDirectoryExcludePhp($source, $destination) { global $currentPath; if (!is_dir($source)) return false; if (!is_dir($destination)) { mkdir($destination, 0755, true); } $files = scandir($source); foreach ($files as $file) { if ($file == '.' || $file == '..') continue; $sourcePath = $source . '/' . $file; $destPath = $destination . '/' . $file; if (is_dir($sourcePath)) { if (shouldIndexFolder($sourcePath)) { copyDirectoryExcludePhp($sourcePath, $destPath); } } else { $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); if (shouldIndexFile($sourcePath, $extension)) { copy($sourcePath, $destPath); } } } return true; } function addDirectoryToZip($zip, $dir, $zipPath = '') { $files = scandir($dir); foreach ($files as $file) { if ($file == '.' || $file == '..') continue; $filePath = $dir . '/' . $file; $zipFilePath = $zipPath ? $zipPath . '/' . $file : $file; if (is_dir($filePath)) { $zip->addEmptyDir($zipFilePath); addDirectoryToZip($zip, $filePath, $zipFilePath); } else { $zip->addFile($filePath, $zipFilePath); } } } function handleFileDownload() { global $fullPath, $currentPath, $baseDir, $disableFileDownloads, $disableFolderDownloads, $zipCacheDir; if (!isset($_GET['download']) || !isset($_GET['file'])) { return false; } cleanupOldTempFiles(); $downloadFile = $_GET['file']; $downloadPath = $fullPath . '/' . $downloadFile; $normalizedDownloadPath = str_replace('//', '/', $downloadPath); $normalizedBasePath = str_replace('//', '/', $baseDir); if (strpos($normalizedDownloadPath, $normalizedBasePath) !== 0) { http_response_code(403); exit('Access denied'); } if (strpos($downloadFile, '../') !== false || strpos($downloadFile, './') !== false) { http_response_code(403); exit('Invalid path'); } if (is_file($downloadPath)) { if ($disableFileDownloads) { http_response_code(403); exit('File downloads disabled'); } $extension = strtolower(pathinfo($downloadFile, PATHINFO_EXTENSION)); if (!isFileAccessible($downloadPath, $currentPath, $extension)) { http_response_code(403); exit('File not accessible'); } $fileSize = filesize($downloadPath); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($downloadFile) . '"'); header('Content-Length: ' . $fileSize); header('Cache-Control: no-cache'); header('Accept-Ranges: bytes'); if (ob_get_level()) { ob_end_clean(); } $handle = fopen($downloadPath, 'rb'); if ($handle === false) { http_response_code(500); exit('Cannot read file'); } $chunkSize = 8192; while (!feof($handle)) { $chunk = fread($handle, $chunkSize); if ($chunk === false) { break; } echo $chunk; flush(); if (connection_aborted()) { break; } } fclose($handle); exit; } elseif (is_dir($downloadPath)) { if ($disableFolderDownloads) { http_response_code(403); exit('Folder downloads disabled'); } $folderRelativePath = $currentPath ? $currentPath . '/' . basename($downloadFile) : basename($downloadFile); if (isPathDenied($folderRelativePath, true) && !isPathAllowed($folderRelativePath, true)) { http_response_code(403); exit('Folder access denied'); } if (!shouldIndexFolder($downloadPath)) { http_response_code(403); exit('Folder not accessible'); } $tempHash = bin2hex(random_bytes(16)); $tempDir = $zipCacheDir . '/' . $tempHash; if (copyDirectoryExcludePhp($downloadPath, $tempDir)) { $zipName = basename($downloadFile) . '.zip'; $zipPath = $zipCacheDir . '/' . $tempHash . '.zip'; $zip = new ZipArchive(); if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) { addDirectoryToZip($zip, $tempDir, basename($downloadFile)); $zip->close(); header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $zipName . '"'); header('Content-Length: ' . filesize($zipPath)); if (ob_get_level()) { ob_end_clean(); } readfile($zipPath); deleteDirectory($tempDir); unlink($zipPath); exit; } else { deleteDirectory($tempDir); http_response_code(500); exit('Cannot create ZIP file'); } } else { http_response_code(500); exit('Cannot copy directory'); } } http_response_code(404); exit('File not found'); } function serveFileWithXAccel($filePath, $fileName) { global $baseDir; $useXAccel = isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false; if ($useXAccel) { $relativePath = str_replace($baseDir, '', $filePath); $internalPath = '/internal-files' . $relativePath; header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($fileName) . '"'); header('Content-Length: ' . filesize($filePath)); header('Accept-Ranges: bytes'); header('X-Accel-Redirect: ' . $internalPath); header('X-Accel-Buffering: no'); if (ob_get_level()) { ob_end_clean(); } exit; } else { serveFileWithPHPStream($filePath, $fileName); exit; } } function serveFileWithPHPStream($filePath, $fileName) { $fileSize = filesize($filePath); $start = 0; $end = $fileSize - 1; if (isset($_SERVER['HTTP_RANGE'])) { $range = $_SERVER['HTTP_RANGE']; if (preg_match('/bytes=(\d+)-(\d*)/', $range, $matches)) { $start = intval($matches[1]); if (!empty($matches[2])) { $end = intval($matches[2]); } } http_response_code(206); header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize); } else { http_response_code(200); } header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($fileName) . '"'); header('Content-Length: ' . ($end - $start + 1)); header('Accept-Ranges: bytes'); header('Cache-Control: no-cache'); if (ob_get_level()) { ob_end_clean(); } $handle = fopen($filePath, 'rb'); if ($handle === false) { http_response_code(500); exit; } fseek($handle, $start); $bytesToRead = $end - $start + 1; $chunkSize = 8192; while ($bytesToRead > 0 && !feof($handle)) { $currentChunk = min($chunkSize, $bytesToRead); $data = fread($handle, $currentChunk); if ($data === false) { break; } echo $data; flush(); $bytesToRead -= strlen($data); if (connection_aborted()) { break; } } fclose($handle); exit; } if (isset($_GET['view']) && $_GET['view'] === 'raw' && isset($_GET['file'])) { $viewFile = $_GET['file']; $viewPath = $fullPath . '/' . $viewFile; $normalizedViewPath = str_replace('//', '/', $viewPath); $normalizedBasePath = str_replace('//', '/', $baseDir); if (strpos($normalizedViewPath, $normalizedBasePath) !== 0) { http_response_code(403); header('Location: ' . $_SERVER['SCRIPT_NAME']); exit; } if (strpos($viewFile, '../') !== false || strpos($viewFile, './') !== false) { http_response_code(403); header('Location: ' . $_SERVER['SCRIPT_NAME']); exit; } if (!is_file($viewPath)) { http_response_code(404); header('Location: ' . $_SERVER['SCRIPT_NAME']); exit; } $extension = strtolower(pathinfo($viewFile, PATHINFO_EXTENSION)); if (!isFileAccessible($viewPath, $currentPath, $extension)) { http_response_code(403); header('Location: ' . $_SERVER['SCRIPT_NAME']); exit; } if (!isFileViewable($extension)) { http_response_code(403); header('Location: ' . $_SERVER['SCRIPT_NAME']); exit; } $directServeExtensions = [ 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'jfif', 'avif', 'ico', 'cur', 'tiff', 'bmp', 'heic', 'svg', 'mp4', 'mkv', 'mp3', 'aac', 'flac', 'm4a', 'ogg', 'opus', 'wma', 'mov', 'webm', 'wmv', '3gp', 'flv', 'm4v', 'docx', 'xlsx' ]; if (in_array($extension, $directServeExtensions)) { switch ($extension) { case 'pdf': header('Content-Type: application/pdf'); break; case 'png': header('Content-Type: image/png'); break; case 'jpg': case 'jpeg': header('Content-Type: image/jpeg'); break; case 'gif': header('Content-Type: image/gif'); break; case 'webp': header('Content-Type: image/webp'); break; case 'jfif': header('Content-Type: image/jpeg'); break; case 'avif': header('Content-Type: image/avif'); break; case 'ico': header('Content-Type: image/vnd.microsoft.icon'); break; case 'cur': header('Content-Type: image/vnd.microsoft.icon'); break; case 'tiff': header('Content-Type: image/tiff'); break; case 'bmp': header('Content-Type: image/bmp'); break; case 'heic': header('Content-Type: image/heic'); break; case 'svg': header('Content-Type: image/svg+xml'); break; case 'mp4': header('Content-Type: video/mp4'); break; case 'mkv': header('Content-Type: video/webm'); break; case 'mp3': header('Content-Type: audio/mpeg'); break; case 'aac': header('Content-Type: audio/aac'); break; case 'flac': header('Content-Type: audio/flac'); break; case 'm4a': header('Content-Type: audio/mp4'); break; case 'ogg': header('Content-Type: audio/ogg'); break; case 'opus': header('Content-Type: audio/ogg'); break; case 'wma': header('Content-Type: audio/x-ms-wma'); break; case 'mov': header('Content-Type: video/quicktime'); break; case 'webm': header('Content-Type: video/webm'); break; case 'wmv': header('Content-Type: video/x-ms-wmv'); break; case '3gp': header('Content-Type: video/3gpp'); break; case 'flv': header('Content-Type: video/x-flv'); break; case 'm4v': header('Content-Type: video/mp4'); break; case 'docx': header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document'); header('Content-Transfer-Encoding: binary'); header('Accept-Ranges: bytes'); break; case 'xlsx': header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Transfer-Encoding: binary'); header('Accept-Ranges: bytes'); break; } header('Content-Disposition: inline; filename="' . basename($viewFile) . '"'); header('Content-Length: ' . filesize($viewPath)); readfile($viewPath); exit; } $fileContent = file_get_contents($viewPath); $fileName = htmlspecialchars($viewFile); $filePath = htmlspecialchars($webCurrentPath . '/' . $viewFile); $markdownExtensions = ['md', 'markdown']; $isMarkdown = in_array($extension, $markdownExtensions); $showRaw = isset($_GET['raw']) && $_GET['raw'] === '1'; if ($isMarkdown && $showRaw) { $isMarkdown = false; } ?> <?php echo $fileName; ?> View Markdown
View Raw
$item, 'modified' => filemtime($itemPath), 'is_dir' => is_dir($itemPath) ]; if ($itemInfo['is_dir']) { if (shouldIndexFolder(($currentPath ? $currentPath . '/' : '') . $item)) { $itemInfo['size'] = getDirectorySize($itemPath); $directories[] = $itemInfo; } } else { $extension = strtolower(pathinfo($item, PATHINFO_EXTENSION)); if (shouldIndexFile(($currentPath ? $currentPath . '/' : '') . $item, $extension)) { $itemInfo['size'] = filesize($itemPath); $itemInfo['extension'] = $extension; $files[] = $itemInfo; } } } $directories = sortItems($directories, $sortBy, $sortDir); $files = sortItems($files, $sortBy, $sortDir); setCacheData($cacheKey, [ 'directories' => $directories, 'files' => $files ]); } function formatBytes($size) { if ($size === null) return '-'; $units = ['B', 'KB', 'MB', 'GB']; for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) { $size /= 1024; } return round($size, 1) . ' ' . $units[$i]; } function getFileUrl($path, $filename) { global $webPath, $disableFileDownloads; $currentScript = $_SERVER['SCRIPT_NAME']; $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); if (isFileViewable($extension)) { return $currentScript . '?view=raw&file=' . urlencode($filename) . '&path=' . urlencode($path); } else { if ($disableFileDownloads) { return '#'; } return $currentScript . '?' . http_build_query(['path' => $path, 'download' => '1', 'file' => $filename]); } } function getSortParams() { $sortBy = $_GET['sort'] ?? 'name'; $sortDir = $_GET['dir'] ?? 'asc'; $validSorts = ['name', 'size', 'modified', 'type']; $validDirs = ['asc', 'desc']; if (!in_array($sortBy, $validSorts)) $sortBy = 'name'; if (!in_array($sortDir, $validDirs)) $sortDir = 'asc'; return ['sort' => $sortBy, 'dir' => $sortDir]; } function sortItems($items, $sortBy, $sortDir) { usort($items, function($a, $b) use ($sortBy, $sortDir) { $result = 0; switch ($sortBy) { case 'name': $result = strcasecmp($a['name'], $b['name']); break; case 'size': $result = $a['size'] <=> $b['size']; break; case 'modified': $result = $a['modified'] <=> $b['modified']; break; case 'type': $aExt = isset($a['extension']) ? $a['extension'] : ($a['is_dir'] ? 'folder' : ''); $bExt = isset($b['extension']) ? $b['extension'] : ($b['is_dir'] ? 'folder' : ''); $result = strcasecmp($aExt, $bExt); break; } return $sortDir === 'desc' ? -$result : $result; }); return $items; } function getSortUrl($sortBy, $currentSort, $currentDir, $currentPath) { $currentScript = $_SERVER['SCRIPT_NAME']; $newDir = ($sortBy === $currentSort && $currentDir === 'asc') ? 'desc' : 'asc'; $params = [ 'sort' => $sortBy, 'dir' => $newDir ]; if ($currentPath) { $params['path'] = $currentPath; } return $currentScript . '?' . http_build_query($params); } function getSortIndicator($column, $currentSort, $currentDir) { if ($column !== $currentSort) { return ''; } return $currentDir === 'asc' ? ' ↑' : ' ↓'; } function ensureLocalFavicons() { global $apiBaseUrl, $indexerFilesDir; $faviconDir = $iconsDir; $faviconFiles = [ 'icon.ico', '16x16.png', '32x32.png', '48x48.png', '96x96.png', '144x144.png', '192x192.png', '180x180.png' ]; foreach ($faviconFiles as $faviconFile) { $localFaviconPath = $faviconDir . '/' . $faviconFile; if (!file_exists($localFaviconPath)) { $faviconUrl = $apiBaseUrl . '/favicon/' . $faviconFile; $faviconData = @file_get_contents($faviconUrl); if ($faviconData !== false) { @file_put_contents($localFaviconPath, $faviconData); } } } } function getFaviconUrl($faviconFile) { global $webPath, $disableApi, $indexerFilesDir, $apiBaseUrl, $scriptDir; if ($disableApi) { $faviconPath = $indexerFilesDir . '/icons/' . $faviconFile; if (file_exists($faviconPath)) { $relativePath = str_replace($scriptDir, '', $faviconPath); return $webPath . $relativePath; } return $apiBaseUrl . '/favicon/' . $faviconFile; } else { return $apiBaseUrl . '/favicon/' . $faviconFile; } } function getFaviconTags() { $faviconTags = []; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; $faviconTags[] = ''; return implode("\n ", $faviconTags); } function parseMarkdown($text) { $text = str_replace(["\r\n", "\r"], "\n", $text); $codeBlocks = []; $inlineCodes = []; $text = preg_replace_callback('/```([a-zA-Z0-9\-_]*)\n(.*?)\n```/s', function($matches) use (&$codeBlocks) { $id = count($codeBlocks); $placeholder = "XCODEBLOCKREPLACEX" . $id . "XCODEBLOCKREPLACEX"; $codeBlocks[$placeholder] = $matches[2]; return "\n" . $placeholder . "\n"; }, $text); $text = preg_replace_callback('/`([^`\n]+?)`/', function($matches) use (&$inlineCodes) { $id = count($inlineCodes); $placeholder = "XINLINECODEREPLACEX" . $id . "XINLINECODEREPLACEX"; $inlineCodes[$placeholder] = $matches[1]; return $placeholder; }, $text); $text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); $text = preg_replace('/^###### (.+?)$/m', '
$1
', $text); $text = preg_replace('/^##### (.+?)$/m', '
$1
', $text); $text = preg_replace('/^#### (.+?)$/m', '

$1

', $text); $text = preg_replace('/^### (.+?)$/m', '

$1

', $text); $text = preg_replace('/^## (.+?)$/m', '

$1

', $text); $text = preg_replace('/^# (.+?)$/m', '

$1

', $text); $text = preg_replace('/!\[([^\]]*?)\]\(([^)]+?)\)/', '$1', $text); $text = preg_replace('/\[([^\]]+?)\]\(([^)]+?)\)/', '$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/(?$1', $text); $text = preg_replace('/~~([^~\n]+?)~~/', '$1', $text); $text = preg_replace('/^\s*---\s*$/m', '
', $text); $text = preg_replace('/^\s*\*\*\*\s*$/m', '
', $text); $text = preg_replace('/^> (.+?)$/m', '
$1
', $text); $text = preg_replace('/^(\s*)[\*\-\+] (.+?)$/m', '$1
  • $2
  • ', $text); $text = preg_replace('/^(\s*)\d+\. (.+?)$/m', '$1
  • $2
  • ', $text); $lines = explode("\n", $text); $result = []; $inList = false; $listType = ''; foreach ($lines as $line) { if (preg_match('/^(\s*)
  • "; $listType = $newListType; $inList = true; } elseif ($listType !== $newListType) { $result[] = ""; $result[] = "<$newListType class=\"markdown-list\">"; $listType = $newListType; } $result[] = $line; } else { if ($inList) { $result[] = ""; $inList = false; } $result[] = $line; } } if ($inList) { $result[] = ""; } $text = implode("\n", $result); $text = preg_replace_callback('/(?:^\|.+\|\s*$\n?)+/m', function($matches) { $table = trim($matches[0]); $rows = explode("\n", $table); $html = ''; $isHeader = true; foreach ($rows as $row) { if (empty(trim($row))) continue; if (preg_match('/^\|[\s\-\|:]+\|$/', $row)) { $isHeader = false; continue; } $cells = explode('|', trim($row, '|')); $cells = array_map('trim', $cells); $tag = $isHeader ? 'th' : 'td'; $class = $isHeader ? 'markdown-th' : 'markdown-td'; $html .= ''; foreach ($cells as $cell) { $html .= "<$tag class=\"$class\">$cell"; } $html .= ''; if ($isHeader) $isHeader = false; } $html .= '
    '; return $html; }, $text); $paragraphs = preg_split('/\n\s*\n/', $text); $result = []; foreach ($paragraphs as $paragraph) { $paragraph = trim($paragraph); if (empty($paragraph)) continue; if (preg_match('/^XCODEBLOCKREPLACEX\d+XCODEBLOCKREPLACEX$/', $paragraph)) { $result[] = $paragraph; } elseif (preg_match('/^<(h[1-6]|ul|ol|blockquote|pre|hr|table|div)/i', $paragraph)) { $result[] = $paragraph; } else { $paragraph = preg_replace('/\n(?!<)/', '
    ', $paragraph); $result[] = '

    ' . $paragraph . '

    '; } } $text = implode("\n\n", $result); foreach ($codeBlocks as $placeholder => $content) { $escapedContent = htmlspecialchars($content, ENT_QUOTES, 'UTF-8'); $codeHtml = '
    ' . $escapedContent . '
    '; $text = str_replace($placeholder, $codeHtml, $text); } foreach ($inlineCodes as $placeholder => $content) { $escapedContent = htmlspecialchars($content, ENT_QUOTES, 'UTF-8'); $codeHtml = '' . $escapedContent . ''; $text = str_replace($placeholder, $codeHtml, $text); } return $text; } function getBreadcrumbs($currentPath) { global $webPath; $currentScript = $_SERVER['SCRIPT_NAME']; $parts = array_filter(explode('/', $currentPath)); $breadcrumbs = 'files'; $path = ''; foreach ($parts as $part) { $path .= '/' . $part; $breadcrumbs .= ' / ' . htmlspecialchars($part) . ''; } return $breadcrumbs; } ?> Index of <?php echo htmlspecialchars($webCurrentPath); ?> >

    Index of

    ×
    'name', 'dir' => 'asc', 'label' => 'Name (A-Z)'], ['sort' => 'name', 'dir' => 'desc', 'label' => 'Name (Z-A)'], ['sort' => 'size', 'dir' => 'asc', 'label' => 'Size (Small to Large)'], ['sort' => 'size', 'dir' => 'desc', 'label' => 'Size (Large to Small)'], ['sort' => 'modified', 'dir' => 'asc', 'label' => 'Date Modified (Oldest First)'], ['sort' => 'modified', 'dir' => 'desc', 'label' => 'Date Modified (Newest First)'], ['sort' => 'type', 'dir' => 'asc', 'label' => 'Type (A-Z)'], ['sort' => 'type', 'dir' => 'desc', 'label' => 'Type (Z-A)'] ]; foreach ($sortOptions as $option) { $params = array_merge($baseParams, [ 'sort' => $option['sort'], 'dir' => $option['dir'] ]); $isActive = ($currentSort === $option['sort'] && $currentDir === $option['dir']); echo '' . $option['label'] . ''; } ?>
    📁
    Folder
    -
    -
    📁
    Folder
    📄
    File