Files
deskhub/DeskHub-Webseite/includes/imap_spam.php
T
2026-10-26 21:19:07 +00:00

164 lines
6.7 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
/**
* includes/imap_spam.php
*
* Markiert per E-Mail eingegangene Tickets als Spam (und macht das rückgängig) –
* verschiebt dabei die Original-E-Mail im zugehörigen IMAP-Postfach in den
* konfigurierten Spam-/Junk-Ordner bzw. bei "Rückgängig" wieder zurück.
*
* set_ticket_spam() ist der gemeinsame Einstiegspunkt für Web (staff/ticket_view.php)
* und die mobile App (api/staff/v1/ticket_spam.php) – beide rufen dieselbe Funktion auf,
* damit sich das Verhalten nie auseinanderentwickelt.
*/
function imap_connect_account(array $account, string $folder): mixed
{
if (!function_exists('imap_open')) return false;
$sslFlag = match ($account['encryption']) {
'ssl' => '/imap/ssl',
'tls' => '/imap/tls',
default => '/imap/notls',
};
$mailbox = '{' . $account['host'] . ':' . $account['port'] . $sslFlag . '}' . $folder;
return @imap_open($mailbox, $account['username'], decrypt_secret($account['password_enc']), 0, 1);
}
function imap_normalize_message_id(?string $value): string
{
return trim(strtolower((string) $value), " \t\r\n<>");
}
/**
* Verschiebt EINE bereits als Ticket importierte Mail in einen Ziel-Ordner
* (Spam beim Markieren, ursprünglicher Posteingang beim Rückgängigmachen).
* Aktualisiert bei Erfolg current_folder in ticket_email_sources, damit
* nachfolgende Aufrufe (erneutes Markieren, Rückgängig machen) wissen, wo die
* Mail gerade tatsächlich liegt.
*/
function move_ticket_email_source(PDO $pdo, array $source, array $account, string $targetFolder): array
{
if (!function_exists('imap_open')) {
return ['ok' => false, 'message' => 'PHP-IMAP ist nicht installiert.'];
}
$sourceFolder = trim((string) ($source['current_folder'] ?? ''));
$targetFolder = trim($targetFolder);
if ($sourceFolder === '' || $targetFolder === '') {
return ['ok' => false, 'message' => 'IMAP-Quell- oder Zielordner fehlt.'];
}
if (strcasecmp($sourceFolder, $targetFolder) === 0) {
return ['ok' => true, 'message' => 'Die E-Mail befindet sich bereits in „' . $targetFolder . '“.'];
}
$mbox = imap_connect_account($account, $sourceFolder);
if (!$mbox) {
return ['ok' => false, 'message' => 'IMAP-Quellordner konnte nicht geöffnet werden: ' . (imap_last_error() ?: 'unbekannter Fehler')];
}
$emailNum = null;
$uid = (int) ($source['message_uid'] ?? 0);
if ($uid > 0) {
$candidate = @imap_msgno($mbox, $uid);
if ($candidate > 0) $emailNum = $candidate;
}
// Nach einem vorherigen Verschieben kann sich die UID geändert haben. Dann über Message-ID suchen.
if (!$emailNum && !empty($source['message_id'])) {
$targetId = imap_normalize_message_id($source['message_id']);
$hits = @imap_search($mbox, 'ALL');
if ($hits) {
foreach ($hits as $num) {
$h = @imap_headerinfo($mbox, $num);
$mid = imap_normalize_message_id($h->message_id ?? '');
if ($mid !== '' && $mid === $targetId) {
$emailNum = (int) $num;
break;
}
}
}
}
if (!$emailNum) {
imap_close($mbox);
return ['ok' => false, 'message' => 'Original-E-Mail wurde im IMAP-Ordner nicht gefunden.'];
}
if (!@imap_mail_move($mbox, (string) $emailNum, $targetFolder)) {
$err = imap_last_error() ?: 'unbekannter IMAP-Fehler';
imap_close($mbox);
return ['ok' => false, 'message' => 'Verschieben nach „' . $targetFolder . '“ fehlgeschlagen: ' . $err];
}
@imap_expunge($mbox);
imap_close($mbox);
if (!empty($source['id'])) {
$pdo->prepare('UPDATE ticket_email_sources SET current_folder = ? WHERE id = ?')
->execute([$targetFolder, (int) $source['id']]);
}
return ['ok' => true, 'message' => 'E-Mail nach „' . $targetFolder . '“ verschoben.'];
}
/**
* Markiert ein E-Mail-Ticket als Spam, oder macht das (mit $restore = true)
* wieder rückgängig. Gemeinsam genutzt von Web und App.
*
* @return array{error: ?string, moved: int, already: int, failed: string[]}
*/
function set_ticket_spam(PDO $pdo, array $ticket, int $staffId, bool $restore = false): array
{
if (($ticket['source'] ?? '') !== 'email') {
return ['error' => 'Nur per E-Mail eingegangene Tickets können als Spam markiert werden.', 'moved' => 0, 'already' => 0, 'failed' => []];
}
if ($restore && empty($ticket['is_spam'])) {
return ['error' => 'Dieses Ticket ist nicht als Spam markiert.', 'moved' => 0, 'already' => 0, 'failed' => []];
}
if (!$restore && !empty($ticket['is_spam'])) {
return ['error' => 'Dieses Ticket ist bereits als Spam markiert.', 'moved' => 0, 'already' => 0, 'failed' => []];
}
$srcStmt = $pdo->prepare(
'SELECT tes.*, ia.label, ia.spam_folder, ia.folder AS inbox_folder, ia.host, ia.port, ia.encryption, ia.username, ia.password_enc
FROM ticket_email_sources tes
JOIN imap_accounts ia ON ia.id = tes.imap_account_id
WHERE tes.ticket_id = ? ORDER BY tes.id'
);
$srcStmt->execute([(int) $ticket['id']]);
$sources = $srcStmt->fetchAll();
$moved = 0;
$already = 0;
$failed = [];
foreach ($sources as $source) {
$targetFolder = $restore
? ($source['inbox_folder'] ?: 'INBOX')
: ($source['spam_folder'] ?: 'Junk');
$result = move_ticket_email_source($pdo, $source, $source, $targetFolder);
if ($result['ok']) {
if (str_contains($result['message'], 'befindet sich bereits')) {
$already++;
} else {
$moved++;
}
} else {
$failed[] = ($source['label'] ?: 'Postfach') . ': ' . $result['message'];
}
}
$now = db_now();
if ($restore) {
$pdo->prepare('UPDATE tickets SET is_spam = 0, updated_at = ? WHERE id = ?')->execute([$now, $ticket['id']]);
log_activity($pdo, $ticket['id'], $staffId, 'Spam-Markierung aufgehoben');
} else {
$pdo->prepare(
"UPDATE tickets SET is_spam=1, status='closed', resolution_code='spam', resolution_note='Als Spam markiert', closed_at=COALESCE(closed_at, ?), updated_at=?, locked_by=NULL, locked_at=NULL WHERE id=?"
)->execute([$now, $now, $ticket['id']]);
ticket_sla_status_transition($pdo, (int) $ticket['id'], (string) $ticket['status'], 'closed', $staffId);
finalize_work_session($pdo, (int) $ticket['id'], $staffId);
apply_tag_to_ticket($pdo, (int) $ticket['id'], 'Spam');
log_activity($pdo, $ticket['id'], $staffId, 'Ticket als Spam markiert');
}
return ['error' => null, 'moved' => $moved, 'already' => $already, 'failed' => $failed];
}