Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1853138/Bug 1886954 - Switch to Email::Sender and Email::Address::XS #103

Merged
merged 9 commits into from
Mar 22, 2024
25 changes: 13 additions & 12 deletions Bugzilla/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,19 @@ sub update_params {
}

# Old mail_delivery_method choices contained no uppercase characters
if (exists $param->{'mail_delivery_method'}
&& $param->{'mail_delivery_method'} !~ /[A-Z]/)
{
my $method = $param->{'mail_delivery_method'};
my %translation = (
'sendmail' => 'Sendmail',
'smtp' => 'SMTP',
'qmail' => 'Qmail',
'testfile' => 'Test',
'none' => 'None'
);
$param->{'mail_delivery_method'} = $translation{$method};
my $mta = $param->{'mail_delivery_method'};
if ($mta) {
if ($mta !~ /[A-Z]/) {
my %translation = (
'sendmail' => 'Sendmail',
'smtp' => 'SMTP',
'qmail' => 'Qmail',
'testfile' => 'Test',
'none' => 'None');
$param->{'mail_delivery_method'} = $translation{$mta};
}
# This will force the parameter to be reset to its default value.
delete $param->{'mail_delivery_method'} if $param->{'mail_delivery_method'} eq 'Qmail';
}

# Convert the old "ssl" parameter to the new "ssl_redirect" parameter.
Expand Down
5 changes: 3 additions & 2 deletions Bugzilla/Config/Common.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;

use Email::Address;
use Email::Address::XS;
use Socket;

use Bugzilla::Util;
Expand Down Expand Up @@ -78,7 +78,8 @@ sub check_regexp {

sub check_email {
my ($value) = @_;
if ($value !~ $Email::Address::mailbox) {
my ($address) = Email::Address::XS->parse($value);
if (!$address->is_valid) {
return "must be a valid email address.";
}
return "";
Expand Down
19 changes: 3 additions & 16 deletions Bugzilla/Config/MTA.pm
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,15 @@ use warnings;

use Bugzilla::Config::Common;

# Return::Value 1.666002 pollutes the error log with warnings about this
# deprecated module. We have to set NO_CLUCK = 1 before loading Email::Send
# to disable these warnings.
BEGIN {
$Return::Value::NO_CLUCK = 1;
}
use Email::Send;

our $sortkey = 1200;

sub get_param_list {
my $class = shift;
my @param_list = (
{
name => 'mail_delivery_method',
type => 's',

# Bugzilla is not ready yet to send mails to newsgroups, and 'IO'
# is of no use for now as we already have our own 'Test' mode.
choices => [
grep { $_ ne 'NNTP' && $_ ne 'IO' } Email::Send->new()->all_mailers(), 'None'
],
name => 'mail_delivery_method',
type => 's',
choices => ['Sendmail', 'SMTP', 'Test', 'None'],
default => 'Sendmail',
checker => \&check_mail_delivery_method
},
Expand Down
3 changes: 0 additions & 3 deletions Bugzilla/Hook.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1099,9 +1099,6 @@ Params:

=item C<email> - The C<Email::MIME> object that's about to be sent.

=item C<mailer_args> - An arrayref that's passed as C<mailer_args> to
L<Email::Send/new>.

=back

=head2 object_before_create
Expand Down
61 changes: 27 additions & 34 deletions Bugzilla/Mailer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,16 @@ use Bugzilla::Util;

use Date::Format qw(time2str);

use Email::Address;
use Email::Address::XS;
use Email::MIME;
use Encode qw(encode);
use Encode::MIME::Header;
use List::MoreUtils qw(none);
use Try::Tiny;

# Return::Value 1.666002 pollutes the error log with warnings about this
# deprecated module. We have to set NO_CLUCK = 1 before loading Email::Send
# to disable these warnings.
BEGIN {
$Return::Value::NO_CLUCK = 1;
}
use Email::Send;
use Email::Sender::Simple qw(sendmail);
use Email::Sender::Transport::SMTP;
use Bugzilla::Sender::Transport::Sendmail;
use Sys::Hostname;
use Bugzilla::Version qw(vers_cmp);

Expand Down Expand Up @@ -119,22 +115,14 @@ sub MessageToMTA {

my $from = $email->header('From');

my ($hostname, @args);
my $mailer_class = $method;
my $hostname;
my $transport;
if ($method eq "Sendmail") {
$mailer_class = 'Bugzilla::Send::Sendmail';
if (ON_WINDOWS) {
$Email::Send::Sendmail::SENDMAIL = SENDMAIL_EXE;
$transport = Bugzilla::Sender::Transport::Sendmail->new({ sendmail => SENDMAIL_EXE });
}
push @args, "-i";

# We want to make sure that we pass *only* an email address.
if ($from) {
my ($email_obj) = Email::Address->parse($from);
if ($email_obj) {
my $from_email = $email_obj->address;
push(@args, "-f$from_email") if $from_email;
}
else {
$transport = Bugzilla::Sender::Transport::Sendmail->new();
}
}
else {
Expand All @@ -160,22 +148,26 @@ sub MessageToMTA {
}

if ($method eq "SMTP") {
push @args,
Host => Bugzilla->params->{"smtpserver"},
username => Bugzilla->params->{"smtp_username"},
password => Bugzilla->params->{"smtp_password"},
Hello => $hostname,
Debug => Bugzilla->params->{'smtp_debug'};
my ($host, $port) = split(/:/, Bugzilla->params->{'smtpserver'}, 2);
$transport = Email::Sender::Transport::SMTP->new({
host => $host,
defined($port) ? (port => $port) : (),
sasl_username => Bugzilla->params->{'smtp_username'},
sasl_password => Bugzilla->params->{'smtp_password'},
helo => $hostname,
ssl => Bugzilla->params->{'smtp_ssl'},
debug => Bugzilla->params->{'smtp_debug'}
});
}

Bugzilla::Hook::process('mailer_before_send',
{email => $email, mailer_args => \@args});
Bugzilla::Hook::process('mailer_before_send', {email => $email});

try {
my $to = $email->header('to') or die qq{Unable to find "To:" address\n};
my @recipients = Email::Address->parse($to);
my @recipients = Email::Address::XS->parse($to);
die qq{Unable to parse "To:" address - $to\n} unless @recipients;
die qq{Did not expect more than one "To:" address in $to\n} if @recipients > 1;
die qq{Invalid address in "To:" address - $to\n} if !$recipients[0]->is_valid;
my $recipient = $recipients[0];
my $badhosts = Bugzilla::Bloomfilter->lookup("badhosts");
if ($badhosts && $badhosts->test($recipient->host)) {
Expand Down Expand Up @@ -230,11 +222,12 @@ sub MessageToMTA {
close TESTFILE;
}
else {
# This is useful for both Sendmail and Qmail, so we put it out here.
# This is useful for Sendmail, so we put it out here.
local $ENV{PATH} = SENDMAIL_PATH;
my $mailer = Email::Send->new({mailer => $mailer_class, mailer_args => \@args});
my $retval = $mailer->send($email);
ThrowCodeError('mail_send_error', {msg => $retval, mail => $email}) if !$retval;
eval { sendmail($email, {transport => $transport}) };
if ($@) {
ThrowCodeError('mail_send_error', {msg => $@->message, mail => $email});
}
}

# insert into email_rates
Expand Down
8 changes: 4 additions & 4 deletions Bugzilla/Migrate/Gnats.pm
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use Bugzilla::Constants;
use Bugzilla::Install::Util qw(indicate_progress);
use Bugzilla::Util qw(format_time trim generate_random_password);

use Email::Address;
use Email::Address::XS;
use Email::MIME;
use File::Basename;
use IO::File;
Expand Down Expand Up @@ -396,10 +396,10 @@ sub _get_gnats_field_data {
if ($originator !~ Bugzilla->params->{emailregexp}) {

# We use the raw header sometimes, because it looks like "From: user"
# which Email::Address won't parse but we can still use.
# which Email::Address::XS won't parse but we can still use.
my $address = $email->header('From');
my ($parsed) = Email::Address->parse($address);
if ($parsed) {
my ($parsed) = Email::Address::XS->parse($address);
if ($parsed->is_valid) {
$address = $parsed->address;
}
if ($address) {
Expand Down
52 changes: 27 additions & 25 deletions Bugzilla/Send/Sendmail.pm → Bugzilla/Sender/Transport/Sendmail.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,55 @@
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::Send::Sendmail;
package Bugzilla::Sender::Transport::Sendmail;

use 5.10.1;
use strict;
use warnings;

use base qw(Email::Send::Sendmail);

use Return::Value;
use Symbol qw(gensym);
use parent qw(Email::Sender::Transport::Sendmail);

sub send {
my ($class, $message, @args) = @_;
my $mailer = $class->_find_sendmail;
use Email::Sender::Failure;

return failure "Couldn't find 'sendmail' executable in your PATH"
. " and Email::Send::Sendmail::SENDMAIL is not set"
unless $mailer;
sub send_email {
my ($self, $email, $envelope) = @_;

return failure "Found $mailer but cannot execute it" unless -x $mailer;
my $pipe = $self->_sendmail_pipe($envelope);

local $SIG{'CHLD'} = 'DEFAULT';
my $string = $email->as_string;
$string =~ s/\x0D\x0A/\x0A/g unless $^O eq 'MSWin32';

my $pipe = gensym;
print $pipe $string
or Email::Sender::Failure->throw("couldn't send message to sendmail: $!");

open($pipe, '|-', "$mailer -t -oi @args")
|| return failure "Error executing $mailer: $!";
print($pipe $message->as_string)
|| return failure "Error printing via pipe to $mailer: $!";
unless (close $pipe) {
return failure "error when closing pipe to $mailer: $!" if $!;
Email::Sender::Failure->throw("error when closing pipe to sendmail: $!") if $!;
my ($error_message, $is_transient) = _map_exitcode($? >> 8);
if (Bugzilla->get_param_with_override('use_mailer_queue')) {

# Return success for errors which are fatal so Bugzilla knows to
# remove them from the queue
if ($is_transient) {
return failure "error when closing pipe to $mailer: $error_message";
Email::Sender::Failure->throw(
"error when closing pipe to sendmail: $error_message");
}
else {
warn "error when closing pipe to $mailer: $error_message\n";
return success;
warn "error when closing pipe to sendmail: $error_message\n";
return $self->success;
}
}
else {
return failure "error when closing pipe to $mailer: $error_message";
Email::Sender::Failure->throw(
"error when closing pipe to sendmail: $error_message");
}
}
return success;
return $self->success;
}

sub _map_exitcode {

# Returns (error message, is_transient)
# from the sendmail source (sendmail/sysexit.h)
# from the sendmail source (sendmail/sysexits.h)
my $code = shift;
if ($code == 64) {
return ("Command line usage error (EX_USAGE)", 1);
Expand Down Expand Up @@ -112,3 +107,10 @@ sub _map_exitcode {

1;

=head1 B<Methods in need of POD>

=over

=item send_email

=back
29 changes: 15 additions & 14 deletions Bugzilla/Util.pm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use Date::Parse;
use DateTime::TimeZone;
use DateTime;
use Digest;
use Email::Address;
use Email::Address::XS;
use Encode qw(encode decode resolve_alias);
use Encode::Guess;
use English qw(-no_match_vars $EGID);
Expand Down Expand Up @@ -227,13 +227,19 @@ sub html_light_quote {
sub email_filter {
my ($toencode) = @_;
if (!Bugzilla->user->id) {
my @emails = Email::Address->parse($toencode);
if (scalar @emails) {
my @hosts = map { quotemeta($_->host) } @emails;
my $hosts_re = join('|', @hosts);
$toencode =~ s/\@(?:$hosts_re)//g;
return $toencode;
my $email_re = qr/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/;
my @hosts;
while ($toencode =~ /$email_re/g) {
my @emails = Email::Address::XS->parse($1);
if (scalar @emails) {
my @these_hosts = map { quotemeta($_->host) } @emails;
push @hosts, @these_hosts;
}
}
my $hosts_re = join('|', @hosts);

$toencode =~ s/\@(?:$hosts_re)//g;
return $toencode;
}
return $toencode;
}
Expand Down Expand Up @@ -740,15 +746,10 @@ sub validate_email_syntax {
my $match = Bugzilla->params->{'emailregexp'};
my $email = $addr . Bugzilla->params->{'emailsuffix'};

# This regexp follows RFC 2822 section 3.4.1.
my $addr_spec = $Email::Address::addr_spec;

# RFC 2822 section 2.1 specifies that email addresses must
# be made of US-ASCII characters only.
# Email::Address::addr_spec doesn't enforce this.
my $address = Email::Address::XS->parse_bare_address($email);
if ( $addr =~ /$match/
&& $address->is_valid
&& $email !~ /\P{ASCII}/
&& $email =~ /^$addr_spec$/
&& length($email) <= 127)
{
return 1;
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM bugzilla/bugzilla-perl-slim:20240320.1
FROM bugzilla/bugzilla-perl-slim:20240322.1

ENV DEBIAN_FRONTEND noninteractive

Expand Down
3 changes: 2 additions & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ my %requires = (
'Devel::NYTProf' => '6.04',
'Digest::SHA' => '5.47',
'EV' => '4.0',
'Email::Address::XS' => '1.05',
'Email::MIME' => '1.904',
'Email::Send' => '1.911',
'Email::Sender' => '2.600',
mrenvoize marked this conversation as resolved.
Show resolved Hide resolved
'FFI::Platypus' => 0,
'Future' => '0.34',
'HTML::Escape' => '1.10',
Expand Down
2 changes: 2 additions & 0 deletions RELEASE_BLOCKERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ their own bugfixes to those modules, but that’s not something we want to do
upstream. Version 5.0 rewrote the email code to use currently-supported Perl
modules. That needs to be ported into Harmony.

**[COMPLETED]**

# Postgresql Compatibility

We suspect, but don’t know for certain, that BMO may have moved to using
Expand Down
Loading
Loading