1
# -*- Mode: perl; indent-tabs-mode: nil -*-
3
# The contents of this file are subject to the Mozilla Public
4
# License Version 1.1 (the "License"); you may not use this file
5
# except in compliance with the License. You may obtain a copy of
6
# the License at http://www.mozilla.org/MPL/
8
# Software distributed under the License is distributed on an "AS
9
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
# implied. See the License for the specific language governing
11
# rights and limitations under the License.
13
# The Original Code is the Bugzilla Bug Tracking System.
15
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
16
# Marc Schumann <wurblzap@gmail.com>
18
package Bugzilla::Install::Requirements;
20
# NOTE: This package MUST NOT "use" any Bugzilla modules other than
21
# Bugzilla::Constants, anywhere. We may "use" standard perl modules.
23
# Subroutines may "require" and "import" from modules, but they
28
use List::Util qw(max);
32
use base qw(Exporter);
39
display_version_and_os
45
use Bugzilla::Constants;
47
# The below two constants are subroutines so that they can implement
48
# a hook. Other than that they are actually constants.
50
# "package" is the perl package we're checking for. "module" is the name
51
# of the actual module we load with "require" to see if the package is
52
# installed or not. "version" is the version we need, or 0 if we'll accept
55
# "blacklist" is an arrayref of regular expressions that describe versions that
56
# are 'blacklisted'--that is, even if the version is high enough, Bugzilla
57
# will refuse to say that it's OK to run with that version.
58
sub REQUIRED_MODULES {
59
my $perl_ver = sprintf('%vd', $^V);
64
# Perl 5.10 requires CGI 3.33 due to a taint issue when
65
# uploading attachments, see bug 416382.
66
version => (vers_cmp($perl_ver, '5.10') > -1) ? '3.33' : '2.93'
69
package => 'TimeDate',
70
module => 'Date::Format',
79
package => 'PathTools',
80
module => 'File::Spec',
84
package => 'Template-Toolkit',
89
package => 'Email-Send',
90
module => 'Email::Send',
91
version => ON_WINDOWS ? '2.16' : '2.00'
94
# This will pull in Email::MIME for us, also.
95
package => 'Email-MIME-Modifier',
96
module => 'Email::MIME::Modifier',
101
my $all_modules = _get_extension_requirements(
102
'REQUIRED_MODULES', \@modules);
106
sub OPTIONAL_MODULES {
112
feature => 'Graphical Reports, New Charts, Old Charts'
115
package => 'Template-GD',
116
# This module tells us whether or not Template-GD is installed
117
# on Template-Toolkits after 2.14, and still works with 2.14 and lower.
118
module => 'Template::Plugin::GD::Image',
120
feature => 'Graphical Reports'
124
module => 'Chart::Base',
126
feature => 'New Charts, Old Charts'
129
package => 'GDGraph',
130
module => 'GD::Graph',
132
feature => 'Graphical Reports'
135
package => 'GDTextUtil',
136
module => 'GD::Text',
138
feature => 'Graphical Reports'
141
package => 'XML-Twig',
142
module => 'XML::Twig',
144
feature => 'Move Bugs Between Installations'
147
package => 'MIME-tools',
148
# MIME::Parser is packaged as MIME::Tools on ActiveState Perl
149
module => ON_WINDOWS ? 'MIME::Tools' : 'MIME::Parser',
151
feature => 'Move Bugs Between Installations'
154
package => 'libwww-perl',
155
module => 'LWP::UserAgent',
157
feature => 'Automatic Update Notifications'
160
package => 'PatchReader',
161
module => 'PatchReader',
163
feature => 'Patch Viewer'
166
package => 'PerlMagick',
167
module => 'Image::Magick',
169
feature => 'Optionally Convert BMP Attachments to PNGs'
172
package => 'perl-ldap',
173
module => 'Net::LDAP',
175
feature => 'LDAP Authentication'
178
package => 'SOAP-Lite',
179
module => 'SOAP::Lite',
181
feature => 'XML-RPC Interface'
184
# We need the 'utf8_mode' method of HTML::Parser, for HTML::Scrubber.
185
package => 'HTML-Parser',
186
module => 'HTML::Parser',
188
feature => 'More HTML in Product/Group Descriptions'
191
package => 'HTML-Scrubber',
192
module => 'HTML::Scrubber',
194
feature => 'More HTML in Product/Group Descriptions'
199
package => 'Email-MIME-Attachment-Stripper',
200
module => 'Email::MIME::Attachment::Stripper',
202
feature => 'Inbound Email'
205
package => 'Email-Reply',
206
module => 'Email::Reply',
208
feature => 'Inbound Email'
213
package => 'mod_perl',
214
module => 'mod_perl2',
215
version => '1.999022',
216
feature => 'mod_perl'
220
# Even very new releases of perl (5.8.5) don't come with this version,
221
# so I didn't want to make it a general requirement just for
222
# running under mod_cgi.
223
# If Perl 5.10 is installed, then CGI 3.33 is already required. So this
224
# check is only relevant with Perl 5.8.x.
225
my $perl_ver = sprintf('%vd', $^V);
226
if (vers_cmp($perl_ver, '5.10') < 0) {
227
push(@modules, { package => 'CGI.pm',
230
feature => 'mod_perl' });
233
my $all_modules = _get_extension_requirements(
234
'OPTIONAL_MODULES', \@modules);
238
# This implements the install-requirements hook described in Bugzilla::Hook.
239
sub _get_extension_requirements {
240
my ($function, $base_modules) = @_;
242
# get a list of all extensions
243
my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*");
244
foreach my $extension (@extensions) {
245
my $file = "$extension/code/install-requirements.pl";
248
# This is a very liberal Safe.
249
$safe->permit(qw(:browse require entereval caller));
255
my $modules = eval { &{$safe->varglob($function)}($base_modules) };
256
next unless $modules;
257
push(@all_modules, @$modules);
261
unshift(@all_modules, @$base_modules);
262
return \@all_modules;
265
sub check_requirements {
268
print "\nChecking perl modules...\n" if $output;
269
my $root = ROOT_USER;
270
my %missing = _check_missing(REQUIRED_MODULES, $output);
272
print "\nChecking available perl DBD modules...\n" if $output;
273
my $have_one_dbd = 0;
274
my $db_modules = DB_MODULE;
275
foreach my $db (keys %$db_modules) {
276
my $dbd = $db_modules->{$db}->{dbd};
277
$have_one_dbd = 1 if have_vers($dbd, $output);
280
print "\nThe following Perl modules are optional:\n" if $output;
281
my %missing_optional = _check_missing(OPTIONAL_MODULES, $output);
283
# If we're running on Windows, reset the input line terminator so that
284
# console input works properly - loading CGI tends to mess it up
285
$/ = "\015\012" if ON_WINDOWS;
287
my $pass = !scalar(keys %missing) && $have_one_dbd;
290
one_dbd => $have_one_dbd,
291
missing => \%missing,
292
optional => \%missing_optional,
293
any_missing => !$pass || scalar(keys %missing_optional),
297
# A helper for check_requirements
299
my ($modules, $output) = @_;
302
foreach my $module (@$modules) {
303
unless (have_vers($module, $output)) {
304
$missing{$module->{package}} = $module;
311
# Returns the build ID of ActivePerl. If several versions of
312
# ActivePerl are installed, it won't be able to know which one
313
# you are currently running. But that's our best guess.
314
sub _get_activestate_build_id {
315
eval 'use Win32::TieRegistry';
317
my $key = Win32::TieRegistry->new('LMachine\Software\ActiveState\ActivePerl')
319
return $key->GetValue("CurrentVersion");
322
sub print_module_instructions {
323
my ($check_results, $output) = @_;
325
# We only print these notes if we have to.
326
if ((!$output && %{$check_results->{missing}})
327
|| ($output && $check_results->{any_missing}))
329
print "\n* NOTE: You must run any commands listed below as "
330
. ROOT_USER . ".\n\n";
333
my $perl_ver = sprintf('%vd', $^V);
335
# URL when running Perl 5.8.x.
336
my $url_to_theory58S = 'http://theoryx5.uwinnipeg.ca/ppms';
339
# Packages for Perl 5.10 are not compatible with Perl 5.8.
340
if (vers_cmp($perl_ver, '5.10') > -1) {
341
$url_to_theory58S = 'http://cpan.uwinnipeg.ca/PPMPackages/10xx/';
343
# ActivePerl older than revision 819 require an additional command.
344
if (_get_activestate_build_id() < 819) {
345
$repo_up_cmd = <<EOT;
347
* Then you have to do (also as an Administrator): *
349
* ppm repo up theory58S *
351
* Do that last command over and over until you see "theory58S" at the *
352
* top of the displayed list. *
356
***********************************************************************
357
* Note For Windows Users *
358
***********************************************************************
359
* In order to install the modules listed below, you first have to run *
360
* the following command as an Administrator: *
362
* ppm repo add theory58S $url_to_theory58S
364
***********************************************************************
370
if (my %missing = %{$check_results->{missing}}) {
372
***********************************************************************
374
***********************************************************************
375
* Bugzilla requires you to install some Perl modules which are either *
376
* missing from your system, or the version on your system is too old. *
378
* The latest versions of each module can be installed by running the *
380
***********************************************************************
383
print "COMMANDS:\n\n";
384
foreach my $package (keys %missing) {
385
my $command = install_command($missing{$package});
391
if (!$check_results->{one_dbd}) {
393
***********************************************************************
395
***********************************************************************
396
* In order to access your database, Bugzilla requires that the *
397
* correct "DBD" module be installed for the database that you are *
400
* Pick and run the correct command below for the database that you *
401
* plan to use with Bugzilla. *
402
***********************************************************************
407
my %db_modules = %{DB_MODULE()};
408
foreach my $db (keys %db_modules) {
409
my $command = install_command($db_modules{$db}->{dbd});
410
printf "%10s: \%s\n", $db_modules{$db}->{name}, $command;
411
print ' ' x 12 . "Minimum version required: "
412
. $db_modules{$db}->{dbd}->{version} . "\n";
417
return unless $output;
419
if (my %missing = %{$check_results->{optional}}) {
421
**********************************************************************
423
**********************************************************************
424
* Certain Perl modules are not required by Bugzilla, but by *
425
* installing the latest version you gain access to additional *
428
* The optional modules you do not have installed are listed below, *
429
* with the name of the feature they enable. If you want to install *
430
* one of these modules, just run the appropriate command in the *
431
* "COMMANDS TO INSTALL" section. *
432
**********************************************************************
435
# We want to sort them so that they are ordered by feature.
436
my @missing_names = sort {$missing{$a}->{feature}
437
cmp $missing{$b}->{feature}} (keys %missing);
439
# Now we have to determine how large the table cols will be.
440
my $longest_name = max(map(length($_), @missing_names));
442
# The first column header is at least 11 characters long.
443
$longest_name = 11 if $longest_name < 11;
445
# The table is 71 characters long. There are seven mandatory
446
# characters (* and space) in the string. So, we have a total
447
# of 64 characters to work with.
448
my $remaining_space = 64 - $longest_name;
449
print '*' x 71 . "\n";
450
printf "* \%${longest_name}s * %-${remaining_space}s *\n",
451
'MODULE NAME', 'ENABLES FEATURE(S)';
452
print '*' x 71 . "\n";
453
foreach my $name (@missing_names) {
454
printf "* \%${longest_name}s * %-${remaining_space}s *\n",
455
$name, $missing{$name}->{feature};
457
print '*' x 71 . "\n";
459
print "COMMANDS TO INSTALL:\n\n";
460
foreach my $module (@missing_names) {
461
my $command = install_command($missing{$module});
462
printf "%15s: $command\n", $module;
470
return 1 if (Bugzilla->params->{'webdotbase'} =~ /^https?:/);
472
printf("Checking for %15s %-9s ", "GraphViz", "(any)") if $output;
475
if(-x Bugzilla->params->{'webdotbase'}) {
476
print "ok: found\n" if $output;
479
print "not a valid executable: " . Bugzilla->params->{'webdotbase'} . "\n";
482
my $webdotdir = bz_locations()->{'webdotdir'};
483
# Check .htaccess allows access to generated images
484
if (-e "$webdotdir/.htaccess") {
485
my $htaccess = new IO::File("$webdotdir/.htaccess", 'r')
486
|| die "$webdotdir/.htaccess: " . $!;
487
if (!grep(/png/, $htaccess->getlines)) {
488
print "Dependency graph images are not accessible.\n";
489
print "delete $webdotdir/.htaccess and re-run checksetup.pl to fix.\n";
497
sub display_version_and_os {
498
# Display version information
499
printf "\n* This is Bugzilla " . BUGZILLA_VERSION . " on perl %vd\n",
501
my @os_details = POSIX::uname;
502
# 0 is the name of the OS, 2 is the major version,
503
my $os_name = $os_details[0] . ' ' . $os_details[2];
506
$os_name = Win32::GetOSName();
508
# 3 is the minor version.
509
print "* Running on $os_name $os_details[3]\n"
512
# This was originally clipped from the libnet Makefile.PL, adapted here to
513
# use the below vers_cmp routine for accurate version checking.
515
my ($params, $output) = @_;
516
my $module = $params->{module};
517
my $package = $params->{package};
520
$package =~ s/::/-/g;
522
my $wanted = $params->{version};
524
my ($msg, $vnum, $vstr);
526
printf("Checking for %15s %-9s ", $package, !$wanted?'(any)':"(v$wanted)")
529
eval "require $module;";
531
# VERSION is provided by UNIVERSAL::
532
$vnum = eval { $module->VERSION } || -1;
534
# CGI's versioning scheme went 2.75, 2.751, 2.752, 2.753, 2.76
535
# That breaks the standard version tests, so we need to manually correct
537
if ($module eq 'CGI' && $vnum =~ /(2\.7\d)(\d+)/) {
538
$vnum = $1 . "." . $2;
541
if ($vnum eq "-1") { # string compare just in case it's non-numeric
544
elsif (vers_cmp($vnum,"0") > -1) {
545
$vstr = "found v$vnum";
548
$vstr = "found unknown version";
551
my $vok = (vers_cmp($vnum,$wanted) > -1);
553
if ($vok && $params->{blacklist}) {
554
$blacklisted = grep($vnum =~ /$_/, @{$params->{blacklist}});
555
$vok = 0 if $blacklisted;
558
my $ok = $vok ? "ok:" : "";
559
my $black_string = $blacklisted ? "(blacklisted)" : "";
560
print "$ok $vstr $black_string\n" if $output;
564
# This is taken straight from Sort::Versions 1.5, which is not included
565
# with perl by default.
569
# Remove leading zeroes - Bug 344661
570
$a =~ s/^0*(\d.+)/$1/;
571
$b =~ s/^0*(\d.+)/$1/;
573
my @A = ($a =~ /([-.]|\d+|[^-.\d]+)/g);
574
my @B = ($b =~ /([-.]|\d+|[^-.\d]+)/g);
580
if ($A eq '-' and $B eq '-') {
582
} elsif ( $A eq '-' ) {
584
} elsif ( $B eq '-') {
586
} elsif ($A eq '.' and $B eq '.') {
588
} elsif ( $A eq '.' ) {
590
} elsif ( $B eq '.' ) {
592
} elsif ($A =~ /^\d+$/ and $B =~ /^\d+$/) {
593
if ($A =~ /^0/ || $B =~ /^0/) {
594
return $A cmp $B if $A cmp $B;
596
return $A <=> $B if $A <=> $B;
601
return $A cmp $B if $A cmp $B;
607
sub install_command {
609
my ($command, $package);
612
$command = 'ppm install %s';
613
$package = $module->{package};
616
$command = "$^X -MCPAN -e 'install \"\%s\"'";
617
# Non-Windows installations need to use module names, because
618
# CPAN doesn't understand package names.
619
$package = $module->{module};
621
return sprintf $command, $package;
631
Bugzilla::Install::Requirements - Functions and variables dealing
632
with Bugzilla's perl-module requirements.
636
This module is used primarily by C<checksetup.pl> to determine whether
637
or not all of Bugzilla's prerequisites are installed. (That is, all the
638
perl modules it requires.)
644
=item C<REQUIRED_MODULES>
646
An arrayref of hashrefs that describes the perl modules required by
647
Bugzilla. The hashes have two keys, C<name> and C<version>, which
648
represent the name of the module and the version that we require.
656
=item C<check_requirements($output)>
658
Description: This checks what optional or required perl modules
659
are installed, like C<checksetup.pl> does.
661
Params: C<$output> - C<true> if you want the function to print
662
out information about what it's doing,
663
and the versions of everything installed.
664
If you don't pass the minimum requirements,
665
the will always print out something,
666
regardless of this parameter.
668
Returns: A hashref containing three values:
669
C<pass> - Whether or not we have all the mandatory
671
C<missing> - A hash showing which mandatory requirements
672
are missing. The key is the module name,
673
and the value is the version we require.
674
C<optional> - Which optional modules are installed and
675
up-to-date enough for Bugzilla.
677
=item C<check_graphviz($output)>
679
Description: Checks if the graphviz binary specified in the
680
C<webdotbase> parameter is a valid binary, or a valid URL.
682
Params: C<$output> - C<$true> if you want the function to
683
print out information about what it's doing.
685
Returns: C<1> if the check was successful, C<0> otherwise.
687
=item C<vers_cmp($a, $b)>
689
Description: This is a comparison function, like you would use in
690
C<sort>, except that it compares two version numbers.
691
It's actually identical to versioncmp from
694
Params: c<$a> and C<$b> are versions you want to compare.
696
Returns: -1 if $a is less than $b, 0 if they are equal, and
697
1 if $a is greater than $b.
699
=item C<have_vers($module, $output)>
701
Description: Tells you whether or not you have the appropriate
702
version of the module requested. It also prints
703
out a message to the user explaining the check
706
Params: C<$module> - A hashref, in the format of an item from
707
L</REQUIRED_MODULES>.
708
C<$output> - Set to true if you want this function to
709
print information to STDOUT about what it's
712
Returns: C<1> if you have the module installed and you have the
713
appropriate version. C<0> otherwise.
715
=item C<install_command($module)>
717
Description: Prints out the appropriate command to install the
718
module specified, depending on whether you're
721
Params: C<$module> - A hashref, in the format of an item from
722
L</REQUIRED_MODULES>.