testserver.pl 7.57 KB
Newer Older
1
#!/usr/bin/perl
2 3 4
# 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/.
5
#
6 7
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
8

9
# testserver.pl is invoked with the baseurl of the Bugzilla installation
10 11 12
# as its only argument.  It attempts to troubleshoot as many installation
# issues as possible.

13
use 5.10.1;
14
use strict;
15 16
use warnings;

17
use lib qw(. lib);
18

19
use Bugzilla;
20
use Bugzilla::Constants;
21
use Bugzilla::Util;
22

23
use Socket;
24

25 26
my $datadir = bz_locations()->{'datadir'};

27 28 29
eval "require LWP; require LWP::UserAgent;";
my $lwp = $@ ? 0 : 1;

30 31 32 33
if ((@ARGV != 1) || ($ARGV[0] !~ /^https?:/i)) {
  say "Usage: $0 <URL to this Bugzilla installation>";
  say "e.g.:  $0 http://www.mycompany.com/bugzilla";
  exit(1);
34 35 36
}


37
# Try to determine the GID used by the web server.
38 39
my @pscmds
  = ('ps -eo comm,gid', 'ps -acxo command,gid', 'ps -acxo command,rgid');
40
my $sgid = 0;
41
if (!ON_WINDOWS) {
42 43 44 45 46 47
  foreach my $pscmd (@pscmds) {
    open PH, '-|', "$pscmd 2>/dev/null";
    while (my $line = <PH>) {
      if ($line =~ /^(?:\S*\/)?(?:httpd|apache?)2?\s+(\d+)$/) {
        $sgid = $1 if $1 > $sgid;
      }
48
    }
49 50
    close(PH);
  }
51 52 53
}

# Determine the numeric GID of $webservergroup
54
my $webgroupnum    = 0;
55
my $webservergroup = Bugzilla->localconfig->{webservergroup};
56
if ($webservergroup =~ /^(\d+)$/) {
57
  $webgroupnum = $1;
58 59
}
else {
60
  eval { $webgroupnum = (getgrnam $webservergroup) || 0; };
61 62 63 64
}

# Check $webservergroup against the server's GID
if ($sgid > 0) {
65 66
  if ($webservergroup eq "") {
    say "WARNING \$webservergroup is set to an empty string.
67
That is a very insecure practice. Please refer to the
68
Bugzilla documentation.";
69 70 71 72 73 74 75
  }
  elsif ($webgroupnum == $sgid || Bugzilla->localconfig->{use_suexec}) {
    say "TEST-OK Webserver is running under group id in \$webservergroup.";
  }
  else {
    say
      "TEST-WARNING Webserver is running under group id not matching \$webservergroup.
76
This if the tests below fail, this is probably the problem.
77
Please refer to the web server configuration section of the Bugzilla guide. 
78
If you are using virtual hosts or suexec, this warning may not apply.";
79
  }
80
}
81
elsif (!ON_WINDOWS) {
82
  say "TEST-WARNING Failed to find the GID for the 'httpd' process, unable
83
to validate webservergroup.";
84 85 86
}


87
# Try to fetch a static file (padlock.png)
88
$ARGV[0] =~ s/\/$//;
89
my $url = $ARGV[0] . "/images/padlock.png";
90
if (fetch($url)) {
91 92 93 94
  say "TEST-OK Got padlock picture.";
}
else {
  say "TEST-FAILED Fetch of images/padlock.png failed
95
Your web server could not fetch $url.
96
Check your web server configuration and try again.";
97
  exit(1);
98 99 100
}

# Try to execute a cgi script
101
my $response = clean_text(fetch($ARGV[0] . "/testagent.cgi"));
102
if ($response =~ /^OK (.*)$/) {
103 104 105 106
  say "TEST-OK Webserver is executing CGIs via $1.";
}
elsif ($response =~ /^#!/) {
  say "TEST-FAILED Webserver is fetching rather than executing CGI files.
107
Check the AddHandler statement in your httpd.conf file.";
108 109 110 111
  exit(1);
}
else {
  say "TEST-FAILED Webserver is not executing CGI files.";
112 113
}

114
# Make sure that the web server is honoring .htaccess files
115 116
my $localconfig = bz_locations()->{'localconfig'};
$localconfig =~ s~^\./~~;
117
$url      = $ARGV[0] . "/$localconfig";
118 119
$response = fetch($url);
if ($response) {
120
  say "TEST-FAILED Webserver is permitting fetch of $url.
121
This is a serious security problem.
122
Check your web server configuration.";
123 124 125 126
  exit(1);
}
else {
  say "TEST-OK Webserver is preventing fetch of $url.";
127 128
}

