diff --git a/modules/helpdesk/pages/helpdesk/ticket-file-data().php b/modules/helpdesk/pages/helpdesk/ticket-file-data().php index e4f54aa..8895bc5 100644 --- a/modules/helpdesk/pages/helpdesk/ticket-file-data().php +++ b/modules/helpdesk/pages/helpdesk/ticket-file-data().php @@ -79,21 +79,47 @@ if (strlen($data) > $maxFileSize) { return; } -// Ensure filename has an extension — derive from content if missing +// Detect file type from magic bytes +$detectedType = match (true) { + str_starts_with($data, "\x89PNG") => ['image/png', '.png'], + str_starts_with($data, "\xFF\xD8\xFF") => ['image/jpeg', '.jpg'], + str_starts_with($data, "GIF8") => ['image/gif', '.gif'], + str_starts_with($data, "%PDF") => ['application/pdf', '.pdf'], + str_starts_with($data, "PK\x03\x04") => ['application/zip', '.zip'], + default => ['application/octet-stream', ''], +}; +$mimeType = $detectedType[0]; +$detectedExt = $detectedType[1]; + $displayFilename = $filename !== '' ? $filename : 'attachment-' . $entryNo; if (!str_contains($displayFilename, '.')) { - $ext = match (true) { - str_starts_with($data, "\x89PNG") => '.png', - str_starts_with($data, "\xFF\xD8\xFF") => '.jpg', - str_starts_with($data, "GIF8") => '.gif', - str_starts_with($data, "%PDF") => '.pdf', - str_starts_with($data, "PK\x03\x04") => '.zip', - default => '', - }; - $displayFilename .= $ext; + $displayFilename .= $detectedExt; } +$inline = trim((string) $request->query('inline', '')) === '1'; +$isImage = str_starts_with($mimeType, 'image/'); + header('X-Content-Type-Options: nosniff'); header('Content-Security-Policy: sandbox'); +// For inline image requests, serve with correct MIME type +if ($inline && $isImage) { + header('Content-Type: ' . $mimeType); + header('Content-Disposition: inline; filename="' . addcslashes($displayFilename, '"\\') . '"'); + header('Content-Length: ' . strlen($data)); + header('Cache-Control: private, max-age=3600'); + + while (ob_get_level()) { + ob_end_clean(); + } + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $data); + rewind($stream); + fpassthru($stream); + fclose($stream); + + return; +} + Router::download($displayFilename, $data); diff --git a/modules/helpdesk/web/js/helpdesk-communication.js b/modules/helpdesk/web/js/helpdesk-communication.js index 8df1813..e044a98 100644 --- a/modules/helpdesk/web/js/helpdesk-communication.js +++ b/modules/helpdesk/web/js/helpdesk-communication.js @@ -119,18 +119,14 @@ export function renderCommunicationFeed(entries, options = {}) { if (typeVariant === 'file' && fileDownloadUrl && entry.file_entry_no) { const fileName = entry.file_name || 'attachment'; const fileUrl = fileDownloadUrl + '?entryNo=' + encodeURIComponent(entry.file_entry_no) + '&ticketNo=' + encodeURIComponent(ticketNo) + '&filename=' + encodeURIComponent(fileName); - const ext = fileName.includes('.') ? fileName.split('.').pop().toLowerCase() : ''; - const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; - const isImage = imageExts.includes(ext); + const imgUrl = fileUrl + '&inline=1'; html += `