Files
2026-11-04 07:55:19 +00:00

127 lines
5.6 KiB
PHP

<?php
/**
* includes/mail_reload.php
*
* Lädt die Anhänge einer schon abgerufenen E-Mail nachträglich ins Ticket (für Mails, die abgerufen wurden, bevor DeskHub Anhänge
* übernommen hat, oder deren Anhang damals nicht übernommen werden konnte). Die Original-Mail wird im Postfach gesucht: über die
* gespeicherte UID im aktuellen Ordner, sonst über die Message-ID.
*/
require_once __DIR__ . '/imap_spam.php';
require_once __DIR__ . '/mail_text.php';
/**
* Nachricht des Tickets, zu der eine Mail gehört: die Kundennachricht, die zur Zeit des Abrufs angelegt wurde (die nächstliegende).
*
* @param array<int,array{id:int|string,created_at:string}> $threads Kundennachrichten des Tickets
*/
function mail_reload_pick_thread(array $threads, string $sourceCreatedAt): ?int
{
$at = strtotime($sourceCreatedAt) ?: 0;
$best = null;
$bestDiff = PHP_INT_MAX;
foreach ($threads as $t) {
$diff = abs((strtotime((string) $t['created_at']) ?: 0) - $at);
if ($diff < $bestDiff) {
$bestDiff = $diff;
$best = (int) $t['id'];
}
}
return $best;
}
/**
* Findet die Mail im geöffneten Ordner: über die UID (und passende Message-ID, falls beide bekannt), sonst nur über die Message-ID.
*/
function mail_reload_find_message(mixed $mbox, array $source): ?int
{
$wanted = imap_normalize_message_id((string) ($source['message_id'] ?? ''));
$uid = (int) ($source['message_uid'] ?? 0);
if ($uid > 0) {
$num = (int) @imap_msgno($mbox, $uid);
if ($num > 0) {
if ($wanted === '') return $num;
$h = @imap_headerinfo($mbox, $num);
if ($h && imap_normalize_message_id($h->message_id ?? '') === $wanted) return $num;
}
}
if ($wanted !== '') {
$hits = @imap_search($mbox, 'ALL') ?: [];
foreach ($hits as $num) {
$h = @imap_headerinfo($mbox, (int) $num);
if ($h && imap_normalize_message_id($h->message_id ?? '') === $wanted) return (int) $num;
}
}
return null;
}
/**
* Lädt die Anhänge aller Original-Mails eines Tickets nach. Bereits vorhandene Anhänge werden nicht doppelt gespeichert.
*
* @return array{error:?string,added:int,inline:int,html:int,skipped:int,existing:int,mails:int,failed:string[]}
*/
function reload_ticket_mail_attachments(PDO $pdo, array $ticket, ?int $staffId = null): array
{
$res = ['error' => null, 'added' => 0, 'inline' => 0, 'html' => 0, 'skipped' => 0, 'existing' => 0, 'mails' => 0, 'failed' => []];
if (!function_exists('imap_open')) {
$res['error'] = 'PHP-IMAP ist nicht installiert.';
return $res;
}
$ticketId = (int) $ticket['id'];
$srcStmt = $pdo->prepare(
'SELECT tes.*, ia.label, 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([$ticketId]);
$sources = $srcStmt->fetchAll();
if (!$sources) {
$res['error'] = 'Zu diesem Ticket ist keine Original-E-Mail gespeichert (nur per E-Mail abgerufene Tickets).';
return $res;
}
$thStmt = $pdo->prepare("SELECT id, created_at FROM ticket_threads WHERE ticket_id = ? AND author_type = 'customer' AND is_internal_note = 0 ORDER BY id");
$thStmt->execute([$ticketId]);
$threads = $thStmt->fetchAll();
if (!$threads) {
$res['error'] = 'Das Ticket hat keine Kundennachricht, an die Anhänge gehängt werden könnten.';
return $res;
}
foreach ($sources as $source) {
$folder = trim((string) ($source['current_folder'] ?? '')) ?: 'INBOX';
$label = (string) ($source['label'] ?: $source['username']);
$mbox = imap_connect_account($source, $folder);
if (!$mbox) {
$res['failed'][] = "[$label] Postfach nicht erreichbar: " . (imap_last_error() ?: 'unbekannter Fehler');
continue;
}
$num = mail_reload_find_message($mbox, $source);
if ($num === null) {
imap_close($mbox);
$res['failed'][] = "[$label] Original-E-Mail wurde im Ordner „{$folder}“ nicht gefunden (verschoben oder gelöscht?).";
continue;
}
$threadId = mail_reload_pick_thread($threads, (string) $source['created_at']);
$structure = @imap_fetchstructure($mbox, $num) ?: null;
$log = [];
$fetchPart = fn(string $no) => (string) imap_fetchbody($mbox, $num, $no);
// Formatierte Fassung der Nachricht (HTML) nachtragen, wenn es noch keine gibt
$htmlBody = null;
if (ticket_mail_html_ready($pdo) && $structure) {
$htmlBody = mail_html_for_display($structure, $fetchPart);
$has = $pdo->prepare('SELECT message_html FROM ticket_threads WHERE id = ?');
$has->execute([(int) $threadId]);
if ($htmlBody !== null && trim((string) $has->fetchColumn()) === '') {
$pdo->prepare('UPDATE ticket_threads SET message_html = ? WHERE id = ?')->execute([$htmlBody, (int) $threadId]);
$res['html']++;
}
}
$c = mail_store_attachments($pdo, $structure, $fetchPart, (int) $threadId, $ticketId, $label, $log, true, $htmlBody !== null);
imap_close($mbox);
$res['mails']++;
foreach (['added', 'inline', 'skipped', 'existing'] as $k) $res[$k] += $c[$k];
}
if ($res['added'] + $res['inline'] + $res['html'] > 0) log_activity($pdo, $ticketId, $staffId, 'Anhänge und Bilder aus der E-Mail nachgeladen: ' . $res['added'] . ' Anhang/Anhänge, ' . $res['inline'] . ' Bild(er)' . ($res['html'] ? ', Formatierung' : ''));
return $res;
}