129
# Test chart generation
130 131
eval 'use GD';
if ($@ eq '') {
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
  undef $/;

  # Ensure major versions of GD and libgd match
  # Windows's GD module include libgd.dll, guaranteed to match
  if (!ON_WINDOWS) {
    my $gdlib = `gdlib-config --version 2>&1` || "";
    $gdlib =~ s/\n$//;
    if (!$gdlib) {
      say "TEST-WARNING Failed to run gdlib-config; can't compare " . "GD versions.";
    }
    else {
      my $gd = $GD::VERSION;

      my $verstring = "GD version $gd, libgd version $gdlib";

      $gdlib =~ s/^([^\.]+)\..*/$1/;
148
      $gd    =~ s/^([^\.]+)\..*/$1/;
149 150 151 152 153 154 155

      if ($gdlib == $gd) {
        say "TEST-OK $verstring; Major versions match.";
      }
      else {
        say "TEST-FAILED $verstring; Major versions do not match.";
      }
156
    }
157 158 159 160 161
  }

  # Test GD
  eval {
    my $image = new GD::Image(100, 100);
162
    my $black = $image->colorAllocate(0, 0, 0);
163
    my $white = $image->colorAllocate(255, 255, 255);
164 165
    my $red   = $image->colorAllocate(255, 0, 0);
    my $blue  = $image->colorAllocate(0, 0, 255);
166 167 168 169
    $image->transparent($white);
    $image->rectangle(0, 0, 99, 99, $black);
    $image->arc(50, 50, 95, 75, 0, 360, $blue);
    $image->fill(50, 50, $red);
170

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    if ($image->can('png')) {
      create_file("$datadir/testgd-local.png", $image->png);
      check_image("$datadir/testgd-local.png", 'GD');
    }
    else {
      say "TEST-FAILED GD doesn't support PNG generation.";
    }
  };
  if ($@ ne '') {
    say "TEST-FAILED GD returned: $@";
  }

  # Test Chart
  eval 'use Chart::Lines';
  if ($@) {
    say "TEST-FAILED Chart::Lines is not installed.";
  }
  else {
189
    eval {
190 191 192 193 194 195 196
      my $chart = Chart::Lines->new(400, 400);

      $chart->add_pt('foo', 30, 25);
      $chart->add_pt('bar', 16, 32);

      $chart->png("$datadir/testchart-local.png");
      check_image("$datadir/testchart-local.png", "Chart");
197 198
    };
    if ($@ ne '') {
199
      say "TEST-FAILED Chart returned: $@";
200
    }
201
  }
202

203 204 205 206 207 208 209 210
  eval 'use Template::Plugin::GD::Image';
  if ($@) {
    say "TEST-FAILED Template::Plugin::GD is not installed.";
  }
  else {
    say "TEST-OK Template::Plugin::GD is installed.";
  }
}
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
sub fetch {
  my $url = shift;
  my $rtn;
  if ($lwp) {
    my $req = HTTP::Request->new(GET => $url);
    my $ua  = LWP::UserAgent->new;
    my $res = $ua->request($req);
    $rtn = ($res->is_success ? $res->content : undef);
  }
  elsif ($url =~ /^https:/i) {
    die("You need LWP installed to use https with testserver.pl");
  }
  else {
    my ($host, $port, $file) = ('', 80, '');
    if ($url =~ m#^http://([^:]+):(\d+)(/.*)#i) {
      ($host, $port, $file) = ($1, $2, $3);
    }
    elsif ($url =~ m#^http://([^/]+)(/.*)#i) {
      ($host, $file) = ($1, $2);
231 232
    }
    else {
233
      die("Cannot parse url");
234
    }
235

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    my $proto = getprotobyname('tcp');
    socket(SOCK, PF_INET, SOCK_STREAM, $proto);
    my $sin = sockaddr_in($port, inet_aton($host));
    if (connect(SOCK, $sin)) {
      binmode SOCK;
      select((select(SOCK), $| = 1)[0]);

      # get content
      print SOCK "GET $file HTTP/1.0\015\012host: $host:$port\015\012\015\012";
      my $header = '';
      while (defined(my $line = <SOCK>)) {
        last if $line eq "\015\012";
        $header .= $line;
      }
      my $content = '';
      while (defined(my $line = <SOCK>)) {
        $content .= $line;
      }

      my ($status) = $header =~ m#^HTTP/\d+\.\d+ (\d+)#;
      $rtn = (($status =~ /^2\d\d/) ? $content : undef);
257
    }
258 259
  }
  return ($rtn);
260 261
}

262
sub check_image {
263 264 265 266 267 268 269 270 271
  my ($local_file, $library) = @_;
  my $filedata = read_file($local_file);
  if ($filedata =~ /^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A/) {
    say "TEST-OK $library library generated a good PNG image.";
    unlink $local_file;
  }
  else {
    say "TEST-WARNING $library library did not generate a good PNG.";
  }
272 273 274
}

sub create_file {
275 276 277 278 279
  my ($filename, $content) = @_;
  open(FH, ">", $filename) or die "Failed to create $filename: $!\n";
  binmode FH;
  print FH $content;
  close FH;
280 281 282
}

sub read_file {
283 284 285 286 287 288
  my ($filename) = @_;
  open(FH, '<', $filename) or die "Failed to open $filename: $!\n";
  binmode FH;
  my $content = <FH>;
  close FH;
  return $content;
289
}