feat: configurable DSN (#36988)

* feat: configurable `DSN`

* chore: validate dsn support on save

* fix: DSN validation
This commit is contained in:
s-aga-r 2026-02-20 10:30:14 +05:30 committed by GitHub
parent 5d67826f25
commit fef9d89ce0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 2 deletions

View file

@ -78,6 +78,7 @@
"smtp_port",
"column_break_38",
"no_smtp_authentication",
"dsn_notify_type",
"always_bcc",
"signature_section",
"add_signature",
@ -754,13 +755,20 @@
"ignore_xss_filter": 1,
"label": "Reply-To Addresses",
"options": "Reply To Address"
},
{
"description": "Select which delivery events should trigger a delivery status notification (DSN) from the SMTP server.",
"fieldname": "dsn_notify_type",
"fieldtype": "Select",
"label": "Delivery Status Notification Type",
"options": "\nSUCCESS\nFAILURE\nDELAY\nSUCCESS,FAILURE\nSUCCESS,FAILURE,DELAY\nNEVER"
}
],
"icon": "fa fa-inbox",
"index_web_pages_for_search": 1,
"links": [],
"make_attachments_public": 1,
"modified": "2026-02-06 11:39:39.412130",
"modified": "2026-02-11 16:18:15.572240",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Account",

View file

@ -84,6 +84,9 @@ class EmailAccount(Document):
default_incoming: DF.Check
default_outgoing: DF.Check
domain: DF.Link | None
dsn_notify_type: DF.Literal[
"SUCCESS", "FAILURE", "DELAY", "SUCCESS,FAILURE", "SUCCESS,FAILURE,DELAY", "NEVER"
]
email_account_name: DF.Data | None
email_id: DF.Data
email_server: DF.Data | None
@ -220,7 +223,8 @@ class EmailAccount(Document):
frappe.throw(_("SMTP Server is required"))
self.flags.validate_smtp_connection = True
self.get_smtp_server().session
session = self.get_smtp_server().session
self.validate_dsn(session)
del self._smtp_server_instance
def validate_reply_to_addresses(self) -> None:
@ -229,6 +233,18 @@ class EmailAccount(Document):
frappe.throw(_("Reply To email is required"))
validate_email_address(reply_to.email, True)
def validate_dsn(self, smtp_session) -> None:
"""Validate if the configured SMTP server supports DSN (Delivery Status Notification)."""
if not self.dsn_notify_type:
return
if not smtp_session.has_extn("DSN"):
self.dsn_notify_type = None
frappe.msgprint(
_("The configured SMTP server does not support DSN (Delivery Status Notification).")
)
def before_save(self):
messages = []
as_list = 1

View file

@ -196,10 +196,20 @@ class EmailQueue(Document):
is_newsletter=is_newsletter,
)
else:
mail_options = []
rcpt_options = []
if ctx.smtp_server.session.has_extn("DSN"):
if dsn_notify_type := ctx.email_account_doc.dsn_notify_type:
mail_options = ["RET=FULL", f"ENVID={self.name}"]
rcpt_options = [f"NOTIFY={dsn_notify_type}"]
ctx.smtp_server.session.sendmail(
from_addr=self.sender,
to_addrs=recipient.recipient,
msg=message.decode("utf-8").encode(),
mail_options=mail_options,
rcpt_options=rcpt_options,
)
ctx.update_recipient_status_to_sent(recipient)

View file

@ -92,6 +92,9 @@ class SMTPServer:
if res[0] != 235:
frappe.msgprint(res[1], raise_exception=frappe.OutgoingEmailError)
# Re-issue EHLO after AUTH to refresh server capabilities
_session.ehlo()
self._session = _session
self._enqueue_connection_closure()
return self._session