178 lines
4.3 KiB
Python
Executable File
178 lines
4.3 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import smtplib
|
|
import subprocess
|
|
import sys
|
|
|
|
SMTP_HOST = "127.0.0.1"
|
|
SMTP_PORT = 10028
|
|
|
|
DECISION_SCRIPT = "/usr/local/sbin/srs-alias-decision.py"
|
|
SRS_MAP = "socketmap:inet:127.0.0.1:10003:forward"
|
|
|
|
EX_TEMPFAIL = 75
|
|
EX_UNAVAILABLE = 69
|
|
|
|
|
|
def run_command(args):
|
|
result = subprocess.run(
|
|
args,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
raise RuntimeError(
|
|
result.stderr.strip()
|
|
or result.stdout.strip()
|
|
or f"Exit-Code {result.returncode}"
|
|
)
|
|
|
|
return result.stdout.strip()
|
|
|
|
|
|
def determine_sender(sender, recipient, original_recipient):
|
|
decision = run_command([
|
|
DECISION_SCRIPT,
|
|
sender,
|
|
recipient,
|
|
original_recipient
|
|
])
|
|
|
|
if decision.startswith("NO_SRS:"):
|
|
return sender
|
|
|
|
if not decision.startswith("SRS:"):
|
|
raise RuntimeError(
|
|
f"Keine eindeutige SRS-Entscheidung: {decision}"
|
|
)
|
|
|
|
rewritten_sender = run_command([
|
|
"/usr/sbin/postmap",
|
|
"-q",
|
|
sender,
|
|
SRS_MAP
|
|
])
|
|
|
|
if not rewritten_sender:
|
|
raise RuntimeError("PostSRSd lieferte keinen Absender")
|
|
|
|
if not rewritten_sender.lower().endswith("@srs.warenform.de"):
|
|
raise RuntimeError(
|
|
f"Unerwartete SRS-Domain: {rewritten_sender}"
|
|
)
|
|
|
|
if not rewritten_sender.upper().startswith(("SRS0=", "SRS1=")):
|
|
raise RuntimeError(
|
|
f"Ungueltige SRS-Adresse: {rewritten_sender}"
|
|
)
|
|
|
|
return rewritten_sender
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) not in (5, 6):
|
|
print(
|
|
"Aufruf: srs-forward.py "
|
|
"sender recipient original_recipient queue_id [envid]",
|
|
file=sys.stderr
|
|
)
|
|
return EX_TEMPFAIL
|
|
|
|
sender, recipient, original_recipient, queue_id = sys.argv[1:5]
|
|
envid = sys.argv[5] if len(sys.argv) == 6 else ""
|
|
|
|
if envid.startswith("envid="):
|
|
envid = envid[len("envid="):]
|
|
|
|
try:
|
|
envelope_sender = determine_sender(
|
|
sender,
|
|
recipient,
|
|
original_recipient
|
|
)
|
|
|
|
message = sys.stdin.buffer.read()
|
|
|
|
with smtplib.SMTP(
|
|
SMTP_HOST,
|
|
SMTP_PORT,
|
|
timeout=30
|
|
) as smtp:
|
|
smtp.ehlo()
|
|
|
|
mail_options = []
|
|
rcpt_options = []
|
|
|
|
if envid:
|
|
if not smtp.has_extn("dsn"):
|
|
raise RuntimeError(
|
|
"SMTP-Ziel unterstuetzt kein DSN/ENVID"
|
|
)
|
|
mail_options.append(f"ENVID={envid}")
|
|
|
|
if original_recipient and smtp.has_extn("dsn"):
|
|
rcpt_options.append(
|
|
f"ORCPT=rfc822;{original_recipient}"
|
|
)
|
|
|
|
smtp.sendmail(
|
|
envelope_sender,
|
|
[recipient],
|
|
message,
|
|
mail_options=mail_options,
|
|
rcpt_options=rcpt_options
|
|
)
|
|
|
|
except smtplib.SMTPRecipientsRefused as exc:
|
|
codes = [
|
|
code
|
|
for code, response in exc.recipients.values()
|
|
]
|
|
|
|
permanent = bool(codes) and all(
|
|
500 <= code < 600 for code in codes
|
|
)
|
|
|
|
result = EX_UNAVAILABLE if permanent else EX_TEMPFAIL
|
|
enhanced_status = "5.0.0" if permanent else "4.0.0"
|
|
|
|
print(
|
|
f"{enhanced_status} SRS queue_id={queue_id}: "
|
|
f"SMTP-Empfaengerablehnung: {exc.recipients!r}",
|
|
file=sys.stderr
|
|
)
|
|
return result
|
|
|
|
except smtplib.SMTPResponseException as exc:
|
|
code = exc.smtp_code
|
|
result = EX_UNAVAILABLE if 500 <= code < 600 else EX_TEMPFAIL
|
|
|
|
enhanced_status = "5.0.0" if result == EX_UNAVAILABLE else "4.0.0"
|
|
|
|
print(
|
|
f"{enhanced_status} SRS queue_id={queue_id}: "
|
|
f"SMTP {code}: {exc.smtp_error!r}",
|
|
file=sys.stderr
|
|
)
|
|
return result
|
|
|
|
except (
|
|
subprocess.TimeoutExpired,
|
|
RuntimeError,
|
|
smtplib.SMTPException,
|
|
OSError
|
|
) as exc:
|
|
print(
|
|
f"SRS queue_id={queue_id}: temporaerer Fehler: {exc}",
|
|
file=sys.stderr
|
|
)
|
|
return EX_TEMPFAIL
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|