diff --git a/lib/Service/AttachmentService.php b/lib/Service/AttachmentService.php index 2373dfb0a47..a7b213c2e78 100755 --- a/lib/Service/AttachmentService.php +++ b/lib/Service/AttachmentService.php @@ -284,6 +284,11 @@ public function uploadAttachment(int $documentId, string $newFileName, $newFileR throw new NotPermittedException('No write permissions'); } $saveDir = $this->getAttachmentDirectoryForFile($textFile, true); + // The filecache stores names in Unicode NFC, while uploads from macOS may use NFD + $normalizedFileName = \Normalizer::normalize($newFileName, \Normalizer::FORM_C); + if ($normalizedFileName !== false) { + $newFileName = $normalizedFileName; + } $fileName = self::getUniqueFileName($saveDir, $newFileName); $this->filenameValidator->validateFilename($fileName); $savedFile = $saveDir->newFile($fileName, $newFileResource); diff --git a/src/services/AttachmentResolver.js b/src/services/AttachmentResolver.js index 48f9ce921b0..06b7a2fbe6d 100644 --- a/src/services/AttachmentResolver.js +++ b/src/services/AttachmentResolver.js @@ -36,7 +36,11 @@ export default class AttachmentResolver { } #findAttachment(fileName) { - return this.#attachmentList.find((a) => a.name === fileName) + // Attachment srcs may use NFD (e.g. uploads from macOS), the attachment list uses NFC + const normalizedName = fileName.normalize('NFC') + return this.#attachmentList.find( + (a) => a.name.normalize('NFC') === normalizedName, + ) } /* diff --git a/src/tests/services/AttachmentResolver.spec.js b/src/tests/services/AttachmentResolver.spec.js index f1593201a39..b665bf7cc25 100644 --- a/src/tests/services/AttachmentResolver.spec.js +++ b/src/tests/services/AttachmentResolver.spec.js @@ -100,4 +100,25 @@ describe('Image resolver', () => { 'http://localhost:3000/remote.php/dav/files/user-uid/parentDir/path/to/some%20image.png', ) }) + + it('matches attachment names regardless of Unicode normalization', async () => { + const nfcName = 'B\u00FCcher.pdf' + const nfdName = 'Bu\u0308cher.pdf' + vi.spyOn(axios, 'post').mockReturnValue({ + data: [ + { + fileId: 1237, + name: nfcName, + mimetype: 'application/pdf', + isImage: false, + }, + ], + }) + const resolver = new AttachmentResolver({ session }) + const attachment = await resolver.resolve( + `.attachments.${session.documentId}/${encodeURIComponent(nfdName)}`, + ) + expect(attachment.isImage).toBe(false) + expect(attachment.name).toBe(nfcName) + }) })