~ubuntu-branches/ubuntu/oneiric/bugzilla/oneiric

« back to all changes in this revision

Viewing changes to Bugzilla/Install/Filesystem.pm

  • Committer: Bazaar Package Importer
  • Author(s): Raphael Bossek
  • Date: 2008-06-27 22:34:34 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20080627223434-0ib57vstn43bb4a3
Tags: 3.0.4.1-1
* Update of French, Russian and German translations. (closes: #488251)
* Added Bulgarian and Belarusian translations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- Mode: perl; indent-tabs-mode: nil -*-
2
 
#
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/
7
 
#
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.
12
 
#
13
 
# The Original Code is the Bugzilla Bug Tracking System.
14
 
#
15
 
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
16
 
#                 Bill Barry <after.fallout@gmail.com>
17
 
 
18
 
package Bugzilla::Install::Filesystem;
19
 
 
20
 
# NOTE: This package may "use" any modules that it likes,
21
 
# and localconfig is available. However, all functions in this
22
 
# package should assume that:
23
 
#
24
 
# * Templates are not available.
25
 
# * Files do not have the correct permissions.
26
 
# * The database does not exist.
27
 
 
28
 
use strict;
29
 
 
30
 
use Bugzilla::Constants;
31
 
use Bugzilla::Error;
32
 
use Bugzilla::Install::Localconfig;
33
 
use Bugzilla::Util;
34
 
 
35
 
use File::Find;
36
 
use File::Path;
37
 
use File::Basename;
38
 
use IO::File;
39
 
use POSIX ();
40
 
 
41
 
use base qw(Exporter);
42
 
our @EXPORT = qw(
43
 
    update_filesystem
44
 
    create_htaccess
45
 
    fix_all_file_permissions
46
 
);
47
 
 
48
 
# This looks like a constant because it effectively is, but
49
 
# it has to call other subroutines and read the current filesystem,
50
 
# so it's defined as a sub. This is not exported, so it doesn't have
51
 
# a perldoc. However, look at the various hashes defined inside this 
52
 
# function to understand what it returns. (There are comments throughout.)
53
 
#
54
 
# The rationale for the file permissions is that the web server generally 
55
 
# runs as apache, so the cgi scripts should not be writable for apache,
56
 
# otherwise someone may find it possible to change the cgis when exploiting
57
 
# some security flaw somewhere (not necessarily in Bugzilla!)
58
 
sub FILESYSTEM {
59
 
    my $datadir       = bz_locations()->{'datadir'};
60
 
    my $attachdir     = bz_locations()->{'attachdir'};
61
 
    my $extensionsdir = bz_locations()->{'extensionsdir'};
62
 
    my $webdotdir     = bz_locations()->{'webdotdir'};
63
 
    my $templatedir   = bz_locations()->{'templatedir'};
64
 
    my $libdir        = bz_locations()->{'libpath'};
65
 
    my $skinsdir      = bz_locations()->{'skinsdir'};
66
 
 
67
 
    my $ws_group      = Bugzilla->localconfig->{'webservergroup'};
68
 
 
69
 
    # The set of permissions that we use:
70
 
 
71
 
    # FILES
72
 
    # Executable by the web server
73
 
    my $ws_executable = $ws_group ? 0750 : 0755;
74
 
    # Executable by the owner only.
75
 
    my $owner_executable = 0700;
76
 
    # Readable by the web server.
77
 
    my $ws_readable = $ws_group ? 0640 : 0644;
78
 
    # Readable by the owner only.
79
 
    my $owner_readable = 0600;
80
 
    # Writeable by the web server.
81
 
    my $ws_writeable = $ws_group ? 0660 : 0666;
82
 
 
83
 
    # DIRECTORIES
84
 
    # Readable by the web server.
85
 
    my $ws_dir_readable  = $ws_group ? 0750 : 0755;
86
 
    # Readable only by the owner.
87
 
    my $owner_dir_readable = 0700;
88
 
    # Writeable by the web server.
89
 
    my $ws_dir_writeable = $ws_group ? 0770 : 01777;
90
 
    # The webserver can overwrite files owned by other users, 
91
 
    # in this directory.
92
 
    my $ws_dir_full_control = $ws_group ? 0770 : 0777;
93
 
 
94
 
    # Note: When being processed by checksetup, these have their permissions
95
 
    # set in this order: %all_dirs, %recurse_dirs, %all_files.
96
 
    #
97
 
    # Each is processed in alphabetical order of keys, so shorter keys
98
 
    # will have their permissions set before longer keys (thus setting
99
 
    # the permissions on parent directories before setting permissions
100
 
    # on their children).
101
 
 
102
 
    # --- FILE PERMISSIONS (Non-created files) --- #
103
 
    my %files = (
104
 
        '*'               => { perms => $ws_readable },
105
 
        '*.cgi'           => { perms => $ws_executable },
106
 
        'whineatnews.pl'  => { perms => $ws_executable },
107
 
        'collectstats.pl' => { perms => $ws_executable },
108
 
        'checksetup.pl'   => { perms => $owner_executable },
109
 
        'importxml.pl'    => { perms => $ws_executable },
110
 
        'runtests.pl'     => { perms => $owner_executable },
111
 
        'testserver.pl'   => { perms => $ws_executable },
112
 
        'whine.pl'        => { perms => $ws_executable },
113
 
        'customfield.pl'  => { perms => $owner_executable },
114
 
        'email_in.pl'     => { perms => $ws_executable },
115
 
 
116
 
        'docs/makedocs.pl'   => { perms => $owner_executable },
117
 
        'docs/rel_notes.txt' => { perms => $ws_readable },
118
 
        'docs/README.docs'   => { perms => $owner_readable },
119
 
        "$datadir/bugzilla-update.xml" => { perms => $ws_writeable },
120
 
        "$datadir/params" => { perms => $ws_writeable },
121
 
        "$datadir/mailer.testfile" => { perms => $ws_writeable },
122
 
    );
123
 
 
124
 
    # Directories that we want to set the perms on, but not
125
 
    # recurse through. These are directories we didn't create
126
 
    # in checkesetup.pl.
127
 
    my %non_recurse_dirs = (
128
 
        '.'  => $ws_dir_readable,
129
 
        docs => $ws_dir_readable,
130
 
    );
131
 
 
132
 
    # This sets the permissions for each item inside each of these 
133
 
    # directories, including the directory itself. 
134
 
    # 'CVS' directories are special, though, and are never readable by 
135
 
    # the webserver.
136
 
    my %recurse_dirs = (
137
 
        # Writeable directories
138
 
        "$datadir/template" => { files => $ws_readable, 
139
 
                                  dirs => $ws_dir_full_control },
140
 
         $attachdir         => { files => $ws_writeable,
141
 
                                  dirs => $ws_dir_writeable },
142
 
         $webdotdir         => { files => $ws_writeable,
143
 
                                  dirs => $ws_dir_writeable },
144
 
         graphs             => { files => $ws_writeable,
145
 
                                  dirs => $ws_dir_writeable },
146
 
 
147
 
         # Readable directories
148
 
         "$datadir/mining"     => { files => $ws_readable,
149
 
                                     dirs => $ws_dir_readable },
150
 
         "$datadir/duplicates" => { files => $ws_readable,
151
 
                                     dirs => $ws_dir_readable },
152
 
         "$libdir/Bugzilla"    => { files => $ws_readable,
153
 
                                     dirs => $ws_dir_readable },
154
 
         $templatedir          => { files => $ws_readable,
155
 
                                     dirs => $ws_dir_readable },
156
 
         images                => { files => $ws_readable,
157
 
                                     dirs => $ws_dir_readable },
158
 
         css                   => { files => $ws_readable,
159
 
                                     dirs => $ws_dir_readable },
160
 
         js                    => { files => $ws_readable,
161
 
                                     dirs => $ws_dir_readable },
162
 
         skins                 => { files => $ws_readable,
163
 
                                     dirs => $ws_dir_readable },
164
 
         t                     => { files => $owner_readable,
165
 
                                     dirs => $owner_dir_readable },
166
 
         'docs/html'           => { files => $ws_readable,
167
 
                                     dirs => $ws_dir_readable },
168
 
         'docs/pdf'            => { files => $ws_readable,
169
 
                                     dirs => $ws_dir_readable },
170
 
         'docs/txt'            => { files => $ws_readable,
171
 
                                     dirs => $ws_dir_readable },
172
 
         'docs/images'         => { files => $ws_readable,
173
 
                                     dirs => $ws_dir_readable },
174
 
         'docs/lib'            => { files => $owner_readable,
175
 
                                     dirs => $owner_dir_readable },
176
 
         'docs/xml'            => { files => $owner_readable,
177
 
                                     dirs => $owner_dir_readable },
178
 
    );
179
 
 
180
 
    # --- FILES TO CREATE --- #
181
 
 
182
 
    # The name of each directory that we should actually *create*,
183
 
    # pointing at its default permissions.
184
 
    my %create_dirs = (
185
 
        $datadir                => $ws_dir_full_control,
186
 
        "$datadir/mimedump-tmp" => $ws_dir_writeable,
187
 
        "$datadir/mining"       => $ws_dir_readable,
188
 
        "$datadir/duplicates"   => $ws_dir_readable,
189
 
        $attachdir              => $ws_dir_writeable,
190
 
        $extensionsdir          => $ws_dir_readable,
191
 
        graphs                  => $ws_dir_writeable,
192
 
        $webdotdir              => $ws_dir_writeable,
193
 
        'skins/custom'          => $ws_dir_readable,
194
 
        'skins/contrib'         => $ws_dir_readable,
195
 
    );
196
 
 
197
 
    # The name of each file, pointing at its default permissions and
198
 
    # default contents.
199
 
    my %create_files = (
200
 
        "$datadir/mail"    => { perms => $ws_readable },
201
 
    );
202
 
 
203
 
    # Each standard stylesheet has an associated custom stylesheet that
204
 
    # we create. Also, we create placeholders for standard stylesheets
205
 
    # for contrib skins which don't provide them themselves.
206
 
    foreach my $skin_dir ("$skinsdir/custom", <$skinsdir/contrib/*>) {
207
 
        next unless -d $skin_dir;
208
 
        next if basename($skin_dir) =~ /^cvs$/i;
209
 
        foreach (<$skinsdir/standard/*.css>) {
210
 
            my $standard_css_file = basename($_);
211
 
            my $custom_css_file = "$skin_dir/$standard_css_file";
212
 
            $create_files{$custom_css_file} = { perms => $ws_readable, contents => <<EOT
213
 
/*
214
 
 * Custom rules for $standard_css_file.
215
 
 * The rules you put here override rules in that stylesheet.
216
 
 */
217
 
EOT
218
 
            }
219
 
        }
220
 
    }
