errorCode; } public function httpStatus(): int { return $this->httpStatus; } } /** Verknüpftes IMAP-Postfach einer Adresse (oder null). */ function email_address_imap(PDO $pdo, int $addressId): ?array { $stmt = $pdo->prepare('SELECT * FROM imap_accounts WHERE email_address_id = ? ORDER BY id LIMIT 1'); $stmt->execute([$addressId]); return $stmt->fetch() ?: null; } /** Eigener SMTP-Zugang einer Absenderadresse (nur aktive Adresse mit aktiviertem SMTP), sonst null. */ function email_address_smtp_for(PDO $pdo, string $fromHeader): ?array { if (!preg_match('/<([^>]+)>/', $fromHeader, $m)) $m = [1 => $fromHeader]; $email = strtolower(trim($m[1])); if ($email === '') return null; $stmt = $pdo->prepare('SELECT smtp_host, smtp_port, smtp_encryption, smtp_username, smtp_password_enc FROM email_addresses WHERE LOWER(email) = ? AND is_active = 1 AND smtp_enabled = 1'); $stmt->execute([$email]); $row = $stmt->fetch(); return ($row && trim((string) $row['smtp_host']) !== '') ? $row : null; } /** * Legt eine E-Mail-Adresse an oder aktualisiert sie – inklusive IMAP-Postfach und eigenem SMTP. * * @param array $in Schlüssel: email,label,department_id,priority_id,help_topic_id,is_default,is_active,notes, * imap_enabled,imap_host,imap_port,imap_encryption,imap_username,imap_password,imap_folder, * imap_after_fetch,imap_archive_folder,imap_spam_folder, * smtp_enabled,smtp_host,smtp_port,smtp_encryption,smtp_username,smtp_password * @return int ID der Adresse * @throws EmailAddressException bei ungültigen Eingaben */ function email_address_save(PDO $pdo, int $id, array $in): int { $email = strtolower(trim((string) ($in['email'] ?? ''))); $label = trim((string) ($in['label'] ?? '')); $deptId = (int) ($in['department_id'] ?? 0); $prioId = isset($in['priority_id']) && $in['priority_id'] !== '' && $in['priority_id'] !== null ? (int) $in['priority_id'] : null; $topicId = isset($in['help_topic_id']) && $in['help_topic_id'] !== '' && $in['help_topic_id'] !== null ? (int) $in['help_topic_id'] : null; $isDefault = !empty($in['is_default']) ? 1 : 0; if (array_key_exists('is_active', $in)) { $isActive = !empty($in['is_active']) ? 1 : 0; } else { // nicht angegeben: bisherigen Status behalten (neu = aktiv) $cur = $pdo->prepare('SELECT is_active FROM email_addresses WHERE id = ?'); $cur->execute([$id]); $isActive = $id ? (int) ($cur->fetchColumn() ?? 1) : 1; } $notes = trim((string) ($in['notes'] ?? '')); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new EmailAddressException('Bitte eine gültige E-Mail-Adresse angeben.', 'INVALID_EMAIL'); $chk = $pdo->prepare('SELECT COUNT(*) FROM departments WHERE id = ?'); $chk->execute([$deptId]); if (!(int) $chk->fetchColumn()) throw new EmailAddressException('Bitte eine Abteilung wählen.', 'INVALID_DEPARTMENT'); if ($topicId !== null) { $chk = $pdo->prepare('SELECT COUNT(*) FROM help_topics WHERE id = ?'); $chk->execute([$topicId]); if (!(int) $chk->fetchColumn()) throw new EmailAddressException('Das gewählte Hilfethema existiert nicht.', 'INVALID_TOPIC'); } $dup = $pdo->prepare('SELECT id FROM email_addresses WHERE LOWER(email) = ? AND id <> ?'); $dup->execute([$email, $id]); if ($dup->fetchColumn()) throw new EmailAddressException('Diese E-Mail-Adresse ist bereits angelegt.', 'DUPLICATE_EMAIL'); // IMAP $existingImap = $id ? email_address_imap($pdo, $id) : null; $imapOn = !empty($in['imap_enabled']) && $isActive; $imap = [ 'host' => trim((string) ($in['imap_host'] ?? '')), 'port' => (int) ($in['imap_port'] ?? 993) ?: 993, 'enc' => in_array((string) ($in['imap_encryption'] ?? ''), ['ssl', 'tls', 'none'], true) ? $in['imap_encryption'] : 'ssl', 'username' => trim((string) ($in['imap_username'] ?? '')) ?: $email, 'password' => trim((string) ($in['imap_password'] ?? '')), 'folder' => trim((string) ($in['imap_folder'] ?? '')) ?: 'INBOX', 'after' => in_array((string) ($in['imap_after_fetch'] ?? ''), ['seen', 'archive', 'delete'], true) ? $in['imap_after_fetch'] : 'seen', 'archive' => trim((string) ($in['imap_archive_folder'] ?? '')) ?: 'Processed', 'spam' => trim((string) ($in['imap_spam_folder'] ?? '')) ?: 'Junk', ]; if (!empty($in['imap_enabled'])) { if ($imap['host'] === '') throw new EmailAddressException('Für den Abruf bitte einen Server (Host) angeben.', 'IMAP_HOST_REQUIRED'); if (!$existingImap && $imap['password'] === '') throw new EmailAddressException('Für den Abruf bitte ein Passwort angeben.', 'IMAP_PASSWORD_REQUIRED'); } // SMTP $existing = null; if ($id) { $s = $pdo->prepare('SELECT smtp_host, smtp_port, smtp_encryption, smtp_username, smtp_password_enc FROM email_addresses WHERE id = ?'); $s->execute([$id]); $existing = $s->fetch() ?: null; } $smtpOn = !empty($in['smtp_enabled']) ? 1 : 0; $smtp = [ 'host' => trim((string) ($in['smtp_host'] ?? '')), 'port' => (int) ($in['smtp_port'] ?? 587) ?: 587, 'enc' => in_array((string) ($in['smtp_encryption'] ?? ''), ['ssl', 'tls', 'none'], true) ? $in['smtp_encryption'] : 'tls', 'username' => trim((string) ($in['smtp_username'] ?? '')), 'password' => (string) ($in['smtp_password'] ?? ''), ]; if ($smtpOn && $smtp['host'] === '') throw new EmailAddressException('Für den eigenen Versand bitte einen SMTP-Server angeben.', 'SMTP_HOST_REQUIRED'); $smtpPassEnc = $smtp['password'] !== '' ? encrypt_secret($smtp['password']) : ($existing['smtp_password_enc'] ?? null); if (!$smtpOn && $existing) { // Eigener Versand ausgeschaltet: hinterlegte Zugangsdaten bleiben für später erhalten $smtp = ['host' => (string) $existing['smtp_host'], 'port' => (int) $existing['smtp_port'], 'enc' => $existing['smtp_encryption'] ?: 'tls', 'username' => (string) $existing['smtp_username'], 'password' => '']; } if ($isDefault) { $pdo->prepare('UPDATE email_addresses SET is_default = 0 WHERE department_id = ?')->execute([$deptId]); } $now = db_now(); if ($id === 0) { $pdo->prepare( 'INSERT INTO email_addresses (email, label, department_id, priority_id, help_topic_id, is_default, is_active, notes, smtp_enabled, smtp_host, smtp_port, smtp_encryption, smtp_username, smtp_password_enc, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)' )->execute([$email, $label, $deptId, $prioId, $topicId, $isDefault, $isActive, $notes ?: null, $smtpOn, $smtp['host'] ?: null, $smtp['port'], $smtp['enc'], $smtp['username'] ?: null, $smtpPassEnc, $now, $now]); $id = (int) $pdo->lastInsertId(); } else { $pdo->prepare( 'UPDATE email_addresses SET email=?, label=?, department_id=?, priority_id=?, help_topic_id=?, is_default=?, is_active=?, notes=?, smtp_enabled=?, smtp_host=?, smtp_port=?, smtp_encryption=?, smtp_username=?, smtp_password_enc=?, updated_at=? WHERE id=?' )->execute([$email, $label, $deptId, $prioId, $topicId, $isDefault, $isActive, $notes ?: null, $smtpOn, $smtp['host'] ?: null, $smtp['port'], $smtp['enc'], $smtp['username'] ?: null, $smtpPassEnc, $now, $id]); } // Verknüpftes Postfach: Abteilung/Priorität/Hilfethema kommen aus dem Konto if ($imapOn) { if ($existingImap) { $sql = 'UPDATE imap_accounts SET label=?, host=?, port=?, encryption=?, username=?, folder=?, after_fetch=?, archive_folder=?, spam_folder=?, department_id=?, priority_id=?, is_active=1'; $params = [$email, $imap['host'], $imap['port'], $imap['enc'], $imap['username'], $imap['folder'], $imap['after'], $imap['archive'], $imap['spam'], $deptId, $prioId]; if ($imap['password'] !== '') { $sql .= ', password_enc=?'; $params[] = encrypt_secret($imap['password']); } $params[] = $existingImap['id']; $pdo->prepare($sql . ' WHERE id=?')->execute($params); } else { $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, email_address_id, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,1,?,?)' )->execute([$email, $imap['host'], $imap['port'], $imap['enc'], $imap['username'], encrypt_secret($imap['password']), $imap['folder'], $imap['after'], $imap['archive'], $imap['spam'], $deptId, $prioId, $id, $now]); } } elseif ($existingImap) { $pdo->prepare('UPDATE imap_accounts SET is_active = 0, department_id = ?, priority_id = ? WHERE id = ?')->execute([$deptId, $prioId, $existingImap['id']]); } return $id; } /** Löscht eine Adresse samt Postfach (oder löst das Postfach, falls Tickets darauf verweisen). Gibt die gelöschte E-Mail zurück. */ function email_address_delete(PDO $pdo, int $id): string { $stmt = $pdo->prepare('SELECT email FROM email_addresses WHERE id = ?'); $stmt->execute([$id]); $email = (string) ($stmt->fetchColumn() ?: $id); if ($imap = email_address_imap($pdo, $id)) { $used = $pdo->prepare('SELECT COUNT(*) FROM ticket_email_sources WHERE imap_account_id = ?'); $used->execute([$imap['id']]); if ((int) $used->fetchColumn() > 0) { $pdo->prepare('UPDATE imap_accounts SET is_active = 0, email_address_id = NULL WHERE id = ?')->execute([$imap['id']]); } else { $pdo->prepare('DELETE FROM imap_accounts WHERE id = ?')->execute([$imap['id']]); } } $pdo->prepare('DELETE FROM email_addresses WHERE id = ?')->execute([$id]); return $email; } /** Sendet eine Test-Mail von dieser Adresse an $recipient. Gibt die Erfolgsmeldung zurück. */ function email_address_send_test(PDO $pdo, int $id, string $recipient): string { $stmt = $pdo->prepare('SELECT email, label, smtp_enabled FROM email_addresses WHERE id = ?'); $stmt->execute([$id]); $addr = $stmt->fetch(); if (!$addr) throw new EmailAddressException('E-Mail-Adresse nicht gefunden.', 'NOT_FOUND', 404); $from = $addr['label'] ? "{$addr['label']} <{$addr['email']}>" : $addr['email']; $via = $addr['smtp_enabled'] ? 'eigenen SMTP-Server' : 'globalen SMTP-Server'; $html = '
Dies ist eine Test-Mail von DeskHub.
Absender: ' . e($addr['email']) . '
Versand über: ' . e($via) . '