Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions Bugzilla/Mailer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ sub MessageToMTA {
$email->header_set('MIME-Version', '1.0')
if !$email->header('MIME-Version');

# We ensure there's a Message-ID header set otherwise some mailsystems
# treat us as spam.
$email->header_set('Message-ID', build_message_id())
if !$email->header('Message-ID');

# Encode the headers correctly in quoted-printable
foreach my $header ($email->header_names) {
$header = lc $header;
Expand Down Expand Up @@ -252,9 +257,9 @@ sub build_thread_marker {

my $sitespec = '@' . Bugzilla->localconfig->urlbase;
$sitespec =~ s/:\/\//\./; # Make the protocol look like part of the domain
$sitespec =~ s/^([^:\/]+):(\d+)/$1/; # Remove a port number, to relocate
if ($2) {
$sitespec = "-$2$sitespec"; # Put the port number back in, before the '@'
$sitespec =~ s/\/.*$//; # Strip path component — / is illegal after @ in Message-ID
if ($sitespec =~ s/^([^:\/]+):(\d+)/$1/) { # Remove port number, to relocate
$sitespec = "-$2$sitespec"; # Put the port number back in, before the '@'
}

my $threadingmarker = "References: <bug-$bug_id-$user_id$sitespec>";
Expand All @@ -270,4 +275,23 @@ sub build_thread_marker {
return $threadingmarker;
}

# Builds Message-ID header
sub build_message_id {
my ($user_id) = @_;

# Don't fall back to current user: this is called from contexts with no logged-in
# user (job queue, email_in.pl). The random bits below ensure uniqueness anyway.
$user_id //= '';

my $sitespec = '@' . Bugzilla->localconfig->urlbase;
$sitespec =~ s/:\/\//\./; # Make the protocol look like part of the domain
$sitespec =~ s/\/.*$//; # Strip path component — / is illegal after @ in Message-ID
if ($sitespec =~ s/^([^:\/]+):(\d+)/$1/) { # Remove port number, to relocate
$sitespec = "-$2$sitespec"; # Put the port number back in, before the '@'
}

my $rand_bits = generate_random_password(10);
my $message_id = '<bugzilla-' . ($user_id ne '' ? "$user_id-" : '') . "$rand_bits$sitespec>";
return $message_id;
}
1;
113 changes: 113 additions & 0 deletions t/014mailer.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

##################
#Bugzilla Test 14#
####Mailer.pm#####

use 5.10.1;
use strict;
use warnings;

use lib qw(. lib local/lib/perl5 t);
use Support::Files;
use Test::More tests => 18;

BEGIN {
use_ok('Bugzilla');
use_ok('Bugzilla::Mailer', 'build_thread_marker');
}

# Inject a stub localconfig into the process cache so the sitespec
# transformation functions can be tested without a real installation.
{

package Bugzilla::Test::FakeLocalconfig;

sub new { my ($class, $urlbase) = @_; bless {urlbase => $urlbase}, $class }
sub urlbase { $_[0]->{urlbase} }
}

sub set_urlbase {
my ($urlbase) = @_;
Bugzilla->process_cache->{localconfig}
= Bugzilla::Test::FakeLocalconfig->new($urlbase);
}

# ---- build_message_id: sitespec transformation ----
#
# The central invariant: whatever urlbase looks like, the resulting
# Message-ID must be a valid <local-part@domain> with no '/' characters
# after the '@'.

my $mid;

# Plain http, trailing slash only — baseline case.
set_urlbase('http://bugs.example.org/');
$mid = Bugzilla::Mailer::build_message_id();
like($mid, qr/^<bugzilla-[A-Za-z0-9]+\@http\.bugs\.example\.org>$/,
'simple http urlbase: correct format and sitespec');
unlike($mid, qr/\@[^>]*\//, 'simple http urlbase: no slash after @');

# Path component after the hostname — the key regression this patch fixes.
set_urlbase('https://bugs.example.org/bugzilla/');
$mid = Bugzilla::Mailer::build_message_id();
like($mid, qr/^<bugzilla-[A-Za-z0-9]+\@https\.bugs\.example\.org>$/,
'https with path: path component stripped from sitespec');
unlike($mid, qr/\@[^>]*\//, 'https with path: no slash after @');

# Non-standard port — port must move before the '@'.
set_urlbase('https://bugs.example.org:8080/');
$mid = Bugzilla::Mailer::build_message_id();
like($mid, qr/^<bugzilla-[A-Za-z0-9]+-8080\@https\.bugs\.example\.org>$/,
'https with port: port relocated before @');
unlike($mid, qr/\@[^>]*\//, 'https with port: no slash after @');

# Port and path together.
set_urlbase('https://bugs.example.org:8080/bugzilla/');
$mid = Bugzilla::Mailer::build_message_id();
like($mid, qr/^<bugzilla-[A-Za-z0-9]+-8080\@https\.bugs\.example\.org>$/,
'https with port and path: path stripped, port relocated');
unlike($mid, qr/\@[^>]*\//, 'https with port and path: no slash after @');

# ---- build_message_id: user_id handling ----

set_urlbase('https://bugs.example.org/');

my $mid_with_user = Bugzilla::Mailer::build_message_id(42);
like($mid_with_user, qr/^<bugzilla-42-[A-Za-z0-9]+\@/,
'with user_id: user_id present in local-part');
unlike($mid_with_user, qr/bugzilla--/,
'with user_id: no double-dash');

my $mid_no_user = Bugzilla::Mailer::build_message_id();
like($mid_no_user, qr/^<bugzilla-[A-Za-z0-9]+\@/,
'without user_id: clean local-part (no double-dash)');
unlike($mid_no_user, qr/bugzilla--/,
'without user_id: no double-dash');

# ---- build_thread_marker: same sitespec logic ----

my $marker;

# Path component must be stripped here too.
set_urlbase('https://bugs.example.org/bugzilla/');
$marker = build_thread_marker(99, 7, 1); # new bug
like($marker, qr/Message-ID: <bug-99-7\@https\.bugs\.example\.org>/,
'new thread with path: path stripped from sitespec');
unlike($marker, qr/\@[^>]*\//, 'new thread with path: no slash after @');

# Port relocation for non-standard port.
set_urlbase('https://bugs.example.org:8080/');
$marker = build_thread_marker(99, 7, 1); # new bug
like($marker, qr/Message-ID: <bug-99-7-8080\@https\.bugs\.example\.org>/,
'new thread with port: port relocated before @');

# Reply includes In-Reply-To pointing at the root message.
$marker = build_thread_marker(99, 7, 0); # reply
like($marker, qr/In-Reply-To: <bug-99-7-8080\@https\.bugs\.example\.org>/,
'reply thread: In-Reply-To uses correct sitespec');
Loading