diff --git a/email_api/email_api/app.py b/email_api/email_api/app.py index 1839088..8c94ff4 100644 --- a/email_api/email_api/app.py +++ b/email_api/email_api/app.py @@ -226,9 +226,15 @@ def retry_domain_emails(domain): if auth != f'Bearer {API_TOKEN}': return jsonify({'error': 'Unauthorized'}), 401 + # 1) Domain-Check ganz am Anfang + if not domain_exists(domain): + logger.info(f"Retry aborted: unknown domain '{domain}'") + return jsonify({'error': f"Unknown domain '{domain}'"}), 404 + bucket = domain.replace('.', '-') + '-emails' paginator = s3_client.get_paginator('list_objects_v2') + # 2) alle unprocessed Keys sammeln unprocessed = [] for page in paginator.paginate(Bucket=bucket): for obj in page.get('Contents', []): @@ -245,24 +251,30 @@ def retry_domain_emails(domain): try: body = s3_client.get_object(Bucket=bucket, Key=key)['Body'].read() msg = BytesParser(policy=default).parsebytes(body) - from_addr = ( getaddresses(msg.get_all('from', []))[0][1] if msg.get_all('from') else f'retry@{domain}' ) - to_addrs = [a for _n, a in getaddresses(msg.get_all('to', []))] - cc_addrs = [a for _n, a in getaddresses(msg.get_all('cc', []))] - bcc_addrs = [a for _n, a in getaddresses(msg.get_all('bcc', []))] - recipients = to_addrs + cc_addrs + bcc_addrs + + # Sammle alle To/Cc/Bcc + recipients = [] + for hdr in ('to', 'cc', 'bcc'): + recipients += [addr for _n, addr in getaddresses(msg.get_all(hdr, []))] if not recipients: - results['failed'].append({'key': key, 'error': 'no recipients'}) + # gar keine Adressen → überspringen + mark_email_as_processed(bucket, key, 'unknownDomain') + results['processed'].append(key) + results['failed'].append({ + 'key': key, + 'status': 'unknownDomain', + 'reason': 'no recipients' + }) continue - # **Neu: Domain-Match prüfen** - domains = {addr.split('@')[-1].lower() for addr in recipients} - if domain.lower() not in domains: - # niemals zur eigenen Domain adressiert → unknownDomain + # 3) Domain-Match: nur Mails, die an die angefragte Domain adressiert sind + domains_in_mail = {addr.split('@')[-1].lower() for addr in recipients if '@' in addr} + if domain.lower() not in domains_in_mail: mark_email_as_processed(bucket, key, 'unknownDomain') results['processed'].append(key) results['failed'].append({ @@ -273,48 +285,66 @@ def retry_domain_emails(domain): }) continue - # mindestens ein passender Empfänger → Sendeversuch + # 4) Inbox-Check: nur existierende Postfächer zulassen + valid_recipients = [] + for addr in recipients: + try: + local, dom = addr.split('@', 1) + except ValueError: + continue + if dom.lower() == domain.lower() and inbox_exists(domain, local): + valid_recipients.append(addr) + else: + logger.info(f"Skipping non-existent inbox: {addr}") + + if not valid_recipients: + mark_email_as_processed(bucket, key, 'unknownUser') + results['processed'].append(key) + results['failed'].append({ + 'key': key, + 'status': 'unknownUser', + 'from': from_addr, + 'to': recipients + }) + continue + + # 5) Versand an die validierten Adressen try: with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as smtp: - smtp.sendmail(from_addr, recipients, body) + smtp.sendmail(from_addr, valid_recipients, body) mark_email_as_processed(bucket, key, 'true') results['processed'].append(key) except smtplib.SMTPRecipientsRefused as e: - refused = e.recipients - status = ( - 'unknownUser' - if any(addr.split('@')[-1].lower() == domain.lower() for addr in refused) - else 'unknownDomain' - ) - # bytes → str - clean = {addr: {'code': code, 'message': (msg.decode('utf-8', 'ignore') - if isinstance(msg, bytes) else str(msg))} - for addr, (code, msg) in refused.items()} - - mark_email_as_processed(bucket, key, status) + # falls Mailcow einzelne Adressen ablehnt + mark_email_as_processed(bucket, key, 'unknownUser') + refused = { + addr: {'code': code, 'message': msg.decode('utf-8','ignore') if isinstance(msg, bytes) else str(msg)} + for addr, (code, msg) in e.recipients.items() + } results['processed'].append(key) results['failed'].append({ 'key': key, - 'status': status, + 'status': 'unknownUser', 'from': from_addr, - 'to': recipients, - 'refused': clean + 'to': valid_recipients, + 'refused': refused }) except Exception as e: - # andere SMTP-Fehler behandeln wie unknownDomain + # alle anderen SMTP-Fehler behandeln wir als unknownDomain mark_email_as_processed(bucket, key, 'unknownDomain') results['processed'].append(key) results['failed'].append({ 'key': key, 'status': 'unknownDomain', 'from': from_addr, - 'to': recipients, + 'to': valid_recipients, 'error': str(e) }) except Exception as e: + # Parsing- oder S3-Fehler results['failed'].append({'key': key, 'error': str(e)}) return jsonify(results), 200