init: projet complet copymail pour Raspberry Pi
Stack: Nginx + PHP 8.2 + MariaDB + Postfix + Dovecot + Samba - install.sh: script installation automatisé pour RPi OS Bookworm - Interface web: visionneuse mails + admin (comptes, domaine, dashboard) - Chaque compte mail génère automatiquement un share Samba - Processeur cron: extraction pièces jointes via zbateson/mail-mime-parser - Zéro injection SQL (PDO + prepared statements) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
122
web/index.php
Normal file
122
web/index.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/db.php';
|
||||
require_once __DIR__ . '/includes/auth.php';
|
||||
require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
auth_check();
|
||||
|
||||
$message = '';
|
||||
|
||||
// Suppression d'un email traité
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_source'])) {
|
||||
$id = (int)$_POST['delete_source'];
|
||||
$st = db()->prepare('SELECT sf.title, CONCAT(sf.file_name) as fname, GROUP_CONCAT(f.path) as fpaths
|
||||
FROM source_file sf LEFT JOIN files f ON sf.id = f.source_file_id
|
||||
WHERE sf.id = ? GROUP BY sf.id');
|
||||
$st->execute([$id]);
|
||||
$row = $st->fetch();
|
||||
if ($row) {
|
||||
if ($row['fpaths']) {
|
||||
foreach (explode(',', $row['fpaths']) as $path) {
|
||||
if (file_exists($path)) unlink($path);
|
||||
}
|
||||
}
|
||||
db()->prepare('DELETE FROM source_file WHERE id = ?')->execute([$id]);
|
||||
flash('success', "Entrée « " . htmlspecialchars($row['title']) . " » supprimée.");
|
||||
}
|
||||
header('Location: /');
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = db()->query(
|
||||
'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username,
|
||||
GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files
|
||||
FROM source_file sf
|
||||
LEFT JOIN accounts a ON sf.account_id = a.id
|
||||
LEFT JOIN files f ON sf.id = f.source_file_id
|
||||
GROUP BY sf.id
|
||||
ORDER BY sf.date_processing DESC'
|
||||
)->fetchAll();
|
||||
|
||||
html_head('Courriers');
|
||||
html_navbar('mail');
|
||||
?>
|
||||
<div class="container-fluid">
|
||||
<?php flash_render(); ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0"><i class="bi bi-envelope-fill me-2"></i>Courriers traités</h5>
|
||||
<span class="badge bg-secondary"><?= count($rows) ?> entrée(s)</span>
|
||||
</div>
|
||||
|
||||
<?php if (empty($rows)): ?>
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle me-2"></i>Aucun courrier traité pour l'instant.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="card shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Compte</th>
|
||||
<th>Sujet</th>
|
||||
<th>Expéditeur</th>
|
||||
<th>Pièces jointes</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($rows as $r): ?>
|
||||
<tr>
|
||||
<td class="text-nowrap text-muted small">
|
||||
<?= htmlspecialchars(date('d/m/Y H:i', strtotime($r['date_processing']))) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($r['username']): ?>
|
||||
<span class="badge bg-primary"><?= htmlspecialchars($r['username']) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">—</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($r['title'] ?? '(sans sujet)') ?></td>
|
||||
<td class="small text-muted"><?= htmlspecialchars($r['sender'] ?? '') ?></td>
|
||||
<td>
|
||||
<?php if ($r['files']): ?>
|
||||
<?php foreach (explode('|', $r['files']) as $f): ?>
|
||||
<?php [$fid, $fname, $fpath] = array_pad(explode(':', $f, 3), 3, ''); ?>
|
||||
<?php if (file_exists($fpath)): ?>
|
||||
<a href="/mail/download.php?id=<?= (int)$fid ?>" class="badge bg-info text-decoration-none me-1">
|
||||
<i class="bi bi-download me-1"></i><?= htmlspecialchars(substr($fname, 0, 20)) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary me-1" title="Fichier introuvable">
|
||||
<?= htmlspecialchars(substr($fname, 0, 20)) ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<span class="text-muted small">Aucune pièce jointe</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<form method="post" onsubmit="return confirm('Supprimer cette entrée ?')">
|
||||
<input type="hidden" name="delete_source" value="<?= $r['id'] ?>">
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(() => location.reload(), 30000);
|
||||
</script>
|
||||
<?php html_foot(); ?>
|
||||
Reference in New Issue
Block a user