query('SELECT animal_id,filename FROM animal_photos WHERE is_public=0') ->fetchAll(PDO::FETCH_ASSOC); foreach ($private as $row) { self::moveIfPresent( self::publicDir((int) $row['animal_id']) . '/' . basename((string) $row['filename']), self::animalDir((int) $row['animal_id']) . '/' . basename((string) $row['filename']), ); } $medical = $db->query('SELECT mp.animal_id,mp.filename FROM medical_photos mp')->fetchAll(PDO::FETCH_ASSOC); foreach ($medical as $row) { self::moveIfPresent( self::publicDir((int) $row['animal_id']) . '/meds/' . basename((string) $row['filename']), self::medicalDir((int) $row['animal_id']) . '/' . basename((string) $row['filename']), ); } } private static function moveIfPresent(string $source, string $target): void { if (!is_file($source) || is_file($target)) { return; } self::ensure(dirname($target)); if (rename($source, $target)) { @chmod($target, 0600); } } public static function send(): void { $kind = (string) ($_GET['kind'] ?? 'photo'); $id = (int) ($_GET['id'] ?? 0); if ($id <= 0) { http_response_code(404); return; } $db = DB::pdo(); if ($kind === 'medical') { if (!PermissionService::can('medical', 'view')) { http_response_code(403); return; } $s = $db->prepare( 'SELECT mp.id,mp.animal_id,mp.filename FROM medical_photos mp JOIN animals a ON a.id=mp.animal_id WHERE mp.id=? AND a.deleted_at IS NULL', ); $s->execute([$id]); $row = $s->fetch(PDO::FETCH_ASSOC); $path = $row ? self::medicalDir((int) $row['animal_id']) . '/' . basename((string) $row['filename']) : ''; } else { if (!PermissionService::can('animals', 'view')) { http_response_code(403); return; } $s = $db->prepare( 'SELECT ap.id,ap.animal_id,ap.filename,ap.care_round_id FROM animal_photos ap JOIN animals a ON a.id=ap.animal_id WHERE ap.id=? AND ap.is_public=0 AND a.deleted_at IS NULL', ); $s->execute([$id]); $row = $s->fetch(PDO::FETCH_ASSOC); if ($row && !empty($row['care_round_id']) && !PermissionService::can('medical', 'view')) { http_response_code(403); return; } $path = $row ? self::animalDir((int) $row['animal_id']) . '/' . basename((string) $row['filename']) : ''; } if (empty($row) || !is_file($path)) { http_response_code(404); return; } $mime = (new finfo(FILEINFO_MIME_TYPE))->file($path) ?: 'application/octet-stream'; PrivacyService::logAccess( 'private_' . $kind, $id, (int) $row['animal_id'], basename((string) $row['filename']), ); AuditService::log( 'private_file_viewed', '/private-media', 'Média privé consulté', 'animal', (int) $row['animal_id'], ['kind' => $kind, 'media_id' => $id], ); header('Content-Type: ' . $mime); header('Content-Length: ' . filesize($path)); header('Cache-Control: private,no-store'); header('X-Content-Type-Options: nosniff'); readfile($path); } }