From c05a32842ee123801b628f5fe0472cb6abbbafdb Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Sun, 2 Jul 2017 15:46:48 -0700 Subject: Bug 1376023 - Undo accidental reversion of bug 1352907 This reverts commit 38b13ae3f1885faa0da1d0040a0dda87dc786515. * change __lbheartbeat__ to "httpd OK" again --- Bugzilla.pm | 6 +- Bugzilla/Extension.pm | 167 +++++++++++++-------------------------- Bugzilla/Install/Requirements.pm | 3 +- Bugzilla/Install/Util.pm | 153 +---------------------------------- extensions/BMO/Extension.pm | 5 +- mod_perl.pl | 2 +- 6 files changed, 67 insertions(+), 269 deletions(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 2d59d4171..55dc93e98 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -238,15 +238,11 @@ sub template_inner { return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang); } -our $extension_packages; sub extensions { my ($class) = @_; my $cache = $class->request_cache; if (!$cache->{extensions}) { - # Under mod_perl, mod_perl.pl populates $extension_packages for us. - if (!$extension_packages) { - $extension_packages = Bugzilla::Extension->load_all(); - } + my $extension_packages = Bugzilla::Extension->load_all(); my @extensions; foreach my $package (@$extension_packages) { my $extension = $package->new(); diff --git a/Bugzilla/Extension.pm b/Bugzilla/Extension.pm index 78c027796..e7f398784 100644 --- a/Bugzilla/Extension.pm +++ b/Bugzilla/Extension.pm @@ -13,12 +13,47 @@ use warnings; use Bugzilla::Constants; use Bugzilla::Error; -use Bugzilla::Install::Util qw( - extension_code_files extension_template_directory - extension_package_directory extension_web_directory); +use Bugzilla::Install::Util qw( extension_code_files ); use File::Basename; use File::Spec; +use Taint::Util qw(untaint); + +BEGIN { push @INC, \&INC_HOOK } + +sub INC_HOOK { + my (undef, $fake_file) = @_; + state $bz_locations = bz_locations(); + my ($vol, $dir, $file) = File::Spec->splitpath($fake_file); + my @dirs = grep { length $_ } File::Spec->splitdir($dir); + + if (@dirs > 2 && $dirs[0] eq 'Bugzilla' && $dirs[1] eq 'Extension') { + my $extension = $dirs[2]; + splice @dirs, 0, 3, File::Spec->splitdir($bz_locations->{extensionsdir}), $extension, "lib"; + my $real_file = Cwd::realpath(File::Spec->catpath($vol, File::Spec->catdir(@dirs), $file)); + + my $first = 1; + untaint($real_file); + $INC{$fake_file} = $real_file; + open my $fh, '<', $real_file or die "invalid file: $real_file"; + return sub { + no warnings; + if ( !$first ) { + return 0 if eof $fh; + $_ = readline $fh + or return 0; + untaint($_); + return 1; + } + else { + $_ = qq{# line 0 "$real_file"\n}; + $first = 0; + return 1; + } + }; + } + return; +}; #################### # Subclass Methods # @@ -59,13 +94,10 @@ sub load { } $package = "${class}::$name"; } - - __do_call($package, 'modify_inc', $config_file); } if ($map and defined $map->{$extension_file}) { $package = $map->{$extension_file}; - $package->modify_inc($extension_file) if !$config_file; } else { my $name = require $extension_file; @@ -74,7 +106,6 @@ sub load { { extension => $extension_file, returned => $name }); } $package = "${class}::$name"; - $package->modify_inc($extension_file) if !$config_file; } $class->_validate_package($package, $extension_file); @@ -107,77 +138,16 @@ sub _validate_package { sub load_all { my $class = shift; - my ($file_sets, $extra_packages) = extension_code_files(); - my @packages; + state $EXTENSIONS = []; + return $EXTENSIONS if @$EXTENSIONS; + + my ($file_sets) = extension_code_files(); foreach my $file_set (@$file_sets) { my $package = $class->load(@$file_set); - push(@packages, $package); + push(@$EXTENSIONS, $package); } - # Extensions from data/extensions/additional - foreach my $package (@$extra_packages) { - # Don't load an "additional" extension if we already have an extension - # loaded with that name. - next if grep($_ eq $package, @packages); - # Untaint the package name - $package =~ /([\w:]+)/; - $package = $1; - eval("require $package") || die $@; - $package->_validate_package($package); - push(@packages, $package); - } - - return \@packages; -} - -# Modifies @INC so that extensions can use modules like -# "use Bugzilla::Extension::Foo::Bar", when Bar.pm is in the lib/ -# directory of the extension. -sub modify_inc { - my ($class, $file) = @_; - - # Note that this package_dir call is necessary to set things up - # for my_inc, even if we didn't take its return value. - my $package_dir = __do_call($class, 'package_dir', $file); - # Don't modify @INC for extensions that are just files in the extensions/ - # directory. We don't want Bugzilla's base lib/CGI.pm being loaded as - # Bugzilla::Extension::Foo::CGI or any other confusing thing like that. - return if $package_dir eq bz_locations->{'extensionsdir'}; - unshift(@INC, sub { __do_call($class, 'my_inc', @_) }); -} - -# This is what gets put into @INC by modify_inc. -sub my_inc { - my ($class, undef, $file) = @_; - - # This avoids infinite recursion in case anything inside of this function - # does a "require". (I know for sure that File::Spec->case_tolerant does - # a "require" on Windows, for example.) - return if $file !~ /^Bugzilla/; - - my $lib_dir = __do_call($class, 'lib_dir'); - my @class_parts = split('::', $class); - my ($vol, $dir, $file_name) = File::Spec->splitpath($file); - my @dir_parts = File::Spec->splitdir($dir); - # File::Spec::Win32 (any maybe other OSes) add an empty directory at the - # end of @dir_parts. - @dir_parts = grep { $_ ne '' } @dir_parts; - # Validate that this is a sub-package of Bugzilla::Extension::Foo ($class). - for (my $i = 0; $i < scalar(@class_parts); $i++) { - return if !@dir_parts; - if (File::Spec->case_tolerant) { - return if lc($class_parts[$i]) ne lc($dir_parts[0]); - } - else { - return if $class_parts[$i] ne $dir_parts[0]; - } - shift(@dir_parts); - } - # For Bugzilla::Extension::Foo::Bar, this would look something like - # extensions/Example/lib/Bar.pm - my $resolved_path = File::Spec->catfile($lib_dir, @dir_parts, $file_name); - open(my $fh, '<', $resolved_path); - return $fh; + return $EXTENSIONS; } #################### @@ -186,44 +156,21 @@ sub my_inc { use constant enabled => 1; -sub lib_dir { - my $invocant = shift; - my $package_dir = __do_call($invocant, 'package_dir'); - # For extensions that are just files in the extensions/ directory, - # use the base lib/ dir as our "lib_dir". Note that Bugzilla never - # uses lib_dir in this case, though, because modify_inc is prevented - # from modifying @INC when we're just a file in the extensions/ directory. - # So this particular code block exists just to make lib_dir return - # something right in case an extension needs it for some odd reason. - if ($package_dir eq bz_locations()->{'extensionsdir'}) { - return bz_locations->{'ext_libpath'}; - } - return File::Spec->catdir($package_dir, 'lib'); +sub package_dir { + my ($class) = @_; + state $bz_locations = bz_locations(); + my (undef, undef, $name) = split(/::/, $class); + return File::Spec->catdir($bz_locations->{extensionsdir}, $name); } -sub template_dir { return extension_template_directory(@_); } -sub package_dir { return extension_package_directory(@_); } -sub web_dir { return extension_web_directory(@_); } - -###################### -# Helper Subroutines # -###################### - -# In order to not conflict with extensions' private subroutines, any helpers -# here should start with a double underscore. - -# This is for methods that can optionally be overridden in Config.pm. -# It falls back to the local implementation if $class cannot do -# the method. This is necessary because Config.pm is not a subclass of -# Bugzilla::Extension. -sub __do_call { - my ($class, $method, @args) = @_; - if ($class->can($method)) { - return $class->$method(@args); - } - my $function_ref; - { no strict 'refs'; $function_ref = \&{$method}; } - return $function_ref->($class, @args); +sub template_dir { + my ($class) = @_; + return File::Spec->catdir($class->package_dir, "template"); +} + +sub web_dir { + my ($class) = @_; + return File::Spec->catdir($class->package_dir, "web"); } 1; diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index 3113388f7..c4813abde 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -18,8 +18,7 @@ use strict; use warnings; use Bugzilla::Constants; -use Bugzilla::Install::Util qw(install_string bin_loc success - extension_requirement_packages); +use Bugzilla::Install::Util qw(install_string bin_loc success); use List::Util qw(max); use Term::ANSIColor; use CPAN::Meta; diff --git a/Bugzilla/Install/Util.pm b/Bugzilla/Install/Util.pm index 33b3d43d3..a1473f0e4 100644 --- a/Bugzilla/Install/Util.pm +++ b/Bugzilla/Install/Util.pm @@ -30,10 +30,6 @@ our @EXPORT_OK = qw( bin_loc get_version_and_os extension_code_files - extension_package_directory - extension_requirement_packages - extension_template_directory - extension_web_directory i_am_persistent indicate_progress install_string @@ -136,121 +132,7 @@ sub extension_code_files { push(@files, \@load_files); } - my @additional; - my $datadir = bz_locations()->{'datadir'}; - my $addl_file = "$datadir/extensions/additional"; - if (-e $addl_file) { - open(my $fh, '<', $addl_file) || die "$addl_file: $!"; - @additional = map { trim($_) } <$fh>; - close($fh); - } - return (\@files, \@additional); -} - -# Used by _get_extension_requirements in Bugzilla::Install::Requirements. -sub extension_requirement_packages { - # If we're in a .cgi script or some time that's not the requirements phase, - # just use Bugzilla->extensions. This avoids running the below code during - # a normal Bugzilla page, which is important because the below code - # doesn't actually function right if it runs after - # Bugzilla::Extension->load_all (because stuff has already been loaded). - # (This matters because almost every page calls Bugzilla->feature, which - # calls OPTIONAL_MODULES, which calls this method.) - # - # We check if Bugzilla.pm is already loaded, instead of doing a "require", - # because we *do* want the code lower down to run during the Requirements - # phase of checksetup.pl, instead of Bugzilla->extensions, and Bugzilla.pm - # actually *can* be loaded during the Requirements phase if all the - # requirements have already been installed. - if ($INC{'Bugzilla.pm'}) { - return Bugzilla->extensions; - } - my $packages = _cache()->{extension_requirement_packages}; - return $packages if $packages; - $packages = []; - my %package_map; - - my ($file_sets, $extra_packages) = extension_code_files('requirements only'); - foreach my $file_set (@$file_sets) { - my $file = shift @$file_set; - my $name = require $file; - if ($name =~ /^\d+$/) { - die install_string('extension_must_return_name', - { file => $file, returned => $name }); - } - my $package = "Bugzilla::Extension::$name"; - if ($package->can('package_dir')) { - $package->package_dir($file); - } - else { - extension_package_directory($package, $file); - } - $package_map{$file} = $package; - push(@$packages, $package); - } - foreach my $package (@$extra_packages) { - eval("require $package") || die $@; - push(@$packages, $package); - } - - _cache()->{extension_requirement_packages} = $packages; - # Used by Bugzilla::Extension->load if it's called after this method - # (which only happens during checksetup.pl, currently). - _cache()->{extension_requirement_package_map} = \%package_map; - return $packages; -} - -# Used in this file and in Bugzilla::Extension. -sub extension_template_directory { - my $extension = shift; - my $class = ref($extension) || $extension; - my $base_dir = extension_package_directory($class); - if ($base_dir eq bz_locations->{'extensionsdir'}) { - return bz_locations->{'templatedir'}; - } - return "$base_dir/template"; -} - -# Used in this file and in Bugzilla::Extension. -sub extension_web_directory { - my $extension = shift; - my $class = ref($extension) || $extension; - my $base_dir = extension_package_directory($class); - return "$base_dir/web"; -} - -# For extensions that are in the extensions/ dir, this both sets and fetches -# the name of the directory that stores an extension's "stuff". We need this -# when determining the template directory for extensions (or other things -# that are relative to the extension's base directory). -sub extension_package_directory { - my ($invocant, $file) = @_; - my $class = ref($invocant) || $invocant; - - # $file is set on the first invocation, store the value in the extension's - # package for retrieval on subsequent calls - my $var; - { - no warnings 'once'; - no strict 'refs'; - $var = \${"${class}::EXTENSION_PACKAGE_DIR"}; - } - if ($file) { - $$var = dirname($file); - } - my $value = $$var; - - # This is for extensions loaded from data/extensions/additional. - if (!$value) { - my $short_path = $class; - $short_path =~ s/::/\//g; - $short_path .= ".pm"; - my $long_path = $INC{$short_path}; - die "$short_path is not in \%INC" if !$long_path; - $value = $long_path; - $value =~ s/\.pm//; - } - return $value; + return (\@files); } sub indicate_progress { @@ -422,45 +304,18 @@ sub _template_lang_directories { # Used by template_include_path. sub _template_base_directories { - # First, we add extension template directories, because extension templates - # override standard templates. Extensions may be localized in the same way - # that Bugzilla templates are localized. - # - # We use extension_requirement_packages instead of Bugzilla->extensions - # because this fucntion is called during the requirements phase of - # installation (so Bugzilla->extensions isn't available). - my $extensions = extension_requirement_packages(); my @template_dirs; - foreach my $extension (@$extensions) { - my $dir; - # If there's a template_dir method available in the extension - # package, then call it. Note that this has to be defined in - # Config.pm for extensions that have a Config.pm, to be effective - # during the Requirements phase of checksetup.pl. - if ($extension->can('template_dir')) { - $dir = $extension->template_dir; - } - else { - $dir = extension_template_directory($extension); - } - if (-d $dir) { - push(@template_dirs, $dir); - } - } - # Extensions may also contain *only* templates, in which case they - # won't show up in extension_requirement_packages. foreach my $path (_extension_paths()) { next if !-d $path; - if (!-e "$path/Extension.pm" and !-e "$path/Config.pm" - and -d "$path/template") - { + if ( -d "$path/template") { push(@template_dirs, "$path/template"); } } - push(@template_dirs, bz_locations()->{'templatedir'}); + state $bz_locations = bz_locations(); + push(@template_dirs, $bz_locations->{'templatedir'}); return \@template_dirs; } diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm index 99986f89f..09bd347d2 100644 --- a/extensions/BMO/Extension.pm +++ b/extensions/BMO/Extension.pm @@ -2532,15 +2532,16 @@ sub install_filesystem { my $extensions_dir = bz_locations()->{extensionsdir}; $create_files->{__lbheartbeat__} = { perms => Bugzilla::Install::Filesystem::WS_SERVE, - overwrite => 1, + overwrite => 1, # the original value for this was wrong, overwrite it contents => 'httpd OK', }; + # version.json needs to have a source attribute pointing to # our repository. We already have this information in the (static) # contribute.json file, so parse that in my $json = JSON::XS->new->pretty->utf8->canonical(); - my $contribute = eval { + my $contribute = eval { $json->decode(scalar read_file(bz_locations()->{cgi_path} . "/contribute.json")); }; my $commit = `git rev-parse HEAD`; diff --git a/mod_perl.pl b/mod_perl.pl index f3beb88db..03438dd4d 100644 --- a/mod_perl.pl +++ b/mod_perl.pl @@ -75,7 +75,7 @@ my $conf = Bugzilla::ModPerl->apache_config($cgi_path); $server->add_config([ grep { length $_ } split("\n", $conf)]); # Pre-load all extensions -$Bugzilla::extension_packages = Bugzilla::Extension->load_all(); +Bugzilla::Extension->load_all(); Bugzilla->preload_features(); -- cgit v1.2.3-24-g4f1b