221
 
 
222
 
    # Because checksetup controls the creation of index.html separately
223
 
    # from all other files, it gets its very own hash.
224
 
    my %index_html = (
225
 
        'index.html' => { perms => $ws_readable, contents => <<EOT
226
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
227
 
<html>
228
 
<head>
229
 
  <meta http-equiv="Refresh" content="0; URL=index.cgi">
230
 
</head>
231
 
<body>
232
 
  <h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1>
233
 
</body>
234
 
</html>
235
 
EOT
236
 
        }
237
 
    );
238
 
 
239
 
    # Because checksetup controls the .htaccess creation separately
240
 
    # by a localconfig variable, these go in a separate variable from
241
 
    # %create_files.
242
 
    my $ht_default_deny = <<EOT;
243
 
# nothing in this directory is retrievable unless overridden by an .htaccess
244
 
# in a subdirectory
245
 
deny from all
246
 
EOT
247
 
 
248
 
    my %htaccess = (
249
 
        "$attachdir/.htaccess"       => { perms    => $ws_readable,
250
 
                                          contents => $ht_default_deny },
251
 
        "$libdir/Bugzilla/.htaccess" => { perms    => $ws_readable,
252
 
                                          contents => $ht_default_deny },
253
 
        "$templatedir/.htaccess"     => { perms    => $ws_readable,
254
 
                                          contents => $ht_default_deny },
255
 
 
256
 
        '.htaccess' => { perms => $ws_readable, contents => <<EOT
257
 
# Don't allow people to retrieve non-cgi executable files or our private data
258
 
<FilesMatch ^(.*\\.pm|.*\\.pl|.*localconfig.*)\$>
259
 
  deny from all
260
 
</FilesMatch>
261
 
EOT
262
 
        },
263
 
 
264
 
        "$webdotdir/.htaccess" => { perms => $ws_readable, contents => <<EOT
265
 
# Restrict access to .dot files to the public webdot server at research.att.com
266
 
# if research.att.com ever changes their IP, or if you use a different
267
 
# webdot server, you'll need to edit this
268
 
<FilesMatch \\.dot\$>
269
 
  Allow from 192.20.225.0/24
270
 
  Deny from all
271
 
</FilesMatch>
272
 
 
273
 
# Allow access to .png files created by a local copy of 'dot'
274
 
<FilesMatch \\.png\$>
275
 
  Allow from all
276
 
</FilesMatch>
277
 
 
278
 
# And no directory listings, either.
279
 
Deny from all
280
 
EOT
281
 
        },
282
 
 
283
 
        # Even though $datadir may not (and should not) be in the webtree,
284
 
        # we can't know for sure, so create the .htaccess anyway. It's harmless
285
 
        # if it's not accessible...
286
 
        "$datadir/.htaccess" => { perms    => $ws_readable, contents => <<EOT
287
 
# Nothing in this directory is retrievable unless overridden by an .htaccess
288
 
# in a subdirectory; the only exception is duplicates.rdf, which is used by
289
 
# duplicates.xul and must be loadable over the web
290
 
deny from all
291
 
<Files duplicates.rdf>
292
 
  allow from all
293
 
</Files>
294
 
EOT
295
 
 
296
 
 
297
 
        },
298
 
    );
