diff --git a/src/Controller/Mail/MailAttachmentDownloadController.php b/src/Controller/Mail/MailAttachmentDownloadController.php new file mode 100644 index 0000000..f2be423 --- /dev/null +++ b/src/Controller/Mail/MailAttachmentDownloadController.php @@ -0,0 +1,93 @@ +accessChecker->ensureCanAccessMail($this->getUser()); + + $decoded = base64_decode(strtr($downloadId, '-_', '+/'), true); + if (false === $decoded || !str_contains($decoded, ':')) { + throw new BadRequestHttpException('Invalid attachment ID format'); + } + + [$messageDbIdStr, $partNumber] = explode(':', $decoded, 2); + $messageDbId = (int) $messageDbIdStr; + + $message = $this->messageRepository->find($messageDbId); + if (null === $message) { + throw new NotFoundHttpException('Message not found'); + } + + try { + $detail = $this->mailProvider->fetchMessage( + $message->getFolder()->getPath(), + $message->getUid() + ); + } catch (MailProviderException) { + throw new NotFoundHttpException('Could not fetch message from IMAP server'); + } + + $targetAttachment = null; + foreach ($detail->attachments as $att) { + if ($att->partNumber === $partNumber) { + $targetAttachment = $att; + + break; + } + } + + if (null === $targetAttachment) { + throw new NotFoundHttpException(sprintf('Attachment part "%s" not found', $partNumber)); + } + + try { + $content = $this->mailProvider->fetchAttachment( + $message->getFolder()->getPath(), + $message->getUid(), + $partNumber + ); + } catch (MailProviderException) { + throw new NotFoundHttpException('Could not fetch attachment content'); + } + + $filename = basename($targetAttachment->filename); + if ('' === $filename || '.' === $filename) { + $filename = 'attachment'; + } + + $response = new Response($content); + $response->headers->set('Content-Type', $targetAttachment->mimeType); + $response->headers->set( + 'Content-Disposition', + sprintf('attachment; filename="%s"', addslashes($filename)) + ); + $response->headers->set('Content-Length', (string) strlen($content)); + $response->headers->set('X-Content-Type-Options', 'nosniff'); + + return $response; + } +}