request_method === 'POST') { $API_devices->checkPermissions('admin-devices-files', 'RW'); # when called from the frontend will not be forwarding to a url since when its called from the frontend it doesnt need a redirection $API_devices->return_url = false; $device_slugify = isset($_POST['device_slugify']) ? preg_replace('/[^a-zA-Z0-9_-]/', '_', $_POST['device_slugify']) : ''; $filetype = $_POST['filetype'] ?? ''; $allowedFiletypes = ['documents' => 'pdf', 'firmware' => 'rom']; if (!array_key_exists($filetype, $allowedFiletypes)) { $API_devices->apiOutput(400, ['error' => 'Invalid file type']); } if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) { $API_devices->apiOutput(400, ['error' => 'No file uploaded or upload error']); } $filename = basename($_FILES['file']['name']); $filename = preg_replace('/[^a-zA-Z0-9_\.\-]/', '_', $filename); $file_extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $expectedExtension = $allowedFiletypes[$filetype]; if ($file_extension !== $expectedExtension) { $API_devices->apiOutput(415, ['error' => "Invalid file extension. Expected: $expectedExtension"]); } $finfo = finfo_open(FILEINFO_MIME_TYPE); $detectedMime = finfo_file($finfo, $_FILES['file']['tmp_name']); finfo_close($finfo); $expectedMimeTypes = [ 'pdf' => 'application/pdf', 'rom' => 'application/octet-stream' ]; if (!str_starts_with($detectedMime, $expectedMimeTypes[$expectedExtension])) { $API_devices->apiOutput(415, ['error' => 'Invalid MIME type: ' . $detectedMime]); } $destination_dir = $_SERVER['DOCUMENT_ROOT'] . '/data/devices/' . $device_slugify . '/' . $filetype; if (!is_dir($destination_dir) && !mkdir($destination_dir, 0775, true) && !is_dir($destination_dir)) { $API_devices->apiOutput(500, ['error' => 'Failed to create directory']); } $destination = $destination_dir . '/' . $filename; if (file_exists($destination)) { $API_devices->apiOutput(409, ['error' => 'File already exists']); } if (move_uploaded_file($_FILES['file']['tmp_name'], $destination)) { chmod($destination, 0644); // Set safe permissions $API_devices->apiOutput(200, ['success' => 'File uploaded succcessfully']); } else { $API_devices->apiOutput(500, ['error' => 'Failed to move uploaded file']); } } elseif ($API_devices->request_method === 'DELETE') { $API_devices->checkPermissions('admin-devices-files', 'RW'); # when called from the frontend will not be forwarding to a url since when its called from the frontend it doesnt need a redirection $API_devices->return_url = false; $relativePath = $_POST['file_name'] ?? ''; // Ensure it's not empty and doesn't contain null byte or backslashes if (empty($relativePath) || str_contains($relativePath, "\0") || str_contains($relativePath, '\\')) { http_response_code(400); echo json_encode(['status' => 'error', 'message' => 'Invalid path input']); exit; } // Normalize base root $root = realpath($_SERVER['DOCUMENT_ROOT'] . '/data/devices'); if (!$root) { http_response_code(500); echo json_encode(['status' => 'error', 'message' => 'Invalid devices root']); exit; } // Resolve full path $requestedPath = realpath($_SERVER['DOCUMENT_ROOT'] . $relativePath); // Validate resolved path if (!$requestedPath || strpos($requestedPath, $root) !== 0) { http_response_code(403); echo json_encode(['status' => 'error', 'message' => 'Access denied']); exit; } // Check if file exists and is a regular file if (!is_file($requestedPath)) { http_response_code(404); echo json_encode(['status' => 'error', 'message' => 'File does not exist']); exit; } // Attempt to delete if (unlink($requestedPath)) { echo json_encode(['status' => 'success', 'message' => 'File deleted']); } else { http_response_code(500); echo json_encode(['status' => 'error', 'message' => 'Failed to delete file']); } }