summaryrefslogtreecommitdiffstats
path: root/Bugzilla
diff options
context:
space:
mode:
authorDylan William Hardison <dylan@hardison.net>2017-07-03 00:46:48 +0200
committerDylan William Hardison <dylan@hardison.net>2017-07-07 00:19:20 +0200
commitc05a32842ee123801b628f5fe0472cb6abbbafdb (patch)
tree6863912f5d307682ebaff749fb57a2bcc8f5ae9b /Bugzilla
parent7176e6d8dabbdd577e99c18fccb99fc9a09e1760 (diff)
downloadbugzilla-c05a32842ee123801b628f5fe0472cb6abbbafdb.tar.gz
bugzilla-c05a32842ee123801b628f5fe0472cb6abbbafdb.tar.xz
Bug 1376023 - Undo accidental reversion of bug 1352907
This reverts commit 38b13ae3f1885faa0da1d0040a0dda87dc786515. * change __lbheartbeat__ to "httpd OK" again
Diffstat (limited to 'Bugzilla')
-rw-r--r--Bugzilla/Extension.pm167
-rw-r--r--Bugzilla/Install/Requirements.pm3
-rw-r--r--Bugzilla/Install/Util.pm153
3 files changed, 62 insertions, 261 deletions
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;
}