diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb index 549fb5c93e..fa42e853f9 100644 --- a/app/mailers/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -231,9 +231,7 @@ def self.receive(raw_email, source = :mailin) # Find which info requests the email is for def requests_matching_email(email) - # We deliberately don't use Envelope-to here, so ones that are BCC - # drop into the holding pen for checking. - addresses = ((email.to || []) + (email.cc || [])).compact + addresses = MailHandler.get_all_addresses(email) InfoRequest.matching_incoming_email(addresses) end @@ -244,8 +242,8 @@ def receive(email, raw_email, source = :mailin) # Find which info requests the email is for reply_info_requests = requests_matching_email(email) - # Nothing found, so save in holding pen - if reply_info_requests.empty? + # Nothing found OR multiple different info requests, so save in holding pen + if reply_info_requests.empty? || reply_info_requests.count > 1 opts[:rejected_reason] = _("Could not identify the request from the email address") request = InfoRequest.holding_pen_request diff --git a/lib/mail_handler/backends/mail_backend.rb b/lib/mail_handler/backends/mail_backend.rb index d10c00b8be..9354f9ff88 100644 --- a/lib/mail_handler/backends/mail_backend.rb +++ b/lib/mail_handler/backends/mail_backend.rb @@ -146,6 +146,7 @@ def get_all_addresses(mail, include_invalid: false) addrs << mail.bcc addrs << mail[:bcc].try(:value) if mail.bcc.nil? && include_invalid addrs << (mail['envelope-to'] ? mail['envelope-to'].value.to_s : nil) + addrs << get_emails_within_received_headers(mail) addrs.flatten.compact.uniq end @@ -396,6 +397,15 @@ def address_from_string(string) mail.from = string mail.from[0] end + + def get_emails_within_received_headers(email) + received_headers = Array(email['Received']) + return [] if received_headers.empty? + received_headers.map(&:to_s). + join(' '). + scan(MySociety::Validate.email_find_regexp). + flatten + end end end end diff --git a/spec/lib/mail_handler/backends/mail_backend_spec.rb b/spec/lib/mail_handler/backends/mail_backend_spec.rb index aa27a8ea53..9a161e061d 100644 --- a/spec/lib/mail_handler/backends/mail_backend_spec.rb +++ b/spec/lib/mail_handler/backends/mail_backend_spec.rb @@ -322,4 +322,88 @@ expect { decode_attached_part(mail.parts.last, mail) }.not_to raise_error end end + + describe :get_emails_within_received_headers do + + it 'returns an empty list if there is no Received header' do + mail = get_fixture_mail('bcc-contact-reply.email') + expect(get_emails_within_received_headers(mail)).to eq([]) + end + + it 'returns an empty list if the Received header contains no email addresses' do + mail = Mail.new(<<~EOF.strip_heredoc) + From: "FOI Person" + To: "Bob Smith" + Cc: bob@example.com + Envelope-To: bob@example.net + Received: abc id abc from notAnEmail + Date: Tue, 13 Nov 2007 11:39:55 +0000 + Bcc: "BCC Person" + Subject: Test + Reply-To: + + Test + EOF + + expect(get_emails_within_received_headers(mail)).to eq([]) + end + + it 'returns a list containing the email if the Received header contains one email address' do + mail = Mail.new(<<~EOF.strip_heredoc) + From: "FOI Person" + To: "Bob Smith" + Cc: bob@example.com + Envelope-To: bob@example.net + Received: abc id abc from aperson@domain.abc + Date: Tue, 13 Nov 2007 11:39:55 +0000 + Bcc: "BCC Person" + Subject: Test + Reply-To: + + Test + EOF + + expect(get_emails_within_received_headers(mail)). + to eq(['aperson@domain.abc']) + end + + it 'returns a (single dimensional) list containing the emails if the Received header contains multiple email addresses' do + mail = Mail.new(<<~EOF.strip_heredoc) + From: "FOI Person" + To: "Bob Smith" + Cc: bob@example.com + Envelope-To: bob@example.net + Received: abc id abc from aperson@domain.abc + also there is anotherperson@domain.abc + Date: Tue, 13 Nov 2007 11:39:55 +0000 + Bcc: "BCC Person" + Subject: Test + Reply-To: + + Test + EOF + + expect(get_emails_within_received_headers(mail)). + to eq(['aperson@domain.abc', 'anotherperson@domain.abc']) + end + + it 'recognises the Received header when it is not capitalised' do + mail = Mail.new(<<~EOF.strip_heredoc) + From: "FOI Person" + To: "Bob Smith" + Cc: bob@example.com + Envelope-To: bob@example.net + received: abc id abc from aperson@domain.abc + Date: Tue, 13 Nov 2007 11:39:55 +0000 + Bcc: "BCC Person" + Subject: Test + Reply-To: + + Test + EOF + + expect(get_emails_within_received_headers(mail)). + to eq(['aperson@domain.abc']) + end + end end diff --git a/spec/mailers/request_mailer_spec.rb b/spec/mailers/request_mailer_spec.rb index fd7bf4598d..07a93433d3 100644 --- a/spec/mailers/request_mailer_spec.rb +++ b/spec/mailers/request_mailer_spec.rb @@ -48,12 +48,29 @@ expect(InfoRequest.holding_pen_request.incoming_messages.count).to eq(1) end - it "puts messages with the request address in Bcc: in the holding pen" do - request = FactoryBot.create(:info_request) - receive_incoming_mail('bcc-contact-reply.email', - email_to: 'dummy@localhost', - email_bcc: request.incoming_email) - expect(InfoRequest.holding_pen_request.incoming_messages.count).to eq(1) + + it "attaches messages with an info request address in the Received headers to the appropriate request" do + ir = info_requests(:fancy_dog_request) + expect(ir.incoming_messages.count).to eq(1) # in the fixture + mail_content = <<~EOF + From: "FOI Person" + Received: from smtp-out.localhost + by example.net with esmtps + (Exim 4.89) + (envelope-from ) + id ABC + for #{ir.incoming_email}.com; Mon, 23 Nov 2020 00:00:00 +0000 + Test + EOF + receive_incoming_mail(mail_content) + expect(ir.incoming_messages.count).to eq(2) # one more arrives + expect(ir.info_request_events[-1].incoming_message_id).not_to be_nil + + deliveries = ActionMailer::Base.deliveries + expect(deliveries.size).to eq(1) + mail = deliveries[0] + expect(mail.to).to eq([ 'bob@localhost' ]) # to the user who sent fancy_dog_request + deliveries.clear end it "puts messages with multiple request addresses in Bcc: in the holding pen" do