299
 
 
300
 
    my %all_files = (%create_files, %htaccess, %index_html, %files);
301
 
    my %all_dirs  = (%create_dirs, %non_recurse_dirs);
302
 
 
303
 
    return {
304
 
        create_dirs  => \%create_dirs,
305
 
        recurse_dirs => \%recurse_dirs,
306
 
        all_dirs     => \%all_dirs,
307
 
 
308
 
        create_files => \%create_files,
309
 
        htaccess     => \%htaccess,
310
 
        index_html   => \%index_html,
311
 
        all_files    => \%all_files,
312
 
    };
313
 
}
314
 
 
315
 
sub update_filesystem {
316
 
    my ($params) = @_;
317
 
    my $fs = FILESYSTEM();
318
 
    my %dirs  = %{$fs->{create_dirs}};
319
 
    my %files = %{$fs->{create_files}};
320
 
 
321
 
    my $datadir = bz_locations->{'datadir'};
322
 
    # If the graphs/ directory doesn't exist, we're upgrading from
323
 
    # a version old enough that we need to update the $datadir/mining 
324
 
    # format.
325
 
    if (-d "$datadir/mining" && !-d 'graphs') {
326
 
        _update_old_charts($datadir);
327
 
    }
328
 
 
329
 
    # By sorting the dirs, we assure that shorter-named directories
330
 
    # (meaning parent directories) are always created before their
331
 
    # child directories.
332
 
    foreach my $dir (sort keys %dirs) {
333
 
        unless (-d $dir) {
334
 
            print "Creating $dir directory...\n";
335
 
            mkdir $dir || die $!;
336
 
            # For some reason, passing in the permissions to "mkdir"
337
 
            # doesn't work right, but doing a "chmod" does.
338
 
            chmod $dirs{$dir}, $dir || die $!;
339
 
        }
340
 
    }
341
 
 
342
 
    _create_files(%files);
343
 
    if ($params->{index_html}) {
344
 
        _create_files(%{$fs->{index_html}});
345
 
    }
346
 
    elsif (-e 'index.html') {
347
 
        my $templatedir = bz_locations()->{'templatedir'};
348
 
        print <<EOT;
349
 
 
350
 
*** It appears that you still have an old index.html hanging around.
351
 
    Either the contents of this file should be moved into a template and 
352
 
    placed in the '$templatedir/en/custom' directory, or you should delete 
353
 
    the file.
354
 
 
355
 
EOT
356
 
    }
357
 
 
358
 
    # Delete old files that no longer need to exist
359
 
 
360
 
    # 2001-04-29 jake@bugzilla.org - Remove oldemailtech
361
 
    #   http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
362
 
    if (-d 'shadow') {
363
 
        print "Removing shadow directory...\n";
364
 
        rmtree("shadow");
365
 
    }
366
 
 
367
 
    if (-e "$datadir/versioncache") {
368
 
        print "Removing versioncache...\n";
369
 
        unlink "$datadir/versioncache";
370
 
    }
371
 
 
372
 
}
373
 
 
374
 
