зеркало из https://github.com/mozilla/pjs.git
Bug 353711: Move to Email:: modules for email sending
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=glob, a=myk
This commit is contained in:
Родитель
65fe77575d
Коммит
5fd2bf7cb8
|
@ -81,6 +81,9 @@ sub init_page {
|
||||||
|
|
||||||
# Some environment variables are not taint safe
|
# Some environment variables are not taint safe
|
||||||
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
||||||
|
# Some modules throw undefined errors (notably File::Spec::Win32) if
|
||||||
|
# PATH is undefined.
|
||||||
|
$ENV{'PATH'} = '';
|
||||||
|
|
||||||
# If Bugzilla is shut down, do not allow anything to run, just display a
|
# If Bugzilla is shut down, do not allow anything to run, just display a
|
||||||
# message to the user about the downtime and log out. Scripts listed in
|
# message to the user about the downtime and log out. Scripts listed in
|
||||||
|
|
|
@ -171,6 +171,19 @@ sub update_params {
|
||||||
delete $param->{'enablequips'};
|
delete $param->{'enablequips'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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};
|
||||||
|
}
|
||||||
|
|
||||||
# --- DEFAULTS FOR NEW PARAMS ---
|
# --- DEFAULTS FOR NEW PARAMS ---
|
||||||
|
|
||||||
_load_params unless %params;
|
_load_params unless %params;
|
||||||
|
@ -216,7 +229,7 @@ sub update_params {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ON_WINDOWS && !-e SENDMAIL_EXE
|
if (ON_WINDOWS && !-e SENDMAIL_EXE
|
||||||
&& $param->{'mail_delivery_method'} eq 'sendmail')
|
&& $param->{'mail_delivery_method'} eq 'Sendmail')
|
||||||
{
|
{
|
||||||
my $smtp = $answer->{'SMTP_SERVER'};
|
my $smtp = $answer->{'SMTP_SERVER'};
|
||||||
if (!$smtp) {
|
if (!$smtp) {
|
||||||
|
@ -233,7 +246,7 @@ sub update_params {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$param->{'mail_delivery_method'} = 'smtp';
|
$param->{'mail_delivery_method'} = 'SMTP';
|
||||||
}
|
}
|
||||||
|
|
||||||
write_params($param);
|
write_params($param);
|
||||||
|
|
|
@ -34,6 +34,7 @@ package Bugzilla::Config::MTA;
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
use Bugzilla::Config::Common;
|
use Bugzilla::Config::Common;
|
||||||
|
use Email::Send;
|
||||||
|
|
||||||
$Bugzilla::Config::MTA::sortkey = "10";
|
$Bugzilla::Config::MTA::sortkey = "10";
|
||||||
|
|
||||||
|
@ -43,10 +44,8 @@ sub get_param_list {
|
||||||
{
|
{
|
||||||
name => 'mail_delivery_method',
|
name => 'mail_delivery_method',
|
||||||
type => 's',
|
type => 's',
|
||||||
choices => $^O =~ /MSWin32/i
|
choices => [Email::Send->new()->all_mailers(), 'None'],
|
||||||
? ['smtp', 'testfile', 'sendmail', 'none']
|
default => 'Sendmail',
|
||||||
: ['sendmail', 'smtp', 'qmail', 'testfile', 'none'],
|
|
||||||
default => 'sendmail',
|
|
||||||
checker => \&check_mail_delivery_method
|
checker => \&check_mail_delivery_method
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ use File::Basename;
|
||||||
ADMIN_GROUP_NAME
|
ADMIN_GROUP_NAME
|
||||||
|
|
||||||
SENDMAIL_EXE
|
SENDMAIL_EXE
|
||||||
|
SENDMAIL_PATH
|
||||||
|
|
||||||
FIELD_TYPE_UNKNOWN
|
FIELD_TYPE_UNKNOWN
|
||||||
FIELD_TYPE_FREETEXT
|
FIELD_TYPE_FREETEXT
|
||||||
|
@ -290,6 +291,8 @@ use constant ADMIN_GROUP_NAME => 'admin';
|
||||||
|
|
||||||
# Path to sendmail.exe (Windows only)
|
# Path to sendmail.exe (Windows only)
|
||||||
use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
|
use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
|
||||||
|
# Paths to search for the sendmail binary (non-Windows)
|
||||||
|
use constant SENDMAIL_PATH => '/usr/lib:/usr/sbin:/usr/ucblib';
|
||||||
|
|
||||||
# Field types. Match values in fielddefs.type column. These are purposely
|
# Field types. Match values in fielddefs.type column. These are purposely
|
||||||
# not named after database column types, since Bugzilla fields comprise not
|
# not named after database column types, since Bugzilla fields comprise not
|
||||||
|
|
|
@ -78,11 +78,6 @@ sub REQUIRED_MODULES {
|
||||||
module => 'Template',
|
module => 'Template',
|
||||||
version => '2.12'
|
version => '2.12'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
package => 'MailTools',
|
|
||||||
module => 'Mail::Mailer',
|
|
||||||
version => '1.67'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
package => 'MIME-Base64',
|
package => 'MIME-Base64',
|
||||||
module => 'MIME::Base64',
|
module => 'MIME::Base64',
|
||||||
|
@ -94,6 +89,17 @@ sub REQUIRED_MODULES {
|
||||||
module => ON_WINDOWS ? 'MIME::Tools' : 'MIME::Parser',
|
module => ON_WINDOWS ? 'MIME::Tools' : 'MIME::Parser',
|
||||||
version => '5.406'
|
version => '5.406'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
package => 'Email-Send',
|
||||||
|
module => 'Email::Send',
|
||||||
|
version => ON_WINDOWS ? '2.16' : '2.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# This will pull in Email::MIME for us, also.
|
||||||
|
package => 'Email-MIME-Modifier',
|
||||||
|
module => 'Email::MIME::Modifier',
|
||||||
|
version => 0
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
my $all_modules = _get_extension_requirements(
|
my $all_modules = _get_extension_requirements(
|
||||||
|
@ -186,15 +192,6 @@ sub OPTIONAL_MODULES {
|
||||||
},
|
},
|
||||||
|
|
||||||
# Inbound Email
|
# Inbound Email
|
||||||
{
|
|
||||||
# Email::MIME::Attachment::Stripper can throw an error with
|
|
||||||
# earlier versions.
|
|
||||||
# This also pulls in Email::MIME and Email::Address for us.
|
|
||||||
package => 'Email-MIME-Modifier',
|
|
||||||
module => 'Email::MIME::Modifier',
|
|
||||||
version => '1.43',
|
|
||||||
feature => 'Inbound Email'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
package => 'Email-MIME-Attachment-Stripper',
|
package => 'Email-MIME-Attachment-Stripper',
|
||||||
module => 'Email::MIME::Attachment::Stripper',
|
module => 'Email::MIME::Attachment::Stripper',
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
# Gervase Markham <gerv@gerv.net>
|
# Gervase Markham <gerv@gerv.net>
|
||||||
# Byron Jones <bugzilla@glob.com.au>
|
# Byron Jones <bugzilla@glob.com.au>
|
||||||
# Frédéric Buclin <LpSolit@gmail.com>
|
# Frédéric Buclin <LpSolit@gmail.com>
|
||||||
|
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||||
|
|
||||||
package Bugzilla::Mailer;
|
package Bugzilla::Mailer;
|
||||||
|
|
||||||
|
@ -37,181 +38,76 @@ use base qw(Exporter);
|
||||||
@Bugzilla::Mailer::EXPORT = qw(MessageToMTA);
|
@Bugzilla::Mailer::EXPORT = qw(MessageToMTA);
|
||||||
|
|
||||||
use Bugzilla::Constants;
|
use Bugzilla::Constants;
|
||||||
|
use Bugzilla::Error;
|
||||||
use Bugzilla::Util;
|
use Bugzilla::Util;
|
||||||
|
|
||||||
use Mail::Header;
|
use Encode qw(encode);
|
||||||
use Mail::Mailer;
|
use Email::MIME;
|
||||||
use Mail::Address;
|
# Loading this gives us encoding_set.
|
||||||
use MIME::Parser;
|
use Email::MIME::Modifier;
|
||||||
use MIME::QuotedPrint;
|
use Email::Send;
|
||||||
use MIME::Base64;
|
|
||||||
|
|
||||||
|
|
||||||
sub MessageToMTA {
|
sub MessageToMTA {
|
||||||
my ($msg) = (@_);
|
my ($msg) = (@_);
|
||||||
my $params = Bugzilla->params;
|
my $method = Bugzilla->params->{'mail_delivery_method'};
|
||||||
return if ($params->{'mail_delivery_method'} eq "none");
|
return if $method eq 'None';
|
||||||
|
|
||||||
my ($header, $body) = $msg =~ /(.*?\n)\n(.*)/s ? ($1, $2) : ('', $msg);
|
my $email = ref($msg) ? $msg : Email::MIME->new($msg);
|
||||||
my $headers;
|
foreach my $part ($email->parts) {
|
||||||
|
$part->charset_set('UTF-8') if Bugzilla->params->{'utf8'};
|
||||||
if ($params->{'utf8'}
|
$part->encoding_set('quoted-printable') if !is_7bit_clean($part->body);
|
||||||
and (!is_7bit_clean($header) or !is_7bit_clean($body)))
|
|
||||||
{
|
|
||||||
($headers, $body) = encode_message($msg);
|
|
||||||
} else {
|
|
||||||
my @header_lines = split(/\n/, $header);
|
|
||||||
$headers = new Mail::Header \@header_lines, Modify => 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Use trim to remove any whitespace (incl. newlines)
|
# Encode the headers correctly in quoted-printable
|
||||||
my $from = trim($headers->get('from'));
|
foreach my $header qw(From To Cc Reply-To Sender Errors-To Subject) {
|
||||||
|
if (my $value = $email->header($header)) {
|
||||||
if ($params->{"mail_delivery_method"} eq "sendmail" && $^O =~ /MSWin32/i) {
|
my $encoded = encode('MIME-Q', $value);
|
||||||
my $cmd = '|' . SENDMAIL_EXE . ' -t -i';
|
$email->header_set($header, $encoded);
|
||||||
if ($from) {
|
|
||||||
# We're on Windows, thus no danger of command injection
|
|
||||||
# via $from. In other words, it is safe to embed $from.
|
|
||||||
$cmd .= qq# -f"$from"#;
|
|
||||||
}
|
}
|
||||||
open(SENDMAIL, $cmd) ||
|
|
||||||
die "Failed to execute " . SENDMAIL_EXE . ": $!\n";
|
|
||||||
print SENDMAIL $headers->as_string;
|
|
||||||
print SENDMAIL "\n";
|
|
||||||
print SENDMAIL $body;
|
|
||||||
close SENDMAIL;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my @args;
|
my $from = $email->header('From');
|
||||||
if ($params->{"mail_delivery_method"} eq "sendmail") {
|
|
||||||
|
my ($hostname, @args);
|
||||||
|
if ($method eq "Sendmail") {
|
||||||
|
if (ON_WINDOWS) {
|
||||||
|
$Email::Send::Sendmail::SENDMAIL = SENDMAIL_EXE;
|
||||||
|
}
|
||||||
push @args, "-i";
|
push @args, "-i";
|
||||||
if ($from) {
|
push(@args, "-f$from") if $from;
|
||||||
push(@args, "-f$from");
|
push(@args, "-ODeliveryMode=deferred")
|
||||||
}
|
if !Bugzilla->params->{"sendmailnow"};
|
||||||
}
|
|
||||||
if ($params->{"mail_delivery_method"} eq "sendmail"
|
|
||||||
&& !$params->{"sendmailnow"})
|
|
||||||
{
|
|
||||||
push @args, "-ODeliveryMode=deferred";
|
|
||||||
}
|
|
||||||
if ($params->{"mail_delivery_method"} eq "smtp") {
|
|
||||||
push @args, Server => $params->{"smtpserver"};
|
|
||||||
if ($from) {
|
|
||||||
$ENV{'MAILADDRESS'} = $from;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
my $mailer = new Mail::Mailer($params->{"mail_delivery_method"}, @args);
|
|
||||||
if ($params->{"mail_delivery_method"} eq "testfile") {
|
|
||||||
$Mail::Mailer::testfile::config{outfile} =
|
|
||||||
bz_locations()->{'datadir'} . '/mailer.testfile';
|
|
||||||
}
|
|
||||||
|
|
||||||
$mailer->open($headers->header_hashref);
|
|
||||||
print $mailer $body;
|
|
||||||
$mailer->close;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub encode_message {
|
|
||||||
my ($msg) = @_;
|
|
||||||
|
|
||||||
my $parser = MIME::Parser->new;
|
|
||||||
$parser->output_to_core(1);
|
|
||||||
$parser->tmp_to_core(1);
|
|
||||||
my $entity = $parser->parse_data($msg);
|
|
||||||
$entity = encode_message_entity($entity);
|
|
||||||
|
|
||||||
my @header_lines = split(/\n/, $entity->header_as_string);
|
|
||||||
my $head = new Mail::Header \@header_lines, Modify => 0;
|
|
||||||
|
|
||||||
my $body = $entity->body_as_string;
|
|
||||||
|
|
||||||
return ($head, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub encode_message_entity {
|
|
||||||
my ($entity) = @_;
|
|
||||||
|
|
||||||
my $head = $entity->head;
|
|
||||||
|
|
||||||
# encode the subject
|
|
||||||
|
|
||||||
my $subject = $head->get('subject');
|
|
||||||
if (defined $subject && !is_7bit_clean($subject)) {
|
|
||||||
$subject =~ s/[\r\n]+$//;
|
|
||||||
$head->replace('subject', encode_qp_words($subject));
|
|
||||||
}
|
|
||||||
|
|
||||||
# encode addresses
|
|
||||||
|
|
||||||
foreach my $field (qw(from to cc reply-to sender errors-to)) {
|
|
||||||
my $high = $head->count($field) - 1;
|
|
||||||
foreach my $index (0..$high) {
|
|
||||||
my $value = $head->get($field, $index);
|
|
||||||
my @addresses;
|
|
||||||
my $changed = 0;
|
|
||||||
foreach my $addr (Mail::Address->parse($value)) {
|
|
||||||
my $phrase = $addr->phrase;
|
|
||||||
if (is_7bit_clean($phrase)) {
|
|
||||||
push @addresses, $addr->format;
|
|
||||||
} else {
|
|
||||||
push @addresses, encode_qp_phrase($phrase) .
|
|
||||||
' <' . $addr->address . '>';
|
|
||||||
$changed = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$changed && $head->replace($field, join(', ', @addresses), $index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# process the body
|
|
||||||
|
|
||||||
if (scalar($entity->parts)) {
|
|
||||||
my $newparts = [];
|
|
||||||
foreach my $part ($entity->parts) {
|
|
||||||
my $newpart = encode_message_entity($part);
|
|
||||||
push @$newparts, $newpart;
|
|
||||||
}
|
|
||||||
$entity->parts($newparts);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# Extract the body from the entity, for examination
|
# Sendmail will automatically append our hostname to the From
|
||||||
# At this point, we can rely on MIME::Tools to do our encoding for us!
|
# address, but other mailers won't.
|
||||||
my $bodyhandle = $entity->bodyhandle;
|
my $urlbase = Bugzilla->params->{'urlbase'};
|
||||||
my $body = $bodyhandle->as_string;
|
$urlbase =~ m|//([^/]+)/?|;
|
||||||
if (!is_7bit_clean($body)) {
|
$hostname = $1;
|
||||||
# count number of 7-bit chars, and use quoted-printable if more
|
$from .= "\@$hostname" if $from !~ /@/;
|
||||||
# than half the message is 7-bit clean
|
$email->header_set('From', $from);
|
||||||
my $count = ($body =~ tr/\x20-\x7E\x0A\x0D//);
|
|
||||||
if ($count > length($body) / 2) {
|
|
||||||
$head->mime_attr('Content-Transfer-Encoding' => 'quoted-printable');
|
|
||||||
} else {
|
|
||||||
$head->mime_attr('Content-Transfer-Encoding' => 'base64');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set the content/type and charset of the part, if not set
|
if ($method eq "SMTP") {
|
||||||
$head->mime_attr('Content-Type' => 'text/plain')
|
push @args, Host => Bugzilla->params->{"smtpserver"},
|
||||||
unless defined $head->mime_attr('content-type');
|
Hello => $hostname;
|
||||||
$head->mime_attr('Content-Type.charset' => 'UTF-8');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$head->mime_attr('MIME-Version' => '1.0');
|
if ($method eq "Test") {
|
||||||
$head->fold(75);
|
my $filename = bz_locations()->{'datadir'} . '/mailer.testfile';
|
||||||
return $entity;
|
open TESTFILE, '>>', $filename;
|
||||||
}
|
print TESTFILE "\n\n---\n\n" . $email->as_string;
|
||||||
|
close TESTFILE;
|
||||||
sub encode_qp_words {
|
|
||||||
my ($line) = (@_);
|
|
||||||
my @encoded;
|
|
||||||
foreach my $word (split / /, $line) {
|
|
||||||
if (!is_7bit_clean($word)) {
|
|
||||||
push @encoded, '=?UTF-8?Q?_' . encode_qp($word, '') . '?=';
|
|
||||||
} else {
|
|
||||||
push @encoded, $word;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
# This is useful for both Sendmail and Qmail, so we put it out here.
|
||||||
|
local $ENV{PATH} = SENDMAIL_PATH;
|
||||||
|
my $mailer = Email::Send->new({ mailer => $method,
|
||||||
|
mailer_args => \@args });
|
||||||
|
my $retval = $mailer->send($email);
|
||||||
|
ThrowCodeError('mail_send_error', { msg => $retval, mail => $email })
|
||||||
|
if !$retval;
|
||||||
}
|
}
|
||||||
return join(' ', @encoded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -28,14 +28,13 @@
|
||||||
mail_delivery_method => "Defines how email is sent, or if it is sent at all.<br>
|
mail_delivery_method => "Defines how email is sent, or if it is sent at all.<br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
'sendmail', 'smtp' and 'qmail' are all MTAs.
|
'Sendmail', 'SMTP' and 'Qmail' are all MTAs.
|
||||||
You need to install a third-party sendmail replacement if
|
You need to install a third-party sendmail replacement if
|
||||||
you want to use sendmail on Windows.
|
you want to use sendmail on Windows.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
'testfile' is useful for debugging: all email is stored
|
'Test' is useful for debugging: all email is stored
|
||||||
in 'data/mailer.testfile' instead of being sent. For more
|
in 'data/mailer.testfile' instead of being sent.
|
||||||
information, see the Mail::Mailer manual.
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
'none' will completely disable email. $terms.Bugzilla continues
|
'none' will completely disable email. $terms.Bugzilla continues
|
||||||
|
|
|
@ -295,6 +295,11 @@
|
||||||
[% ELSIF error == "ldap_server_not_defined" %]
|
[% ELSIF error == "ldap_server_not_defined" %]
|
||||||
The LDAP server for authentication has not been defined.
|
The LDAP server for authentication has not been defined.
|
||||||
|
|
||||||
|
[% ELSIF error == "mail_send_error" %]
|
||||||
|
There was an error sending mail from '[% mail.header('From') FILTER html %]'
|
||||||
|
to '[% mail.header('To') FILTER html %]':
|
||||||
|
[% msg FILTER html %]
|
||||||
|
|
||||||
[% ELSIF error == "missing_bug_id" %]
|
[% ELSIF error == "missing_bug_id" %]
|
||||||
No [% terms.bug %] ID was given.
|
No [% terms.bug %] ID was given.
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче