--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network\Entity;
+
+use Friendica\BaseEntity;
+
+/**
+ * Implementation of the Content-Type header value from the MIME type RFC
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc2045#section-5
+ *
+ * @property-read string $type
+ * @property-read string $subtype
+ * @property-read array $parameters
+ */
+class MimeType extends BaseEntity
+{
+ /** @var string */
+ protected $type;
+ /** @var string */
+ protected $subtype;
+ /** @var array */
+ protected $parameters;
+
+ public function __construct(string $type, string $subtype, array $parameters = [])
+ {
+ $this->type = $type;
+ $this->subtype = $subtype;
+ $this->parameters = $parameters;
+ }
+
+ public function __toString(): string
+ {
+ $parameters = array_map(function (string $attribute, string $value) {
+ if (
+ strpos($value, '"') !== false ||
+ strpos($value, '\\') !== false ||
+ strpos($value, "\r") !== false
+ ) {
+ $value = '"' . str_replace(['\\', '"', "\r"], ['\\\\', '\\"', "\\\r"], $value) . '"';
+ }
+
+ return '; ' . $attribute . '=' . $value;
+ }, array_keys($this->parameters), array_values($this->parameters));
+
+ return $this->type . '/' .
+ $this->subtype .
+ implode('', $parameters);
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network\Factory;
+
+use Friendica\BaseFactory;
+use Friendica\Core\System;
+use Friendica\Network\Entity;
+
+/**
+ * Implementation of the Content-Type header value from the MIME type RFC
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc2045#section-5
+ */
+class MimeType extends BaseFactory
+{
+ public function createFromContentType(?string $contentType): Entity\MimeType
+ {
+ if ($contentType) {
+ $parameterStrings = explode(';', $contentType);
+ $mimetype = array_shift($parameterStrings);
+
+ $types = explode('/', $mimetype);
+ if (count($types) >= 2) {
+ $filetype = strtolower($types[0]);
+ $subtype = strtolower($types[1]);
+ } else {
+ $this->logger->notice('Unknown MimeType', ['type' => $contentType, 'callstack' => System::callstack(10)]);
+ }
+
+ $parameters = [];
+ foreach ($parameterStrings as $parameterString) {
+ $parameterString = trim($parameterString);
+ $parameterParts = explode('=', $parameterString, 2);
+ if (count($parameterParts) < 2) {
+ continue;
+ }
+
+ $attribute = trim($parameterParts[0]);
+ $valueString = trim($parameterParts[1]);
+
+ if ($valueString[0] == '"' && $valueString[strlen($valueString) - 1] == '"') {
+ $valueString = substr(str_replace(['\\"', '\\\\', "\\\r"], ['"', '\\', "\r"], $valueString), 1, -1);
+ }
+
+ $value = preg_replace('#\s*\([^()]*?\)#', '', $valueString);
+
+ $parameters[$attribute] = $value;
+ }
+ }
+
+ return new Entity\MimeType(
+ $filetype ?? 'unkn',
+ $subtype ?? 'unkn',
+ $parameters ?? [],
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Test\src\Network;
+
+use Friendica\Network\Entity;
+use Friendica\Network\Factory;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+
+class MimeTypeTest extends TestCase
+{
+ public function dataCreateFromContentType(): array
+ {
+ return [
+ 'image/jpg' => [
+ 'expected' => new Entity\MimeType('image', 'jpg'),
+ 'contentType' => 'image/jpg',
+ ],
+ 'image/jpg;charset=utf8' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ 'contentType' => 'image/jpg; charset=utf8',
+ ],
+ 'image/jpg; charset=utf8' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ 'contentType' => 'image/jpg; charset=utf8',
+ ],
+ 'image/jpg; charset = utf8' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ 'contentType' => 'image/jpg; charset=utf8',
+ ],
+ 'image/jpg; charset="utf8"' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ 'contentType' => 'image/jpg; charset="utf8"',
+ ],
+ 'image/jpg; charset="\"utf8\""' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => '"utf8"']),
+ 'contentType' => 'image/jpg; charset="\"utf8\""',
+ ],
+ 'image/jpg; charset="\"utf8\" (comment)"' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => '"utf8"']),
+ 'contentType' => 'image/jpg; charset="\"utf8\" (comment)"',
+ ],
+ 'image/jpg; charset=utf8 (comment)' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ 'contentType' => 'image/jpg; charset="utf8 (comment)"',
+ ],
+ 'image/jpg; charset=utf8; attribute=value' => [
+ 'expected' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8', 'attribute' => 'value']),
+ 'contentType' => 'image/jpg; charset=utf8; attribute=value',
+ ],
+ 'empty' => [
+ 'expected' => new Entity\MimeType('unkn', 'unkn'),
+ 'contentType' => '',
+ ],
+ 'unknown' => [
+ 'expected' => new Entity\MimeType('unkn', 'unkn'),
+ 'contentType' => 'unknown',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataCreateFromContentType
+ * @param Entity\MimeType $expected
+ * @param string $contentType
+ * @return void
+ */
+ public function testCreateFromContentType(Entity\MimeType $expected, string $contentType)
+ {
+ $factory = new Factory\MimeType(new NullLogger());
+
+ $this->assertEquals($expected, $factory->createFromContentType($contentType));
+ }
+
+ public function dataToString(): array
+ {
+ return [
+ 'image/jpg' => [
+ 'expected' => 'image/jpg',
+ 'mimeType' => new Entity\MimeType('image', 'jpg'),
+ ],
+ 'image/jpg;charset=utf8' => [
+ 'expected' => 'image/jpg; charset=utf8',
+ 'mimeType' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8']),
+ ],
+ 'image/jpg; charset="\"utf8\""' => [
+ 'expected' => 'image/jpg; charset="\"utf8\""',
+ 'mimeType' => new Entity\MimeType('image', 'jpg', ['charset' => '"utf8"']),
+ ],
+ 'image/jpg; charset=utf8; attribute=value' => [
+ 'expected' => 'image/jpg; charset=utf8; attribute=value',
+ 'mimeType' => new Entity\MimeType('image', 'jpg', ['charset' => 'utf8', 'attribute' => 'value']),
+ ],
+ 'empty' => [
+ 'expected' => 'unkn/unkn',
+ 'mimeType' => new Entity\MimeType('unkn', 'unkn'),
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataToString
+ * @param string $expected
+ * @param Entity\MimeType $mimeType
+ * @return void
+ */
+ public function testToString(string $expected, Entity\MimeType $mimeType)
+ {
+ $this->assertEquals($expected, $mimeType->__toString());
+ }
+
+ public function dataRoundtrip(): array
+ {
+ return [
+ ['image/jpg'],
+ ['image/jpg; charset=utf8'],
+ ['image/jpg; charset="\"utf8\""'],
+ ['image/jpg; charset=utf8; attribute=value'],
+ ];
+ }
+
+ /**
+ * @dataProvider dataRoundtrip
+ * @param string $expected
+ * @return void
+ */
+ public function testRoundtrip(string $expected)
+ {
+ $factory = new Factory\MimeType(new NullLogger());
+
+ $this->assertEquals($expected, $factory->createFromContentType($expected)->__toString());
+ }
+}