From eb42b7213b4b74c1c5e09c9e96dd2b4954c5e796 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Thu, 21 May 2026 21:17:21 +0200 Subject: [PATCH] fix(http): avoid iconv for header ascii fallback iconv transliteration is locale- and config-dependent and fails silently on some setups. UnicodeString::ascii() from symfony/string uses a built-in transliteration table backed by symfony/polyfill-intl-normalizer, so it works on all setups without requiring optional PHP extensions. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- apps/dav/lib/CardDAV/ImageExportPlugin.php | 5 ++--- apps/dav/lib/Provisioning/Apple/AppleProvisioningPlugin.php | 5 ++--- lib/public/AppFramework/Http/DownloadResponse.php | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/dav/lib/CardDAV/ImageExportPlugin.php b/apps/dav/lib/CardDAV/ImageExportPlugin.php index 088d8d4efcfcd..42c15e94a7f74 100644 --- a/apps/dav/lib/CardDAV/ImageExportPlugin.php +++ b/apps/dav/lib/CardDAV/ImageExportPlugin.php @@ -15,6 +15,7 @@ use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Symfony\Component\HttpFoundation\HeaderUtils; +use Symfony\Component\String\UnicodeString; class ImageExportPlugin extends ServerPlugin { @@ -89,9 +90,7 @@ public function httpGet(RequestInterface $request, ResponseInterface $response) $response->setHeader('Content-Type', $file->getMimeType()); $fileName = $node->getName() . '.' . PhotoCache::ALLOWED_CONTENT_TYPES[$file->getMimeType()]; $sanitized = str_replace(['/', '\\'], '-', $fileName); - $fallback = @iconv('UTF-8', 'ASCII//TRANSLIT', $sanitized) ?: $sanitized; - $fallback = preg_replace('/[^\x20-\x7e]/', '', $fallback); - $fallback = str_replace('%', '', $fallback); + $fallback = str_replace('%', '', (new UnicodeString($sanitized))->ascii()->toString()); $response->setHeader('Content-Disposition', HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $sanitized, $fallback)); $response->setStatus(Http::STATUS_OK); diff --git a/apps/dav/lib/Provisioning/Apple/AppleProvisioningPlugin.php b/apps/dav/lib/Provisioning/Apple/AppleProvisioningPlugin.php index 3c2a6119e0d3f..8c99ba437d78f 100644 --- a/apps/dav/lib/Provisioning/Apple/AppleProvisioningPlugin.php +++ b/apps/dav/lib/Provisioning/Apple/AppleProvisioningPlugin.php @@ -18,6 +18,7 @@ use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Symfony\Component\HttpFoundation\HeaderUtils; +use Symfony\Component\String\UnicodeString; class AppleProvisioningPlugin extends ServerPlugin { /** @@ -126,9 +127,7 @@ public function httpGet(RequestInterface $request, ResponseInterface $response): $response->setStatus(Http::STATUS_OK); $sanitized = str_replace(['/', '\\'], '-', $filename); - $fallback = @iconv('UTF-8', 'ASCII//TRANSLIT', $sanitized) ?: $sanitized; - $fallback = preg_replace('/[^\x20-\x7e]/', '', $fallback); - $fallback = str_replace('%', '', $fallback); + $fallback = str_replace('%', '', (new UnicodeString($sanitized))->ascii()->toString()); $response->setHeader('Content-Disposition', HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $sanitized, $fallback)); $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); $response->setBody($body); diff --git a/lib/public/AppFramework/Http/DownloadResponse.php b/lib/public/AppFramework/Http/DownloadResponse.php index 25822c4d57048..a31a8d2953604 100644 --- a/lib/public/AppFramework/Http/DownloadResponse.php +++ b/lib/public/AppFramework/Http/DownloadResponse.php @@ -9,6 +9,7 @@ use OCP\AppFramework\Http; use Symfony\Component\HttpFoundation\HeaderUtils; +use Symfony\Component\String\UnicodeString; /** * Prompts the user to download the a file @@ -31,9 +32,7 @@ public function __construct(string $filename, string $contentType, int $status = parent::__construct($status, $headers); $sanitized = str_replace(['/', '\\'], '-', $filename); - $fallback = @iconv('UTF-8', 'ASCII//TRANSLIT', $sanitized) ?: $sanitized; - $fallback = preg_replace('/[^\x20-\x7e]/', '', $fallback); - $fallback = str_replace('%', '', $fallback); + $fallback = str_replace('%', '', (new UnicodeString($sanitized))->ascii()->toString()); $this->addHeader('Content-Disposition', HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $sanitized, $fallback)); $this->addHeader('Content-Type', $contentType); }