sub create_htaccess {
375
 
    _create_files(%{FILESYSTEM()->{htaccess}});
376
 
 
377
 
    # Repair old .htaccess files
378
 
    my $htaccess = new IO::File('.htaccess', 'r') || die ".htaccess: $!";
379
 
    my $old_data;
380
 
    { local $/; $old_data = <$htaccess>; }
381
 
    $htaccess->close;
382
 
 
383
 
    my $repaired = 0;
384
 
    if ($old_data =~ s/\|localconfig\|/\|.*localconfig.*\|/) {
385
 
        $repaired = 1;
386
 
    }
387
 
    if ($old_data !~ /\(\.\*\\\.pm\|/) {
388
 
        $old_data =~ s/\(/(.*\\.pm\|/;
389
 
        $repaired = 1;
390
 
    }
391
 
    if ($repaired) {
392
 
        print "Repairing .htaccess...\n";
393
 
        $htaccess = new IO::File('.htaccess', 'w') || die $!;
394
 
        print $htaccess $old_data;
395
 
        $htaccess->close;
396
 
    }
397
 
 
398
 
 
399
 
    my $webdot_dir = bz_locations()->{'webdotdir'};
400
 
    # The public webdot IP address changed.
401
 
    my $webdot = new IO::File("$webdot_dir/.htaccess", 'r')
402
 
        || die "$webdot_dir/.htaccess: $!";
403
 
    my $webdot_data;
404
 
    { local $/; $webdot_data = <$webdot>; }
405
 
    $webdot->close;
406
 
    if ($webdot_data =~ /192\.20\.225\.10/) {
407
 
        print "Repairing $webdot_dir/.htaccess...\n";
408
 
        $webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g;
409
 
        $webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!;
410
 
        print $webdot $webdot_data;
411
 
        $webdot->close;
412
 
    }
413
 
}
414
 
 
415
 
# A helper for the above functions.
416
 
sub _create_files {
417
 
    my (%files) = @_;
418
 
 
419
 
    # It's not necessary to sort these, but it does make the
420
 
    # output of checksetup.pl look a bit nicer.
421
 
    foreach my $file (sort keys %files) {
422
 
        unless (-e $file) {
423
 
            print "Creating $file...\n";
424
 
            my $info = $files{$file};
425
 
            my $fh = new IO::File($file, O_WRONLY | O_CREAT, $info->{perms})
426
 
                || die $!;
427
 
            print $fh $info->{contents} if $info->{contents};
428
 
            $fh->close;
429
 
        }
430
 
    }
431
 
}
432
 
 
433
 
# If you ran a REALLY old version of Bugzilla, your chart files are in the
434
 
# wrong format. This code is a little messy, because it's very old, and
435
 
# when moving it into this module, I couldn't test it so I left it almost 
436
 
# completely alone.
437
 
sub _update_old_charts {
438
 
    my ($datadir) = @_;
439
 
    print "Updating old chart storage format...\n";
440
 
    foreach my $in_file (glob("$datadir/mining/*")) {
441
 
        # Don't try and upgrade image or db files!
442
 
        next if (($in_file =~ /\.gif$/i) ||
443
 
                 ($in_file =~ /\.png$/i) ||
444
 
                 ($in_file =~ /\.db$/i) ||
445
 
                 ($in_file =~ /\.orig$/i));
446
 
 
447
 
        rename("$in_file", "$in_file.orig") or next;
448
 
        open(IN, "$in_file.orig") or next;
449
 
        open(OUT, '>', $in_file) or next;
450
 
 
451
 
        # Fields in the header
452
 
        my @declared_fields;
453
 
 
454
 
        # Fields we changed to half way through by mistake
455
 
        # This list comes from an old version of collectstats.pl
456
 
        # This part is only for people who ran later versions of 2.11 (devel)
457
 
        my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED
458
 
                                     RESOLVED VERIFIED CLOSED);
459
 
 
460
 
        # Fields we actually want (matches the current collectstats.pl)
461
 
        my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED
462
 
                            VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND
463
 
                            DUPLICATE WORKSFORME MOVED);
464
 
 
465
 
         while (<IN>) {
466
 
            if (/^# fields?: (.*)\s$/) {
467
 
                @declared_fields = map uc, (split /\||\r/, $1);
468
 
                print OUT "# fields: ", join('|', @out_fields), "\n";
469
 
            }
470
 
            elsif (/^(\d+\|.*)/) {
471
 
                my @data = split(/\||\r/, $1);
472
 
                my %data;
473
 
                if (@data == @declared_fields) {
474
 
                    # old format
475
 
                    for my $i (0 .. $#declared_fields) {
476
 
                        $data{$declared_fields[$i]} = $data[$i];
477
 
                    }
478
 
                }
479
 
                elsif (@data == @intermediate_fields) {
480
 
                    # Must have changed over at this point
481
 
                    for my $i (0 .. $#intermediate_fields) {
482
 
                        $data{$intermediate_fields[$i]} = $data[$i];
483
 
                    }
484
 
                }
485
 
                elsif (@data == @out_fields) {
486
 
                    # This line's fine - it has the right number of entries
487
 
                    for my $i (0 .. $#out_fields) {
488
 
                        $data{$out_fields[$i]} = $data[$i];
489
 
                    }
490
 
                }
491
 
                else {
492
 
                    print "Oh dear, input line $. of $in_file had " .
493
 
                          scalar(@data) . " fields\nThis was unexpected.",
494
 
                          " You may want to check your data files.\n";
495
 
                }
496
 
 
497
 
                print OUT join('|', 
498
 
                    map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields),
499
 
                    "\n";
500
 
            }
501
 
            else {
502
 
                print OUT;
503
 
            }
504
 
        }
505
 
 
506
 
        close(IN);
507
 
        close(OUT);
508
 
    } 
509
 
}
510
 
 
511
 
 
512
 
sub fix_all_file_permissions {
513
 
    my ($output) = @_;
514
 
 
515
 
    my $ws_group = Bugzilla->localconfig->{'webservergroup'};
516
 
    my $group_id = _check_web_server_group($ws_group, $output);
517
 
 
518
 
    return if ON_WINDOWS;
519
 
 
520
 
    my $fs = FILESYSTEM();
521
 
    my %files = %{$fs->{all_files}};
522
 
    my %dirs  = %{$fs->{all_dirs}};
523
 
    my %recurse_dirs = %{$fs->{recurse_dirs}};
524
 
 
525
 
    print get_text('install_file_perms_fix') . "\n" if $output;
526
 
 
527
 
    my $owner_id = POSIX::getuid();
528
 
    $group_id = POSIX::getgid() unless defined $group_id;
529
 
 
530
 
    foreach my $dir (sort keys %dirs) {
531
 
        next unless -d $dir;
532
 
        _fix_perms($dir, $owner_id, $group_id, $dirs{$dir});
533
 
    }
534
 
 
535
 
    foreach my $dir (sort keys %recurse_dirs) {
536
 
        next unless -d $dir;
537
 
        # Set permissions on the directory itself.
538
 
        my $perms = $recurse_dirs{$dir};
539
 
        _fix_perms($dir, $owner_id, $group_id, $perms->{dirs});
540
 
        # Now recurse through the directory and set the correct permissions
541
 
        # on subdirectories and files.
542
 
        find({ no_chdir => 1, wanted => sub {
543
 
            my $name = $File::Find::name;
544
 
            if (-d $name) {
545
 
                _fix_perms($name, $owner_id, $group_id, $perms->{dirs});
546
 
            }
547
 
            else {
548
 
                _fix_perms($name, $owner_id, $group_id, $perms->{files});
549
 
            }
550
 
        }}, $dir);
551
 
    }
552
 
 
553
 
    foreach my $file (sort keys %files) {
554
 
        # %files supports globs
555
 
        foreach my $filename (glob $file) {
556
 
            # Don't touch directories.
557
 
            next if -d $filename || !-e $filename;
558
 
            _fix_perms($filename, $owner_id, $group_id, 
559
 
                       $files{$file}->{perms});
560
 
        }
561
 
    }
562
 
 
563
 
    _fix_cvs_dirs($owner_id, '.');
564
 
}
565
 
 
566
 
# A helper for fix_all_file_permissions
567
 
sub _fix_cvs_dirs {
568
 
    my ($owner_id, $dir) = @_;
569
 
    my $owner_gid = POSIX::getgid();
570
 
    find({ no_chdir => 1, wanted => sub {
571
 
        my $name = $File::Find::name;
572
 
        if ($File::Find::dir =~ /\/CVS/ || $_ eq '.cvsignore'
573
 
            || (-d $name && $_ eq 'CVS')) {
574
 
            _fix_perms($name, $owner_id, $owner_gid, 0700);
575
 
        }
576
 
    }}, $dir);
577
 
}
578
 
 
579
 
sub _fix_perms {
580
 
    my ($name, $owner, $group, $perms) = @_;
581
 
    #printf ("Changing $name to %o\n", $perms);
582
 
    chown $owner, $group, $name 
583
 
        || warn "Failed to change ownership of $name: $!";
584
 
    chmod $perms, $name
585
 
        || warn "Failed to change permissions of $name: $!";
586
 
}
587
 
 
588
 
sub _check_web_server_group {
589
 
    my ($group, $output) = @_;
590
 
 
591
 
    my $filename = bz_locations()->{'localconfig'};
592
 
    my $group_id;
593
 
 
594
 
    # If we are on Windows, webservergroup does nothing
595
 
    if (ON_WINDOWS && $group && $output) {
596
 
        print "\n\n" . get_text('install_webservergroup_windows') . "\n\n";
597
 
    }
598
 
 
599
 
    # If we're not on Windows, make sure that webservergroup isn't
600
 
    # empty.
601
 
    elsif (!ON_WINDOWS && !$group && $output) {
602
 
        print "\n\n" . get_text('install_webservergroup_empty') . "\n\n";
603
 
    }
604
 
 
605
 
    # If we're not on Windows, make sure we are actually a member of
606
 
    # the webservergroup.
607
 
    elsif (!ON_WINDOWS && $group) {
608
 
        $group_id = getgrnam($group);
609
 
        ThrowCodeError('invalid_webservergroup', { group => $group }) 
610
 
            unless defined $group_id;
611
 
 
612
 
        # If on unix, see if we need to print a warning about a webservergroup
613
 
        # that we can't chgrp to
614
 
        if ($output && $< != 0 && !grep($_ eq $group_id, split(" ", $)))) {
615
 
            print "\n\n" . get_text('install_webservergroup_not_in') . "\n\n";
616
 
        }
617
 
    }
618
 
 
619
 
    return $group_id;
620
 
}
621
 
 
622
 
