false, 'error' => 'Authentication required']); exit; } // Get attachment ID $attachmentId = $_GET['id'] ?? null; if (!$attachmentId || !is_numeric($attachmentId)) { http_response_code(400); header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'Valid attachment ID is required']); exit; } $attachmentId = (int)$attachmentId; try { $attachmentModel = new AttachmentModel(); // Get attachment details $attachment = $attachmentModel->getAttachment($attachmentId); if (!$attachment) { http_response_code(404); header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'Attachment not found']); exit; } // Build file path $uploadDir = $GLOBALS['config']['UPLOAD_DIR'] ?? dirname(__DIR__) . '/uploads'; $filePath = $uploadDir . '/' . $attachment['ticket_id'] . '/' . $attachment['filename']; // Check if file exists if (!file_exists($filePath)) { http_response_code(404); header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'File not found on server']); exit; } // Determine if we should display inline or force download $inline = isset($_GET['inline']) && $_GET['inline'] === '1'; $inlineTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf', 'text/plain']; // Set headers $disposition = ($inline && in_array($attachment['mime_type'], $inlineTypes)) ? 'inline' : 'attachment'; // Sanitize filename for Content-Disposition $safeFilename = preg_replace('/[^\w\s\-\.]/', '_', $attachment['original_filename']); header('Content-Type: ' . $attachment['mime_type']); header('Content-Disposition: ' . $disposition . '; filename="' . $safeFilename . '"'); header('Content-Length: ' . $attachment['file_size']); header('Cache-Control: private, max-age=3600'); header('X-Content-Type-Options: nosniff'); // Prevent PHP from timing out on large files set_time_limit(0); // Clear output buffer if (ob_get_level()) { ob_end_clean(); } // Stream file $handle = fopen($filePath, 'rb'); if ($handle === false) { http_response_code(500); header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'Failed to open file']); exit; } while (!feof($handle)) { echo fread($handle, 8192); flush(); } fclose($handle); exit; } catch (Exception $e) { http_response_code(500); header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'Failed to download attachment']); exit; }