From 67b3ffa0ac729b7afdacfe843369f926e5408e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Sch=C3=BCffler?= Date: Fri, 5 Jul 2024 22:24:36 +0200 Subject: [PATCH 1/2] Add message token to SMTP FROM We add the per-message individual message token to the SMTP FROM, which is mainly used in the return path (i.e. for bounce messages). By adding the token to the mail address, we now are able to assign incoming bounces not only by the x-postal-msgId header, but also by their individual message tag / address. --- app/senders/smtp_sender.rb | 4 ++-- spec/senders/smtp_sender_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/senders/smtp_sender.rb b/app/senders/smtp_sender.rb index 1f3c863fd..8beaf53cc 100644 --- a/app/senders/smtp_sender.rb +++ b/app/senders/smtp_sender.rb @@ -142,10 +142,10 @@ def determine_mail_from_for_message(message) # If the domain has a valid custom return path configured, return # that. if message.domain.return_path_status == "OK" - return "#{message.server.token}@#{message.domain.return_path_domain}" + return "#{message.server.token}+#{message.token}@#{message.domain.return_path_domain}" end - "#{message.server.token}@#{Postal::Config.dns.return_path_domain}" + "#{message.server.token}+#{message.token}@#{Postal::Config.dns.return_path_domain}" end # Return the RCPT TO to use for the given message in this sending session diff --git a/spec/senders/smtp_sender_spec.rb b/spec/senders/smtp_sender_spec.rb index 6008a2655..5a669ca5d 100644 --- a/spec/senders/smtp_sender_spec.rb +++ b/spec/senders/smtp_sender_spec.rb @@ -263,7 +263,7 @@ sender.send_message(message) expect(sender.endpoints.last).to have_received(:send_message).with( kind_of(String), - "#{server.token}@#{domain.return_path_domain}", + "#{server.token}+#{message.token}@#{domain.return_path_domain}", ["john@example.com"] ) end @@ -274,7 +274,7 @@ sender.send_message(message) expect(sender.endpoints.last).to have_received(:send_message).with( kind_of(String), - "#{server.token}@#{Postal::Config.dns.return_path_domain}", + "#{server.token}+#{message.token}@#{Postal::Config.dns.return_path_domain}", ["john@example.com"] ) end @@ -308,7 +308,7 @@ it "adds the resent-sender header" do sender.send_message(message) expect(sender.endpoints.last).to have_received(:send_message).with( - "Resent-Sender: #{server.token}@#{Postal::Config.dns.return_path_domain}\r\n#{message.raw_message}", + "Resent-Sender: #{server.token}+#{message.token}@#{Postal::Config.dns.return_path_domain}\r\n#{message.raw_message}", kind_of(String), kind_of(Array) ) From 57af325af9b30429d221fdaa0909d6377d61f78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Sch=C3=BCffler?= Date: Fri, 5 Jul 2024 23:37:08 +0200 Subject: [PATCH 2/2] assign bounces by their address tag, optional Beside the search based on X-Postal-MsgID header, we also can assign incoming bounces by their individually generated mail address (with the same tag as in the X-Postal-MsgID). This is configurable, with the default disabled to have it backwards compatible. From a performance perspective, searching by this address based approach always is faster than scanning the full mail for the presence of the header, so we could also consider to make this feature enabled by default. --- doc/config/yaml.yml | 2 ++ lib/postal/config_schema.rb | 5 +++++ lib/postal/message_db/message.rb | 12 +++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/config/yaml.yml b/doc/config/yaml.yml index 1035ec99f..01ad56adc 100644 --- a/doc/config/yaml.yml +++ b/doc/config/yaml.yml @@ -23,6 +23,8 @@ postal: use_local_ns_for_domain_verification: false # Append a Resend-Sender header to all outgoing e-mails use_resent_sender_header: true + # Use message tags when assigning bounces to their corresponding messages + use_message_tags_for_bounces: false # Path to the private key used for signing signing_key_path: $config-file-root/signing.key # An array of SMTP relays in the format of smtp://host:port diff --git a/lib/postal/config_schema.rb b/lib/postal/config_schema.rb index af3c7330e..b63e64ea4 100644 --- a/lib/postal/config_schema.rb +++ b/lib/postal/config_schema.rb @@ -66,6 +66,11 @@ module Postal default true end + boolean :use_message_tags_for_bounces do + description "Use message tags when assigning bounces to their corresponding messages" + default false + end + string :signing_key_path do description "Path to the private key used for signing" default "$config-file-root/signing.key" diff --git a/lib/postal/message_db/message.rb b/lib/postal/message_db/message.rb index 3a2c7a690..ee4f5d8c9 100644 --- a/lib/postal/message_db/message.rb +++ b/lib/postal/message_db/message.rb @@ -499,7 +499,17 @@ def create_link(url) def original_messages return nil unless bounce - other_message_ids = raw_message.scan(/\X-Postal-MsgID:\s*([a-z0-9]+)/i).flatten + # if this is enabled, we search first for the message tag as this is faster + # than to scan the full message (which is probably many mbytes in size) + if Postal::Config.postal.use_message_tags_for_bounces? + uname, domain = rcpt_to.split("@", 2) + uname, other_message_ids = uname.split("+", 2) + end + # if we did not found any messages, or this was not enabled, then fall back to + # the old header based search + if other_message_ids.nil? || other_message_ids.empty? + other_message_ids = raw_message.scan(/\X-Postal-MsgID:\s*([a-z0-9]+)/i).flatten + end if other_message_ids.empty? [] else