getConnection(); // Find the thesis that owns this file path $visStmt = $mediaPdo->prepare(" SELECT t.access_type_id FROM theses t JOIN thesis_files tf ON tf.thesis_id = t.id WHERE tf.file_path = ? LIMIT 1 "); $visStmt->execute([$requestedPath]); $visRow = $visStmt->fetch(); if ($visRow) { $accessTypeId = (int)($visRow['access_type_id'] ?? 1); // 3 = Interdit — block entirely if ($accessTypeId === 3) { http_response_code(403); exit; } // 2 = Interne — allow (no session auth requirement for now; could add later) } } catch (\Throwable $e) { // On DB error, fail open (don't block legitimate requests) error_log("media.php visibility check error: " . $e->getMessage()); } } // --- 3. Verify MIME type from file content (not extension) -------------------- $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeType = $finfo->file($realFull); $allowedMimes = [ 'image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'video/mp4', 'application/zip', ]; if (!in_array($mimeType, $allowedMimes, true)) { http_response_code(403); exit; } // --- 4. Send response headers ------------------------------------------------- header('Content-Type: ' . $mimeType); header('Content-Length: ' . filesize($realFull)); header('X-Content-Type-Options: nosniff'); $ext = strtolower(pathinfo($realFull, PATHINFO_EXTENSION)); if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif'], true)) { // Images: cache publicly for 7 days header('Cache-Control: public, max-age=604800'); } elseif ($ext === 'pdf') { // PDFs: cache for 1 day, display inline header('Cache-Control: public, max-age=86400'); header('Content-Disposition: inline'); } else { // Everything else: no public caching header('Cache-Control: private, no-store'); } // --- 5. Stream file ----------------------------------------------------------- readfile($realFull);