123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- # -*- Mode: perl; indent-tabs-mode: nil -*-
- #
- # The contents of this file are subject to the Mozilla Public
- # License Version 1.1 (the "License"); you may not use this file
- # except in compliance with the License. You may obtain a copy of
- # the License at http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS
- # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- # implied. See the License for the specific language governing
- # rights and limitations under the License.
- #
- # The Original Code is the Bugzilla Bug Tracking System.
- #
- # Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
- # Bill Barry <after.fallout@gmail.com>
- package Bugzilla::Install::Filesystem;
- # NOTE: This package may "use" any modules that it likes,
- # and localconfig is available. However, all functions in this
- # package should assume that:
- #
- # * Templates are not available.
- # * Files do not have the correct permissions.
- # * The database does not exist.
- use strict;
- use Bugzilla::Constants;
- use Bugzilla::Error;
- use Bugzilla::Install::Localconfig;
- use Bugzilla::Util;
- use File::Find;
- use File::Path;
- use File::Basename;
- use IO::File;
- use POSIX ();
- use base qw(Exporter);
- our @EXPORT = qw(
- update_filesystem
- create_htaccess
- fix_all_file_permissions
- );
- # This looks like a constant because it effectively is, but
- # it has to call other subroutines and read the current filesystem,
- # so it's defined as a sub. This is not exported, so it doesn't have
- # a perldoc. However, look at the various hashes defined inside this
- # function to understand what it returns. (There are comments throughout.)
- #
- # The rationale for the file permissions is that the web server generally
- # runs as apache, so the cgi scripts should not be writable for apache,
- # otherwise someone may find it possible to change the cgis when exploiting
- # some security flaw somewhere (not necessarily in Bugzilla!)
- sub FILESYSTEM {
- my $datadir = bz_locations()->{'datadir'};
- my $attachdir = bz_locations()->{'attachdir'};
- my $extensionsdir = bz_locations()->{'extensionsdir'};
- my $webdotdir = bz_locations()->{'webdotdir'};
- my $templatedir = bz_locations()->{'templatedir'};
- my $libdir = bz_locations()->{'libpath'};
- my $extlib = bz_locations()->{'ext_libpath'};
- my $skinsdir = bz_locations()->{'skinsdir'};
- my $ws_group = Bugzilla->localconfig->{'webservergroup'};
- # The set of permissions that we use:
- # FILES
- # Executable by the web server
- my $ws_executable = $ws_group ? 0750 : 0755;
- # Executable by the owner only.
- my $owner_executable = 0700;
- # Readable by the web server.
- my $ws_readable = $ws_group ? 0640 : 0644;
- # Readable by the owner only.
- my $owner_readable = 0600;
- # Writeable by the web server.
- my $ws_writeable = $ws_group ? 0660 : 0666;
- # DIRECTORIES
- # Readable by the web server.
- my $ws_dir_readable = $ws_group ? 0750 : 0755;
- # Readable only by the owner.
- my $owner_dir_readable = 0700;
- # Writeable by the web server.
- my $ws_dir_writeable = $ws_group ? 0770 : 01777;
- # The web server can overwrite files owned by other users,
- # in this directory.
- my $ws_dir_full_control = $ws_group ? 0770 : 0777;
- # Note: When being processed by checksetup, these have their permissions
- # set in this order: %all_dirs, %recurse_dirs, %all_files.
- #
- # Each is processed in alphabetical order of keys, so shorter keys
- # will have their permissions set before longer keys (thus setting
- # the permissions on parent directories before setting permissions
- # on their children).
- # --- FILE PERMISSIONS (Non-created files) --- #
- my %files = (
- '*' => { perms => $ws_readable },
- '*.cgi' => { perms => $ws_executable },
- 'whineatnews.pl' => { perms => $ws_executable },
- 'collectstats.pl' => { perms => $ws_executable },
- 'checksetup.pl' => { perms => $owner_executable },
- 'importxml.pl' => { perms => $ws_executable },
- 'runtests.pl' => { perms => $owner_executable },
- 'testserver.pl' => { perms => $ws_executable },
- 'whine.pl' => { perms => $ws_executable },
- 'customfield.pl' => { perms => $owner_executable },
- 'email_in.pl' => { perms => $ws_executable },
- 'sanitycheck.pl' => { perms => $ws_executable },
- 'install-module.pl' => { perms => $owner_executable },
- 'docs/makedocs.pl' => { perms => $owner_executable },
- 'docs/style.css' => { perms => $ws_readable },
- 'docs/*/rel_notes.txt' => { perms => $ws_readable },
- 'docs/*/README.docs' => { perms => $owner_readable },
- "$datadir/bugzilla-update.xml" => { perms => $ws_writeable },
- "$datadir/params" => { perms => $ws_writeable },
- "$datadir/mailer.testfile" => { perms => $ws_writeable },
- );
- # Directories that we want to set the perms on, but not
- # recurse through. These are directories we didn't create
- # in checkesetup.pl.
- my %non_recurse_dirs = (
- '.' => $ws_dir_readable,
- docs => $ws_dir_readable,
- );
- # This sets the permissions for each item inside each of these
- # directories, including the directory itself.
- # 'CVS' directories are special, though, and are never readable by
- # the webserver.
- my %recurse_dirs = (
- # Writeable directories
- "$datadir/template" => { files => $ws_readable,
- dirs => $ws_dir_full_control },
- $attachdir => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
- $webdotdir => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
- graphs => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
- # Readable directories
- "$datadir/mining" => { files => $ws_readable,
- dirs => $ws_dir_readable },
- "$datadir/duplicates" => { files => $ws_readable,
- dirs => $ws_dir_readable },
- "$libdir/Bugzilla" => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $extlib => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $templatedir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $extensionsdir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- images => { files => $ws_readable,
- dirs => $ws_dir_readable },
- css => { files => $ws_readable,
- dirs => $ws_dir_readable },
- js => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $skinsdir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- t => { files => $owner_readable,
- dirs => $owner_dir_readable },
- 'docs/*/html' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/pdf' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/txt' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/images' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/lib' => { files => $owner_readable,
- dirs => $owner_dir_readable },
- 'docs/*/xml' => { files => $owner_readable,
- dirs => $owner_dir_readable },
- );
- # --- FILES TO CREATE --- #
- # The name of each directory that we should actually *create*,
- # pointing at its default permissions.
- my %create_dirs = (
- $datadir => $ws_dir_full_control,
- "$datadir/mining" => $ws_dir_readable,
- "$datadir/duplicates" => $ws_dir_readable,
- $attachdir => $ws_dir_writeable,
- $extensionsdir => $ws_dir_readable,
- graphs => $ws_dir_writeable,
- $webdotdir => $ws_dir_writeable,
- "$skinsdir/custom" => $ws_dir_readable,
- "$skinsdir/contrib" => $ws_dir_readable,
- );
- # The name of each file, pointing at its default permissions and
- # default contents.
- my %create_files = ();
- # Each standard stylesheet has an associated custom stylesheet that
- # we create. Also, we create placeholders for standard stylesheets
- # for contrib skins which don't provide them themselves.
- foreach my $skin_dir ("$skinsdir/custom", <$skinsdir/contrib/*>) {
- next if basename($skin_dir) =~ /^cvs$/i;
- $create_dirs{"$skin_dir/yui"} = $ws_dir_readable;
- foreach my $base_css (<$skinsdir/standard/*.css>) {
- _add_custom_css($skin_dir, basename($base_css), \%create_files, $ws_readable);
- }
- foreach my $dir_css (<$skinsdir/standard/*/*.css>) {
- $dir_css =~ s{.+?([^/]+/[^/]+)$}{$1};
- _add_custom_css($skin_dir, $dir_css, \%create_files, $ws_readable);
- }
- }
- # Because checksetup controls the creation of index.html separately
- # from all other files, it gets its very own hash.
- my %index_html = (
- 'index.html' => { perms => $ws_readable, contents => <<EOT
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <meta http-equiv="Refresh" content="0; URL=index.cgi">
- </head>
- <body>
- <h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1>
- </body>
- </html>
- EOT
- }
- );
- # Because checksetup controls the .htaccess creation separately
- # by a localconfig variable, these go in a separate variable from
- # %create_files.
- my $ht_default_deny = <<EOT;
- # nothing in this directory is retrievable unless overridden by an .htaccess
- # in a subdirectory
- deny from all
- EOT
- my %htaccess = (
- "$attachdir/.htaccess" => { perms => $ws_readable,
- contents => $ht_default_deny },
- "$libdir/Bugzilla/.htaccess" => { perms => $ws_readable,
- contents => $ht_default_deny },
- "$extlib/.htaccess" => { perms => $ws_readable,
- contents => $ht_default_deny },
- "$templatedir/.htaccess" => { perms => $ws_readable,
- contents => $ht_default_deny },
- '.htaccess' => { perms => $ws_readable, contents => <<EOT
- # Don't allow people to retrieve non-cgi executable files or our private data
- <FilesMatch ^(.*\\.pm|.*\\.pl|.*localconfig.*)\$>
- deny from all
- </FilesMatch>
- EOT
- },
- "$webdotdir/.htaccess" => { perms => $ws_readable, contents => <<EOT
- # Restrict access to .dot files to the public webdot server at research.att.com
- # if research.att.com ever changes their IP, or if you use a different
- # webdot server, you'll need to edit this
- <FilesMatch \\.dot\$>
- Allow from 192.20.225.0/24
- Deny from all
- </FilesMatch>
- # Allow access to .png files created by a local copy of 'dot'
- <FilesMatch \\.png\$>
- Allow from all
- </FilesMatch>
- # And no directory listings, either.
- Deny from all
- EOT
- },
- # Even though $datadir may not (and should not) be accessible from the
- # web server, we can't know for sure, so create the .htaccess anyway.
- # It's harmless if it isn't accessible...
- "$datadir/.htaccess" => { perms => $ws_readable, contents => <<EOT
- # Nothing in this directory is retrievable unless overridden by an .htaccess
- # in a subdirectory; the only exception is duplicates.rdf, which is used by
- # duplicates.xul and must be accessible from the web server
- deny from all
- <Files duplicates.rdf>
- allow from all
- </Files>
- EOT
- },
- );
- my %all_files = (%create_files, %htaccess, %index_html, %files);
- my %all_dirs = (%create_dirs, %non_recurse_dirs);
- return {
- create_dirs => \%create_dirs,
- recurse_dirs => \%recurse_dirs,
- all_dirs => \%all_dirs,
- create_files => \%create_files,
- htaccess => \%htaccess,
- index_html => \%index_html,
- all_files => \%all_files,
- };
- }
- sub update_filesystem {
- my ($params) = @_;
- my $fs = FILESYSTEM();
- my %dirs = %{$fs->{create_dirs}};
- my %files = %{$fs->{create_files}};
- my $datadir = bz_locations->{'datadir'};
- # If the graphs/ directory doesn't exist, we're upgrading from
- # a version old enough that we need to update the $datadir/mining
- # format.
- if (-d "$datadir/mining" && !-d 'graphs') {
- _update_old_charts($datadir);
- }
- # By sorting the dirs, we assure that shorter-named directories
- # (meaning parent directories) are always created before their
- # child directories.
- foreach my $dir (sort keys %dirs) {
- unless (-d $dir) {
- print "Creating $dir directory...\n";
- mkdir $dir || die $!;
- # For some reason, passing in the permissions to "mkdir"
- # doesn't work right, but doing a "chmod" does.
- chmod $dirs{$dir}, $dir || die $!;
- }
- }
- _create_files(%files);
- if ($params->{index_html}) {
- _create_files(%{$fs->{index_html}});
- }
- elsif (-e 'index.html') {
- my $templatedir = bz_locations()->{'templatedir'};
- print <<EOT;
- *** It appears that you still have an old index.html hanging around.
- Either the contents of this file should be moved into a template and
- placed in the '$templatedir/en/custom' directory, or you should delete
- the file.
- EOT
- }
- # Delete old files that no longer need to exist
- # 2001-04-29 jake@bugzilla.org - Remove oldemailtech
- # http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
- if (-d 'shadow') {
- print "Removing shadow directory...\n";
- rmtree("shadow");
- }
- if (-e "$datadir/versioncache") {
- print "Removing versioncache...\n";
- unlink "$datadir/versioncache";
- }
- }
- # A simple helper for creating "empty" CSS files.
- sub _add_custom_css {
- my ($skin_dir, $path, $create_files, $perms) = @_;
- $create_files->{"$skin_dir/$path"} = { perms => $perms, contents => <<EOT
- /*
- * Custom rules for $path.
- * The rules you put here override rules in that stylesheet.
- */
- EOT
- };
- }
- sub create_htaccess {
- _create_files(%{FILESYSTEM()->{htaccess}});
- # Repair old .htaccess files
- my $htaccess = new IO::File('.htaccess', 'r') || die ".htaccess: $!";
- my $old_data;
- { local $/; $old_data = <$htaccess>; }
- $htaccess->close;
- my $repaired = 0;
- if ($old_data =~ s/\|localconfig\|/\|.*localconfig.*\|/) {
- $repaired = 1;
- }
- if ($old_data !~ /\(\.\*\\\.pm\|/) {
- $old_data =~ s/\(/(.*\\.pm\|/;
- $repaired = 1;
- }
- if ($repaired) {
- print "Repairing .htaccess...\n";
- $htaccess = new IO::File('.htaccess', 'w') || die $!;
- print $htaccess $old_data;
- $htaccess->close;
- }
- my $webdot_dir = bz_locations()->{'webdotdir'};
- # The public webdot IP address changed.
- my $webdot = new IO::File("$webdot_dir/.htaccess", 'r')
- || die "$webdot_dir/.htaccess: $!";
- my $webdot_data;
- { local $/; $webdot_data = <$webdot>; }
- $webdot->close;
- if ($webdot_data =~ /192\.20\.225\.10/) {
- print "Repairing $webdot_dir/.htaccess...\n";
- $webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g;
- $webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!;
- print $webdot $webdot_data;
- $webdot->close;
- }
- }
- # A helper for the above functions.
- sub _create_files {
- my (%files) = @_;
- # It's not necessary to sort these, but it does make the
- # output of checksetup.pl look a bit nicer.
- foreach my $file (sort keys %files) {
- unless (-e $file) {
- print "Creating $file...\n";
- my $info = $files{$file};
- my $fh = new IO::File($file, O_WRONLY | O_CREAT, $info->{perms})
- || die $!;
- print $fh $info->{contents} if $info->{contents};
- $fh->close;
- }
- }
- }
- # If you ran a REALLY old version of Bugzilla, your chart files are in the
- # wrong format. This code is a little messy, because it's very old, and
- # when moving it into this module, I couldn't test it so I left it almost
- # completely alone.
- sub _update_old_charts {
- my ($datadir) = @_;
- print "Updating old chart storage format...\n";
- foreach my $in_file (glob("$datadir/mining/*")) {
- # Don't try and upgrade image or db files!
- next if (($in_file =~ /\.gif$/i) ||
- ($in_file =~ /\.png$/i) ||
- ($in_file =~ /\.db$/i) ||
- ($in_file =~ /\.orig$/i));
- rename("$in_file", "$in_file.orig") or next;
- open(IN, "$in_file.orig") or next;
- open(OUT, '>', $in_file) or next;
- # Fields in the header
- my @declared_fields;
- # Fields we changed to half way through by mistake
- # This list comes from an old version of collectstats.pl
- # This part is only for people who ran later versions of 2.11 (devel)
- my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED
- RESOLVED VERIFIED CLOSED);
- # Fields we actually want (matches the current collectstats.pl)
- my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED
- VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND
- DUPLICATE WORKSFORME MOVED);
- while (<IN>) {
- if (/^# fields?: (.*)\s$/) {
- @declared_fields = map uc, (split /\||\r/, $1);
- print OUT "# fields: ", join('|', @out_fields), "\n";
- }
- elsif (/^(\d+\|.*)/) {
- my @data = split(/\||\r/, $1);
- my %data;
- if (@data == @declared_fields) {
- # old format
- for my $i (0 .. $#declared_fields) {
- $data{$declared_fields[$i]} = $data[$i];
- }
- }
- elsif (@data == @intermediate_fields) {
- # Must have changed over at this point
- for my $i (0 .. $#intermediate_fields) {
- $data{$intermediate_fields[$i]} = $data[$i];
- }
- }
- elsif (@data == @out_fields) {
- # This line's fine - it has the right number of entries
- for my $i (0 .. $#out_fields) {
- $data{$out_fields[$i]} = $data[$i];
- }
- }
- else {
- print "Oh dear, input line $. of $in_file had " .
- scalar(@data) . " fields\nThis was unexpected.",
- " You may want to check your data files.\n";
- }
- print OUT join('|',
- map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields),
- "\n";
- }
- else {
- print OUT;
- }
- }
- close(IN);
- close(OUT);
- }
- }
- sub fix_all_file_permissions {
- my ($output) = @_;
- my $ws_group = Bugzilla->localconfig->{'webservergroup'};
- my $group_id = _check_web_server_group($ws_group, $output);
- return if ON_WINDOWS;
- my $fs = FILESYSTEM();
- my %files = %{$fs->{all_files}};
- my %dirs = %{$fs->{all_dirs}};
- my %recurse_dirs = %{$fs->{recurse_dirs}};
- print get_text('install_file_perms_fix') . "\n" if $output;
- my $owner_id = POSIX::getuid();
- $group_id = POSIX::getgid() unless defined $group_id;
- foreach my $dir (sort keys %dirs) {
- next unless -d $dir;
- _fix_perms($dir, $owner_id, $group_id, $dirs{$dir});
- }
- foreach my $dir (sort keys %recurse_dirs) {
- next unless -d $dir;
- # Set permissions on the directory itself.
- my $perms = $recurse_dirs{$dir};
- _fix_perms($dir, $owner_id, $group_id, $perms->{dirs});
- # Now recurse through the directory and set the correct permissions
- # on subdirectories and files.
- find({ no_chdir => 1, wanted => sub {
- my $name = $File::Find::name;
- if (-d $name) {
- _fix_perms($name, $owner_id, $group_id, $perms->{dirs});
- }
- else {
- _fix_perms($name, $owner_id, $group_id, $perms->{files});
- }
- }}, $dir);
- }
- foreach my $file (sort keys %files) {
- # %files supports globs
- foreach my $filename (glob $file) {
- # Don't touch directories.
- next if -d $filename || !-e $filename;
- _fix_perms($filename, $owner_id, $group_id,
- $files{$file}->{perms});
- }
- }
- _fix_cvs_dirs($owner_id, '.');
- }
- # A helper for fix_all_file_permissions
- sub _fix_cvs_dirs {
- my ($owner_id, $dir) = @_;
- my $owner_gid = POSIX::getgid();
- find({ no_chdir => 1, wanted => sub {
- my $name = $File::Find::name;
- if ($File::Find::dir =~ /\/CVS/ || $_ eq '.cvsignore'
- || (-d $name && $_ eq 'CVS')) {
- _fix_perms($name, $owner_id, $owner_gid, 0700);
- }
- }}, $dir);
- }
- sub _fix_perms {
- my ($name, $owner, $group, $perms) = @_;
- #printf ("Changing $name to %o\n", $perms);
- chown $owner, $group, $name
- || warn "Failed to change ownership of $name: $!";
- chmod $perms, $name
- || warn "Failed to change permissions of $name: $!";
- }
- sub _check_web_server_group {
- my ($group, $output) = @_;
- my $filename = bz_locations()->{'localconfig'};
- my $group_id;
- # If we are on Windows, webservergroup does nothing
- if (ON_WINDOWS && $group && $output) {
- print "\n\n" . get_text('install_webservergroup_windows') . "\n\n";
- }
- # If we're not on Windows, make sure that webservergroup isn't
- # empty.
- elsif (!ON_WINDOWS && !$group && $output) {
- print "\n\n" . get_text('install_webservergroup_empty') . "\n\n";
- }
- # If we're not on Windows, make sure we are actually a member of
- # the webservergroup.
- elsif (!ON_WINDOWS && $group) {
- $group_id = getgrnam($group);
- ThrowCodeError('invalid_webservergroup', { group => $group })
- unless defined $group_id;
- # If on unix, see if we need to print a warning about a webservergroup
- # that we can't chgrp to
- if ($output && $< != 0 && !grep($_ eq $group_id, split(" ", $)))) {
- print "\n\n" . get_text('install_webservergroup_not_in') . "\n\n";
- }
- }
- return $group_id;
- }
- 1;
- __END__
- =head1 NAME
- Bugzilla::Install::Filesystem - Fix up the filesystem during
- installation.
- =head1 DESCRIPTION
- This module is used primarily by L<checksetup.pl> to modify the
- filesystem during installation, including creating the data/ directory.
- =head1 SUBROUTINES
- =over
- =item C<update_filesystem({ index_html => 0 })>
- Description: Creates all the directories and files that Bugzilla
- needs to function but doesn't ship with. Also does
- any updates to these files as necessary during an
- upgrade.
- Params: C<index_html> - Whether or not we should create
- the F<index.html> file.
- Returns: nothing
- =item C<create_htaccess()>
- Description: Creates all of the .htaccess files for Apache,
- in the various Bugzilla directories. Also updates
- the .htaccess files if they need updating.
- Params: none
- Returns: nothing
- =item C<fix_all_file_permissions($output)>
- Description: Sets all the file permissions on all of Bugzilla's files
- to what they should be. Note that permissions are different
- depending on whether or not C<$webservergroup> is set
- in F<localconfig>.
- Params: C<$output> - C<true> if you want this function to print
- out information about what it's doing.
- Returns: nothing
- =back
|