From 1d664850684747698a9bed27a1e0aa6ffa5827df Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Mon, 9 Feb 2026 10:58:03 -0600 Subject: [PATCH] new method _send_via_legacy_smtp --- .../email_processing/rules_processor.py | 107 +++++++++++++++--- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/email-worker/email_processing/rules_processor.py b/email-worker/email_processing/rules_processor.py index 5673d5a..15486d4 100644 --- a/email-worker/email_processing/rules_processor.py +++ b/email-worker/email_processing/rules_processor.py @@ -77,7 +77,8 @@ class RulesProcessor: forwards, domain, worker_name, - metrics_callback + metrics_callback, + rule=rule ) def _handle_ooo( @@ -141,34 +142,104 @@ class RulesProcessor: forwards: list, domain: str, worker_name: str, - metrics_callback=None + metrics_callback=None, + rule: dict = None # NEU ): """Handle email forwarding""" + # NEU: SMTP Override aus Rule lesen + smtp_override = None + if rule: + smtp_override = rule.get('forward_smtp_override') + for forward_to in forwards: try: - fwd_msg = self._create_forward_message(parsed, recipient, forward_to, original_from) + fwd_msg = self._create_forward_message( + parsed, recipient, forward_to, original_from + ) fwd_bytes = fwd_msg.as_bytes() - - # Distinguish: Internal (Port 2525) vs External (SES) - if is_internal_address(forward_to): - # Internal address → direct via Port 2525 (no loop!) - success = self._send_internal_email(recipient, forward_to, fwd_bytes, worker_name) + + # NEU: Legacy SMTP Override (Migration) + if smtp_override: + success = self._send_via_legacy_smtp( + recipient, forward_to, fwd_bytes, + smtp_override, worker_name + ) if success: - log(f"✓ Forwarded internally to {forward_to}", 'SUCCESS', worker_name) + log(f"✓ Forwarded via legacy SMTP to {forward_to} " + f"({smtp_override.get('host', '?')})", + 'SUCCESS', worker_name) else: - log(f"⚠ Internal forward failed to {forward_to}", 'WARNING', worker_name) - else: - # External address → via SES - success = self.ses.send_raw_email(recipient, forward_to, fwd_bytes, worker_name) + log(f"⚠ Legacy SMTP forward failed to {forward_to}", + 'WARNING', worker_name) + + elif is_internal_address(forward_to): + success = self._send_internal_email( + recipient, forward_to, fwd_bytes, worker_name + ) if success: - log(f"✓ Forwarded externally to {forward_to} via SES", 'SUCCESS', worker_name) - + log(f"✓ Forwarded internally to {forward_to}", + 'SUCCESS', worker_name) + else: + log(f"⚠ Internal forward failed to {forward_to}", + 'WARNING', worker_name) + else: + success = self.ses.send_raw_email( + recipient, forward_to, fwd_bytes, worker_name + ) + if success: + log(f"✓ Forwarded externally to {forward_to} via SES", + 'SUCCESS', worker_name) + if metrics_callback: metrics_callback('forward', domain) - + except Exception as e: - log(f"⚠ Forward failed to {forward_to}: {e}", 'ERROR', worker_name) - + log(f"⚠ Forward failed to {forward_to}: {e}", + 'ERROR', worker_name) + + @staticmethod + def _send_via_legacy_smtp( + from_addr: str, + to_addr: str, + raw_message: bytes, + smtp_config: dict, + worker_name: str + ) -> bool: + """ + Send email directly to a legacy SMTP server (for migration). + Bypasses SES completely to avoid mail loops. + """ + try: + host = smtp_config.get('host', '') + + # DynamoDB speichert Zahlen als Decimal, daher int() + port = int(smtp_config.get('port', 25)) + use_tls = smtp_config.get('tls', False) + username = smtp_config.get('username') + password = smtp_config.get('password') + + if not host: + log(f" ✗ Legacy SMTP: no host configured", 'ERROR', worker_name) + return False + + with smtplib.SMTP(host, port, timeout=30) as conn: + conn.ehlo() + if use_tls: + conn.starttls() + conn.ehlo() + if username and password: + conn.login(username, password) + conn.sendmail(from_addr, [to_addr], raw_message) + return True + + except Exception as e: + log( + f" ✗ Legacy SMTP failed ({smtp_config.get('host', '?')}:" + f"{smtp_config.get('port', '?')}): {e}", + 'ERROR', worker_name + ) + return False + @staticmethod def _send_internal_email(from_addr: str, to_addr: str, raw_message: bytes, worker_name: str) -> bool: """