diff options
Diffstat (limited to 'Bugzilla/Quantum')
-rw-r--r-- | Bugzilla/Quantum/CGI.pm | 246 | ||||
-rw-r--r-- | Bugzilla/Quantum/Home.pm | 28 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/BlockIP.pm | 38 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/Glue.pm | 217 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/Helpers.pm | 66 | ||||
-rw-r--r-- | Bugzilla/Quantum/Static.pm | 18 | ||||
-rw-r--r-- | Bugzilla/Quantum/Stdout.pm | 50 |
7 files changed, 415 insertions, 248 deletions
diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 7548c0809..79fbcfde6 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -19,143 +19,159 @@ use File::Spec::Functions qw(catfile); use File::Slurper qw(read_text); use English qw(-no_match_vars); use Bugzilla::Quantum::Stdout; -use Bugzilla::Constants qw(bz_locations); +use Bugzilla::Constants qw(bz_locations USAGE_MODE_BROWSER); our $C; my %SEEN; sub load_all { - my ( $class, $r ) = @_; + my ($class, $r) = @_; - foreach my $file ( glob '*.cgi' ) { - my $name = _file_to_method($file); - $class->load_one( $name, $file ); - $r->any("/$file")->to("CGI#$name"); - } + foreach my $file (glob '*.cgi') { + my $name = _file_to_method($file); + $class->load_one($name, $file); + $r->any("/$file")->to("CGI#$name"); + } } sub load_one { - my ( $class, $name, $file ) = @_; - my $package = __PACKAGE__ . "::$name", my $inner_name = "_$name"; - my $content = read_text( catfile( bz_locations->{cgi_path}, $file ) ); - $content = "package $package; $content"; - untaint($content); - my %options = ( - package => $package, - file => $file, - line => 1, - no_defer => 1, - ); - die "Tried to load $file more than once" if $SEEN{$file}++; - my $inner = quote_sub $inner_name, $content, {}, \%options; - my $wrapper = sub { - my ($c) = @_; - my $stdin = $c->_STDIN; - local $C = $c; - local %ENV = $c->_ENV($file); - local $CGI::Compile::USE_REAL_EXIT = 0; - local $PROGRAM_NAME = $file; - local *STDIN; ## no critic (local) - open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; - tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) - try { - Bugzilla->init_page(); - $inner->(); - } - catch { - die $_ unless ref $_ eq 'ARRAY' && $_->[0] eq "EXIT\n"; - } - finally { - my $error = shift; - untie *STDOUT; - $c->finish unless $error; - Bugzilla->cleanup; - CGI::initialize_globals(); - }; + my ($class, $name, $file) = @_; + my $package = __PACKAGE__ . "::$name", my $inner_name = "_$name"; + my $content = read_text(catfile(bz_locations->{cgi_path}, $file)); + $content = "package $package; $content"; + untaint($content); + my %options = (package => $package, file => $file, line => 1, no_defer => 1,); + die "Tried to load $file more than once" if $SEEN{$file}++; + my $inner = quote_sub $inner_name, $content, {}, \%options; + my $wrapper = sub { + my ($c) = @_; + my $stdin = $c->_STDIN; + local $C = $c; + local %ENV = $c->_ENV($file); + local $CGI::Compile::USE_REAL_EXIT = 0; + local $PROGRAM_NAME = $file; + local *STDIN; ## no critic (local) + open STDIN, '<', $stdin->path + or die "STDIN @{[$stdin->path]}: $!" + if -s $stdin->path; + tie *STDOUT, 'Bugzilla::Quantum::Stdout', + controller => $c; ## no critic (tie) + + # the finally block calls cleanup. + $c->stash->{cleanup_guard}->dismiss; + Bugzilla->usage_mode(USAGE_MODE_BROWSER); + try { + Bugzilla->init_page(); + $inner->(); + } + catch { + die $_ unless _is_exit($_); + } + finally { + my $error = shift; + untie *STDOUT; + $c->finish if !$error || _is_exit($error); + Bugzilla->cleanup; + CGI::initialize_globals(); }; + }; - no strict 'refs'; ## no critic (strict) - *{$name} = subname( $name, $wrapper ); - return 1; + no strict 'refs'; ## no critic (strict) + *{$name} = subname($name, $wrapper); + return 1; } sub _ENV { - my ( $c, $script_name ) = @_; - my $tx = $c->tx; - my $req = $tx->req; - my $headers = $req->headers; - my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; - my %env_headers = ( HTTP_COOKIE => '', HTTP_REFERER => '' ); - - for my $name ( @{ $headers->names } ) { - my $key = uc "http_$name"; - $key =~ s/\W/_/g; - $env_headers{$key} = $headers->header($name); - } - - my $remote_user; - if ( my $userinfo = $req->url->to_abs->userinfo ) { - $remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; - } - elsif ( my $authenticate = $headers->authorization ) { - $remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; - $remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; - } - my $path_info = $c->stash->{'mojo.captures'}{'PATH_INFO'}; - my %captures = %{ $c->stash->{'mojo.captures'} // {} }; - foreach my $key ( keys %captures ) { - if ( $key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/ ) { - delete $captures{$key}; - } + my ($c, $script_name) = @_; + my $tx = $c->tx; + my $req = $tx->req; + my $headers = $req->headers; + my $content_length + = $req->content->is_multipart ? $req->body_size : $headers->content_length; + my %env_headers = (HTTP_COOKIE => '', HTTP_REFERER => ''); + + $headers->content_type('application/x-www-form-urlencoded; charset=utf-8') + unless $headers->content_type; + for my $name (@{$headers->names}) { + my $key = uc "http_$name"; + $key =~ s/\W/_/g; + $env_headers{$key} = $headers->header($name); + } + + my $remote_user; + if (my $userinfo = $req->url->to_abs->userinfo) { + $remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; + } + elsif (my $authenticate = $headers->authorization) { + $remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; + $remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; + } + my $path_info = $c->stash->{'mojo.captures'}{'PATH_INFO'}; + my %captures = %{$c->stash->{'mojo.captures'} // {}}; + foreach my $key (keys %captures) { + if ( $key eq 'controller' + || $key eq 'action' + || $key eq 'PATH_INFO' + || $key =~ /^REWRITE_/) + { + delete $captures{$key}; } - my $cgi_query = Mojo::Parameters->new(%captures); - $cgi_query->append( $req->url->query ); - my $prefix = $c->stash->{bmo_prefix} ? '/bmo/' : '/'; - - return ( - %ENV, - CONTENT_LENGTH => $content_length || 0, - CONTENT_TYPE => $headers->content_type || '', - GATEWAY_INTERFACE => 'CGI/1.1', - HTTPS => $req->is_secure ? 'on' : 'off', - %env_headers, - QUERY_STRING => $cgi_query->to_string, - PATH_INFO => $path_info ? "/$path_info" : '', - REMOTE_ADDR => $tx->original_remote_address, - REMOTE_HOST => $tx->original_remote_address, - REMOTE_PORT => $tx->remote_port, - REMOTE_USER => $remote_user || '', - REQUEST_METHOD => $req->method, - SCRIPT_NAME => "$prefix$script_name", - SERVER_NAME => hostname, - SERVER_PORT => $tx->local_port, - SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing - SERVER_SOFTWARE => __PACKAGE__, - ); + } + my $cgi_query = Mojo::Parameters->new(%captures); + $cgi_query->append($req->url->query); + my $prefix = $c->stash->{bmo_prefix} ? '/bmo/' : '/'; + + return ( + %ENV, + CONTENT_LENGTH => $content_length || 0, + CONTENT_TYPE => $headers->content_type || '', + GATEWAY_INTERFACE => 'CGI/1.1', + HTTPS => $req->is_secure ? 'on' : 'off', + %env_headers, + QUERY_STRING => $cgi_query->to_string, + PATH_INFO => $path_info ? "/$path_info" : '', + REMOTE_ADDR => $tx->original_remote_address, + REMOTE_HOST => $tx->original_remote_address, + REMOTE_PORT => $tx->remote_port, + REMOTE_USER => $remote_user || '', + REQUEST_METHOD => $req->method, + SCRIPT_NAME => "$prefix$script_name", + SERVER_NAME => hostname, + SERVER_PORT => $tx->local_port, + SERVER_PROTOCOL => $req->is_secure + ? 'HTTPS' + : 'HTTP', # TODO: Version is missing + SERVER_SOFTWARE => __PACKAGE__, + ); } sub _STDIN { - my $c = shift; - my $stdin; - - if ( $c->req->content->is_multipart ) { - $stdin = Mojo::Asset::File->new; - $stdin->add_chunk( $c->req->build_body ); - } - else { - $stdin = $c->req->content->asset; - } - - return $stdin if $stdin->isa('Mojo::Asset::File'); - return Mojo::Asset::File->new->add_chunk( $stdin->slurp ); + my $c = shift; + my $stdin; + + if ($c->req->content->is_multipart) { + $stdin = Mojo::Asset::File->new; + $stdin->add_chunk($c->req->build_body); + } + else { + $stdin = $c->req->content->asset; + } + + return $stdin if $stdin->isa('Mojo::Asset::File'); + return Mojo::Asset::File->new->add_chunk($stdin->slurp); } sub _file_to_method { - my ($name) = @_; - $name =~ s/\./_/s; - $name =~ s/\W+/_/gs; - return $name; + my ($name) = @_; + $name =~ s/\./_/s; + $name =~ s/\W+/_/gs; + return $name; +} + +sub _is_exit { + my ($error) = @_; + return ref $error eq 'ARRAY' && $error->[0] eq "EXIT\n"; } 1; diff --git a/Bugzilla/Quantum/Home.pm b/Bugzilla/Quantum/Home.pm new file mode 100644 index 000000000..48d5e47bd --- /dev/null +++ b/Bugzilla/Quantum/Home.pm @@ -0,0 +1,28 @@ +# 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::Home; +use Mojo::Base 'Mojolicious::Controller'; + +use Bugzilla::Error; +use Try::Tiny; +use Bugzilla::Constants; + +sub index { + my ($c) = @_; + $c->bugzilla->login(LOGIN_REQUIRED) or return; + try { + ThrowUserError('invalid_username', {login => 'batman'}) + if $c->param('error'); + $c->render(handler => 'bugzilla', template => 'index'); + } + catch { + $c->bugzilla->error_page($_); + }; +} + +1; diff --git a/Bugzilla/Quantum/Plugin/BlockIP.pm b/Bugzilla/Quantum/Plugin/BlockIP.pm index 058ecbf64..974eebff9 100644 --- a/Bugzilla/Quantum/Plugin/BlockIP.pm +++ b/Bugzilla/Quantum/Plugin/BlockIP.pm @@ -9,35 +9,35 @@ use constant BLOCK_TIMEOUT => 60 * 60; my $MEMCACHED = Bugzilla::Memcached->new()->{memcached}; sub register { - my ( $self, $app, $conf ) = @_; + my ($self, $app, $conf) = @_; - $app->hook( before_routes => \&_before_routes ); - $app->helper( block_ip => \&_block_ip ); - $app->helper( unblock_ip => \&_unblock_ip ); + $app->hook(before_routes => \&_before_routes); + $app->helper(block_ip => \&_block_ip); + $app->helper(unblock_ip => \&_unblock_ip); } sub _block_ip { - my ( $class, $ip ) = @_; - $MEMCACHED->set( "block_ip:$ip" => 1, BLOCK_TIMEOUT ) if $MEMCACHED; + my ($class, $ip) = @_; + $MEMCACHED->set("block_ip:$ip" => 1, BLOCK_TIMEOUT) if $MEMCACHED; } sub _unblock_ip { - my ( $class, $ip ) = @_; - $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; + my ($class, $ip) = @_; + $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; } sub _before_routes { - my ($c) = @_; - return if $c->stash->{'mojo.static'}; - - my $ip = $c->tx->remote_address; - if ( $MEMCACHED && $MEMCACHED->get("block_ip:$ip") ) { - $c->block_ip($ip); - $c->res->code(429); - $c->res->message('Too Many Requests'); - $c->res->body('Too Many Requests'); - $c->finish; - } + my ($c) = @_; + return if $c->stash->{'mojo.static'}; + + my $ip = $c->tx->remote_address; + if ($MEMCACHED && $MEMCACHED->get("block_ip:$ip")) { + $c->block_ip($ip); + $c->res->code(429); + $c->res->message('Too Many Requests'); + $c->res->body('Too Many Requests'); + $c->finish; + } } 1; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index ded4daf15..f04b9c025 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -13,89 +13,152 @@ use Try::Tiny; use Bugzilla::Constants; use Bugzilla::Logging; use Bugzilla::RNG (); -use JSON::MaybeXS qw(decode_json); +use Bugzilla::Util qw(with_writable_database); +use Mojo::Util qw(secure_compare); +use Mojo::JSON qw(decode_json); +use Scalar::Util qw(blessed); +use Scope::Guard; sub register { - my ( $self, $app, $conf ) = @_; - - my %D; - if ( $ENV{BUGZILLA_HTTPD_ARGS} ) { - my $args = decode_json( $ENV{BUGZILLA_HTTPD_ARGS} ); - foreach my $arg (@$args) { - if ( $arg =~ /^-D(\w+)$/ ) { - $D{$1} = 1; - } - else { - die "Unknown httpd arg: $arg"; - } - } + my ($self, $app, $conf) = @_; + + my %D; + if ($ENV{BUGZILLA_HTTPD_ARGS}) { + my $args = decode_json($ENV{BUGZILLA_HTTPD_ARGS}); + foreach my $arg (@$args) { + if ($arg =~ /^-D(\w+)$/) { + $D{$1} = 1; + } + else { + die "Unknown httpd arg: $arg"; + } } + } - # hypnotoad is weird and doesn't look for MOJO_LISTEN itself. - $app->config( - hypnotoad => { - proxy => 1, - listen => [ $ENV{MOJO_LISTEN} ], - }, - ); - - # Make sure each httpd child receives a different random seed (bug 476622). - # Bugzilla::RNG has one srand that needs to be called for - # every process, and Perl has another. (Various Perl modules still use - # the built-in rand(), even though we never use it in Bugzilla itself, - # so we need to srand() both of them.) - # Also, ping the dbh to force a reconnection. - Mojo::IOLoop->next_tick( - sub { - Bugzilla::RNG::srand(); - srand(); - try { Bugzilla->dbh->ping }; - } - ); - - $app->hook( - before_dispatch => sub { - my ($c) = @_; - if ( $D{HTTPD_IN_SUBDIR} ) { - my $path = $c->req->url->path; - if ( $path =~ s{^/bmo}{}s ) { - $c->stash->{bmo_prefix} = 1; - $c->req->url->path($path); - } - } - Log::Log4perl::MDC->put( request_id => $c->req->request_id ); - } - ); - - - $app->secrets( [ Bugzilla->localconfig->{side_wide_secret} ] ); - - $app->renderer->add_handler( - 'bugzilla' => sub { - my ( $renderer, $c, $output, $options ) = @_; - my $vars = delete $c->stash->{vars}; - - # Helpers - my %helper; - foreach my $method ( grep {m/^\w+\z/} keys %{ $renderer->helpers } ) { - my $sub = $renderer->helpers->{$method}; - $helper{$method} = sub { $c->$sub(@_) }; - } - $vars->{helper} = \%helper; - - # The controller - $vars->{c} = $c; - my $name = $options->{template}; - unless ( $name =~ /\./ ) { - $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; - } - my $template = Bugzilla->template; - $template->process( $name, $vars, $output ) - or die $template->error; + $app->hook( + before_dispatch => sub { + my ($c) = @_; + if ($D{HTTPD_IN_SUBDIR}) { + my $path = $c->req->url->path; + if ($path =~ s{^/bmo}{}s) { + $c->stash->{bmo_prefix} = 1; + $c->req->url->path($path); } - ); + } + Log::Log4perl::MDC->put(request_id => $c->req->request_id); + $c->stash->{cleanup_guard} = Scope::Guard->new(\&Bugzilla::cleanup); + Bugzilla->usage_mode(USAGE_MODE_MOJO); + } + ); + + $app->secrets([Bugzilla->localconfig->{side_wide_secret}]); + + $app->renderer->add_handler( + 'bugzilla' => sub { + my ($renderer, $c, $output, $options) = @_; + + my %params; + + # Helpers + foreach my $method (grep {m/^\w+\z/} keys %{$renderer->helpers}) { + my $sub = $renderer->helpers->{$method}; + $params{$method} = sub { $c->$sub(@_) }; + } + + # Stash values + $params{$_} = $c->stash->{$_} for grep {m/^\w+\z/} keys %{$c->stash}; + + $params{self} = $params{c} = $c; + + my $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; + my $template = Bugzilla->template; + $template->process($name, \%params, $output) or die $template->error; + } + ); + $app->helper( + 'bugzilla.login_redirect_if_required' => sub { + my ($c, $type) = @_; + + if ($type == LOGIN_REQUIRED) { + $c->redirect_to('/login'); + return undef; + } + else { + return Bugzilla->user; + } + } + ); + $app->helper( + 'bugzilla.login' => sub { + my ($c, $type) = @_; + $type //= LOGIN_NORMAL; + + return Bugzilla->user if Bugzilla->user->id; + + $type = LOGIN_REQUIRED + if $c->param('GoAheadAndLogIn') || Bugzilla->params->{requirelogin}; + + # Allow templates to know that we're in a page that always requires + # login. + if ($type == LOGIN_REQUIRED) { + Bugzilla->request_cache->{page_requires_login} = 1; + } + + my $login_cookie = $c->cookie("Bugzilla_logincookie"); + my $user_id = $c->cookie("Bugzilla_login"); + my $ip_addr = $c->tx->remote_address; + + return $c->bugzilla->login_redirect_if_required($type) + unless ($login_cookie && $user_id); + + my $db_cookie = Bugzilla->dbh->selectrow_array( + q{ + SELECT cookie + FROM logincookies + WHERE cookie = ? + AND userid = ? + AND (restrict_ipaddr = 0 OR ipaddr = ?) + }, undef, ($login_cookie, $user_id, $ip_addr) + ); + + if (defined $db_cookie && secure_compare($login_cookie, $db_cookie)) { + my $user = Bugzilla::User->check({id => $user_id, cache => 1}); + + # If we logged in successfully, then update the lastused + # time on the login cookie + with_writable_database { + Bugzilla->dbh->do( + q{ UPDATE logincookies SET lastused = NOW() WHERE cookie = ? }, + undef, $login_cookie); + }; + Bugzilla->set_user($user); + return $user; + } + else { + return $c->bugzilla->login_redirect_if_required($type); + } + } + ); + $app->helper( + 'bugzilla.error_page' => sub { + my ($c, $error) = @_; + if (blessed $error && $error->isa('Bugzilla::Error::Base')) { + $c->render( + handler => 'bugzilla', + template => $error->template, + error => $error->message, + %{$error->vars} + ); + } + else { + $c->reply->exception($error); + } + } + ); - $app->log( MojoX::Log::Log4perl::Tiny->new( logger => Log::Log4perl->get_logger( ref $app ) ) ); + $app->log(MojoX::Log::Log4perl::Tiny->new( + logger => Log::Log4perl->get_logger(ref $app) + )); } 1; diff --git a/Bugzilla/Quantum/Plugin/Helpers.pm b/Bugzilla/Quantum/Plugin/Helpers.pm new file mode 100644 index 000000000..72dd96cf9 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/Helpers.pm @@ -0,0 +1,66 @@ +# 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::Helpers; +use 5.10.1; +use Mojo::Base qw(Mojolicious::Plugin); + +use Bugzilla::Logging; +use Carp; + +sub register { + my ($self, $app, $conf) = @_; + + $app->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; + } + ); + $app->routes->add_shortcut( + static_file => sub { + my ($r, $path, $option) = @_; + my $file = $option->{file}; + my $content_type = $option->{content_type} // 'text/plain'; + unless ($file) { + $file = $path; + $file =~ s!^/!!; + } + + return $r->get( + $path => sub { + my ($c) = @_; + $c->res->headers->content_type($content_type); + $c->reply->file($c->app->home->child($file)); + } + ); + } + ); + $app->routes->add_shortcut( + page => sub { + my ($r, $path, $id) = @_; + + return $r->any($path)->to('CGI#page_cgi' => {id => $id}); + } + ); +} + +1; diff --git a/Bugzilla/Quantum/Static.pm b/Bugzilla/Quantum/Static.pm index c01f062a4..6ac803e96 100644 --- a/Bugzilla/Quantum/Static.pm +++ b/Bugzilla/Quantum/Static.pm @@ -11,20 +11,20 @@ use Bugzilla::Constants qw(bz_locations); my $LEGACY_RE = qr{ ^ (?:static/v[0-9]+\.[0-9]+/) ? - ( (?:extensions/[^/]+/web|(?:image|graph|skin|j)s)/.+) + ( (?:extensions/[^/]+/web|(?:image|skin|j|graph)s)/.+) $ }xs; sub file { - my ( $self, $rel ) = @_; + my ($self, $rel) = @_; - if ( my ($legacy_rel) = $rel =~ $LEGACY_RE ) { - local $self->{paths} = [ bz_locations->{cgi_path} ]; - return $self->SUPER::file($legacy_rel); - } - else { - return $self->SUPER::file($rel); - } + if (my ($legacy_rel) = $rel =~ $LEGACY_RE) { + local $self->{paths} = [bz_locations->{cgi_path}]; + return $self->SUPER::file($legacy_rel); + } + else { + return $self->SUPER::file($rel); + } } 1; diff --git a/Bugzilla/Quantum/Stdout.pm b/Bugzilla/Quantum/Stdout.pm index 9cf19992c..10be0b664 100644 --- a/Bugzilla/Quantum/Stdout.pm +++ b/Bugzilla/Quantum/Stdout.pm @@ -13,48 +13,42 @@ use Bugzilla::Logging; use Encode; use English qw(-no_match_vars); -has 'controller' => ( - is => 'ro', - required => 1, -); +has 'controller' => (is => 'ro', required => 1,); -has '_encoding' => ( - is => 'rw', - default => '', -); +has '_encoding' => (is => 'rw', default => '',); sub TIEHANDLE { ## no critic (unpack) - my $class = shift; + my $class = shift; - return $class->new(@_); + return $class->new(@_); } sub PRINTF { ## no critic (unpack) - my $self = shift; - $self->PRINT( sprintf @_ ); + my $self = shift; + $self->PRINT(sprintf @_); } sub PRINT { ## no critic (unpack) - my $self = shift; - my $c = $self->controller; - my $bytes = join '', @_; - return unless $bytes; - if ( $self->_encoding ) { - $bytes = encode( $self->_encoding, $bytes ); - } - $c->write($bytes . ( $OUTPUT_RECORD_SEPARATOR // '' ) ); + my $self = shift; + my $c = $self->controller; + my $bytes = join '', @_; + return unless $bytes; + if ($self->_encoding) { + $bytes = encode($self->_encoding, $bytes); + } + $c->write($bytes . ($OUTPUT_RECORD_SEPARATOR // '')); } sub BINMODE { - my ( $self, $mode ) = @_; - if ($mode) { - if ( $mode eq ':bytes' or $mode eq ':raw' ) { - $self->_encoding(''); - } - elsif ( $mode eq ':utf8' ) { - $self->_encoding('utf8'); - } + my ($self, $mode) = @_; + if ($mode) { + if ($mode eq ':bytes' or $mode eq ':raw') { + $self->_encoding(''); + } + elsif ($mode eq ':utf8') { + $self->_encoding('utf8'); } + } } 1; |