diff --git a/.gitattributes b/.gitattributes index 0f74df2c..ffc41f0c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,7 +6,7 @@ tests export-ignore .* export-ignore box.json.dist export-ignore composer.lock export-ignore -composer-require-* export-ignore +composer-require-* export-ignore docker-compose.yaml export-ignore Makefile export-ignore phpunit.xml* export-ignore diff --git a/src/Traffic/Message/Multipart/File.php b/src/Traffic/Message/Multipart/File.php index 4d866731..a5acfed5 100644 --- a/src/Traffic/Message/Multipart/File.php +++ b/src/Traffic/Message/Multipart/File.php @@ -130,20 +130,23 @@ public function isEmbedded(): bool */ public function getEmbeddingId(): ?string { + $matches = []; $result = match (true) { // Content-Disposition is inline and name is present \str_starts_with($this->getHeaderLine('Content-Disposition'), 'inline') && \preg_match( - '/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/', + '/(?:\\s|^|;|,)name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/', $this->getHeaderLine('Content-Disposition'), $matches, - ) === 1 => $matches[1], + PREG_UNMATCHED_AS_NULL, + ) === 1 => $matches[1] ?? $matches[2] ?? $matches[3], // Content-Type is image/* and has name \str_starts_with($this->getHeaderLine('Content-Type'), 'image/') && \preg_match( - '/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/', + '/(?:\\s|^|;|,)name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/', $this->getHeaderLine('Content-Type'), $matches, - ) === 1 => $matches[1], + PREG_UNMATCHED_AS_NULL, + ) === 1 => $matches[1] ?? $matches[2] ?? $matches[3], default => null, }; diff --git a/tests/Unit/Traffic/Message/Multipart/FileTest.php b/tests/Unit/Traffic/Message/Multipart/FileTest.php index c9bf9992..5894c25f 100644 --- a/tests/Unit/Traffic/Message/Multipart/FileTest.php +++ b/tests/Unit/Traffic/Message/Multipart/FileTest.php @@ -6,6 +6,7 @@ use Buggregator\Trap\Support\StreamHelper; use Buggregator\Trap\Traffic\Message\Multipart\File; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class FileTest extends TestCase @@ -29,6 +30,29 @@ public function testWithHeader(): void self::assertSame('baz', $new->getHeaderLine('foo')); } + public static function provideEmbeddings(): iterable + { + yield [['Content-Type' => 'image/jpeg'], null]; + yield [['Content-Type' => 'image/jpeg', 'Content-Disposition' => 'inline; filename="foo.jpg"'], null]; + yield [['Content-Type' => 'image/jpeg', 'Content-Disposition' => 'inline; filename="foo.jpg"; id="bar"'], null]; + yield [['Content-Type' => 'image/png; name="embedding-name"; id="bar"'], 'embedding-name']; + yield [['Content-Type' => 'image/png; a-name=test; name=embedding-name; b-name=test'], 'embedding-name']; + yield [['Content-Type' => 'image/png; name=\'embedding-name\''], 'embedding-name']; + yield [['Content-Disposition' => 'inline; name="embedding-name"'], 'embedding-name']; + yield [['Content-Disposition' => 'inline; ; a-name="a"; name=embedding; file-name=3'], 'embedding']; + yield [['Content-Disposition' => 'inline; name=\'embedding-1\''], 'embedding-1']; + } + + #[DataProvider('provideEmbeddings')] + public function testEmbeddingId(array $headers, ?string $result): void + { + $field = File::fromArray([ + 'headers' => $headers, + ]); + + self::assertSame($result, $field->getEmbeddingId()); + } + public function testFromArray(): void { $field = File::fromArray([ diff --git a/tests/Unit/Traffic/Parser/MultipartBodyParserTest.php b/tests/Unit/Traffic/Parser/MultipartBodyParserTest.php index 001efcd8..2f4b303c 100644 --- a/tests/Unit/Traffic/Parser/MultipartBodyParserTest.php +++ b/tests/Unit/Traffic/Parser/MultipartBodyParserTest.php @@ -113,6 +113,52 @@ public function testWithFileAttach(): void self::assertSame($file2, $file->getStream()->__toString()); } + public function testBase64Encoded(): void + { + $file1 = \file_get_contents(__DIR__ . '/../../../Stub/deburger.png'); + $file2 = \file_get_contents(__DIR__ . '/../../../Stub/buggregator.png'); + + $encoded1 = \base64_encode($file1); + $encoded2 = \base64_encode($file2); + $body = $this->makeStream( + <<parse($body, 'Asrf456BGe4h'); + + self::assertCount(2, $result); + $file = $result[0]; + // Uploaded files + self::assertInstanceOf(File::class, $file); + self::assertNull($file->getName()); + self::assertSame('logo-embeddable', $file->getClientFilename()); + self::assertSame('image/png', $file->getClientMediaType()); + self::assertSame('4486bda9ad8b1f422deaf6a750194668@trap', $file->getEmbeddingId()); + self::assertSame($file1, $file->getStream()->__toString()); + + $file = $result[1]; + self::assertInstanceOf(File::class, $file); + self::assertSame('AttachedFile2', $file->getName()); + self::assertSame('logo-embeddable', $file->getClientFilename()); + self::assertSame('image/png', $file->getClientMediaType()); + self::assertSame('AttachedFile2', $file->getEmbeddingId()); + self::assertSame($file2, $file->getStream()->__toString()); + } + private function makeStream(string $body): StreamInterface { $stream = Stream::create($body);