SMTP: implicit TLS (port 465) support + fix quarantined mail
Build and push server image / build-and-push (push) Successful in 38s
Build and push server image / build-and-push (push) Successful in 38s
Two real fixes to app/mail.py, both found by testing against an actual
mail server rather than just a fake stub:
- Replaces the STARTTLS-only smtp_use_tls boolean with a three-way
smtp_encryption ("none"/"starttls"/"ssl"). Implicit TLS (port 465,
what Purelymail and most providers offer alongside 587/STARTTLS) is a
different handshake entirely -- TLS from the first byte, not a
plaintext connection that gets upgraded -- so it needs its own
smtplib.SMTP_SSL code path, not just a skipped starttls() call.
Schema migration v3 adds the column, backfills it from the old
boolean, and drops the boolean (safe on a live, populated DB).
- Outgoing mail was missing Date and Message-ID headers -- email.mime
doesn't set either automatically, and a missing Message-ID in
particular is enough for a strict content filter (confirmed via a
real Postfix+Amavis mail server's logs: SPF/DKIM/DMARC all passed
cleanly, but Amavis quarantined the message as "BAD-HEADER-0" purely
for the missing id) to silently swallow an otherwise-legitimate
email, even though smtplib reports success -- the send genuinely
succeeds to the relay, it just never survives the recipient's own
filtering. Both headers are now set, with the Message-ID's domain
matching the From address.
Verified: SMTP_SSL path against a hand-rolled implicit-TLS fake server
(self-signed cert, client-side verification relaxed only in the test
harness -- production code keeps ssl.create_default_context()'s real
verification), the v2->v3 migration against live data, the full admin
SMTP-save + test-email round trip over HTTP, and the standing legacy-
device curl suite.
This commit is contained in:
@@ -218,7 +218,11 @@ class ServerSettings(Base):
|
||||
smtp_username: Mapped[str] = mapped_column(String, default="")
|
||||
smtp_password: Mapped[str] = mapped_column(String, default="")
|
||||
smtp_from_address: Mapped[str] = mapped_column(String, default="")
|
||||
smtp_use_tls: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
# "none" (plaintext, port 25 typically), "starttls" (upgrades a
|
||||
# plaintext connection, port 587 typically), or "ssl" (TLS from the
|
||||
# first byte -- a different handshake entirely, not just starttls()
|
||||
# skipped; port 465 typically). See app/mail.py.
|
||||
smtp_encryption: Mapped[str] = mapped_column(String, default="starttls")
|
||||
|
||||
|
||||
class PasswordResetToken(Base):
|
||||
|
||||
Reference in New Issue
Block a user