Code index php
Depuis votre terminal, connectez-vous a votre Raspberry PI et rentrez la commande suivante pour créer le fichier index.php.
sudo nano /var/www/html/index.phpCopier le code suivant et coller le dans le fichier qui vient de s'ouvrir..
<?php
// Force le fuseau horaire sur Europe/Paris
date_default_timezone_set('Europe/Paris');
session_start();
// --- CONFIGURATION CRITIQUE ---
define('PIN_HASH_STORAGE', 'Remplacer ce texte par le hash de votre code pin');
define('MAX_ATTEMPTS', 3);
define('BLACKLIST_FILE', '/var/www/html/data/ip_blacklist.json');
define('LOG_FILE', '/var/log/camera_status.log');
// ------------------------------
// >> SYSTÈME ANTI-BRUTE FORCE PHP <<
$client_ip = $_SERVER['REMOTE_ADDR'];
function get_blacklist() {
if (file_exists(BLACKLIST_FILE)) {
$data = file_get_contents(BLACKLIST_FILE);
return json_decode($data, true) ?: [];
}
return [];
}
function is_ip_blacklisted($ip) {
$blacklist = get_blacklist();
return isset($blacklist[$ip]);
}
if (!isset($_SESSION['pin_attempts'])) {
$_SESSION['pin_attempts'] = 0;
}
$is_ip_blocked = is_ip_blacklisted($client_ip);
$attempts_exceeded = ($_SESSION['pin_attempts'] >= MAX_ATTEMPTS);
if ($is_ip_blocked || $attempts_exceeded) {
if (!$is_ip_blocked && $attempts_exceeded) {
$blacklist = get_blacklist();
$timestamp = date('Y-m-d H:i:s');
$blacklist[$client_ip] = ['blocked_time' => $timestamp, 'reason' => 'Tentatives de PIN dépassées'];
file_put_contents(BLACKLIST_FILE, json_encode($blacklist, JSON_PRETTY_PRINT));
session_destroy();
}
header('HTTP/1.0 403 Forbidden');
echo '<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8"><title>Accès Refusé</title><style>body { font-family: sans-serif; text-align: center; padding-top: 50px; background-color: #fdd; color: #800; }</style></head><body><h1>❌ ACCÈS TEMPORAIREMENT BLOQUÉ</h1><p>Votre adresse IP a été bloquée pour raisons de sécurité suite à trop de tentatives de connexion échouées.</p><p>Veuillez contacter l\'administrateur pour débloquer votre accès.</p></body></html>';
exit;
}
// --- LOGIQUE DE VÉRIFICATION DU STATUT DE LA CAMÉRA ---
function get_motioneye_status() {
$max_attempts = 3;
$status = 'unknown';
for ($i = 0; $i < $max_attempts; $i++) {
$output = shell_exec('STATUS=$(/bin/systemctl is-active motioneye 2>/dev/null); echo "$STATUS"');
$status = trim($output);
$status = strtolower($status);
if ($status === 'active' || $status === 'inactive') {
break;
}
sleep(1);
}
if ($status === 'active') {
return ['text' => 'ACTIVE', 'class' => 'status-active', 'icon' => '🟢'];
} elseif ($status === 'inactive') {
return ['text' => 'INACTIVE', 'class' => 'status-inactive', 'icon' => '🔴'];
} elseif ($status === 'activating' || $status === 'deactivating') {
return ['text' => 'EN TRANSITION', 'class' => 'status-error', 'icon' => '🟡'];
} else {
return ['text' => 'ERREUR', 'class' => 'status-error', 'icon' => '⚠️'];
}
}
$status_data = get_motioneye_status();
$page_title = "Alarme Maison";
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $page_title; ?> - Statut</title>
<style>
/* Styles CSS (omission pour la concision) */
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: #f0f0f5; color: #333; margin: 0; padding: 20px; text-align: center; }
h1 { color: #1a1a1a; font-size: 24px; margin-bottom: 30px; }
.status-box, .control-box { background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); padding: 20px; margin: 20px auto; max-width: 500px; }
.status-indicator { font-size: 40px; margin-bottom: 10px; }
.status-text { font-size: 32px; font-weight: bold; text-transform: uppercase; padding: 10px 0; }
.status-active { color: #007aff; }
.status-inactive { color: #ff3b30; }
.status-error { color: #ffcc00; }
.control-box a { display: block; font-size: 18px; font-weight: 600; padding: 15px 0; margin-bottom: 10px; border-radius: 8px; text-decoration: none; cursor: pointer; transition: background-color 0.2s; }
.btn-marche { background-color: #34c759; color: white; }
.btn-arret { background-color: #ff3b30; color: white; }
.pin-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.8); display: none; justify-content: center; align-items: center; z-index: 1000; }
.pin-container { background: white; padding: 30px; border-radius: 15px; text-align: center; width: 80%; max-width: 300px; }
.pin-input { width: 100%; padding: 10px; font-size: 20px; margin: 10px 0 20px; text-align: center; letter-spacing: 5px; }
.log-box { background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); padding: 20px; margin: 20px auto; max-width: 800px; text-align: left;}
.log-box h2 { margin-top: 0; font-size: 20px; border-bottom: 1px solid #eee; padding-bottom: 10px; color: #1a1a1a; text-align: center;}
table { width: 100%; border-collapse: collapse; margin-top: 15px; font-size: 0.85em; table-layout: fixed;}
table th, table td { padding: 10px 8px; text-align: left; border-bottom: 1px solid #ddd; word-wrap: break-word;}
table th { background-color: #f2f2f2; font-weight: bold; color: #333;}
.action-start { color: green; font-weight: bold;}
.action-stop { color: red; font-weight: bold;}
.action-info { color: #007aff; font-weight: 500; }
.action-manuel { font-style: italic;}
</style>
<meta http-equiv="refresh" content="15">
</head>
<body>
<h1><?php echo $page_title; ?></h1>
<div class="status-box">
<div class="status-indicator">
<?php echo $status_data['icon']; ?>
</div>
<div class="status-text <?php echo $status_data['class']; ?>">
<?php echo $status_data['text']; ?>
</div>
</div>
<div class="control-box">
<h2>Contrôle Manuel</h2>
<a href="#" class="btn-marche" data-action="marche" onclick="showPin('marche');">
Caméra MARCHE
</a>
<a href="#" class="btn-arret" data-action="arret" onclick="showPin('arret');">
Caméra ARRÊT
</a>
</div>
<div class="last-update">
Dernière vérification : <?php echo date('H:i:s'); ?>
</div>
<div id="pinOverlay" class="pin-overlay">
<div class="pin-container">
<h3>Entrez votre code PIN</h3>
<input type="password" id="pinInput" class="pin-input" maxlength="4" inputmode="numeric" oninput="checkPin();">
<p style="font-size: 14px; color: #555;">Tentatives restantes: <span id="attempts_left"><?php echo MAX_ATTEMPTS - $_SESSION['pin_attempts']; ?></span></p>
<p id="pinMessage" style="color: red;"></p>
</div>
</div>
<div class="log-box">
<h2>Historique des 10 dernières actions</h2>
<table>
<thead>
<tr>
<th style="width: 20%;">Date & Heure</th>
<th style="width: 20%;">Source</th>
<th style="width: 35%;">Action / Mouvement</th>
<th style="width: 25%;">Détails</th>
</tr>
</thead>
<tbody>
<?php
$log_file = LOG_FILE;
$log_lines = [];
$permission_error = false;
if (file_exists($log_file)) {
$logs_raw = shell_exec("tail -n 10 " . escapeshellarg($log_file) . " 2>&1");
if (strpos($logs_raw, 'Permission denied') !== false) {
$permission_error = true;
} elseif (!empty($logs_raw)) {
$log_lines = array_reverse(array_filter(explode("\n", trim($logs_raw))));
}
}
if ($permission_error) {
echo '<tr><td colspan="4" style="color:red; text-align:center;">Erreur: Accès refusé aux logs. Vérifiez les permissions de ' . htmlspecialchars($log_file) . '.</td></tr>';
} elseif (!empty($log_lines)) {
foreach ($log_lines as $line) {
// Regex Ultra-Tolérante : retire la parenthèse fermante optionnelle, capture tout après la première ouvrante s'il y en a une.
if (preg_match('/^\[(.*?)\] \[(.*?)\] (.*?)(?: \((.*))?$/', $line, $matches)) {
$timestamp = trim($matches[1]);
$source = trim($matches[2]);
$message = trim($matches[3]);
// Utilise $matches[4] ou une chaîne vide si non capturé
$details = isset($matches[4]) ? trim($matches[4]) : '';
} else {
echo '<tr><td colspan="4" class="action-info">' . htmlspecialchars($line) . '</td></tr>';
continue;
}
// --- Détermination de la présentation ---
$action_text = '';
$action_class = 'action-info';
$detail_display = 'N/A';
$appareil_nom = '';
$mouvement = '';
// 1. Détermination de l'Action
if (strpos($message, 'DÉMARRÉE') !== false) {
$action_text = "Activation Caméra (ON)";
$action_class = 'action-start';
} elseif (strpos($message, 'ARRÊTÉE') !== false) {
$action_text = "Désactivation Caméra (OFF)";
$action_class = 'action-stop';
} else {
$action_text = htmlspecialchars($message);
}
// 3. Extraction des Détails (Nom de l'appareil, Mouvement, etc.)
if (!empty($details)) {
// 3a. Recherche du mouvement (et mise à jour de l'Action)
if (preg_match('/(Premier Entré|Dernier Sorti)/i', $details, $mouv_matches)) {
$mouvement = htmlspecialchars($mouv_matches[1]);
$mouvement_display = ($mouvement === 'Premier Entré') ? 'Arrivée/Entrée' : 'Départ/Sortie';
$action_text = $mouvement_display . " - " . $action_text;
}
// 3b. Recherche du nom de l'appareil
if (preg_match('/(iphone_[a-z0-9_-]+)/i', $details, $device_matches)) {
$appareil_nom = htmlspecialchars($device_matches[1]);
$detail_display = "Appareil: " . $appareil_nom;
} elseif ($source === 'MANUAL' && strpos($message, 'manuellement par l\'interface web') !== false) {
$detail_display = 'Interface Web';
} else {
// Afficher les détails bruts si non reconnu pour le débogage
$detail_display = htmlspecialchars($details);
}
}
// 2. Détermination de la Source
$source_display = $source;
if ($source === 'MANUAL') {
$source_display = "Manuel (Web)";
$action_class .= ' action-manuel';
} elseif ($source === 'RACC. iOS') {
$source_display = "Raccourci iOS";
$action_class .= ' action-manuel';
} elseif ($source === 'AUTO') {
$source_display = "Automatique";
}
// --- Affichage ---
echo '<tr>';
echo '<td>' . htmlspecialchars($timestamp) . '</td>';
echo '<td>' . $source_display . '</td>';
echo '<td class="' . $action_class . '">' . $action_text . '</td>';
echo '<td>' . $detail_display . '</td>';
echo '</tr>';
}
} else {
echo '<tr><td colspan="4" style="text-align:center;">Aucun historique récent trouvé.</td></tr>';
}
?>
</tbody>
</table>
</div>
<script>
// ... (Le code JavaScript est inchangé et complet)
const CORRECT_PIN_HASH = "<?php echo PIN_HASH_STORAGE; ?>";
const SECURE_ACTION_URL = "secure_action.php?action=";
let pendingAction = '';
// La fonction sha256 a été omise ici pour la concision mais elle est incluse dans le code à copier/coller.
function sha256(str) {
// ... (Fonction complète de sha256)
function rotateRight(n, count) {
return (n >>> count) | (n << (32 - count));
}
function safeAdd(x, y) {
const lsw = (x & 0xFFFF) + (y & 0xFFFF);
const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
const K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
let H = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
];
const utf8 = unescape(encodeURIComponent(str));
const w = [];
let i;
for (i = 0; i < utf8.length * 8; i += 8) {
w[i >> 5] |= (utf8.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
}
w[i >> 5] |= 0x80 << (24 - i % 32);
w[((i + 64) >>> 9 << 4) + 15] = i;
for (let j = 0; j < w.length; j += 16) {
const W = new Array(64);
let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
for (let t = 0; t < 64; t++) {
if (t < 16) {
W[t] = w[j + t];
} else {
const s0 = rotateRight(W[t - 15], 7) ^ rotateRight(W[t - 15], 18) ^ (W[t - 15] >>> 3);
const s1 = rotateRight(W[t - 2], 17) ^ rotateRight(W[t - 2], 19) ^ (W[t - 2] >>> 10);
W[t] = safeAdd(safeAdd(safeAdd(W[t - 16], s0), W[t - 7]), s1);
}
const S1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
const ch = (e & f) ^ ((~e) & g);
const temp1 = safeAdd(safeAdd(safeAdd(safeAdd(h, S1), ch), K[t]), W[t]);
const S0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
const maj = (a & b) ^ (a & c) ^ (b & c);
const temp2 = safeAdd(S0, maj);
h = g;
g = f;
f = e;
e = safeAdd(d, temp1);
d = c;
c = b;
b = a;
a = safeAdd(temp1, temp2);
}
H[0] = safeAdd(a, H[0]);
H[1] = safeAdd(b, H[1]);
H[2] = safeAdd(c, H[2]);
H[3] = safeAdd(d, H[3]);
H[4] = safeAdd(e, H[4]);
H[5] = safeAdd(f, H[5]);
H[6] = safeAdd(g, H[6]);
H[7] = safeAdd(h, H[7]);
}
let result = '';
for (let t = 0; t < H.length; t++) {
result += H[t].toString(16).padStart(8, '0');
}
return result.replace(/[^0-9a-f]/gi, '').toLowerCase();
}
function showPin(action) {
pendingAction = action;
document.getElementById('pinInput').value = '';
document.getElementById('pinMessage').textContent = '';
document.getElementById('pinOverlay').style.display = 'flex';
document.getElementById('pinInput').focus();
}
function checkPin() {
const pinInput = document.getElementById('pinInput');
const pin = pinInput.value;
const messageElement = document.getElementById('pinMessage');
if (pin.length === 4) {
messageElement.textContent = "Vérification...";
// Hachage synchrone
const hashedInput = sha256(pin);
if (hashedInput === CORRECT_PIN_HASH) {
messageElement.textContent = "Connexion OK. Établissement de la session...";
fetch('pin_login.php')
.then(response => {
if (!response.ok) throw new Error("Erreur de connexion (pin_login.php).");
return response.json();
})
.then(data => {
if (data.status === 'success') {
messageElement.textContent = "Session OK. Exécution...";
executeWebhook(pendingAction);
} else {
messageElement.textContent = "Erreur interne de session.";
}
})
.catch(error => {
messageElement.textContent = "Erreur de communication : " + error.message;
console.error(error);
});
} else {
fetch('pin_failed.php')
.then(response => {
if (!response.ok) throw new Error("Erreur serveur lors de la tentative échouée.");
return response.json();
})
.then(data => {
if (data.status === 'blocked') {
window.location.reload();
} else {
messageElement.textContent = "Code PIN incorrect.";
pinInput.value = '';
document.getElementById('attempts_left').textContent = data.attempts_left;
}
})
.catch(error => {
messageElement.textContent = "Erreur de communication avec le serveur (pin_failed.php).";
console.error(error);
});
}
}
}
function executeWebhook(action) {
const finalAction = (action === 'marche' ? 'start' : 'stop');
const url = "secure_action.php";
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=' + finalAction
})
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}. Le serveur relais a échoué.`);
}
return response.json();
})
.then(data => {
if (data.status === 'error') {
alert(`ERREUR LORS DE L'EXÉCUTION (${data.message}). Clé ou URL invalide.`);
} else {
alert(`Action '${action}' lancée. Statut : ${data.status}. ${data.message}`);
}
document.getElementById('pinOverlay').style.display = 'none';
window.location.reload();
})
.catch(error => {
alert('Erreur critique de communication : ' + error);
document.getElementById('pinOverlay').style.display = 'none';
});
}
document.getElementById('pinOverlay').addEventListener('click', function(e) {
if (e.target === this) {
this.style.display = 'none';
}
});
</script>
</body>
</html>
- Vous devez remplacer la valeur de la variable " PIN_HASH_STORAGE " par le Hash de votre code pin. Pour connaitre la valeur du hash utiliser cette commande en remplacant "1234" par votre code pin.
echo -n "1234" | sha256sum- Le résultat est "03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"