1;
623
 
 
624
 
__END__
625
 
 
626
 
=head1 NAME
627
 
 
628
 
Bugzilla::Install::Filesystem - Fix up the filesystem during
629
 
  installation.
630
 
 
631
 
=head1 DESCRIPTION
632
 
 
633
 
This module is used primarily by L<checksetup.pl> to modify the 
634
 
filesystem during installation, including creating the data/ directory.
635
 
 
636
 
=head1 SUBROUTINES
637
 
 
638
 
=over
639
 
 
640
 
=item C<update_filesystem({ index_html => 0 })>
641
 
 
642
 
Description: Creates all the directories and files that Bugzilla
643
 
             needs to function but doesn't ship with. Also does
644
 
             any updates to these files as necessary during an
645
 
             upgrade.
646
 
 
647
 
Params:      C<index_html> - Whether or not we should create
648
 
               the F<index.html> file.
649
 
 
650
 
Returns:     nothing
651
 
 
652
 
=item C<create_htaccess()>
653
 
 
654
 
Description: Creates all of the .htaccess files for Apache,
655
 
             in the various Bugzilla directories. Also updates
656
 
             the .htaccess files if they need updating.
657
 
 
658
 
Params:      none
659
 
 
660
 
Returns:     nothing
661
 
 
662
 
=item C<fix_all_file_permissions($output)>
663
 
 
664
 
Description: Sets all the file permissions on all of Bugzilla's files
665
 
             to what they should be. Note that permissions are different
666
 
             depending on whether or not C<$webservergroup> is set
667
 
             in F<localconfig>.
668
 
 
669
 
Params:      C<$output> - C<true> if you want this function to print
670
 
                 out information about what it's doing.
671
 
 
672
 
Returns:     nothing
673
 
 
674
 
=back