From: Alexander Ebert Date: Sat, 22 Jun 2024 18:03:51 +0000 (+0200) Subject: Rename endpoint classes to match the naming schema X-Git-Tag: 6.1.0_Alpha_1~48^2 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=9e196bcaa4c0792e6be874ba35f4d6a6c089e8fa;p=GitHub%2FWoltLab%2FWCF.git Rename endpoint classes to match the naming schema --- diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index 6b06c3296d..f3554ed8ec 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -117,9 +117,9 @@ return static function (): void { \wcf\event\endpoint\ControllerCollecting::class, static function (\wcf\event\endpoint\ControllerCollecting $event) { $event->register(new \wcf\system\endpoint\controller\core\files\DeleteFile); - $event->register(new \wcf\system\endpoint\controller\core\files\PostGenerateThumbnails); - $event->register(new \wcf\system\endpoint\controller\core\files\PostUpload); - $event->register(new \wcf\system\endpoint\controller\core\files\upload\PostChunk); + $event->register(new \wcf\system\endpoint\controller\core\files\GenerateThumbnails); + $event->register(new \wcf\system\endpoint\controller\core\files\PrepareUpload); + $event->register(new \wcf\system\endpoint\controller\core\files\upload\SaveChunk); $event->register(new \wcf\system\endpoint\controller\core\comments\CreateComment); $event->register(new \wcf\system\endpoint\controller\core\comments\DeleteComment); $event->register(new \wcf\system\endpoint\controller\core\comments\EditComment); diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/GenerateThumbnails.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/GenerateThumbnails.class.php new file mode 100644 index 0000000000..e923fa442c --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/GenerateThumbnails.class.php @@ -0,0 +1,47 @@ +generateWebpVariant($file); + FileProcessor::getInstance()->generateThumbnails($file); + + $thumbnails = []; + foreach ($this->getThumbnails($file) as $thumbnail) { + $thumbnails[] = [ + 'identifier' => $thumbnail->identifier, + 'link' => $thumbnail->getLink(), + ]; + } + + return new JsonResponse($thumbnails); + } + + /** + * @return FileThumbnail[] + */ + private function getThumbnails(File $file): array + { + $thumbnailList = new FileThumbnailList(); + $thumbnailList->getConditionBuilder()->add("fileID = ?", [$file->fileID]); + $thumbnailList->readObjects(); + + return $thumbnailList->getObjects(); + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostGenerateThumbnails.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostGenerateThumbnails.class.php deleted file mode 100644 index 01887f8faa..0000000000 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostGenerateThumbnails.class.php +++ /dev/null @@ -1,48 +0,0 @@ -generateWebpVariant($file); - FileProcessor::getInstance()->generateThumbnails($file); - - $thumbnails = []; - foreach ($this->getThumbnails($file) as $thumbnail) { - $thumbnails[] = [ - 'identifier' => $thumbnail->identifier, - 'link' => $thumbnail->getLink(), - ]; - } - - return new JsonResponse($thumbnails); - } - - /** - * @return FileThumbnail[] - */ - private function getThumbnails(File $file): array - { - $thumbnailList = new FileThumbnailList(); - $thumbnailList->getConditionBuilder()->add("fileID = ?", [$file->fileID]); - $thumbnailList->readObjects(); - - return $thumbnailList->getObjects(); - } -} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostUpload.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostUpload.class.php deleted file mode 100644 index 899bd24f6e..0000000000 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PostUpload.class.php +++ /dev/null @@ -1,107 +0,0 @@ -getProcessorByName($parameters->objectType); - if ($fileProcessor === null) { - throw new UserInputException('objectType', 'unknown'); - } - - try { - $decodedContext = JSON::decode($parameters->context); - } catch (SystemException) { - throw new UserInputException('context', 'invalid'); - } - - if ($parameters->fileSize > FileProcessor::getInstance()->getMaximumFileSize()) { - throw new UserInputException('fileSize', 'tooLarge'); - } - - // Check if the maximum number of accepted files has already been uploaded. - if (FileProcessor::getInstance()->hasReachedUploadLimit($fileProcessor, $decodedContext)) { - throw new UserInputException('preflight', 'tooManyFiles'); - } - - $validationResult = $fileProcessor->acceptUpload($parameters->filename, $parameters->fileSize, $decodedContext); - if (!$validationResult->ok()) { - match ($validationResult) { - FileProcessorPreflightResult::InsufficientPermissions => throw new PermissionDeniedException(), - FileProcessorPreflightResult::InvalidContext => throw new UserInputException('context', 'invalid'), - default => throw new UserInputException('preflight', $validationResult->toString()), - }; - } - - $numberOfChunks = FileTemporary::getNumberOfChunks($parameters->fileSize); - $fileTemporary = $this->createTemporaryFile($parameters, $numberOfChunks); - - return new JsonResponse([ - 'identifier' => $fileTemporary->identifier, - 'numberOfChunks' => $numberOfChunks, - ]); - } - - private function createTemporaryFile(PostUploadParameters $parameters, int $numberOfChunks): FileTemporary - { - $identifier = \bin2hex(\random_bytes(20)); - $objectType = FileProcessor::getInstance()->getObjectType($parameters->objectType); - - $action = new FileTemporaryAction([], 'create', [ - 'data' => [ - 'identifier' => $identifier, - 'time' => \TIME_NOW, - 'filename' => $parameters->filename, - 'fileSize' => $parameters->fileSize, - 'fileHash' => $parameters->fileHash, - 'objectTypeID' => $objectType?->objectTypeID, - 'context' => $parameters->context, - 'chunks' => \str_repeat('0', $numberOfChunks), - ], - ]); - - return $action->executeAction()['returnValues']; - } -} - -/** @internal */ -final class PostUploadParameters -{ - public function __construct( - /** @var non-empty-string */ - public readonly string $filename, - - /** @var positive-int **/ - public readonly int $fileSize, - - /** @var non-empty-string */ - public readonly string $fileHash, - - /** @var non-empty-string */ - public readonly string $objectType, - - /** @var non-empty-string */ - public readonly string $context, - ) { - } -} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PrepareUpload.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PrepareUpload.class.php new file mode 100644 index 0000000000..845e587e88 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/PrepareUpload.class.php @@ -0,0 +1,107 @@ +getProcessorByName($parameters->objectType); + if ($fileProcessor === null) { + throw new UserInputException('objectType', 'unknown'); + } + + try { + $decodedContext = JSON::decode($parameters->context); + } catch (SystemException) { + throw new UserInputException('context', 'invalid'); + } + + if ($parameters->fileSize > FileProcessor::getInstance()->getMaximumFileSize()) { + throw new UserInputException('fileSize', 'tooLarge'); + } + + // Check if the maximum number of accepted files has already been uploaded. + if (FileProcessor::getInstance()->hasReachedUploadLimit($fileProcessor, $decodedContext)) { + throw new UserInputException('preflight', 'tooManyFiles'); + } + + $validationResult = $fileProcessor->acceptUpload($parameters->filename, $parameters->fileSize, $decodedContext); + if (!$validationResult->ok()) { + match ($validationResult) { + FileProcessorPreflightResult::InsufficientPermissions => throw new PermissionDeniedException(), + FileProcessorPreflightResult::InvalidContext => throw new UserInputException('context', 'invalid'), + default => throw new UserInputException('preflight', $validationResult->toString()), + }; + } + + $numberOfChunks = FileTemporary::getNumberOfChunks($parameters->fileSize); + $fileTemporary = $this->createTemporaryFile($parameters, $numberOfChunks); + + return new JsonResponse([ + 'identifier' => $fileTemporary->identifier, + 'numberOfChunks' => $numberOfChunks, + ]); + } + + private function createTemporaryFile(PostUploadParameters $parameters, int $numberOfChunks): FileTemporary + { + $identifier = \bin2hex(\random_bytes(20)); + $objectType = FileProcessor::getInstance()->getObjectType($parameters->objectType); + + $action = new FileTemporaryAction([], 'create', [ + 'data' => [ + 'identifier' => $identifier, + 'time' => \TIME_NOW, + 'filename' => $parameters->filename, + 'fileSize' => $parameters->fileSize, + 'fileHash' => $parameters->fileHash, + 'objectTypeID' => $objectType?->objectTypeID, + 'context' => $parameters->context, + 'chunks' => \str_repeat('0', $numberOfChunks), + ], + ]); + + return $action->executeAction()['returnValues']; + } +} + +/** @internal */ +final class PostUploadParameters +{ + public function __construct( + /** @var non-empty-string */ + public readonly string $filename, + + /** @var positive-int **/ + public readonly int $fileSize, + + /** @var non-empty-string */ + public readonly string $fileHash, + + /** @var non-empty-string */ + public readonly string $objectType, + + /** @var non-empty-string */ + public readonly string $context, + ) { + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/PostChunk.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/PostChunk.class.php deleted file mode 100644 index 00e3345577..0000000000 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/PostChunk.class.php +++ /dev/null @@ -1,142 +0,0 @@ -getHeaderLine('chunk-checksum-sha256'); - if ($checksum === '' || \str_contains($checksum, ',')) { - // Reject a missing header of multiple values provided by the client. - throw new UserInputException('chunk-checksum-sha256'); - } - - $sequenceNo = $variables['sequenceNo']; - - // Check if this is a valid sequence no. - if ($sequenceNo >= $fileTemporary->getChunkCount()) { - throw new UserInputException('sequenceNo', 'outOfRange'); - } - - // Check if this chunk has already been written. - if ($fileTemporary->hasChunk($sequenceNo)) { - throw new UserInputException('sequenceNo', 'alreadyExists'); - } - - // Validate the chunk size. - $chunkSize = $fileTemporary->getChunkSize(); - $stream = $request->getBody(); - $receivedSize = $stream->getSize(); - if ($receivedSize !== null && $receivedSize > $chunkSize) { - throw new UserInputException('payload', 'tooLarge'); - } - - $tmpPath = $fileTemporary->getPath(); - if (!\is_dir($tmpPath)) { - \mkdir($tmpPath, recursive: true); - } - - $file = new File($tmpPath . $fileTemporary->getFilename(), 'cb+'); - $file->lock(\LOCK_EX); - $file->seek($sequenceNo * $chunkSize); - - // Check if the checksum matches the received data. - $ctx = \hash_init('sha256'); - $total = 0; - while (!$stream->eof()) { - // Write the chunk using a buffer to avoid blowing up the memory limit. - // See https://stackoverflow.com/a/61997147 - $chunk = $stream->read(self::FREAD_BUFFER_SIZE); - $total += \strlen($chunk); - - if ($total > $chunkSize) { - throw new UserInputException('file', 'exceedsFileSize'); - } - - \hash_update($ctx, $chunk); - $file->write($chunk); - } - $file->sync(); - $file->close(); - - $result = \hash_final($ctx); - - if ($result !== $checksum) { - throw new UserInputException('payload', 'checksum'); - } - - // Mark the chunk as written. - $chunks = $fileTemporary->chunks; - $chunks[$sequenceNo] = '1'; - (new FileTemporaryEditor($fileTemporary))->update([ - 'chunks' => $chunks, - ]); - - // Check if we have all chunks. - if ($chunks === \str_repeat('1', $fileTemporary->getChunkCount())) { - // Check if the final result matches the expected checksum. - $checksum = \hash_file('sha256', $tmpPath . $fileTemporary->getFilename()); - if ($checksum !== $fileTemporary->fileHash) { - throw new UserInputException('file', 'checksum'); - } - - $file = FileEditor::createFromTemporary($fileTemporary); - - $context = $fileTemporary->getContext(); - (new FileTemporaryEditor($fileTemporary))->delete(); - unset($fileTemporary); - - $processor = $file->getProcessor(); - $processor?->adopt($file, $context); - - $generateThumbnails = false; - if ($processor !== null && $file->isImage()) { - $thumbnailFormats = $processor->getThumbnailFormats(); - if ($thumbnailFormats !== []) { - $generateThumbnails = true; - } - } - - $data = []; - if ($processor !== null) { - $data = $processor->getUploadResponse($file); - } - - return new JsonResponse([ - 'completed' => true, - 'generateThumbnails' => $generateThumbnails, - 'fileID' => $file->fileID, - 'objectTypeID' => $file->objectTypeID, - 'mimeType' => $file->mimeType, - 'link' => $file->getLink(), - 'data' => $data, - ]); - } - - return new JsonResponse([ - 'completed' => false, - ]); - } -} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/SaveChunk.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/SaveChunk.class.php new file mode 100644 index 0000000000..02f65da5a8 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/files/upload/SaveChunk.class.php @@ -0,0 +1,142 @@ +getHeaderLine('chunk-checksum-sha256'); + if ($checksum === '' || \str_contains($checksum, ',')) { + // Reject a missing header of multiple values provided by the client. + throw new UserInputException('chunk-checksum-sha256'); + } + + $sequenceNo = $variables['sequenceNo']; + + // Check if this is a valid sequence no. + if ($sequenceNo >= $fileTemporary->getChunkCount()) { + throw new UserInputException('sequenceNo', 'outOfRange'); + } + + // Check if this chunk has already been written. + if ($fileTemporary->hasChunk($sequenceNo)) { + throw new UserInputException('sequenceNo', 'alreadyExists'); + } + + // Validate the chunk size. + $chunkSize = $fileTemporary->getChunkSize(); + $stream = $request->getBody(); + $receivedSize = $stream->getSize(); + if ($receivedSize !== null && $receivedSize > $chunkSize) { + throw new UserInputException('payload', 'tooLarge'); + } + + $tmpPath = $fileTemporary->getPath(); + if (!\is_dir($tmpPath)) { + \mkdir($tmpPath, recursive: true); + } + + $file = new File($tmpPath . $fileTemporary->getFilename(), 'cb+'); + $file->lock(\LOCK_EX); + $file->seek($sequenceNo * $chunkSize); + + // Check if the checksum matches the received data. + $ctx = \hash_init('sha256'); + $total = 0; + while (!$stream->eof()) { + // Write the chunk using a buffer to avoid blowing up the memory limit. + // See https://stackoverflow.com/a/61997147 + $chunk = $stream->read(self::FREAD_BUFFER_SIZE); + $total += \strlen($chunk); + + if ($total > $chunkSize) { + throw new UserInputException('file', 'exceedsFileSize'); + } + + \hash_update($ctx, $chunk); + $file->write($chunk); + } + $file->sync(); + $file->close(); + + $result = \hash_final($ctx); + + if ($result !== $checksum) { + throw new UserInputException('payload', 'checksum'); + } + + // Mark the chunk as written. + $chunks = $fileTemporary->chunks; + $chunks[$sequenceNo] = '1'; + (new FileTemporaryEditor($fileTemporary))->update([ + 'chunks' => $chunks, + ]); + + // Check if we have all chunks. + if ($chunks === \str_repeat('1', $fileTemporary->getChunkCount())) { + // Check if the final result matches the expected checksum. + $checksum = \hash_file('sha256', $tmpPath . $fileTemporary->getFilename()); + if ($checksum !== $fileTemporary->fileHash) { + throw new UserInputException('file', 'checksum'); + } + + $file = FileEditor::createFromTemporary($fileTemporary); + + $context = $fileTemporary->getContext(); + (new FileTemporaryEditor($fileTemporary))->delete(); + unset($fileTemporary); + + $processor = $file->getProcessor(); + $processor?->adopt($file, $context); + + $generateThumbnails = false; + if ($processor !== null && $file->isImage()) { + $thumbnailFormats = $processor->getThumbnailFormats(); + if ($thumbnailFormats !== []) { + $generateThumbnails = true; + } + } + + $data = []; + if ($processor !== null) { + $data = $processor->getUploadResponse($file); + } + + return new JsonResponse([ + 'completed' => true, + 'generateThumbnails' => $generateThumbnails, + 'fileID' => $file->fileID, + 'objectTypeID' => $file->objectTypeID, + 'mimeType' => $file->mimeType, + 'link' => $file->getLink(), + 'data' => $data, + ]); + } + + return new JsonResponse([ + 'completed' => false, + ]); + } +}