summaryrefslogtreecommitdiffstats
path: root/Bugzilla
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla')
-rw-r--r--Bugzilla/Config/General.pm8
-rw-r--r--Bugzilla/Constants.pm4
-rw-r--r--Bugzilla/Elastic/Role/HasClient.pm2
-rw-r--r--Bugzilla/Error/disabled0
-rw-r--r--Bugzilla/Group.pm29
-rw-r--r--Bugzilla/Install.pm40
-rw-r--r--Bugzilla/Install/DB.pm11
-rw-r--r--Bugzilla/Install/Filesystem.pm8
-rw-r--r--Bugzilla/PSGI.pm42
-rw-r--r--Bugzilla/Quantum/Plugin/BasicAuth.pm40
-rw-r--r--Bugzilla/Quantum/Plugin/Hostage.pm113
-rw-r--r--Bugzilla/Quantum/SES.pm414
-rw-r--r--Bugzilla/Search.pm26
-rw-r--r--Bugzilla/Template.pm2
14 files changed, 423 insertions, 316 deletions
diff --git a/Bugzilla/Config/General.pm b/Bugzilla/Config/General.pm
index c870c7376..fa7cf2d08 100644
--- a/Bugzilla/Config/General.pm
+++ b/Bugzilla/Config/General.pm
@@ -25,6 +25,14 @@ use constant get_param_list => (
},
{
+ name => 'nobody_user',
+ type => 't',
+ no_reset => '1',
+ default => 'nobody@mozilla.org',
+ checker => \&check_email
+ },
+
+ {
name => 'docs_urlbase',
type => 't',
default => 'docs/%lang%/html/',
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm
index d0b74b5e3..cd478c33e 100644
--- a/Bugzilla/Constants.pm
+++ b/Bugzilla/Constants.pm
@@ -196,6 +196,8 @@ use Memoize;
EMAIL_LIMIT_EXCEPTION
JOB_QUEUE_VIEW_MAX_JOBS
+
+ BZ_PERSISTENT
);
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
@@ -700,6 +702,8 @@ sub _bz_locations {
};
}
+use constant BZ_PERSISTENT => $main::BUGZILLA_PERSISTENT;
+
# This makes us not re-compute all the bz_locations data every time it's
# called.
BEGIN { memoize('_bz_locations') };
diff --git a/Bugzilla/Elastic/Role/HasClient.pm b/Bugzilla/Elastic/Role/HasClient.pm
index 8e2687880..a971392e0 100644
--- a/Bugzilla/Elastic/Role/HasClient.pm
+++ b/Bugzilla/Elastic/Role/HasClient.pm
@@ -8,7 +8,6 @@ package Bugzilla::Elastic::Role::HasClient;
use 5.10.1;
use Moo::Role;
-use Search::Elasticsearch;
has 'client' => (is => 'lazy');
@@ -16,6 +15,7 @@ has 'client' => (is => 'lazy');
sub _build_client {
my ($self) = @_;
+ require Search::Elasticsearch;
return Search::Elasticsearch->new(
nodes => [ split(/\s+/, Bugzilla->params->{elasticsearch_nodes}) ],
cxn_pool => 'Sniff',
diff --git a/Bugzilla/Error/disabled b/Bugzilla/Error/disabled
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Bugzilla/Error/disabled
diff --git a/Bugzilla/Group.pm b/Bugzilla/Group.pm
index c941482f0..7f684ea15 100644
--- a/Bugzilla/Group.pm
+++ b/Bugzilla/Group.pm
@@ -26,17 +26,24 @@ use Scalar::Util qw(blessed);
use constant IS_CONFIG => 1;
-use constant DB_COLUMNS => qw(
- groups.id
- groups.name
- groups.description
- groups.isbuggroup
- groups.userregexp
- groups.isactive
- groups.icon_url
- groups.owner_user_id
- groups.idle_member_removal
-);
+sub DB_COLUMNS {
+ my $class = shift;
+ my @columns = qw(
+ id
+ name
+ description
+ isbuggroup
+ userregexp
+ isactive
+ icon_url
+ owner_user_id
+ idle_member_removal
+ );
+ my $dbh = Bugzilla->dbh;
+ my $table = $class->DB_TABLE;
+
+ return map { "$table.$_" } grep { $dbh->bz_column_info($table, $_) } @columns;
+}
use constant DB_TABLE => 'groups';
diff --git a/Bugzilla/Install.pm b/Bugzilla/Install.pm
index 8bce9b5e7..c9935f34d 100644
--- a/Bugzilla/Install.pm
+++ b/Bugzilla/Install.pm
@@ -31,21 +31,31 @@ use Bugzilla::Util qw(get_text);
use Bugzilla::Version;
use constant STATUS_WORKFLOW => (
- [undef, 'UNCONFIRMED'],
- [undef, 'CONFIRMED'],
- [undef, 'IN_PROGRESS'],
- ['UNCONFIRMED', 'CONFIRMED'],
- ['UNCONFIRMED', 'IN_PROGRESS'],
- ['UNCONFIRMED', 'RESOLVED'],
- ['CONFIRMED', 'IN_PROGRESS'],
- ['CONFIRMED', 'RESOLVED'],
- ['IN_PROGRESS', 'CONFIRMED'],
- ['IN_PROGRESS', 'RESOLVED'],
- ['RESOLVED', 'UNCONFIRMED'],
- ['RESOLVED', 'CONFIRMED'],
- ['RESOLVED', 'VERIFIED'],
- ['VERIFIED', 'UNCONFIRMED'],
- ['VERIFIED', 'CONFIRMED'],
+ [ undef, 'UNCONFIRMED' ],
+ [ undef, 'NEW' ],
+ [ undef, 'ASSIGNED' ],
+ [ 'UNCONFIRMED', 'NEW' ],
+ [ 'UNCONFIRMED', 'ASSIGNED' ],
+ [ 'UNCONFIRMED', 'RESOLVED' ],
+ [ 'NEW', 'UNCONFIRMED' ],
+ [ 'NEW', 'ASSIGNED' ],
+ [ 'NEW', 'RESOLVED' ],
+ [ 'ASSIGNED', 'UNCONFIRMED' ],
+ [ 'ASSIGNED', 'NEW' ],
+ [ 'ASSIGNED', 'RESOLVED' ],
+ [ 'REOPENED', 'UNCONFIRMED' ],
+ [ 'REOPENED', 'NEW' ],
+ [ 'REOPENED', 'ASSIGNED' ],
+ [ 'REOPENED', 'RESOLVED' ],
+ [ 'RESOLVED', 'UNCONFIRMED' ],
+ [ 'RESOLVED', 'REOPENED' ],
+ [ 'RESOLVED', 'VERIFIED' ],
+ [ 'VERIFIED', 'UNCONFIRMED' ],
+ [ 'VERIFIED', 'REOPENED' ],
+ [ 'VERIFIED', 'RESOLVED' ],
+ [ 'CLOSED', 'UNCONFIRMED' ],
+ [ 'CLOSED', 'REOPENED' ],
+ [ 'CLOSED', 'RESOLVED' ],
);
sub SETTINGS {
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index 2e5ae5ff2..8b3d4b8cc 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -3908,7 +3908,16 @@ sub _migrate_group_owners {
my $dbh = Bugzilla->dbh;
return if $dbh->bz_column_info('groups', 'owner_user_id');
$dbh->bz_add_column('groups', 'owner_user_id', {TYPE => 'INT3'});
- my $nobody = Bugzilla::User->check('nobody@mozilla.org');
+ my $nobody = Bugzilla::User->new({ name => Bugzilla->params->{'nobody_user'}, cache => 1 });
+ unless ($nobody) {
+ $nobody = Bugzilla::User->create(
+ {
+ login_name => Bugzilla->params->{'nobody_user'},
+ realname => 'Nobody (ok to assign bugs to)',
+ cryptpassword => '*',
+ }
+ );
+ }
$dbh->do('UPDATE groups SET owner_user_id = ?', undef, $nobody->id);
}
diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm
index 317152962..cb1b1ad15 100644
--- a/Bugzilla/Install/Filesystem.pm
+++ b/Bugzilla/Install/Filesystem.pm
@@ -164,6 +164,7 @@ sub FILESYSTEM {
# users to be able to cron them or otherwise run
# them as a secure user, like the webserver owner.
'*.cgi' => { perms => WS_EXECUTE },
+ '*.psgi' => { perms => CGI_READ },
'whineatnews.pl' => { perms => WS_EXECUTE },
'collectstats.pl' => { perms => WS_EXECUTE },
'importxml.pl' => { perms => WS_EXECUTE },
@@ -295,7 +296,7 @@ sub FILESYSTEM {
'contrib' => { files => OWNER_EXECUTE,
dirs => DIR_OWNER_WRITE, },
'scripts' => { files => OWNER_EXECUTE,
- dirs => DIR_OWNER_WRITE, },
+ dirs => DIR_WS_SERVE, },
);
# --- FILES TO CREATE --- #
@@ -379,6 +380,9 @@ sub FILESYSTEM {
contents => $yui3_all_css },
);
+ # Create static error pages.
+ $create_dirs{"errors"} = DIR_CGI_READ;
+
# Because checksetup controls the creation of index.html separately
# from all other files, it gets its very own hash.
my %index_html = (
@@ -464,7 +468,7 @@ sub update_filesystem {
# Delete old files that no longer need to exist
# 2001-04-29 jake@bugzilla.org - Remove oldemailtech
- # http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
+ # http://bugzilla.mozilla.org/show_bug.cgi?id=71552
if (-d 'shadow') {
print "Removing shadow directory...\n";
rmtree("shadow");
diff --git a/Bugzilla/PSGI.pm b/Bugzilla/PSGI.pm
new file mode 100644
index 000000000..46352b319
--- /dev/null
+++ b/Bugzilla/PSGI.pm
@@ -0,0 +1,42 @@
+# 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.
+
+package Bugzilla::PSGI;
+use 5.10.1;
+use strict;
+use warnings;
+
+use base qw(Exporter);
+
+use Bugzilla::Logging;
+our @EXPORT_OK = qw(compile_cgi);
+
+sub compile_cgi {
+ my ($script) = @_;
+ require CGI::Compile;
+ require CGI::Emulate::PSGI;
+
+ my $cgi = CGI::Compile->compile($script);
+ my $app = CGI::Emulate::PSGI->handler(
+ sub {
+ Bugzilla::init_page();
+ $cgi->();
+ }
+ );
+ return sub {
+ my $env = shift;
+ if ($env->{'psgix.cleanup'}) {
+ push @{ $env->{'psgix.cleanup.handler'} }, \&Bugzilla::_cleanup;
+ }
+ my $res = $app->($env);
+ Bugzilla::_cleanup() if not $env->{'psgix.cleanup'};
+ return $res;
+ };
+}
+
+
+1; \ No newline at end of file
diff --git a/Bugzilla/Quantum/Plugin/BasicAuth.pm b/Bugzilla/Quantum/Plugin/BasicAuth.pm
new file mode 100644
index 000000000..e17273404
--- /dev/null
+++ b/Bugzilla/Quantum/Plugin/BasicAuth.pm
@@ -0,0 +1,40 @@
+# 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.
+package Bugzilla::Quantum::Plugin::BasicAuth;
+use 5.10.1;
+use Mojo::Base qw(Mojolicious::Plugin);
+
+use Bugzilla::Logging;
+use Carp;
+
+sub register {
+ my ( $self, $app, $conf ) = @_;
+
+ $app->renderer->add_helper(
+ basic_auth => sub {
+ my ( $c, $realm, $auth_user, $auth_pass ) = @_;
+ my $req = $c->req;
+ my ( $user, $password ) = $req->url->to_abs->userinfo =~ /^([^:]+):(.*)/;
+
+ unless ( $realm && $auth_user && $auth_pass ) {
+ croak 'basic_auth() called with missing parameters.';
+ }
+
+ unless ( $user eq $auth_user && $password eq $auth_pass ) {
+ WARN('username and password do not match');
+ $c->res->headers->www_authenticate("Basic realm=\"$realm\"");
+ $c->res->code(401);
+ $c->rendered;
+ return 0;
+ }
+
+ return 1;
+ }
+ );
+}
+
+1; \ No newline at end of file
diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm
index 63fad2be2..418b09a0c 100644
--- a/Bugzilla/Quantum/Plugin/Hostage.pm
+++ b/Bugzilla/Quantum/Plugin/Hostage.pm
@@ -1,85 +1,80 @@
package Bugzilla::Quantum::Plugin::Hostage;
use 5.10.1;
use Mojo::Base 'Mojolicious::Plugin';
-use Bugzilla::Logging;
sub _attachment_root {
- my ($base) = @_;
- return undef unless $base;
- return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} ? $1 : undef;
+ my ($base) = @_;
+ return undef unless $base;
+ return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)}
+ ? $1
+ : undef;
}
sub _attachment_host_regex {
- my ($base) = @_;
- return undef unless $base;
- my $val = $base;
- $val =~ s{^https?://}{}s;
- $val =~ s{/$}{}s;
- my $regex = quotemeta $val;
- $regex =~ s/\\\%bugid\\\%/\\d+/g;
- return qr/^$regex$/s;
+ my ($base) = @_;
+ return undef unless $base;
+ my $val = $base;
+ $val =~ s{^https?://}{}s;
+ $val =~ s{/$}{}s;
+ my $regex = quotemeta $val;
+ $regex =~ s/\\\%bugid\\\%/\\d+/g;
+ return qr/^$regex$/s;
}
sub register {
- my ($self, $app, $conf) = @_;
+ my ( $self, $app, $conf ) = @_;
- $app->hook(before_routes => \&_before_routes);
+ $app->hook(before_routes => \&_before_routes);
}
sub _before_routes {
- my ($c) = @_;
- state $urlbase = Bugzilla->localconfig->{urlbase};
- state $urlbase_uri = URI->new($urlbase);
- state $urlbase_host = $urlbase_uri->host;
- state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/;
- state $attachment_base = Bugzilla->localconfig->{attachment_base};
- state $attachment_root = _attachment_root($attachment_base);
- state $attachment_host_regex = _attachment_host_regex($attachment_base);
+ my ( $c ) = @_;
+ state $urlbase = Bugzilla->localconfig->{urlbase};
+ state $urlbase_uri = URI->new($urlbase);
+ state $urlbase_host = $urlbase_uri->host;
+ state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/;
+ state $attachment_base = Bugzilla->localconfig->{attachment_base};
+ state $attachment_root = _attachment_root($attachment_base);
+ state $attachment_host_regex = _attachment_host_regex($attachment_base);
- my $stash = $c->stash;
- my $req = $c->req;
- my $url = $req->url->to_abs;
+ my $stash = $c->stash;
+ my $req = $c->req;
+ my $url = $req->url->to_abs;
- return if $stash->{'mojo.static'};
+ return if $stash->{'mojo.static'};
- my $hostname = $url->host;
- return if $hostname eq $urlbase_host;
+ my $hostname = $url->host;
+ return if $hostname eq $urlbase_host;
- my $path = $url->path;
- return if $path eq '/__lbheartbeat__';
+ my $path = $url->path;
+ return if $path eq '/__lbheartbeat__';
- if ($attachment_base && $hostname eq $attachment_root) {
- DEBUG("redirecting to $urlbase because $hostname is $attachment_root");
- $c->redirect_to($urlbase);
- return;
- }
- elsif ($attachment_base && $hostname =~ $attachment_host_regex) {
- if ($path =~ m{^/attachment\.cgi}s) {
- return;
+ if ($attachment_base && $hostname eq $attachment_root) {
+ $c->redirect_to($urlbase);
+ return;
+ }
+ elsif ($attachment_base && $hostname =~ $attachment_host_regex) {
+ if ($path =~ m{^/attachment\.cgi}s) {
+ return;
+ } else {
+ my $new_uri = $url->clone;
+ $new_uri->scheme($urlbase_uri->scheme);
+ $new_uri->host($urlbase_host);
+ $c->redirect_to($new_uri);
+ return;
+ }
+ }
+ elsif (my ($id) = $hostname =~ $urlbase_host_regex) {
+ my $new_uri = $urlbase_uri->clone;
+ $new_uri->path('/show_bug.cgi');
+ $new_uri->query_form(id => $id);
+ $c->redirect_to($new_uri);
+ return;
}
else {
- my $new_uri = $url->clone;
- $new_uri->scheme($urlbase_uri->scheme);
- $new_uri->host($urlbase_host);
- DEBUG(
- "redirecting to $new_uri because $hostname matches attachment regex");
- $c->redirect_to($new_uri);
- return;
+ $c->redirect_to($urlbase);
+ return;
}
- }
- elsif (my ($id) = $hostname =~ $urlbase_host_regex) {
- my $new_uri = $urlbase_uri->clone;
- $new_uri->path('/show_bug.cgi');
- $new_uri->query_form(id => $id);
- DEBUG("redirecting to $new_uri because $hostname includes bug id");
- $c->redirect_to($new_uri);
- return;
- }
- else {
- DEBUG("redirecting to $urlbase because $hostname doesn't make sense");
- $c->redirect_to($urlbase);
- return;
- }
}
1;
diff --git a/Bugzilla/Quantum/SES.pm b/Bugzilla/Quantum/SES.pm
index 2d19de240..03916075d 100644
--- a/Bugzilla/Quantum/SES.pm
+++ b/Bugzilla/Quantum/SES.pm
@@ -1,5 +1,4 @@
package Bugzilla::Quantum::SES;
-
# 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/.
@@ -19,252 +18,237 @@ use JSON::MaybeXS qw(decode_json);
use LWP::UserAgent ();
use Try::Tiny qw(catch try);
-use Type::Library -base, -declare => qw(
- Self
- Notification NotificationType TypeField
- BounceNotification BouncedRecipients
- ComplaintNotification ComplainedRecipients
-);
-use Type::Utils -all;
-use Types::Standard -all;
-use Type::Params qw(compile);
-
-class_type Self, {class => __PACKAGE__};
-
-declare ComplainedRecipients,
- as ArrayRef [Dict [emailAddress => Str, slurpy Any]];
-declare ComplaintNotification,
- as Dict [
- complaint => Dict [
- complainedRecipients => ComplainedRecipients,
- complaintFeedbackType => Str,
- slurpy Any,
- ],
- slurpy Any,
- ];
-
-declare BouncedRecipients,
- as ArrayRef [
- Dict [
- emailAddress => Str,
- action => Optional [Str],
- diagnosticCode => Optional [Str],
- status => Optional [Str],
- slurpy Any,
- ],
- ];
-declare BounceNotification,
- as Dict [
- bounce => Dict [
- bouncedRecipients => BouncedRecipients,
- reportingMTA => Str,
- bounceSubType => Str,
- bounceType => Str,
- slurpy Any,
- ],
- slurpy Any,
- ];
-
-declare NotificationType, as Enum [qw( Bounce Complaint )];
-declare TypeField, as Enum [qw(eventType notificationType)];
-declare Notification,
- as Dict [
- eventType => Optional [NotificationType],
- notificationType => Optional [NotificationType],
- slurpy Any,
- ];
+use Types::Standard qw( :all );
+use Type::Utils;
+use Type::Params qw( compile );
+
+my $Invocant = class_type { class => __PACKAGE__ };
sub main {
- my ($self) = @_;
- try {
- $self->_main;
- }
- catch {
- FATAL("Error in SES Handler: ", $_);
- $self->_respond(400 => 'Bad Request');
- };
+ my ($self) = @_;
+ try {
+ $self->_main;
+ }
+ catch {
+ FATAL("Error in SES Handler: ", $_);
+ $self->_respond( 400 => 'Bad Request' );
+ };
}
sub _main {
- my ($self) = @_;
- Bugzilla->error_mode(ERROR_MODE_DIE);
- my $message = $self->_decode_json_wrapper($self->req->body) // return;
- my $message_type = $self->req->headers->header('X-Amz-SNS-Message-Type')
- // '(missing)';
-
- if ($message_type eq 'SubscriptionConfirmation') {
- $self->_confirm_subscription($message);
- }
-
- elsif ($message_type eq 'Notification') {
- my $notification = $self->_decode_json_wrapper($message->{Message})
- // return;
- unless (
-# https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html
- $self->_handle_notification($notification, 'eventType')
-
-# https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
- || $self->_handle_notification($notification, 'notificationType')
- )
- {
- WARN('Failed to find notification type');
- $self->_respond(400 => 'Bad Request');
+ my ($self) = @_;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+ my $message = $self->_decode_json_wrapper( $self->req->body ) // return;
+ my $message_type = $self->req->headers->header('X-Amz-SNS-Message-Type') // '(missing)';
+
+ if ( $message_type eq 'SubscriptionConfirmation' ) {
+ $self->_confirm_subscription($message);
+ }
+
+ elsif ( $message_type eq 'Notification' ) {
+ my $notification = $self->_decode_json_wrapper( $message->{Message} ) // return;
+ unless (
+ # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html
+ $self->_handle_notification( $notification, 'eventType' )
+
+ # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
+ || $self->_handle_notification( $notification, 'notificationType' )
+ )
+ {
+ WARN('Failed to find notification type');
+ $self->_respond( 400 => 'Bad Request' );
+ }
}
- }
- else {
- WARN("Unsupported message-type: $message_type");
- $self->_respond(200 => 'OK');
- }
+ else {
+ WARN("Unsupported message-type: $message_type");
+ $self->_respond( 200 => 'OK' );
+ }
}
sub _confirm_subscription {
- state $check = compile(Self, Dict [SubscribeURL => Str, slurpy Any]);
- my ($self, $message) = $check->(@_);
-
- my $subscribe_url = $message->{SubscribeURL};
- if (!$subscribe_url) {
- WARN('Bad SubscriptionConfirmation request: missing SubscribeURL');
- $self->_respond(400 => 'Bad Request');
- return;
- }
-
- my $ua = ua();
- my $res = $ua->get($message->{SubscribeURL});
- if (!$res->is_success) {
- WARN('Bad response from SubscribeURL: ' . $res->status_line);
- $self->_respond(400 => 'Bad Request');
- return;
- }
-
- $self->_respond(200 => 'OK');
+ state $check = compile($Invocant, Dict[SubscribeURL => Str, slurpy Any]);
+ my ($self, $message) = $check->(@_);
+
+ my $subscribe_url = $message->{SubscribeURL};
+ if ( !$subscribe_url ) {
+ WARN('Bad SubscriptionConfirmation request: missing SubscribeURL');
+ $self->_respond( 400 => 'Bad Request' );
+ return;
+ }
+
+ my $ua = ua();
+ my $res = $ua->get( $message->{SubscribeURL} );
+ if ( !$res->is_success ) {
+ WARN( 'Bad response from SubscribeURL: ' . $res->status_line );
+ $self->_respond( 400 => 'Bad Request' );
+ return;
+ }
+
+ $self->_respond( 200 => 'OK' );
}
+my $NotificationType = Enum [qw( Bounce Complaint )];
+my $TypeField = Enum [qw(eventType notificationType)];
+my $Notification = Dict [
+ eventType => Optional [$NotificationType],
+ notificationType => Optional [$NotificationType],
+ slurpy Any,
+];
+
sub _handle_notification {
- state $check = compile(Self, Notification, TypeField);
- my ($self, $notification, $type_field) = $check->(@_);
-
- if (!exists $notification->{$type_field}) {
- return 0;
- }
- my $type = $notification->{$type_field};
-
- if ($type eq 'Bounce') {
- $self->_process_bounce($notification);
- }
- elsif ($type eq 'Complaint') {
- $self->_process_complaint($notification);
- }
- else {
- WARN("Unsupported notification-type: $type");
- $self->_respond(200 => 'OK');
- }
- return 1;
-}
+ state $check = compile($Invocant, $Notification, $TypeField );
+ my ( $self, $notification, $type_field ) = $check->(@_);
-sub _process_bounce {
- state $check = compile(Self, BounceNotification);
- my ($self, $notification) = $check->(@_);
-
- # disable each account that is bouncing
- foreach my $recipient (@{$notification->{bounce}->{bouncedRecipients}}) {
- my $address = $recipient->{emailAddress};
- my $reason = sprintf '(%s) %s', $recipient->{action} // 'error',
- $recipient->{diagnosticCode} // 'unknown';
-
- my $user = Bugzilla::User->new({name => $address, cache => 1});
- if ($user) {
-
- # never auto-disable admin accounts
- if ($user->in_group('admin')) {
- Bugzilla->audit("ignoring bounce for admin <$address>: $reason");
- }
-
- else {
- my $template = Bugzilla->template_inner();
- my $vars = {
- mta => $notification->{bounce}->{reportingMTA} // 'unknown',
- reason => $reason,
- };
- my $disable_text;
- $template->process('admin/users/bounce-disabled.txt.tmpl',
- $vars, \$disable_text)
- || die $template->error();
-
- $user->set_disabledtext($disable_text);
- $user->set_disable_mail(1);
- $user->update();
- Bugzilla->audit(
- "bounce for <$address> disabled userid-" . $user->id . ": $reason");
- }
+ if ( !exists $notification->{$type_field} ) {
+ return 0;
}
+ my $type = $notification->{$type_field};
+ if ( $type eq 'Bounce' ) {
+ $self->_process_bounce($notification);
+ }
+ elsif ( $type eq 'Complaint' ) {
+ $self->_process_complaint($notification);
+ }
else {
- Bugzilla->audit("bounce for <$address> has no user: $reason");
+ WARN("Unsupported notification-type: $type");
+ $self->_respond( 200 => 'OK' );
+ }
+ return 1;
+}
+
+my $BouncedRecipients = ArrayRef[
+ Dict[
+ emailAddress => Str,
+ action => Str,
+ diagnosticCode => Str,
+ slurpy Any,
+ ],
+];
+my $BounceNotification = Dict [
+ bounce => Dict [
+ bouncedRecipients => $BouncedRecipients,
+ reportingMTA => Str,
+ bounceSubType => Str,
+ bounceType => Str,
+ slurpy Any,
+ ],
+ slurpy Any,
+];
+
+sub _process_bounce {
+ state $check = compile($Invocant, $BounceNotification);
+ my ($self, $notification) = $check->(@_);
+
+ # disable each account that is bouncing
+ foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) {
+ my $address = $recipient->{emailAddress};
+ my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown';
+
+ my $user = Bugzilla::User->new( { name => $address, cache => 1 } );
+ if ($user) {
+
+ # never auto-disable admin accounts
+ if ( $user->in_group('admin') ) {
+ Bugzilla->audit("ignoring bounce for admin <$address>: $reason");
+ }
+
+ else {
+ my $template = Bugzilla->template_inner();
+ my $vars = {
+ mta => $notification->{bounce}->{reportingMTA} // 'unknown',
+ reason => $reason,
+ };
+ my $disable_text;
+ $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text )
+ || die $template->error();
+
+ $user->set_disabledtext($disable_text);
+ $user->set_disable_mail(1);
+ $user->update();
+ Bugzilla->audit( "bounce for <$address> disabled userid-" . $user->id . ": $reason" );
+ }
+ }
+
+ else {
+ Bugzilla->audit("bounce for <$address> has no user: $reason");
+ }
}
- }
- $self->_respond(200 => 'OK');
+ $self->_respond( 200 => 'OK' );
}
+my $ComplainedRecipients = ArrayRef[Dict[ emailAddress => Str, slurpy Any ]];
+my $ComplaintNotification = Dict[
+ complaint => Dict [
+ complainedRecipients => $ComplainedRecipients,
+ complaintFeedbackType => Str,
+ slurpy Any,
+ ],
+ slurpy Any,
+];
+
sub _process_complaint {
- state $check = compile(Self, ComplaintNotification);
- my ($self, $notification) = $check->(@_);
- my $template = Bugzilla->template_inner();
- my $json = JSON::MaybeXS->new(pretty => 1, utf8 => 1, canonical => 1,);
-
- foreach my $recipient (@{$notification->{complaint}->{complainedRecipients}})
- {
- my $reason = $notification->{complaint}->{complaintFeedbackType}
- // 'unknown';
- my $address = $recipient->{emailAddress};
- Bugzilla->audit("complaint for <$address> for '$reason'");
- my $vars = {
- email => $address,
- user => Bugzilla::User->new({name => $address, cache => 1}),
- reason => $reason,
- notification => $json->encode($notification),
- };
- my $message;
- $template->process('email/ses-complaint.txt.tmpl', $vars, \$message)
- || die $template->error();
- MessageToMTA($message);
- }
+ state $check = compile($Invocant, $ComplaintNotification);
+ my ($self, $notification) = $check->(@_);
+ my $template = Bugzilla->template_inner();
+ my $json = JSON::MaybeXS->new(
+ pretty => 1,
+ utf8 => 1,
+ canonical => 1,
+ );
+
+ foreach my $recipient ( @{ $notification->{complaint}->{complainedRecipients} } ) {
+ my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown';
+ my $address = $recipient->{emailAddress};
+ Bugzilla->audit("complaint for <$address> for '$reason'");
+ my $vars = {
+ email => $address,
+ user => Bugzilla::User->new( { name => $address, cache => 1 } ),
+ reason => $reason,
+ notification => $json->encode($notification),
+ };
+ my $message;
+ $template->process( 'email/ses-complaint.txt.tmpl', $vars, \$message )
+ || die $template->error();
+ MessageToMTA($message);
+ }
- $self->_respond(200 => 'OK');
+ $self->_respond( 200 => 'OK' );
}
sub _respond {
- my ($self, $code, $message) = @_;
- $self->render(text => "$message\n", status => $code);
+ my ( $self, $code, $message ) = @_;
+ $self->render(text => "$message\n", status => $code);
}
sub _decode_json_wrapper {
- state $check = compile(Self, Str);
- my ($self, $json) = $check->(@_);
- my $result;
- my $ok = try {
- $result = decode_json($json);
- }
- catch {
- WARN('Malformed JSON from ' . $self->tx->remote_address);
- $self->_respond(400 => 'Bad Request');
- return undef;
- };
- return $ok ? $result : undef;
+ state $check = compile($Invocant, Str);
+ my ($self, $json) = $check->(@_);
+ my $result;
+ my $ok = try {
+ $result = decode_json($json);
+ }
+ catch {
+ WARN( 'Malformed JSON from ' . $self->tx->remote_address );
+ $self->_respond( 400 => 'Bad Request' );
+ return undef;
+ };
+ return $ok ? $result : undef;
}
sub ua {
- my $ua = LWP::UserAgent->new();
- $ua->timeout(10);
- $ua->protocols_allowed(['http', 'https']);
- if (my $proxy_url = Bugzilla->params->{'proxy_url'}) {
- $ua->proxy(['http', 'https'], $proxy_url);
- }
- else {
- $ua->env_proxy;
- }
- return $ua;
+ my $ua = LWP::UserAgent->new();
+ $ua->timeout(10);
+ $ua->protocols_allowed( [ 'http', 'https' ] );
+ if ( my $proxy_url = Bugzilla->params->{'proxy_url'} ) {
+ $ua->proxy( [ 'http', 'https' ], $proxy_url );
+ }
+ else {
+ $ua->env_proxy;
+ }
+ return $ua;
}
1;
diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm
index f419955dc..e15c60f7f 100644
--- a/Bugzilla/Search.pm
+++ b/Bugzilla/Search.pm
@@ -802,18 +802,20 @@ sub data {
# BMO - to avoid massive amounts of joins, if we're selecting a lot of
# tracking flags, replace them with placeholders. the values will be
# retrieved later and injected into the result.
- my %tf_map = map { $_ => 1 } Bugzilla::Extension::TrackingFlags::Flag->get_all_names();
- my @tf_selected = grep { exists $tf_map{$_} } @orig_fields;
- # mysql has a limit of 61 joins, and we want to avoid massive amounts of joins
- # 30 ensures we won't hit the limit, nor generate too many joins
- if (scalar @tf_selected > 30) {
- foreach my $column (@tf_selected) {
- $self->COLUMNS->{$column}->{name} = "'---'";
+ if (Bugzilla->has_extension('TrackingFlags')) {
+ my %tf_map = map { $_ => 1 } Bugzilla::Extension::TrackingFlags::Flag->get_all_names();
+ my @tf_selected = grep { exists $tf_map{$_} } @orig_fields;
+ # mysql has a limit of 61 joins, and we want to avoid massive amounts of joins
+ # 30 ensures we won't hit the limit, nor generate too many joins
+ if (scalar @tf_selected > 30) {
+ foreach my $column (@tf_selected) {
+ $self->COLUMNS->{$column}->{name} = "'---'";
+ }
+ $self->{tracking_flags} = \@tf_selected;
+ }
+ else {
+ $self->{tracking_flags} = [];
}
- $self->{tracking_flags} = \@tf_selected;
- }
- else {
- $self->{tracking_flags} = [];
}
my $start_time = [gettimeofday()];
@@ -863,7 +865,7 @@ sub data {
$self->{data} = [map { $data{$_} } @$bug_ids];
# BMO - get tracking flags values, and insert into result
- if (@{ $self->{tracking_flags} }) {
+ if (Bugzilla->has_extension('TrackingFlags') && @{ $self->{tracking_flags} }) {
# read values
my $values;
$sql = "
diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm
index c337b5af8..36435d637 100644
--- a/Bugzilla/Template.pm
+++ b/Bugzilla/Template.pm
@@ -995,6 +995,8 @@ sub create {
'feature_enabled' => sub { return Bugzilla->feature(@_); },
+ 'has_extension' => sub { return Bugzilla->has_extension(@_); },
+
# field_descs can be somewhat slow to generate, so we generate
# it only once per-language no matter how many times
# $template->process() is called.