true, 'csrf_token' => $_SESSION['csrf_token'], ]); return; } } // ---- 2) Auth + account-level flags ---- if (empty($_SESSION['authenticated'])) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); return; } $username = (string)($_SESSION['username'] ?? ''); $userPerms = loadUserPermissions($username) ?: []; $isAdmin = ACL::isAdmin($userPerms); // Admins should never be blocked by account-level "disableUpload" if (!$isAdmin && !empty($userPerms['disableUpload'])) { http_response_code(403); echo json_encode(['error' => 'Upload disabled for this user.']); return; } // ---- 3) Folder-level WRITE permission (ACL) ---- // Prefer the unified param array, fall back to GET only if needed. $folderParam = isset($requestParams['folder']) ? (string)$requestParams['folder'] : (isset($_GET['folder']) ? (string)$_GET['folder'] : 'root'); // Decode %xx (e.g., "test%20folder") then normalize $folderParam = rawurldecode($folderParam); $targetFolder = ACL::normalizeFolder($folderParam); // Admins bypass folder canWrite checks if (!$isAdmin && !ACL::canUpload($username, $userPerms, $targetFolder)) { http_response_code(403); echo json_encode([ 'error' => 'Forbidden: no write access to folder "' . $targetFolder . '".', ]); return; } // ---- 4) Delegate to model (force the sanitized folder) ---- $requestParams['folder'] = $targetFolder; // Keep legacy behavior for anything still reading $_POST directly $_POST['folder'] = $targetFolder; $result = UploadModel::handleUpload($requestParams, $_FILES); // ---- 5) Special handling for Resumable.js GET tests ---- // Resumable only inspects HTTP status: // 200 => chunk exists (skip) // 404/other => chunk missing (upload) if ($isResumableTest && isset($result['status'])) { if ($result['status'] === 'found') { http_response_code(200); } else { http_response_code(202); // 202 Accepted = chunk not found } echo json_encode($result); return; } // ---- 6) Normal response handling ---- if (isset($result['error'])) { http_response_code(400); echo json_encode($result); return; } if (isset($result['status'])) { echo json_encode($result); return; } echo json_encode([ 'success' => $result['success'] ?? 'File uploaded successfully', 'newFilename' => $result['newFilename'] ?? null, ]); } public function removeChunks(): void { header('Content-Type: application/json'); $receivedToken = isset($_POST['csrf_token']) ? trim((string)$_POST['csrf_token']) : ''; if ($receivedToken !== ($_SESSION['csrf_token'] ?? '')) { http_response_code(403); echo json_encode(['error' => 'Invalid CSRF token']); return; } if (!isset($_POST['folder'])) { http_response_code(400); echo json_encode(['error' => 'No folder specified']); return; } $folderRaw = (string)$_POST['folder']; $folder = ACL::normalizeFolder(rawurldecode($folderRaw)); echo json_encode(UploadModel::removeChunks($folder)); } }