Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
// /messages/api/download_attachment.php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../_bootstrap.php';
|
||||
|
||||
// Important: aucune sortie HTML avant les headers !
|
||||
/**
|
||||
* Règles d’accès:
|
||||
* - On reçoit ?message_id=ID (GET).
|
||||
* - Vérifie que l’utilisateur courant participe à la conversation du message.
|
||||
* - Vérifie que le message n’est pas rétracté et pas "supprimé pour moi".
|
||||
* - Sert le fichier avec le bon Content-Type et Content-Disposition.
|
||||
*/
|
||||
|
||||
$messageId = (int)($_GET['message_id'] ?? 0);
|
||||
if ($messageId <= 0) {
|
||||
http_response_code(400);
|
||||
echo "Bad params";
|
||||
exit;
|
||||
}
|
||||
|
||||
$role = $GLOBALS['MESSAGES_CTX']['role']; // 'praticien'|'client' (en CLI, inutilisé)
|
||||
$meId = $GLOBALS['MESSAGES_CTX']['me_id'];
|
||||
|
||||
$hideCol = ($role === 'praticien') ? 'deleted_for_praticien' : 'deleted_for_client';
|
||||
|
||||
// Récupère message + conversation
|
||||
$sql = "SELECT m.id, m.conversation_id, m.attachment_path, m.attachment_name,
|
||||
m.retracted_for_all, m.$hideCol AS hidden_for_me,
|
||||
c.praticien_id, c.client_id
|
||||
FROM messages m
|
||||
JOIN conversations c ON c.id = m.conversation_id
|
||||
WHERE m.id = ?
|
||||
LIMIT 1";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$messageId]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
http_response_code(404);
|
||||
echo "Message not found";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Accès: dois être participant de la conversation
|
||||
assert_conversation_access($pdo, (int)$row['conversation_id'], $role, (int)$meId);
|
||||
|
||||
// Visibilité: pas rétracté, pas supprimé pour moi, et PJ existante
|
||||
if (!empty($row['retracted_for_all']) || !empty($row['hidden_for_me'])) {
|
||||
http_response_code(403);
|
||||
echo "Forbidden";
|
||||
exit;
|
||||
}
|
||||
$webRel = (string)($row['attachment_path'] ?? '');
|
||||
if ($webRel === '') {
|
||||
http_response_code(404);
|
||||
echo "No attachment";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sécurité chemin: doit commencer par messages/uploads/
|
||||
if (strpos($webRel, 'messages/uploads/') !== 0) {
|
||||
http_response_code(400);
|
||||
echo "Invalid path";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Résolution chemin disque
|
||||
$siteRoot = realpath(__DIR__ . '/../../'); // racine site
|
||||
$uploadsAbs = $siteRoot . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $webRel);
|
||||
$real = realpath($uploadsAbs);
|
||||
$uploadsRoot = realpath(__DIR__ . '/../uploads/');
|
||||
|
||||
if (!$real || !$uploadsRoot || strpos($real, $uploadsRoot) !== 0 || !is_file($real)) {
|
||||
http_response_code(404);
|
||||
echo "File not found";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Détection MIME + nom fichier
|
||||
$finfo = function_exists('finfo_open') ? finfo_open(FILEINFO_MIME_TYPE) : false;
|
||||
$mime = ($finfo && is_file($real)) ? finfo_file($finfo, $real) : null;
|
||||
if ($finfo) { @finfo_close($finfo); }
|
||||
if (!$mime) $mime = 'application/octet-stream';
|
||||
|
||||
$name = (string)($row['attachment_name'] ?? basename($real));
|
||||
$size = filesize($real);
|
||||
|
||||
// En-têtes (évite le sniffing, force téléchargement)
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Content-Length: ' . (string)$size);
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('Content-Security-Policy: default-src \'none\'; frame-ancestors \'none\'; sandbox');
|
||||
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($name));
|
||||
|
||||
// Lecture par chunks (robuste)
|
||||
$fp = @fopen($real, 'rb');
|
||||
if (!$fp) {
|
||||
http_response_code(500);
|
||||
echo "Cannot open file";
|
||||
exit;
|
||||
}
|
||||
|
||||
while (!feof($fp)) {
|
||||
$buf = fread($fp, 8192);
|
||||
if ($buf === false) break;
|
||||
echo $buf;
|
||||
// facultatif: flush pour gros fichiers
|
||||
if (function_exists('fastcgi_finish_request')) { /* noop here */ }
|
||||
}
|
||||
fclose($fp);
|
||||
exit;
|
||||
Reference in new issue
Block a user