Files
deskhub/DeskHub-Webseite/staff/imap_accounts.php
T
2026-10-16 19:12:21 +00:00

263 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = require_admin_or_permission('manage_email_addresses');
$pdo = get_db();
$errors = [];
$departments = departments_for_select($pdo, array_map('intval', $pdo->query('SELECT department_id FROM imap_accounts WHERE department_id IS NOT NULL')->fetchAll(PDO::FETCH_COLUMN)));
$priorities = $pdo->query('SELECT * FROM priorities ORDER BY sort_order')->fetchAll();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$action = $_POST['action'] ?? '';
if ($action === 'create' || $action === 'update') {
$label = trim($_POST['label'] ?? '') ?: 'Postfach';
$host = trim($_POST['host'] ?? '');
$port = (int) ($_POST['port'] ?? 993) ?: 993;
$enc = in_array($_POST['encryption'] ?? 'ssl', ['ssl','tls','none'], true) ? $_POST['encryption'] : 'ssl';
$username = trim($_POST['username'] ?? '');
$folder = trim($_POST['folder'] ?? '') ?: 'INBOX';
$afterFetch = in_array($_POST['after_fetch'] ?? 'seen', ['seen','archive','delete'], true) ? $_POST['after_fetch'] : 'seen';
$archiveFolder = trim($_POST['archive_folder'] ?? '') ?: 'Processed';
$spamFolder = trim($_POST['spam_folder'] ?? '') ?: 'Junk';
$deptId = $_POST['department_id'] !== '' ? (int) $_POST['department_id'] : null;
$prioId = $_POST['priority_id'] !== '' ? (int) $_POST['priority_id'] : null;
$isActive = !empty($_POST['is_active']) ? 1 : 0;
$plainPass = trim($_POST['password'] ?? '');
if ($host === '') $errors[] = 'Bitte einen Server (Host) angeben.';
if ($username === '') $errors[] = 'Bitte einen Benutzernamen angeben.';
if ($action === 'create' && $plainPass === '') $errors[] = 'Bitte ein Passwort angeben.';
if (!$errors) {
if ($action === 'create') {
$pdo->prepare(
'INSERT INTO imap_accounts (label, host, port, encryption, username, password_enc, folder, after_fetch, archive_folder, spam_folder, department_id, priority_id, is_active, created_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
)->execute([
$label, $host, $port, $enc, $username, encrypt_secret($plainPass), $folder,
$afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, db_now(),
]);
audit_log($staff['id'], "IMAP-Postfach angelegt: {$label} ({$host})");
flash('success', 'Postfach angelegt.');
} else {
$id = (int) $_POST['id'];
if ($plainPass !== '') {
$pdo->prepare(
'UPDATE imap_accounts SET label=?, host=?, port=?, encryption=?, username=?, password_enc=?, folder=?, after_fetch=?, archive_folder=?, spam_folder=?, department_id=?, priority_id=?, is_active=? WHERE id=?'
)->execute([$label, $host, $port, $enc, $username, encrypt_secret($plainPass), $folder, $afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, $id]);
} else {
// Passwort leer = unverändert lassen
$pdo->prepare(
'UPDATE imap_accounts SET label=?, host=?, port=?, encryption=?, username=?, folder=?, after_fetch=?, archive_folder=?, spam_folder=?, department_id=?, priority_id=?, is_active=? WHERE id=?'
)->execute([$label, $host, $port, $enc, $username, $folder, $afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, $id]);
}
audit_log($staff['id'], "IMAP-Postfach aktualisiert: {$label} ({$host})");
flash('success', 'Postfach gespeichert.');
}
redirect('/staff/imap_accounts.php');
}
}
if ($action === 'delete') {
$id = (int) $_POST['id'];
$lblStmt = $pdo->prepare('SELECT label FROM imap_accounts WHERE id = ?');
$lblStmt->execute([$id]);
$lbl = $lblStmt->fetchColumn();
$pdo->prepare('DELETE FROM imap_accounts WHERE id = ?')->execute([$id]);
audit_log($staff['id'], "IMAP-Postfach gelöscht: {$lbl}");
flash('success', 'Postfach gelöscht.');
redirect('/staff/imap_accounts.php');
}
if ($action === 'test') {
$id = (int) $_POST['id'];
$stmt = $pdo->prepare('SELECT * FROM imap_accounts WHERE id = ?');
$stmt->execute([$id]);
$acc = $stmt->fetch();
if (!$acc) {
flash('error', 'Postfach nicht gefunden.');
} elseif (!function_exists('imap_open')) {
flash('error', 'PHP-IMAP-Erweiterung (php-imap) ist auf diesem Server nicht installiert.');
} else {
$sslFlag = match ($acc['encryption']) {
'ssl' => '/imap/ssl', 'tls' => '/imap/tls', default => '/imap/notls',
};
$mailbox = '{' . $acc['host'] . ':' . $acc['port'] . $sslFlag . '}' . $acc['folder'];
$mbox = @imap_open($mailbox, $acc['username'], decrypt_secret($acc['password_enc']), 0, 1);
if ($mbox) {
$unseen = imap_search($mbox, 'UNSEEN') ?: [];
imap_close($mbox);
flash('success', "Verbindung zu „{$acc['label']}" . "\" erfolgreich. " . count($unseen) . ' ungelesene Mail(s) im Postfach.');
} else {
flash('error', "Verbindung zu „{$acc['label']}\" fehlgeschlagen: " . imap_last_error());
}
}
redirect('/staff/imap_accounts.php');
}
}
$accounts = $pdo->query(
'SELECT ia.*, d.name AS department_name, p.name AS priority_name
FROM imap_accounts ia
LEFT JOIN departments d ON d.id = ia.department_id
LEFT JOIN priorities p ON p.id = ia.priority_id
WHERE ia.email_address_id IS NULL
ORDER BY ia.id'
)->fetchAll();
$editId = $_GET['edit'] ?? null;
$editAcc = null;
if ($editId) {
$stmt = $pdo->prepare('SELECT * FROM imap_accounts WHERE id = ?');
$stmt->execute([$editId]);
$editAcc = $stmt->fetch();
}
$pageTitle = 'Postfächer (E-Mail-Abruf)';
$activePage = 'imap_accounts';
require __DIR__ . '/_header.php';
?>
<div class="flash info" style="margin-bottom:16px">
Postfächer werden jetzt direkt bei der jeweiligen <a href="/staff/email_addresses.php"><strong>E-Mail-Adresse</strong></a> im Reiter „Entferntes Postfach“ eingerichtet.
Diese Seite zeigt nur noch ältere, nicht verknüpfte Postfächer.
</div>
<h1>Postfächer für den E-Mail-Abruf</h1>
<p class="muted">
Jedes Postfach wird beim Abruf (Cronjob <code>fetch_emails.php</code>) einzeln per IMAP abgefragt.
Nutzt ihr getrennte Mail-Konten pro Abteilung (statt eines Sammelpostfachs mit Weiterleitungen),
legt hier einfach für jede Abteilung ein eigenes Postfach an und wählt die passende Standardabteilung –
die <a href="/staff/email_addresses.php">Abteilungs-Routing-Tabelle</a> per Empfängeradresse ist dann optional
und greift nur noch zusätzlich, falls ein einzelnes Postfach doch Mails für mehrere Abteilungen empfängt.
</p>
<?php foreach ($errors as $err): ?>
<div class="flash error"><?= e($err) ?></div>
<?php endforeach; ?>
<div style="display:flex; gap:24px; align-items:flex-start">
<div style="flex:2">
<div class="card">
<table>
<thead><tr><th>Postfach</th><th>Server</th><th>Standardabteilung</th><th>Aktiv</th><th>Letzter Abruf</th><th></th></tr></thead>
<tbody>
<?php foreach ($accounts as $a): ?>
<tr>
<td><strong><?= e($a['label']) ?></strong><br><span class="muted" style="font-size:.78rem"><?= e($a['username']) ?></span></td>
<td class="muted" style="font-size:.82rem"><?= e($a['host']) ?>:<?= e($a['port']) ?></td>
<td><?= e($a['department_name'] ?? 'Automatisch (Routing/Standard)') ?></td>
<td><?= $a['is_active'] ? '✓' : '–' ?></td>
<td class="muted" style="font-size:.82rem"><?= e(cron_last_run_label($a['last_run_at'] ?? null)) ?></td>
<td style="white-space:nowrap">
<form method="post" style="display:inline">
<?= csrf_field() ?>
<input type="hidden" name="action" value="test">
<input type="hidden" name="id" value="<?= $a['id'] ?>">
<button type="submit" style="background:none;border:none;color:var(--primary);cursor:pointer;padding:0;font-size:.85rem">Testen</button>
</form> ·
<a href="/staff/imap_accounts.php?edit=<?= $a['id'] ?>">Bearbeiten</a> ·
<form method="post" style="display:inline" onsubmit="return confirm('Postfach wirklich löschen?');">
<?= csrf_field() ?>
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $a['id'] ?>">
<button type="submit" style="background:none;border:none;color:#dc2626;cursor:pointer;padding:0;font-size:.85rem">Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php if (!$accounts): ?>
<tr><td colspan="6" class="muted">Noch kein Postfach angelegt.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div style="flex:1; min-width:300px">
<div class="card">
<h3><?= $editAcc ? 'Postfach bearbeiten' : 'Neues Postfach' ?></h3>
<form method="post">
<?= csrf_field() ?>
<input type="hidden" name="action" value="<?= $editAcc ? 'update' : 'create' ?>">
<?php if ($editAcc): ?><input type="hidden" name="id" value="<?= $editAcc['id'] ?>"><?php endif; ?>
<label for="label">Bezeichnung</label>
<input type="text" id="label" name="label" value="<?= e($editAcc['label'] ?? '') ?>" placeholder="z.B. Technik-Postfach" required>
<div style="display:grid;grid-template-columns:1fr auto;gap:8px;align-items:flex-end;margin-top:10px">
<div>
<label for="host">Server</label>
<input type="text" id="host" name="host" value="<?= e($editAcc['host'] ?? '') ?>" placeholder="imap.beispiel.de" required>
</div>
<div style="width:80px">
<label for="port">Port</label>
<input type="number" id="port" name="port" value="<?= e($editAcc['port'] ?? '993') ?>">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">
<div>
<label for="username">Benutzername / E-Mail</label>
<input type="text" id="username" name="username" value="<?= e($editAcc['username'] ?? '') ?>" autocomplete="off" required>
</div>
<div>
<label for="password">Passwort</label>
<input type="password" id="password" name="password" placeholder="<?= $editAcc ? '(unverändert)' : '' ?>" autocomplete="new-password">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">
<div>
<label for="folder">Postfach / Ordner</label>
<input type="text" id="folder" name="folder" value="<?= e($editAcc['folder'] ?? 'INBOX') ?>">
</div>
<div>
<label for="encryption">Verschlüsselung</label>
<select id="encryption" name="encryption">
<option value="ssl" <?= ($editAcc['encryption'] ?? 'ssl')==='ssl' ?'selected':'' ?>>SSL/TLS (993)</option>
<option value="tls" <?= ($editAcc['encryption'] ?? '')==='tls' ?'selected':'' ?>>STARTTLS (143)</option>
<option value="none" <?= ($editAcc['encryption'] ?? '')==='none'?'selected':'' ?>>Keine</option>
</select>
</div>
</div>
<label for="department_id" style="margin-top:10px">Standardabteilung</label>
<select id="department_id" name="department_id">
<option value="">Automatisch (Routing-Tabelle / globale Standardabteilung)</option>
<?php foreach ($departments as $d): ?>
<option value="<?= $d['id'] ?>" <?= ($editAcc['department_id'] ?? '') == $d['id'] ? 'selected' : '' ?>><?= e($d['name']) ?></option>
<?php endforeach; ?>
</select>
<label for="priority_id">Standardpriorität (optional)</label>
<select id="priority_id" name="priority_id">
<option value="">Automatisch</option>
<?php foreach ($priorities as $p): ?>
<option value="<?= $p['id'] ?>" <?= ($editAcc['priority_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name']) ?></option>
<?php endforeach; ?>
</select>
<label for="after_fetch" style="margin-top:10px">Aktion nach Abruf</label>
<select id="after_fetch" name="after_fetch" onchange="document.getElementById('archive-row').style.display=this.value==='archive'?'block':'none'">
<option value="seen" <?= ($editAcc['after_fetch'] ?? 'seen')==='seen' ?'selected':'' ?>>Als gelesen markieren</option>
<option value="archive" <?= ($editAcc['after_fetch'] ?? '')==='archive'?'selected':'' ?>>In Ordner verschieben</option>
<option value="delete" <?= ($editAcc['after_fetch'] ?? '')==='delete' ?'selected':'' ?>>Löschen</option>
</select>
<div id="archive-row" style="display:<?= ($editAcc['after_fetch'] ?? '')==='archive'?'block':'none' ?>;margin-top:10px">
<label for="archive_folder">Zielordner</label>
<input type="text" id="archive_folder" name="archive_folder" value="<?= e($editAcc['archive_folder'] ?? 'Processed') ?>">
</div>
<div style="margin-top:10px">
<label for="spam_folder">Spam-/Junk-Ordner</label>
<input type="text" id="spam_folder" name="spam_folder" value="<?= e($editAcc['spam_folder'] ?? 'Junk') ?>" placeholder="z.B. Junk, Spam oder INBOX.Junk">
<div class="muted" style="font-size:.75rem;margin-top:3px">Wird verwendet, wenn ein eingegangenes E-Mail-Ticket als Spam markiert wird.</div>
</div>
<label style="display:flex; align-items:center; gap:8px; margin-top:14px">
<input type="checkbox" name="is_active" style="width:auto" <?= ($editAcc['is_active'] ?? 1) ? 'checked' : '' ?>>
Aktiv (wird beim Abruf berücksichtigt)
</label>
<div class="btn-row">
<button type="submit" class="btn"><?= $editAcc ? 'Speichern' : 'Erstellen' ?></button>
<?php if ($editAcc): ?><a href="/staff/imap_accounts.php" class="btn secondary">Abbrechen</a><?php endif; ?>
</div>
</form>
</div>
</div>
</div>
<?php require __DIR__ . '/_footer.php'; ?>