"Unauthorized"]); exit; } // Ensure the request method is POST. if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['error' => 'Invalid request method.']); exit; } // CSRF check. $headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $receivedToken = $headersArr['x-csrf-token'] ?? ''; if (!isset($_SESSION['csrf_token']) || trim($receivedToken) !== $_SESSION['csrf_token']) { http_response_code(403); echo json_encode(['error' => 'Invalid CSRF token.']); exit; } // Check permissions. $username = $_SESSION['username'] ?? ''; $userPermissions = loadUserPermissions($username); if ($username && isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { http_response_code(403); echo json_encode([ "success" => false, "error" => "Read-only users are not allowed to create folders." ]); exit; } // Get and decode JSON input. $input = json_decode(file_get_contents('php://input'), true); if (!isset($input['folderName'])) { echo json_encode(['error' => 'Folder name not provided.']); exit; } $folderName = trim($input['folderName']); $parent = isset($input['parent']) ? trim($input['parent']) : ""; // Basic sanitation for folderName. if (!preg_match(REGEX_FOLDER_NAME, $folderName)) { http_response_code(400); echo json_encode(['error' => 'Invalid folder name.']); exit; } // Optionally sanitize the parent. if ($parent && !preg_match(REGEX_FOLDER_NAME, $parent)) { http_response_code(400); echo json_encode(['error' => 'Invalid parent folder name.']); exit; } // Delegate to FolderModel. $result = FolderModel::createFolder($folderName, $parent); echo json_encode($result); exit; } /** * @OA\Post( * path="/api/folder/deleteFolder.php", * summary="Delete an empty folder", * description="Deletes a specified folder if it is empty and not the root folder, and also removes its metadata file.", * operationId="deleteFolder", * tags={"Folders"}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"folder"}, * @OA\Property(property="folder", type="string", example="Documents/Subfolder") * ) * ), * @OA\Response( * response=200, * description="Folder deleted successfully", * @OA\JsonContent( * @OA\Property(property="success", type="boolean", example=true) * ) * ), * @OA\Response( * response=400, * description="Bad Request (e.g., invalid folder name or folder not empty)" * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=403, * description="Invalid CSRF token or permission denied" * ) * ) * * Deletes a folder if it is empty and not the root folder. * * @return void Outputs a JSON response. */ public function deleteFolder(): void { header('Content-Type: application/json'); // Ensure user is authenticated. if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { http_response_code(401); echo json_encode(["error" => "Unauthorized"]); exit; } // Ensure the request is a POST. if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(["error" => "Invalid request method."]); exit; } // CSRF Protection. $headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : ''; if (!isset($_SESSION['csrf_token']) || $receivedToken !== $_SESSION['csrf_token']) { http_response_code(403); echo json_encode(["error" => "Invalid CSRF token."]); exit; } // Check user permissions. $username = $_SESSION['username'] ?? ''; $userPermissions = loadUserPermissions($username); if ($username && isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { echo json_encode(["error" => "Read-only users are not allowed to delete folders."]); exit; } // Get and decode JSON input. $input = json_decode(file_get_contents('php://input'), true); if (!isset($input['folder'])) { echo json_encode(["error" => "Folder name not provided."]); exit; } $folder = trim($input['folder']); // Prevent deletion of the root folder. if (strtolower($folder) === 'root') { echo json_encode(["error" => "Cannot delete root folder."]); exit; } // Delegate to the model. $result = FolderModel::deleteFolder($folder); echo json_encode($result); exit; } /** * @OA\Post( * path="/api/folder/renameFolder.php", * summary="Rename a folder", * description="Renames an existing folder and updates its associated metadata files.", * operationId="renameFolder", * tags={"Folders"}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"oldFolder", "newFolder"}, * @OA\Property(property="oldFolder", type="string", example="Documents/OldFolder"), * @OA\Property(property="newFolder", type="string", example="Documents/NewFolder") * ) * ), * @OA\Response( * response=200, * description="Folder renamed successfully", * @OA\JsonContent( * @OA\Property(property="success", type="boolean", example=true) * ) * ), * @OA\Response( * response=400, * description="Invalid folder names or folder does not exist" * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=403, * description="Invalid CSRF token or permission denied" * ) * ) * * Renames a folder by validating inputs and delegating to the model. * * @return void Outputs a JSON response. */ public function renameFolder(): void { header('Content-Type: application/json'); // Ensure user is authenticated. if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { http_response_code(401); echo json_encode(["error" => "Unauthorized"]); exit; } // Ensure the request method is POST. if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['error' => 'Invalid request method.']); exit; } // CSRF Protection. $headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : ''; if (!isset($_SESSION['csrf_token']) || $receivedToken !== $_SESSION['csrf_token']) { http_response_code(403); echo json_encode(["error" => "Invalid CSRF token."]); exit; } // Check that the user is not read-only. $username = $_SESSION['username'] ?? ''; $userPermissions = loadUserPermissions($username); if ($username && isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { echo json_encode(["error" => "Read-only users are not allowed to rename folders."]); exit; } // Get JSON input. $input = json_decode(file_get_contents('php://input'), true); if (!isset($input['oldFolder']) || !isset($input['newFolder'])) { echo json_encode(['error' => 'Required folder names not provided.']); exit; } $oldFolder = trim($input['oldFolder']); $newFolder = trim($input['newFolder']); // Validate folder names. if (!preg_match(REGEX_FOLDER_NAME, $oldFolder) || !preg_match(REGEX_FOLDER_NAME, $newFolder)) { echo json_encode(['error' => 'Invalid folder name(s).']); exit; } // Delegate to the model. $result = FolderModel::renameFolder($oldFolder, $newFolder); echo json_encode($result); exit; } /** * @OA\Get( * path="/api/folder/getFolderList.php", * summary="Get list of folders", * description="Retrieves the list of folders in the upload directory, including file counts and metadata file names for each folder.", * operationId="getFolderList", * tags={"Folders"}, * @OA\Parameter( * name="folder", * in="query", * description="Optional folder name to filter the listing", * required=false, * @OA\Schema(type="string", example="Documents") * ), * @OA\Response( * response=200, * description="Folder list retrieved successfully", * @OA\JsonContent( * type="array", * @OA\Items(type="object") * ) * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=400, * description="Bad request" * ) * ) * * Retrieves the folder list and associated metadata. * * @return void Outputs JSON response. */ public function getFolderList(): void { header('Content-Type: application/json'); if (empty($_SESSION['authenticated'])) { http_response_code(401); echo json_encode(["error" => "Unauthorized"]); exit; } $parent = $_GET['folder'] ?? null; $folderList = FolderModel::getFolderList($parent); echo json_encode($folderList); exit; } /** * @OA\Get( * path="/api/folder/shareFolder.php", * summary="Display a shared folder", * description="Renders an HTML view of a shared folder's contents. Supports password protection, file listing with pagination, and an upload container if uploads are allowed.", * operationId="shareFolder", * tags={"Folders"}, * @OA\Parameter( * name="token", * in="query", * description="The share token for the folder", * required=true, * @OA\Schema(type="string") * ), * @OA\Parameter( * name="pass", * in="query", * description="The password if the folder is protected", * required=false, * @OA\Schema(type="string") * ), * @OA\Parameter( * name="page", * in="query", * description="Page number for pagination", * required=false, * @OA\Schema(type="integer", example=1) * ), * @OA\Response( * response=200, * description="Shared folder displayed", * @OA\MediaType(mediaType="text/html") * ), * @OA\Response( * response=400, * description="Invalid request" * ), * @OA\Response( * response=403, * description="Access forbidden (expired link or invalid password)" * ), * @OA\Response( * response=404, * description="Share folder not found" * ) * ) * * Displays a shared folder with file listings, pagination, and an upload container if allowed. * * @return void Outputs HTML content. */ function formatBytes($bytes) { if ($bytes < 1024) { return $bytes . " B"; } elseif ($bytes < 1024 * 1024) { return round($bytes / 1024, 2) . " KB"; } elseif ($bytes < 1024 * 1024 * 1024) { return round($bytes / (1024 * 1024), 2) . " MB"; } else { return round($bytes / (1024 * 1024 * 1024), 2) . " GB"; } } public function shareFolder(): void { // Retrieve GET parameters. $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING); $providedPass = filter_input(INPUT_GET, 'pass', FILTER_SANITIZE_STRING); $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT); if ($page === false || $page < 1) { $page = 1; } if (empty($token)) { http_response_code(400); header('Content-Type: application/json'); echo json_encode(["error" => "Missing token."]); exit; } // Delegate to the model. $data = FolderModel::getSharedFolderData($token, $providedPass, $page); // If a password is needed, output an HTML form. if (isset($data['needs_password']) && $data['needs_password'] === true) { header("Content-Type: text/html; charset=utf-8"); ?>
This folder is protected by a password. Please enter the password to view its contents.