Adding test for Event module
[koha.git] / misc / Install.pm
index 4b8c244..8bf7b76 100644 (file)
@@ -2,6 +2,7 @@ package Install; #assumes Install.pm
 
 
 # Copyright 2000-2002 Katipo Communications
+# Contains parts Copyright 2003 MJ Ray
 #
 # This file is part of Koha.
 #
@@ -17,9 +18,17 @@ package Install; #assumes Install.pm
 # You should have received a copy of the GNU General Public License along with
 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
 # Suite 330, Boston, MA  02111-1307 USA
+#
+# Recent Authors
+# MJR: my.cnf, etcdir, prefix, new display, apache conf, copying fixups
 
 use strict;
 use POSIX;
+#MJR: everyone will have these modules, right?
+# They look like part of perl core to me
+use Term::Cap;
+use Term::ANSIColor qw(:constants);
+use Text::Wrap;
 require Exporter;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@@ -61,9 +70,26 @@ $VERSION = 0.01;
                &updatedatabase
                &populatedatabase
                &restartapache
+               &finalizeconfigfile
                &loadconfigfile
+               &backupmycnf
+               &restoremycnf
                );
 
+use vars qw( $kohaversion );                   # set in installer.pl
+use vars qw( $language );                      # set in installer.pl
+use vars qw( $domainname );                    # set in installer.pl
+
+use vars qw( $etcdir );                                # set in installer.pl, usu. /etc
+use vars qw( $intranetdir $opacdir $kohalogdir );
+use vars qw( $realhttpdconf $httpduser );
+use vars qw( $servername $svr_admin $opacport $intranetport );
+use vars qw( $mysqldir );
+use vars qw( $database $mysqluser );
+use vars qw( $mysqlpass );                     # normally should not be used
+use vars qw( $dbname $hostname $user $pass );  # virtual hosting
+
+use vars qw( $newversion );                    # XXX this seems to be unused
 
 =item heading
 
@@ -72,34 +98,46 @@ $VERSION = 0.01;
 
 The heading function takes one string, the text to be displayed as
 the heading, and returns a formatted heading (currently formatted
-in the "traditional Koha installer" style, i.e., surrounded by a
-box of equal signs).
+with ANSI colours).
 
 This reduces the likelihood of pod2man(1) etc. misinterpreting
-a line of equal signs as POD directives.
+a line of equal signs as illegal POD directives.
 
 =cut
 
+my $termios = POSIX::Termios->new();
+$termios->getattr();
+my $terminal = Term::Cap->Tgetent({OSPEED=>$termios->getospeed()});
+my $clear_string = "\n\n"; #MJR: was $terminal->Tputs('cl');
+
 sub heading ($) {
-   my($s) = @_;
-   my $n = length($s) + 4;
-   my $line = ('=' x $n) . "\n";
-   "\n$line= $s =\n$line\n";
+  my $title = shift;
+  my $bal = 5;
+  return($clear_string.ON_BLUE.WHITE.BOLD." "x$bal.uc($title)." "x$bal.RESET."\n\n");
 }
 
+my $mycnf = $ENV{HOME}."/.my.cnf";
+my $mytmpcnf = `mktemp my.cnf.koha.XXXXXX`;
+
 my $messages;
 $messages->{'continuing'}->{en}="Great!  Continuing setup.\n\n";
 $messages->{'WelcomeToKohaInstaller'}->{en} =
    heading('Welcome to the Koha Installer') . qq|
 Welcome to the Koha install script!  This script will prompt you for some
-basic information about your desired setup, then install Koha according to
-your specifications.  To accept the default value for any question, simply hit
-Enter at the prompt.
+basic information about your desired setup, then install Koha for you.
+
+If you want to install the Koha configuration file somewhere other than /etc
+(eg for non-root installation, or multiple Koha versions on one system), you
+should set the etcdir and prefix environment variables.  If this is your
+only koha installation on this machine and you are running this as root, the
+default should be OK.
+
+To accept the default value for any question, simply hit Enter at the prompt.
 
 Please be sure to read the documentation, or visit the Koha website at
 http://www.koha.org for more information.
 
-Are you ready to begin the installation? (Y/[N]): |;
+Are you ready to begin the installation? ([Y]/N): |;
 $messages->{'ReleaseCandidateWarning'}->{en} =
    heading('RELEASE CANDIDATE') . qq|
 WARNING WARNING WARNING WARNING WARNING
@@ -121,13 +159,22 @@ $messages->{'NETZ3950Missing'}->{en}=qq|
 
 The Net::Z3950 module is missing.  This module is necessary if you want to use
 Koha's Z39.50 client to download bibliographic records from other libraries.
+
 To install this module, you will need the yaz client installed from
 http://www.indexdata.dk/yaz/ and then you can install the perl module with the
 command:
 
 perl -MCPAN -e 'install Net::Z3950'
 
-Press the <ENTER> key to continue: |;
+IMPORTANT NOTE : If you use PERL5.8.0 (RedHat 8.0 or Mandrake 9.x), you MUST install 
+manually the Net::Z3950 and edit Makefile.PL and yazwrap/Makefile.PL to include:
+    'DEFINE' => '-D_GNU_SOURCE',
+Also note that some installations of Perl on Red Hat will generate a lot of
+"'my_perl' undeclared" errors when running make in Net-Z3950.  This is fixed by
+inserting the following line in yazwrap/ywpriv.h :
+   #include "XSUB.h"
+
+Press the <ENTER> key to continue: |;  #'
 
 $messages->{'CheckingPerlModules'}->{en} = heading('PERL & MODULES') . qq|
 Checking perl modules ...
@@ -163,14 +210,14 @@ $messages->{'GetOpacDir'}->{en} = heading('OPAC DIRECTORY') . qq|
 Please supply the directory you want Koha to store its OPAC files in.  This
 directory will be auto-created for you if it doesn't exist.
 
-OPAC Directory [%s]: |;
+OPAC Directory [%s]: |;        #'
 
 $messages->{'GetIntranetDir'}->{en} =
    heading('INTRANET/LIBRARIANS DIRECTORY') . qq|
 Please supply the directory you want Koha to store its Intranet/Librarians
 files in.  This directory will be auto-created for you if it doesn't exist.
 
-Intranet Directory [%s]: |;
+Intranet Directory [%s]: |;    #'
 
 $messages->{'GetKohaLogDir'}->{en} = heading('KOHA LOG DIRECTORY') . qq|
 Specify a log directory where any Koha daemons can create log files.
@@ -192,19 +239,20 @@ Congratulations ... your Koha installation is complete!
 You will be able to connect to your Librarian interface at:
 
    http://%s\:%s/
+   use mysql login and password to connect to this interface. Then, go to admin page, and create whatever fits your needs.
 
 and the OPAC interface at :
 
    http://%s\:%s/
 
-Be sure to read the INSTALL, and Hints files.
+Be sure to read the Hints file.
 
 For more information visit http://www.koha.org
 
 Press <ENTER> to exit the installer: |;
 
 sub releasecandidatewarning {
-    my $message=getmessage('ReleaseCandidateWarning', [$::newversion, $::newversion]);
+    my $message=getmessage('ReleaseCandidateWarning', [$newversion, $newversion]);
     my $answer=showmessage($message, 'yn', 'n');
 
     if ($answer =~ /y/i) {
@@ -217,6 +265,105 @@ sub releasecandidatewarning {
 }
 
 
+=back
+
+=head2 Accessor functions (for installer.pl)
+
+=over 4
+
+=cut
+
+=item setlanguage
+
+    setlanguage('en');
+
+Sets the installation language, normally "en" (English).
+In fact, only "en" is supported.
+
+=cut
+
+sub setlanguage ($) {
+    ($language) = @_;
+}
+
+=item setdomainname
+
+    setdomainname('example.org');
+
+Sets the domain name of the host.
+
+The domain name should not contain a leading dot;
+otherwise, the results are undefined.
+
+=cut
+
+sub setdomainname ($) {
+    ($domainname) = @_;
+}
+
+=item setetcdir
+
+    setetcdir('/etc');
+
+Sets the sysconfdir, normally /etc.
+This should be an absolute path; a trailing / is not required.
+
+=cut
+
+sub setetcdir ($) {
+    ($etcdir) = @_;
+}
+
+=item setkohaversion
+
+    setkohaversion('1.3.3RC26');
+
+Sets the Koha version as known by the installer.
+
+=cut
+
+sub setkohaversion ($) {
+    ($kohaversion) = @_;
+}
+
+=item getservername
+
+    my $servername = getservername;
+
+Gets the name of the Koha virtual server as specified by the user.
+
+=cut
+
+sub getservername () {
+    $servername;
+}
+
+=item getopacport
+
+    $port = getopacport;
+
+Gets the port that will run the Koha OPAC virtual server,
+as specified by the user.
+
+=cut
+
+sub getopacport () {
+    $opacport;
+}
+
+=item getintranetport
+
+    $port = getintranetport;
+
+Gets the port that will run the Koha INTRANET virtual server,
+as specified by the user.
+
+=cut
+
+sub getintranetport () {
+    $intranetport;
+}
+
 =back
 
 =head2 Miscellaneous utility functions
@@ -277,6 +424,201 @@ sub mkdir_parents ($;$) {
     return $ok;
 }
 
+
+=item getmessage
+
+    getmessage($msgid);
+    getmessage($msgid, $variables);
+
+Gets a localized message (format string) with message id $msgid,
+and, if an array reference of variables $variables is given,
+substitutes variables in the format string with @$variables.
+Returns the found message string, with variable substitutions
+if specified.
+
+$msgid must be the message identifier corresponding to a defined
+message string (a valid key to the $messages hash in the Installer
+package). getmessage throws an exception if the message cannot be
+found.
+
+=cut
+
+sub getmessage {
+    my $messagename=shift;
+    my $variables=shift;
+    my $message=$messages->{$messagename}->{$language} || $messages->{$messagename}->{en} || RED.BOLD."Error: No message named $messagename in Install.pm\n";
+    if (defined($variables)) {
+       $message=sprintf $message, @$variables;
+    }
+    return $message;
+}
+
+
+=item showmessage
+
+    showmessage($message, 'none');
+    showmessage($message, 'none', undef, $noclear);
+
+    $result = showmessage($message, 'yn');
+    $result = showmessage($message, 'yn', $defaultresponse);
+    $result = showmessage($message, 'yn', $defaultresponse, $noclear);
+
+    $result = showmessage($message, 'restrictchar CHARS');
+    $result = showmessage($message, 'free');
+    $result = showmessage($message, 'silentfree');
+    $result = showmessage($message, 'numerical');
+    $result = showmessage($message, 'email');
+    $result = showmessage($message, 'PressEnter');
+
+Shows a message and optionally gets a response from the user.
+
+The first two arguments, the message and the response type,
+are mandatory.  The message must be the actual string to
+display; the caller is responsible for calling getmessage if
+required.
+
+The response type must be one of "none", "yn", "free", "silentfree"
+"numerical", "email", "PressEnter", or a string consisting
+of "restrictchar " followed by a list of allowed characters
+(space can be specified). (Case is not significant, but case is
+significant in the list of allowed characters.) If a response
+type other than the above-listed is specified, the result is
+undefined.
+
+Note that the response type "yn" is equivalent to "restrictchar yn".
+Because "restrictchar" is case-sensitive, the user is expected
+to enter "y" or "n" in lowercase only.
+
+Note that the response type of "email" does not actually
+guarantee that the returned value is a well-formed RFC-822
+email address, nor does it accept all well-formed RFC-822 email
+addresses. What it does is to restrict the returned value to a
+string that is looks reasonably likely to be an email address
+in the "real world", given the premise that the user is trying
+to enter a real email address.
+
+If a response type other than "none" or "PressEnter" is
+specified, a third argument, specifying the default value, can
+be specified:  If this default response is not specified, the
+default response is the first allowed character if the response
+type is "restrictchar", otherwise the default response is the
+empty string. This default response is used when the user does
+not specify a value (i.e., presses Enter without typing in
+anything), showmessage will assume that the default response is
+the user's response.
+
+Note that because the response type "yn" is equivalent to
+"restrictchar yn", the default value for response type "yn",
+if unspecified, is "y".
+
+The screen is normally cleared before the message is displayed;
+if a fourth argument is specified and is nonzero, this
+screen-clearing is not done.
+
+=cut
+#'
+
+sub showmessage {
+    #MJR: Maybe refactor to use anonymous functions that
+    # check the responses instead of RnP branching.
+    my $message=join('',fill('','',(shift)));
+    my $responsetype=shift;
+    my $defaultresponse=shift;
+    my $noclear=shift;
+    $noclear = 0 unless defined $noclear; # defaults to "clear"
+    ($noclear) || (print $clear_string);
+    if ($responsetype =~ /^yn$/) {
+       $responsetype='restrictchar ynYN';
+    }
+    print RESET.$message;
+    if ($responsetype =~/^restrictchar (.*)/i) {
+       my $response='\0';
+       my $options=$1;
+       until ($options=~/$response/) {
+           (defined($defaultresponse)) || ($defaultresponse=substr($options,0,1));
+           $response=<STDIN>;
+           chomp $response;
+           (length($response)) || ($response=$defaultresponse);
+            if ( $response=~/.*[\:\(\)\^\$\*\!\\].*/ ) {
+                ($noclear) || (print $clear_string);
+                print RED."Response contains invalid characters.  Choose from [$options].\n\n";
+                print RESET.$message;
+                $response='\0';
+            } else {
+                unless ($options=~/$response/) {
+                    ($noclear) || (print $clear_string);
+                    print RED."Invalid Response.  Choose from [$options].\n\n";
+                    print RESET.$message;
+                }
+            }
+       }
+       return $response;
+    } elsif ($responsetype =~/^(silent)?free$/i) {
+       (defined($defaultresponse)) || ($defaultresponse='');
+       if ($responsetype =~/^(silent)/i) { setecho(0) }; 
+       my $response=<STDIN>;
+       if ($responsetype =~/^(silent)/i) { setecho(1) }; 
+       chomp $response;
+       ($response) || ($response=$defaultresponse);
+       return $response;
+    } elsif ($responsetype =~/^numerical$/i) {
+       (defined($defaultresponse)) || ($defaultresponse='');
+       my $response='';
+       until ($response=~/^\d+$/) {
+           $response=<STDIN>;
+           chomp $response;
+           ($response) || ($response=$defaultresponse);
+           unless ($response=~/^\d+$/) {
+               ($noclear) || (print $clear_string);
+               print RED."Invalid Response ($response).  Response must be a number.\n\n";
+               print RESET.$message;
+           }
+       }
+       return $response;
+    } elsif ($responsetype =~/^email$/i) {
+       (defined($defaultresponse)) || ($defaultresponse='');
+       my $response='';
+       until ($response=~/.*\@.*\..*/) {
+           $response=<STDIN>;
+           chomp $response;
+           ($response) || ($response=$defaultresponse);
+           if ($response!~/.*\@.*\..*/) {
+                       ($noclear) || (print $clear_string);
+                       print RED."Invalid Response ($response).  Response must be a valid email address.\n\n";
+                       print RESET.$message;
+           }
+       }
+       return $response;
+    } elsif ($responsetype =~/^PressEnter$/i) {
+       <STDIN>;
+       return;
+    } elsif ($responsetype =~/^none$/i) {
+       return;
+    } else {
+       # FIXME: There are a few places where we will get an undef as the
+       # response type. Should we thrown an exception here, or should we
+       # legitimize this usage and say "none" is the default if not specified?
+       #die "Illegal response type \"$responsetype\"";
+    }
+}
+
+
+=back
+
+=item startsysout
+
+       startsysout;
+
+Changes the display to show system output until the next showmessage call.
+At the time of writing, this means using red text.
+
+=cut
+
+sub startsysout {
+       print RED."\n";
+}
+
+
 =back
 
 =head2 Subtasks of doing an installation
@@ -290,22 +632,19 @@ sub mkdir_parents ($;$) {
     checkabortedinstall;
 
 Checks whether a previous installation process has been abnormally
-aborted, by checking whether $::etcidr/koha.conf is a symlink or not.
-If an aborted installation is detected, gives the user a chance to
-abort, before trying to recover the aborted installation.
-
-(Assuming that Koha will be installed on a modern Unix with symlinks,
-it is possible to code the installer so that aborted installs can be
-detected. In case of such an event we can do our best to "roll back"
-the aborted install.)
+aborted, by checking whether $etcidr/koha.conf is a symlink matching
+a particular pattern.  If an aborted installation is detected, give
+the user a chance to abort, before trying to recover the aborted
+installation.
 
-FIXME: The "roll back" is not complete!
+FIXME: The recovery is not complete; it only partially rolls back
+some changes.
 
 =cut
 
 sub checkabortedinstall () {
-    if (-l("$::etcdir/koha.conf")
-        && readlink("$::etcdir/koha.conf") =~ /\.tmp$/
+    if (-l("$etcdir/koha.conf")
+        && readlink("$etcdir/koha.conf") =~ /\.tmp$/
     ) {
         print qq|
 I have detected that you tried to install Koha before, but the installation
@@ -317,8 +656,8 @@ database is already created.
         <STDIN>;
 
         # Remove the symlink after the <STDIN>, so the user can back out
-        unlink "$::etcdir/koha.conf"
-            || die "Failed to remove incomplete $::etcdir/koha.conf: $!\n";
+        unlink "$etcdir/koha.conf"
+            || die "Failed to remove incomplete $etcdir/koha.conf: $!\n";
     }
 }
 
@@ -341,25 +680,30 @@ sub checkperlmodules {
     my $message = getmessage('CheckingPerlModules');
     showmessage($message, 'none');
 
-    # FIXME: Perl 5.6 is BUGGY!!! IT SHOULD NOT BE USED in production!!!
-    unless (eval "require 5.006_000") {
-       die getmessage('PerlVersionFailure', ['5.6.0']);
+    unless ($] >= 5.006001) {                  # Bug 179
+       die getmessage('PerlVersionFailure', ['5.6.1']);
     }
+       startsysout();
 
     my @missing = ();
-    unless (eval {require DBI})               { push @missing,"DBI" };
-    unless (eval {require Date::Manip})       { push @missing,"Date::Manip" };
-    unless (eval {require DBD::mysql})        { push @missing,"DBD::mysql" };
-    unless (eval {require HTML::Template})          { push @missing,"HTML::Template" };
-    unless (eval {require Set::Scalar})       { push @missing,"Set::Scalar" };
-    unless (eval {require Digest::MD5})       { push @missing,"Digest::MD5" };
-    unless (eval {require MARC::Record})       { push @missing,"MARC::Record" };
-    unless (eval {require Net::Z3950})        {
-       my $message = getmessage('NETZ3950Missing');
-       showmessage($message, 'PressEnter', '', 1);
-       if ($#missing>=0) {
-           push @missing, "Net::Z3950";
-       }
+    unless (eval {require DBI})              { push @missing,"DBI" };
+    unless (eval {require Date::Manip})      { push @missing,"Date::Manip" };
+    unless (eval {require DBD::mysql})       { push @missing,"DBD::mysql" };
+    unless (eval {require HTML::Template})   { push @missing,"HTML::Template" };
+#    unless (eval {require Set::Scalar})      { push @missing,"Set::Scalar" };
+    unless (eval {require Digest::MD5})      { push @missing,"Digest::MD5" };
+    unless (eval {require MARC::Record})     { push @missing,"MARC::Record" };
+    unless (eval {require Mail::Sendmail})   { push @missing,"Mail::Sendmail" };
+    unless (eval {require Event})       {
+               if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
+                   push @missing, "Event";
+               }
+    }
+    unless (eval {require Net::Z3950})       {
+       showmessage(getmessage('NETZ3950Missing'), 'PressEnter', '', 1);
+               if ($#missing>=0) { # see above note
+                   push @missing, "Net::Z3950";
+               }
     }
 
 #
@@ -379,6 +723,7 @@ sub checkperlmodules {
     }
 
 
+       startsysout();
     unless (-x "/usr/bin/perl") {
        my $realperl=`which perl`;
        chomp $realperl;
@@ -388,6 +733,7 @@ sub checkperlmodules {
        }
        my $response=showmessage(getmessage('ConfirmPerlExecutableSymlink', $realperl), 'yn', 'y', 1);
        unless ($response eq 'n') {
+               startsysout();
            system("ln -s $realperl /usr/bin/perl");
        }
     }
@@ -409,163 +755,11 @@ The Koha scripts will _not_ work without a symlink from %s to /usr/bin/perl
 May I create this symlink? ([Y]/N):
 : |;
 
+$messages->{'DirFailed'}->{en} = RED.qq|
+We could not create %s, but continuing anyway...
 
-=item getmessage
-
-    getmessage($msgid);
-    getmessage($msgid, $variables);
-
-Gets a localized message (format string) with message id $msgid,
-and, if an array reference of variables $variables is given,
-substitutes variables in the format string with @$variables.
-Returns the found message string, with variable substitutions
-if specified.
-
-$msgid must be the message identifier corresponding to a defined
-message string (a valid key to the $messages hash in the Installer
-package). getmessage throws an exception if the message cannot be
-found.
-
-=cut
-
-sub getmessage {
-    my $messagename=shift;
-    my $variables=shift;
-    my $message=$messages->{$messagename}->{$::language} || $messages->{$messagename}->{en} || "Error: No message named $messagename in Install.pm\n";
-    if (defined($variables)) {
-       $message=sprintf $message, @$variables;
-    }
-    return $message;
-}
-
-
-=item showmessage
-
-    showmessage($message, 'none');
-    showmessage($message, 'none', undef, $noclear);
-
-    $result = showmessage($message, 'yn');
-    $result = showmessage($message, 'yn', $defaultresponse);
-    $result = showmessage($message, 'yn', $defaultresponse, $noclear);
-
-    $result = showmessage($message, 'restrictchar CHARS');
-    $result = showmessage($message, 'free');
-    $result = showmessage($message, 'numerical');
-    $result = showmessage($message, 'email');
-    $result = showmessage($message, 'PressEnter');
-
-Shows a message and optionally gets a response from the user.
-
-The first two arguments, the message and the response type,
-are mandatory.  The message must be the actual string to
-display. The caller is responsible for calling getmessage if
-required.
-
-The response type must be one of "none", "yn", "free",
-"numerical", "email", "PressEnter", or a string consisting
-of "restrictchar " followed by a list of allowed characters
-(space can be specified). (Case is not significant, but case is
-significant in the list of allowed characters.) If a response
-type other than the above-listed is specified, the result is
-undefined.
-
-If a response type other than "none" or "PressEnter" is
-specified, a third argument, specifying the default value, can
-be specified:  If this default response is not specified, the
-default response is the first allowed character if the response
-type is "restrictchar", otherwise the default response is the
-empty string. This default response is used when the user does
-not specify a value (i.e., presses Enter without typing in
-anything), showmessage will assume that the default response is
-the user's response.
-
-The screen is normally cleared before the message is displayed;
-if a fourth argument is specified and is nonzero, this
-screen-clearing is not done.
-
-FIXME: A default response of "0" cannot be specified. This is
-wrong; the default response should be checked for undef, not
-for 0.
-
-FIXME: If $noclear is not specified or specified as undef, we
-just test it for a non-zero value without testing it for being
-undef first.
-
-=cut
+|;
 
-sub showmessage {
-    my $message=shift;
-    my $responsetype=shift;
-    my $defaultresponse=shift;
-    my $noclear=shift;
-    ($noclear) || (system('clear'));
-    if ($responsetype =~ /^yn$/) {
-       $responsetype='restrictchar yn';
-    }
-    print $message;
-    SWITCH: {
-       if ($responsetype =~/^restrictchar (.*)/i) {
-           my $response='\0';
-           my $options=$1;
-           until ($options=~/$response/) {
-               ($defaultresponse) || ($defaultresponse=substr($options,0,1));
-               $response=<STDIN>;
-               chomp $response;
-               (length($response)) || ($response=$defaultresponse);
-               unless ($options=~/$response/) {
-                   ($noclear) || (system('clear'));
-                   print "Invalid Response.  Choose from [$options].\n\n";
-                   print $message;
-               }
-           }
-           return $response;
-       }
-       if ($responsetype =~/^free$/i) {
-           (defined($defaultresponse)) || ($defaultresponse='');
-           my $response=<STDIN>;
-           chomp $response;
-           ($response) || ($response=$defaultresponse);
-           return $response;
-       }
-       if ($responsetype =~/^numerical$/i) {
-           (defined($defaultresponse)) || ($defaultresponse='');
-           my $response='';
-           until ($response=~/^\d+$/) {
-               $response=<STDIN>;
-               chomp $response;
-               ($response) || ($response=$defaultresponse);
-               unless ($response=~/^\d+$/) {
-                   ($noclear) || (system('clear'));
-                   print "Invalid Response ($response).  Response must be a number.\n\n";
-                   print $message;
-               }
-           }
-           return $response;
-       }
-       if ($responsetype =~/^email$/i) {
-           (defined($defaultresponse)) || ($defaultresponse='');
-           my $response='';
-           until ($response=~/.*\@.*\..*/) {
-               $response=<STDIN>;
-               chomp $response;
-               ($response) || ($response=$defaultresponse);
-               unless ($response=~/.*\@.*\..*/) {
-                   ($noclear) || (system('clear'));
-                   print "Invalid Response ($response).  Response must be a valid email address.\n\n";
-                   print $message;
-               }
-           }
-           return $response;
-       }
-       if ($responsetype =~/^PressEnter$/i) {
-           <STDIN>;
-           return;
-       }
-       if ($responsetype =~/^none$/i) {
-           return;
-       }
-    }
-}
 
 
 =item getinstallationdirectories
@@ -581,59 +775,60 @@ function does not return any values.
 =cut
 
 sub getinstallationdirectories {
-    $::opacdir = '/usr/local/koha/opac';
-    $::intranetdir = '/usr/local/koha/intranet';
+       if (!$ENV{prefix}) { $ENV{prefix} = "/usr/local"; }
+    $opacdir = $ENV{prefix}.'/koha/opac';
+    $intranetdir = $ENV{prefix}.'/koha/intranet';
     my $getdirinfo=1;
     while ($getdirinfo) {
        # Loop until opac directory and koha directory are different
-       my $message=getmessage('GetOpacDir', [$::opacdir]);
-       $::opacdir=showmessage($message, 'free', $::opacdir);
+       my $message=getmessage('GetOpacDir', [$opacdir]);
+       $opacdir=showmessage($message, 'free', $opacdir);
 
-       $message=getmessage('GetIntranetDir', [$::intranetdir]);
-       $::intranetdir=showmessage($message, 'free', $::intranetdir);
+       $message=getmessage('GetIntranetDir', [$intranetdir]);
+       $intranetdir=showmessage($message, 'free', $intranetdir);
 
-       if ($::intranetdir eq $::opacdir) {
+       if ($intranetdir eq $opacdir) {
            print qq|
 
 You must specify different directories for the OPAC and INTRANET files!
- :: $::intranetdir :: $::opacdir ::
+ :: $intranetdir :: $opacdir ::
 |;
 <STDIN>
        } else {
            $getdirinfo=0;
        }
     }
-    $::kohalogdir='/var/log/koha';
-    my $message=getmessage('GetKohaLogDir', [$::kohalogdir]);
-    $::kohalogdir=showmessage($message, 'free', $::kohalogdir);
+    $kohalogdir=$ENV{prefix}.'/koha/log';
+    my $message=getmessage('GetKohaLogDir', [$kohalogdir]);
+    $kohalogdir=showmessage($message, 'free', $kohalogdir);
 
 
-    # FIXME: Missing error handling for all mkdir calls here
-    unless ( -d $::intranetdir ) {
-       mkdir_parents (dirname($::intranetdir), 0775);
-       mkdir ($::intranetdir,                  0770);
-       chown (oct(0), (getgrnam($::httpduser))[2], "$::intranetdir");
-       chmod (oct(770), "$::intranetdir");
+    # FIXME: Need better error handling for all mkdir calls here
+    unless ( -d $intranetdir ) {
+       mkdir_parents (dirname($intranetdir), 0775) || print getmessage('DirFailed',['parents of '.$intranetdir]);
+       mkdir ($intranetdir,                  0770) || print getmessage('DirFailed',[$intranetdir]);
+       if ($>==0) { chown (oct(0), (getgrnam($httpduser))[2], "$intranetdir"); }
+       chmod 0770, "$intranetdir";
     }
-    mkdir_parents ("$::intranetdir/htdocs",    0750);
-    mkdir_parents ("$::intranetdir/cgi-bin",   0750);
-    mkdir_parents ("$::intranetdir/modules",   0750);
-    mkdir_parents ("$::intranetdir/scripts",   0750);
-    unless ( -d $::opacdir ) {
-       mkdir_parents (dirname($::opacdir),     0775);
-       mkdir ($::opacdir,                      0770);
-       chown (oct(0), (getgrnam($::httpduser))[2], "$::opacdir");
-       chmod (oct(770), "$::opacdir");
+    mkdir_parents ("$intranetdir/htdocs",    0750);
+    mkdir_parents ("$intranetdir/cgi-bin",   0750);
+    mkdir_parents ("$intranetdir/modules",   0750);
+    mkdir_parents ("$intranetdir/scripts",   0750);
+    unless ( -d $opacdir ) {
+       mkdir_parents (dirname($opacdir),     0775) || print getmessage('DirFailed',['parents of '.$opacdir]);
+       mkdir ($opacdir,                      0770) || print getmessage('DirFailed',[$opacdir]);
+       if ($>==0) { chown (oct(0), (getgrnam($httpduser))[2], "$opacdir"); }
+       chmod (oct(770), "$opacdir");
     }
-    mkdir_parents ("$::opacdir/htdocs",        0750);
-    mkdir_parents ("$::opacdir/cgi-bin",       0750);
+    mkdir_parents ("$opacdir/htdocs",        0750);
+    mkdir_parents ("$opacdir/cgi-bin",       0750);
 
 
-    unless ( -d $::kohalogdir ) {
-       mkdir_parents (dirname($::kohalogdir),  0775);
-       mkdir ($::kohalogdir,                   0770);
-       chown (oct(0), (getgrnam($::httpduser))[2,3], "$::kohalogdir");
-       chmod (oct(770), "$::kohalogdir");
+    unless ( -d $kohalogdir ) {
+       mkdir_parents (dirname($kohalogdir),  0775) || print getmessage('DirFailed',['parents of '.$kohalogdir]);
+       mkdir ($kohalogdir,                   0770) || print getmessage('DirFailed',[$kohalogdir]);
+       if ($>==0) { chown (oct(0), (getgrnam($httpduser))[2,3], "$kohalogdir"); }
+       chmod (oct(770), "$kohalogdir");
     }
 }
 
@@ -653,7 +848,8 @@ function does not return any values.
 =cut
 
 $messages->{'DatabaseName'}->{en} = heading('Name of MySQL database') . qq|
-Please provide the name of the mysql database for your koha installation.
+Please provide the name that you wish to give your koha database.
+It must not exist already on the database server.
 
 Database name [%s]: |;
 
@@ -664,7 +860,7 @@ another machine this will be "localhost".
 Database host [%s]: |;
 
 $messages->{'DatabaseUser'}->{en} = heading('Database User') . qq|
-Please provide the name of the user, who will have full administrative rights
+Please provide the name of the user who will have full administrative rights
 to the %s database, when authenticating from %s.
 
 This user will also be used to access Koha's INTRANET interface.
@@ -674,42 +870,44 @@ Database user [%s]: |;
 $messages->{'DatabasePassword'}->{en} = heading('Database Password') . qq|
 Please provide a good password for the user %s.
 
-Database Password: |;
+This password will also be used to access Koha's INTRANET interface.
+
+Password for database user %s: |;
 
 $messages->{'BlankPassword'}->{en} = heading('BLANK PASSWORD') . qq|
-You must not use a blank password for your MySQL user!
+You must not use a blank password for your MySQL user.
 
 Press <ENTER> to try again: 
 |;
 
 sub getdatabaseinfo {
 
-    $::dbname = 'Koha';
-    $::hostname = 'localhost';
-    $::user = 'kohaadmin';
-    $::pass = '';
+    $dbname = 'Koha';
+    $hostname = 'localhost';
+    $user = 'kohaadmin';
+    $pass = '';
 
 #Get the database name
 
-    my $message=getmessage('DatabaseName', [$::dbname]);
-    $::dbname=showmessage($message, 'free', $::dbname);
+    my $message=getmessage('DatabaseName', [$dbname]);
+    $dbname=showmessage($message, 'free', $dbname);
 
 #Get the hostname for the database
     
-    $message=getmessage('DatabaseHost', [$::hostname]);
-    $::hostname=showmessage($message, 'free', $::hostname);
+    $message=getmessage('DatabaseHost', [$hostname]);
+    $hostname=showmessage($message, 'free', $hostname);
 
 #Get the username for the database
 
-    $message=getmessage('DatabaseUser', [$::dbname, $::hostname, $::user]);
-    $::user=showmessage($message, 'free', $::user);
+    $message=getmessage('DatabaseUser', [$dbname, $hostname, $user]);
+    $user=showmessage($message, 'free', $user);
 
 #Get the password for the database user
 
-    while ($::pass eq '') {
-       my $message=getmessage('DatabasePassword', [$::user]);
-       $::pass=showmessage($message, 'free', $::pass);
-       if ($::pass eq '') {
+    while ($pass eq '') {
+       my $message=getmessage('DatabasePassword', [$user, $user]);
+       $pass=showmessage($message, 'free', $pass);
+       if ($pass eq '') {
            my $message=getmessage('BlankPassword');
            showmessage($message,'PressEnter');
        }
@@ -772,6 +970,8 @@ sub getapacheinfo {
                          /usr/local/etc/apache/httpd.conf
                          /usr/local/etc/apache/apache.conf
                          /var/www/conf/httpd.conf
+                         /etc/apache2/httpd.conf
+                         /etc/apache2/apache.conf
                          /etc/apache/conf/httpd.conf
                          /etc/apache/conf/apache.conf
                          /etc/apache-ssl/conf/apache.conf
@@ -786,10 +986,10 @@ sub getapacheinfo {
     if ($#confpossibilities==-1) {
        my $message=getmessage('NoApacheConfFiles');
        my $choice='';
-       until (-f $::realhttpdconf) {
+       until (-f $realhttpdconf) {
            $choice=showmessage($message, "free", 1);
            if (-f $choice) {
-               $::realhttpdconf=$choice;
+               $realhttpdconf=$choice;
            } else {
                showmessage(getmessage('NotAFile', [$choice]),'PressEnter', '', 1);
            }
@@ -805,51 +1005,65 @@ sub getapacheinfo {
        }
        my $message=getmessage('FoundMultipleApacheConfFiles', [$conffiles]);
        my $choice=showmessage($message, "restrictchar $options", 1);
-       $::realhttpdconf=$confpossibilities[$choice-1];
+       $realhttpdconf=$confpossibilities[$choice-1];
     } else {
-       $::realhttpdconf=$confpossibilities[0];
+       $realhttpdconf=$confpossibilities[0];
     }
-    unless (open (HTTPDCONF, "<$::realhttpdconf")) {
-       warn "Insufficient privileges to open $::realhttpdconf for reading.\n";
+    unless (open (HTTPDCONF, "<$realhttpdconf")) {
+       warn RED."Insufficient privileges to open $realhttpdconf for reading.\n";
        sleep 4;
     }
 
     while (<HTTPDCONF>) {
        if (/^\s*User\s+"?([-\w]+)"?\s*$/) {
-           $::httpduser = $1;
+           $httpduser = $1;
        }
     }
     close(HTTPDCONF);
 
-
-
-
-    unless ($::httpduser) {
-       my $message=getmessage('EnterApacheUser', [$::etcdir]);
-       until (length($::httpduser) && getpwnam($::httpduser)) {
-           $::httpduser=showmessage($message, "free", '');
-           if (length($::httpduser)>0) {
-               unless (getpwnam($::httpduser)) {
-                   my $message=getmessage('InvalidUserid', [$::httpduser]);
+    unless (defined($httpduser)) {
+       my $message=getmessage('EnterApacheUser', [$etcdir]);
+       until (defined($httpduser) && length($httpduser) && getpwnam($httpduser)) {
+           $httpduser=showmessage($message, "free", '');
+           if (length($httpduser)>0) {
+               unless (getpwnam($httpduser)) {
+                   my $message=getmessage('InvalidUserid', [$httpduser]);
                    showmessage($message,'PressEnter');
                }
            } else {
            }
        }
-       print "AU: $::httpduser\n";
     }
 }
 
 
+=item getapachevhostinfo
+
+    getapachevhostinfo;
+
+Gets various pieces of information related to virtual hosting:
+the webmaster email address, virtual hostname, and the ports
+that the OPAC and INTRANET modules run on.
+
+These pieces of information are saved to global variables; the
+function does not return any values.
+
+=cut
+
 $messages->{'ApacheConfigIntroduction'}->{en} =
    heading('APACHE CONFIGURATION') . qq|
-Koha needs to setup your Apache configuration file for the
+Koha needs to write an Apache configuration file for the
 OPAC and LIBRARIAN virtual hosts.  By default this installer
 will do this by using one ip address and two different ports
 for the virtual hosts.  There are other ways to set this up,
-and the installer will leave comments in httpd.conf detailing
+and the installer will leave comments in
+%s/koha-httpd.conf detailing
 what these other options are.
 
+NOTE: You will need to add lines to your main httpd.conf to
+  Include %s/koha-httpd.conf
+and to make sure it is listening on the right ports
+(using the Listen directive).
 
 Press <ENTER> to continue: |;
 
@@ -862,13 +1076,14 @@ E-mail contact [%s]: |;
 
 $messages->{'GetServerName'}->{en} =
    heading('WEB SERVER HOST NAME OR IP ADDRESS') . qq|
-Please enter the domain name or ip address of your computer.
+Please enter the host name or IP address that you wish to use for koha.
+Normally, this should be a name or IP that belongs to this machine.
 
 Host name or IP Address [%s]: |;
 
 $messages->{'GetOpacPort'}->{en} = heading('OPAC VIRTUAL HOST PORT') . qq|
 Please enter the port for your OPAC interface.  This defaults to port 80, but
-if you are already serving web content from this server, you should change it
+if you are already serving web content from this host, you should change it
 to a different port (8000 might be a good choice).
 
 Enter the OPAC Port [%s]: |;
@@ -883,39 +1098,57 @@ Enter the Intranet Port [%s]: |;
 
 sub getapachevhostinfo {
 
-    $::svr_admin = "webmaster\@$::domainname";
-    $::servername=`hostname`;
-    chomp $::servername;
-    $::opacport=80;
-    $::intranetport=8080;
+    $svr_admin = "webmaster\@$domainname";
+    $servername=`hostname`;
+    chomp $servername;
+    $opacport=80;
+    $intranetport=8080;
 
-    showmessage(getmessage('ApacheConfigIntroduction'), 'PressEnter');
+    showmessage(getmessage('ApacheConfigIntroduction',[$etcdir,$etcdir]), 'PressEnter');
 
-    $::svr_admin=showmessage(getmessage('GetVirtualHostEmail', [$::svr_admin]), 'email', $::svr_admin);
-    $::servername=showmessage(getmessage('GetServerName', [$::servername]), 'free', $::servername);
+    $svr_admin=showmessage(getmessage('GetVirtualHostEmail', [$svr_admin]), 'email', $svr_admin);
+    $servername=showmessage(getmessage('GetServerName', [$servername]), 'free', $servername);
 
 
-    $::opacport=showmessage(getmessage('GetOpacPort', [$::opacport]), 'numerical', $::opacport);
-    $::intranetport=showmessage(getmessage('GetIntranetPort', [$::opacport, $::intranetport]), 'numerical', $::intranetport);
+    $opacport=showmessage(getmessage('GetOpacPort', [$opacport]), 'numerical', $opacport);
+    $intranetport=showmessage(getmessage('GetIntranetPort', [$opacport, $intranetport]), 'numerical', $intranetport);
 
 }
 
-$messages->{'StartUpdateApache'}->{en} =
-   heading('UPDATING APACHE CONFIGURATION') . qq|
-Checking for modules that need to be loaded...
-|;
 
-$messages->{'LoadingApacheModuleModEnv'}->{en}="Loading SetEnv Apache module.\n";
+=item updateapacheconf
+
+    updateapacheconf;
+
+Updates the Apache config file according to parameters previously
+specified by the user.
+
+It will append fully-commented directives at the end of the original
+Apache config file.  The old config file is renamed with an extension
+of .prekoha.
+
+If you need to uninstall Koha for any reason, the lines between
+
+    # Ports to listen to for Koha
 
-$messages->{'LoadingApacheModuleModInc'}->{en}="Loading Includes Apache module.\n";
+and the block of comments beginning with
 
-$messages->{'ApacheConfigBackupFailed'}->{en} =
-   heading('APACHE CONFIGURATION BACKUP FAILED') . qq|
-An error occurred while trying to make a backup copy of %s.
+    # If you want to use name based Virtual Hosting:
 
-  %s
+must be removed.
 
-No changes will be made to the apache configuration file at this time.
+=cut
+
+$messages->{'StartUpdateApache'}->{en} =
+   heading('UPDATING APACHE CONFIGURATION') . qq|
+Checking for modules that need to be loaded...
+|;
+
+$messages->{'ApacheConfigMissingModules'}->{en} =
+   heading('APACHE CONFIGURATION NEEDS UPDATE') . qq|
+Koha uses the mod_env and mod_include apache features, but the
+installer did not find statements for them in your config.  Please
+make sure that they are enabled for your Koha host.
 
 Press <ENTER> to continue: |;
 
@@ -924,65 +1157,36 @@ $messages->{'ApacheAlreadyConfigured'}->{en} =
    heading('APACHE ALREADY CONFIGURED') . qq|
 %s appears to already have an entry for Koha
 Virtual Hosts.  You may need to edit %s
-f anything has changed since it was last set up.  This
+if anything has changed since it was last set up.  This
 script will not attempt to modify an existing Koha apache
 configuration.
 
 Press <ENTER> to continue: |;
 
 sub updateapacheconf {
-    my $logfiledir=`grep ^ErrorLog "$::realhttpdconf"`;
-    chomp $logfiledir;
-
-    if ($logfiledir) {
-       $logfiledir=~m#ErrorLog (.*)/[^/]*$#
-           or die "Can't parse ErrorLog directive\n";
-       $logfiledir=$1;
-    }
-
-    unless ($logfiledir) {
-       $logfiledir='logs';
-    }
-
+    my $logfiledir=$kohalogdir;
+    my $httpdconf = $etcdir."/koha-httpd.conf";
+   
     showmessage(getmessage('StartUpdateApache'), 'none');
+       # to be polite about it: I don't think this should touch the main httpd.conf
 
-    my $httpdconf;
+       # QUESTION: Should we warn for includes_module too?
     my $envmodule=0;
     my $includesmodule=0;
-    open HC, "<$::realhttpdconf";
+    open HC, "<$realhttpdconf";
     while (<HC>) {
        if (/^\s*#\s*LoadModule env_module /) {
-           s/^\s*#\s*//;
-           showmessage(getmessage('LoadingApacheModuleModEnv'));
+           showmessage(getmessage('ApacheConfigMissingModules'));
            $envmodule=1;
        }
-       if (/^\s*#\s*LoadModule includes_module /) {
-           s/^\s*#\s*//;
-           showmessage(getmessage('LoadingApacheModuleModInc'));
-       }
        if (/\s*LoadModule includes_module / ) {
            $includesmodule=1;
        }
-       $httpdconf.=$_;
     }
 
-    my $backupfailed=0;
-    $backupfailed=`cp -f $::realhttpdconf $::realhttpdconf\.prekoha`;
-    if ($backupfailed) {
-       showmessage(getmessage('ApacheConfigBackupFailed', [$::realhttpdconf,$backupfailed ]), 'PressEnter');
-       return;
-    }
-
-    if ($envmodule || $includesmodule) {
-       open HC, ">$::realhttpdconf";
-       print HC $httpdconf;
-       close HC;
-    }
-
-
-    
-    if (`grep 'VirtualHost $::servername' "$::realhttpdconf"`) {
-       showmessage(getmessage('ApacheAlreadyConfigured', [$::realhttpdconf, $::realhttpdconf]), 'PressEnter');
+       startsysout;
+    if (`grep 'VirtualHost $servername' "$httpdconf"`) {
+       showmessage(getmessage('ApacheAlreadyConfigured', [$httpdconf, $httpdconf]), 'PressEnter');
        return;
     } else {
        my $includesdirectives='';
@@ -990,55 +1194,58 @@ sub updateapacheconf {
            $includesdirectives.="Options +Includes\n";
            $includesdirectives.="   AddHandler server-parsed .html\n";
        }
-       open(SITE,">>$::realhttpdconf") or warn "Insufficient priveleges to open $::realhttpdconf for writing.\n";
+       open(SITE,">$httpdconf") or warn "Insufficient priveleges to open $httpdconf for writing.\n";
        my $opaclisten = '';
-       if ($::opacport != 80) {
-           $opaclisten="Listen $::opacport";
+       if ($opacport != 80) {
+           $opaclisten="Listen $opacport";
        }
        my $intranetlisten = '';
-       if ($::intranetport != 80) {
-           $intranetlisten="Listen $::intranetport";
+       if ($intranetport != 80) {
+           $intranetlisten="Listen $intranetport";
        }
        print SITE <<EOP
 
 # Ports to listen to for Koha
-$opaclisten
-$intranetlisten
+# uncomment these if they aren't already in main httpd.conf
+#$opaclisten
+#$intranetlisten
 
 # NameVirtualHost is used by one of the optional configurations detailed below
 
 #NameVirtualHost 11.22.33.44
 
 # KOHA's OPAC Configuration
-<VirtualHost $::servername\:$::opacport>
-   ServerAdmin $::svr_admin
-   DocumentRoot $::opacdir/htdocs
-   ServerName $::servername
-   ScriptAlias /cgi-bin/koha/ $::opacdir/cgi-bin/
+<VirtualHost $servername\:$opacport>
+   ServerAdmin $svr_admin
+   DocumentRoot $opacdir/htdocs
+   ServerName $servername
+   ScriptAlias /cgi-bin/koha/ $opacdir/cgi-bin/
    ErrorLog $logfiledir/opac-error_log
    TransferLog $logfiledir/opac-access_log
-   SetEnv PERL5LIB "$::intranetdir/modules"
+   SetEnv PERL5LIB "$intranetdir/modules"
+   SetEnv KOHA_CONF "$etcdir/koha.conf"
    $includesdirectives
 </VirtualHost>
 
 # KOHA's INTRANET Configuration
-<VirtualHost $::servername\:$::intranetport>
-   ServerAdmin $::svr_admin
-   DocumentRoot $::intranetdir/htdocs
-   ServerName $::servername
-   ScriptAlias /cgi-bin/koha/ "$::intranetdir/cgi-bin/"
+<VirtualHost $servername\:$intranetport>
+   ServerAdmin $svr_admin
+   DocumentRoot $intranetdir/htdocs
+   ServerName $servername
+   ScriptAlias /cgi-bin/koha/ "$intranetdir/cgi-bin/"
    ErrorLog $logfiledir/koha-error_log
    TransferLog $logfiledir/koha-access_log
-   SetEnv PERL5LIB "$::intranetdir/modules"
+   SetEnv PERL5LIB "$intranetdir/modules"
+   SetEnv KOHA_CONF "$etcdir/koha.conf"
    $includesdirectives
 </VirtualHost>
 
 # If you want to use name based Virtual Hosting:
 #   1. remove the two Listen lines
-#   2. replace $::servername\:$::opacport wih your.opac.domain.name
-#   3. replace ServerName $::servername wih ServerName your.opac.domain.name
-#   4. replace $::servername\:$::intranetport wih your intranet domain name
-#   5. replace ServerName $::servername wih ServerName your.intranet.domain.name
+#   2. replace $servername\:$opacport wih your.opac.domain.name
+#   3. replace ServerName $servername wih ServerName your.opac.domain.name
+#   4. replace $servername\:$intranetport wih your intranet domain name
+#   5. replace ServerName $servername wih ServerName your.intranet.domain.name
 #
 # If you want to use NameVirtualHost'ing (using two names on one ip address):
 #   1.  Follow steps 1-5 above
@@ -1050,6 +1257,19 @@ EOP
     }
 }
 
+
+=item basicauthentication
+
+    basicauthentication;
+
+Asks the user whether HTTP basic authentication is wanted, and,
+if so, the user name and password for the basic authentication.
+
+These pieces of information are saved to global variables; the
+function does not return any values.
+
+=cut
+
 $messages->{'IntranetAuthenticationQuestion'}->{en} =
    heading('INTRANET AUTHENTICATION') . qq|
 I can set it up so that the Intranet/Librarian site is password protected using
@@ -1059,7 +1279,7 @@ This is going to be phased out very soon. However, setting this up can provide
 an extra layer of security before the new authentication system is completely
 in place.
 
-Would you like to do this ([Y]/N): |;
+Would you like to do this ([Y]/N): |;  #'
 
 $messages->{'BasicAuthUsername'}->{en}="Please enter a userid for intranet access [%s]: ";
 $messages->{'BasicAuthPassword'}->{en}="Please enter a password for %s: ";
@@ -1068,6 +1288,7 @@ $messages->{'BasicAuthPasswordWasBlank'}->{en}="\nYou cannot use a blank passwor
 sub basicauthentication {
     my $message=getmessage('IntranetAuthenticationQuestion');
     my $answer=showmessage($message, 'yn', 'y');
+    my $httpdconf = $etcdir."/koha-httpd.conf";
 
     my $apacheauthusername='librarian';
     my $apacheauthpassword='';
@@ -1080,17 +1301,17 @@ sub basicauthentication {
                ($apacheauthpassword) = showmessage(getmessage('BasicAuthPasswordWasBlank'), 'none', '', 1);
            }
        }
-       open AUTH, ">$::etcdir/kohaintranet.pass";
+       open AUTH, ">$etcdir/kohaintranet.pass";
        my $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        my $salt=substr($chars, int(rand(length($chars))),1);
        $salt.=substr($chars, int(rand(length($chars))),1);
        print AUTH $apacheauthusername.":".crypt($apacheauthpassword, $salt)."\n";
        close AUTH;
-       open(SITE,">>$::realhttpdconf") or warn "Insufficient priveleges to open $::realhttpdconf for writing.\n";
+       open(SITE,">>$httpdconf") or warn "Insufficient priveleges to open $realhttpdconf for writing.\n";
        print SITE <<EOP
 
-<Directory $::intranetdir>
-    AuthUserFile $::etcdir/kohaintranet.pass
+<Directory $intranetdir>
+    AuthUserFile $etcdir/kohaintranet.pass
     AuthType Basic
     AuthName "Koha Intranet (for librarians only)"
     Require  valid-user
@@ -1100,6 +1321,20 @@ EOP
     close(SITE);
 }
 
+
+=item installfiles
+
+    installfiles
+
+Install the Koha files to the specified OPAC and INTRANET
+directories (usually in /usr/local/koha).
+
+The koha.conf file is created, but as koha.conf.tmp. The
+caller is responsible for calling finalizeconfigfile when
+installation is completed, to rename it back to koha.conf.
+
+=cut
+
 $messages->{'InstallFiles'}->{en} = heading('INSTALLING FILES') . qq|
 Copying files to installation directories:
 
@@ -1112,65 +1347,91 @@ $messages->{'CopyingFiles'}->{en}="Copying %s to %s.\n";
 
 sub installfiles {
 
+       #MJR: preserve old files, just in case
+       sub neatcopy {
+               my $desc = shift;
+               my $src = shift;
+               my $tgt = shift;
+               
+               if (-d $tgt) {
+               print getmessage('CopyingFiles', ["old ".$desc,$tgt.".old"]);
+                       startsysout;
+                       system("mv ".$tgt." ".$tgt.".old");
+               }
+
+       print getmessage('CopyingFiles', [$desc,$tgt]);
+       startsysout;
+           system("cp -R ".$src." ".$tgt);
+       }
 
     showmessage(getmessage('InstallFiles'),'none');
-    print getmessage('CopyingFiles', ['intranet-html', "$::intranetdir/htdocs" ]);
-    system("cp -R intranet-html/* $::intranetdir/htdocs/");
-    print getmessage('CopyingFiles', ['intranet-cgi', "$::intranetdir/cgi-bin" ]);
-    system("cp -R intranet-cgi/* $::intranetdir/cgi-bin/");
-    print getmessage('CopyingFiles', ['stand-alone scripts', "$::intranetdir/scripts" ]);
-    system("cp -R scripts/* $::intranetdir/scripts/");
-    print getmessage('CopyingFiles', ['perl modules', "$::intranetdir/modules" ]);
-    system("cp -R modules/* $::intranetdir/modules/");
-    print getmessage('CopyingFiles', ['opac-html', "$::opacdir/htdocs" ]);
-    system("cp -R opac-html/* $::opacdir/htdocs/");
-    print getmessage('CopyingFiles', ['opac-cgi', "$::opacdir/cgi-bin" ]);
-    system("cp -R opac-cgi/* $::opacdir/cgi-bin/");
-    system("touch $::opacdir/cgi-bin/opac");
-
-    system("chown -R root:$::httpduser $::opacdir");
-    system("chown -R root:$::httpduser $::intranetdir");
+
+    neatcopy("admin templates", 'intranet-html', "$intranetdir/htdocs");
+    neatcopy("admin interface", 'intranet-cgi', "$intranetdir/cgi-bin");
+    neatcopy("main scripts", 'scripts', "$intranetdir/scripts");
+    neatcopy("perl modules", 'modules', "$intranetdir/modules");
+    neatcopy("OPAC templates", 'opac-html', "$opacdir/htdocs");
+    neatcopy("OPAC interface", 'opac-cgi', "$opacdir/cgi-bin");
+       startsysout();
+    system("touch $opacdir/cgi-bin/opac");
+
+       #MJR: is this necessary?
+       if ($> == 0) {
+           system("chown -R $httpduser:$httpduser $opacdir $intranetdir");
+    }
+       system("chmod -R a+rx $opacdir $intranetdir");
 
     # Create /etc/koha.conf
 
     my $old_umask = umask(027); # make sure koha.conf is never world-readable
-    open(SITES,">$::etcdir/koha.conf.tmp") or warn "Couldn't create file at $::etcdir. Must have write capability.\n";
+    open(SITES,">$etcdir/koha.conf.tmp") or warn "Couldn't create file at $etcdir. Must have write capability.\n";
     print SITES qq|
-database=$::dbname
-hostname=$::hostname
-user=$::user
-pass=$::pass
-includes=$::opacdir/htdocs/includes
-intranetdir=$::intranetdir
-opacdir=$::opacdir
-kohalogdir=$::kohalogdir
-kohaversion=$::kohaversion
-httpduser=$::httpduser
-intrahtdocs=$::intranetdir/htdocs/intranet-tmpl
-opachtdocs=$::opacdir/htdocs/opac-tmpl
+database=$dbname
+hostname=$hostname
+user=$user
+pass=$pass
+includes=$opacdir/htdocs/includes
+intranetdir=$intranetdir
+opacdir=$opacdir
+kohalogdir=$kohalogdir
+kohaversion=$kohaversion
+httpduser=$httpduser
+intrahtdocs=$intranetdir/htdocs/intranet-tmpl
+opachtdocs=$opacdir/htdocs/opac-tmpl
 |;
     close(SITES);
     umask($old_umask);
 
-    chown((getpwnam($::httpduser)) [2,3], "$::etcdir/koha.conf.tmp") or warn "can't chown koha.conf: $!";
-    chmod 0440, "$::etcdir/koha.conf.tmp";
+       startsysout();
+       #MJR: can't help but this be broken, can we?
+    chmod 0440, "$etcdir/koha.conf.tmp";
+       
+       #MJR: does this contain any passwords?
+    chmod 0755, "$intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh", "$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh", "$intranetdir/scripts/z3950daemon/processz3950queue";
+
+       if ($> == 0) {
+           chown((getpwnam($httpduser)) [2,3], "$etcdir/koha.conf.tmp") or warn "can't chown koha.conf: $!";
+       chown(0, (getpwnam($httpduser)) [3], "$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh") or warn "can't chown $intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh: $!";
+       chown(0, (getpwnam($httpduser)) [3], "$intranetdir/scripts/z3950daemon/processz3950queue") or warn "can't chown $intranetdir/scripts/z3950daemon/processz3950queue: $!";
+       } #MJR: FIXME: Should report that we haven't chown()d.
+}
 
-    chmod 0750, "$::intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh";
-    chmod 0750, "$::intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh";
-    chmod 0750, "$::intranetdir/scripts/z3950daemon/processz3950queue";
-    chown(0, (getpwnam($::httpduser)) [3], "$::intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh") or warn "can't chown $::intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh: $!";
-    chown(0, (getpwnam($::httpduser)) [3], "$::intranetdir/scripts/z3950daemon/processz3950queue") or warn "can't chown $::intranetdir/scripts/z3950daemon/processz3950queue: $!";
 
-}
+=item databasesetup
+
+    databasesetup;
+
+Finds out where the MySQL utitlities are located in the system,
+then create the Koha database structure and MySQL permissions.
+
+=cut
 
 $messages->{'MysqlRootPassword'}->{en} =
    heading('MYSQL ROOT USER PASSWORD') . qq|
-To allow us to create the koha database please supply your
+To allow us to create the koha database please enter your
 mysql server's root user password:
 
-Enter MySQL root user password: |;
-
-$messages->{'InvalidMysqlRootPassword'}->{en}="Invalid Password.  Please try again.";
+Password: |;   #'
 
 $messages->{'CreatingDatabase'}->{en} = heading('CREATING DATABASE') . qq|
 Creating the MySQL database for Koha...
@@ -1182,7 +1443,7 @@ $messages->{'CreatingDatabaseError'}->{en} =
 Couldn't connect to the MySQL server for the reason given above.
 This is a serious problem, the database will not get installed.\a
 
-Press <ENTER> to continue: |;
+Press <ENTER> to continue: |;  #'
 
 $messages->{'SampleData'}->{en} = heading('SAMPLE DATA') . qq|
 If you are installing Koha for evaluation purposes,  I have a batch of sample
@@ -1191,7 +1452,7 @@ data that you can install now.
 If you are installing Koha with the intention of populating it with your own
 data, you probably don't want this sample data installed.
 
-Would you like to install the sample data? Y/[N]: |;
+Would you like to install the sample data? Y/[N]: |;   #'
 
 $messages->{'SampleDataInstalled'}->{en} =
    heading('SAMPLE DATA INSTALLED') . qq|
@@ -1215,33 +1476,25 @@ $messages->{'BranchName'}->{en}="Branch Name [%s]: ";
 $messages->{'BranchCode'}->{en}="Branch Code (4 letters or numbers) [%s]: ";
 $messages->{'PrinterQueue'}->{en}="Printer Queue [%s]: ";
 $messages->{'PrinterName'}->{en}="Printer Name [%s]: ";
-$messages->{'BlankMysqlPassword'}->{en} = heading('Blank MySQL Password') . qq|
-Do not leave your MySQL root password blank unless you know exactly what you
-are doing.  To change your MySQL root password use the mysqladmin command:
-
-mysqladmin password NEWPASSWORDHERE
-
-Press <ENTER> to continue:
-|;
 
 sub databasesetup {
-    $::mysqluser = 'root';
-    $::mysqlpass = '';
+    $mysqluser = 'root';
+    $mysqlpass = '';
 
     foreach my $mysql (qw(/usr/local/mysql
                          /opt/mysql
                          /usr
                          )) {
        if ( -d $mysql  && -f "$mysql/bin/mysqladmin") {
-           $::mysqldir=$mysql;
+           $mysqldir=$mysql;
        }
     }
-    if (!$::mysqldir){
+    if (!$mysqldir){
        print "I don't see mysql in the usual places.\n";
        for (;;) {
            print "Where have you installed mysql? ";
-           chomp($::mysqldir = <STDIN>);
-           last if -f "$::mysqldir/bin/mysqladmin";
+           chomp($mysqldir = <STDIN>);
+           last if -f "$mysqldir/bin/mysqladmin";
        print <<EOP;
 
 I can't find it there either. If you compiled mysql yourself,
@@ -1251,48 +1504,51 @@ The file mysqladmin should be in bin/mysqladmin under the directory that you
 provide here.
 
 EOP
+#'
        }
     }
-
-
-    my $needpassword=1;
-    while ($needpassword) {
-       $::mysqlpass=showmessage(getmessage('MysqlRootPassword'), 'free');
-       $::mysqlpass_quoted = $::mysqlpass;
-       $::mysqlpass_quoted =~ s/"/\\"/g;
-       $::mysqlpass_quoted="-p\"$::mysqlpass_quoted\"";
-       $::mysqlpass eq '' and $::mysqlpass_quoted='';
-       my $result=system("$::mysqldir/bin/mysqladmin -u$::mysqluser $::mysqlpass_quoted proc > /dev/null 2>&1");
+    # we must not put the mysql root password on the command line
+       $mysqlpass=     showmessage(getmessage('MysqlRootPassword'),'silentfree');
+       
+       showmessage(getmessage('CreatingDatabase'),'none');
+       # set the login up
+       setmysqlclipass($mysqlpass);
+       # Set up permissions
+       startsysout();
+       print system("$mysqldir/bin/mysql -u$mysqluser mysql -e \"insert into user (Host,User,Password) values ('$hostname','$user',password('$pass'))\"\;");
+       system("$mysqldir/bin/mysql -u$mysqluser mysql -e \"insert into db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv, index_priv, alter_priv) values ('%','$dbname','$user','Y','Y','Y','Y','Y','Y','Y','Y')\"");
+       system("$mysqldir/bin/mysqladmin -u$mysqluser reload");
+       # Change to admin user login
+       setmysqlclipass($pass);
+       my $result=system("$mysqldir/bin/mysqladmin", "-u$user", "create", "$dbname");
        if ($result) {
-           print getmessage('InvalidMysqlRootPassword');
+               showmessage(getmessage('CreatingDatabaseError'),'PressEnter', '', 1);
        } else {
-           if ($::mysqlpass eq '') {
-               showmessage(getmessage('BlankMysqlPassword'), 'PressEnter');
-           }
-           $needpassword=0;
+               # Create the database structure
+               startsysout();
+               system("$mysqldir/bin/mysql -u$user $dbname < koha.mysql");
        }
-    }
 
-    showmessage(getmessage('CreatingDatabase'),'none');
+}
 
-    my $result=system("$::mysqldir/bin/mysqladmin", "-u$::mysqluser", "-p$::mysqlpass", "create", "$::dbname");
-    if ($result) {
-       showmessage(getmessage('CreatingDatabaseError'),'PressEnter', '', 1);
-    } else {
-       # Populate the Koha database
-       system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname < koha.mysql");
-       # Set up permissions
-       system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted mysql -e \"insert into user (Host,User,Password) values ('$::hostname','$::user',password('$::pass'))\"\;");
-       system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted mysql -e \"insert into db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv, index_priv, alter_priv) values ('%','$::dbname','$::user','Y','Y','Y','Y','Y','Y','Y','Y')\"");
-       system("$::mysqldir/bin/mysqladmin -u$::mysqluser $::mysqlpass_quoted reload");
 
+=item updatedatabase
 
+    updatedatabase;
 
+Updates the Koha database structure, including the addition of
+MARC tables.
 
+The MARC tables are also populated in addition to being created.
 
-    }
+Because updatedatabase calls scripts/updater/updatedatabase to
+do the actual update, and that script uses C4::Context,
+$etcdir/koha.conf must exist at this point. We use the KOHA_CONF
+environment variable to do this.
 
-}
+FIXME: (See checkabortedinstall as it depends on old symlink way.)
+
+=cut
 
 $messages->{'UpdateMarcTables'}->{en} =
    heading('UPDATING MARC FIELD DEFINITION TABLES') . qq|
@@ -1300,63 +1556,88 @@ You can import marc parameters for :
 
   1 MARC21
   2 UNIMARC
-  3 none
+  N none
 
-Please choose which parameter you want to install. Note if you choose 3,
+Please choose which parameter you want to install. Note if you choose N,
 nothing will be added, and it can be a BIG job to manually create those tables
 
 Choose MARC definition [1]: |;
 
 $messages->{'Language'}->{en} = heading('CHOOSE LANGUAGES') . qq|
 This version of koha supports a few languages.
-Enter you languages preferences : either en, fr, es or pl.
+Enter your language preference : either en, fr, es, pl or zh_TW
+
 Note that the en is always choosen when the system does not finds the
 language you choose in a specific screen.
-fr : opac is translated (except pictures)
+
+fr : all is translated (except pictures)
 es : a few intranet is translated (including pictures)
-pl : opac is translated (UNTESTED in this release)
-|;
+pl : OPAC and a few intranet is translated
+zh_TW : partial translation
+
+Whether you specify a language here, you can always go to the
+intranet interface and change it from the system preferences.
+
+Which language do you choose? |;
 
 sub updatedatabase {
-    # At this point, $::etcdir/koha.conf must exist, for C4::Context
-    # We must somehow temporarily enable $::etcdir/koha.conf. A symlink can
-    # do this & at the same time facilitate detection of aborted installs.
-       my $result=system ("perl -I $::intranetdir/modules scripts/updater/updatedatabase");
+    # At this point, $etcdir/koha.conf must exist, for C4::Context
+    $ENV{"KOHA_CONF"}=$etcdir.'/koha.conf.tmp';
+       startsysout();  
+       my $result=system ("perl -I $intranetdir/modules scripts/updater/updatedatabase");
        if ($result) {
+               restoremycnf();
                print "Problem updating database...\n";
                exit;
        }
 
-       my $response=showmessage(getmessage('UpdateMarcTables'), 'restrictchar 123', '1');
+       my $response=showmessage(getmessage('UpdateMarcTables'), 'restrictchar 12N', '1');
 
-       if ($response == 1) {
-               system("cat scripts/misc/marc_datas/marc21_en/structure_def.sql | $::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname");
+       startsysout();
+       if ($response eq '1') {
+               system("cat scripts/misc/marc_datas/marc21_en/structure_def.sql | $mysqldir/bin/mysql -u$user $dbname");
        }
-       if ($response == 2) {
-               system("cat scripts/misc/marc_datas/unimarc_fr/structure_def.sql | $::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname");
-               system("cat scripts/misc/lang-datas/fr/stopwords.sql | $::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname");
+       if ($response eq '2') {
+               system("cat scripts/misc/marc_datas/unimarc_fr/structure_def.sql | $mysqldir/bin/mysql -u$user $dbname");
+               system("cat scripts/misc/lang-datas/fr/stopwords.sql | $mysqldir/bin/mysql -u$user $dbname");
        }
 
-       $result = system ("perl -I $::intranetdir/modules scripts/marc/updatedb2marc.pl");
+       $result = system ("perl -I $intranetdir/modules scripts/marc/updatedb2marc.pl");
        if ($result) {
                print "Problem updating database to MARC...\n";
+               restoremycnf();
                exit;
        }
+       delete($ENV{"KOHA_CONF"});
 
-       print "\n\nFinished updating of database. Press <ENTER> to continue...";
+       print RESET."\n\nFinished updating of database. Press <ENTER> to continue...";
        <STDIN>;
 }
 
+
+=item populatedatabase
+
+    populatedatabase;
+
+Populate the non-MARC tables. If the user wants to install the
+sample data, install them.
+
+=cut
+
 sub populatedatabase {
-       my $response=showmessage(getmessage('SampleData'), 'yn', 'n');
-       if ($response =~/^y/i) {
-               system("gunzip -d < sampledata-1.2.gz | $::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branches (branchcode,branchname,issuing) values ('MAIN', 'Main Library', 1)\"");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'IS')\"");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'CU')\"");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into printers (printername,printqueue,printtype) values ('Circulation Desk Printer', 'lp', 'hp')\"");
-               showmessage(getmessage('SampleDataInstalled'), 'PressEnter','',1);
-       } else {
+#      my $response=showmessage(getmessage('SampleData'), 'yn', 'n');
+#      if ($response =~/^y/i) {
+#
+# FIXME: These calls are now unsafe and should either be removed
+# or updated to use -u$user and no mysqlpass_quoted
+#
+#              system("gunzip -d < sampledata-1.2.gz | $mysqldir/bin/mysql -u$mysqluser $mysqlpass_quoted $dbname");
+#              system("$mysqldir/bin/mysql -u$mysqluser $mysqlpass_quoted $dbname -e \"insert into branches (branchcode,branchname,issuing) values ('MAIN', 'Main Library', 1)\"");
+#              system("$mysqldir/bin/mysql -u$mysqluser $mysqlpass_quoted $dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'IS')\"");
+#              system("$mysqldir/bin/mysql -u$mysqluser $mysqlpass_quoted $dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'CU')\"");
+#              system("$mysqldir/bin/mysql -u$mysqluser $mysqlpass_quoted $dbname -e \"insert into printers (printername,printqueue,printtype) values ('Circulation Desk Printer', 'lp', 'hp')\"");
+#              showmessage(getmessage('SampleDataInstalled'), 'PressEnter','',1);
+#      } else {
                my $input;
                my $response=showmessage(getmessage('AddBranchPrinter'), 'yn', 'y');
 
@@ -1375,9 +1656,10 @@ sub populatedatabase {
                $branchcode=substr($branchcode,0,4);
                $branchcode or $branchcode='DEF';
 
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branches (branchcode,branchname,issuing) values ('$branchcode', '$branch', 1)\"");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'IS')\"");
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'CU')\"");
+               startsysout();
+               system("$mysqldir/bin/mysql -u$user $dbname -e \"insert into branches (branchcode,branchname,issuing) values ('$branchcode', '$branch', 1)\"");
+               system("$mysqldir/bin/mysql -u$user $dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'IS')\"");
+               system("$mysqldir/bin/mysql -u$user $dbname -e \"insert into branchrelations (branchcode,categorycode) values ('MAIN', 'CU')\"");
 
                my $printername='Library Printer';
                $printername=showmessage(getmessage('PrinterName', [$printername]), 'free', $printername, 1);
@@ -1386,17 +1668,34 @@ sub populatedatabase {
                my $printerqueue='lp';
                $printerqueue=showmessage(getmessage('PrinterQueue', [$printerqueue]), 'free', $printerqueue, 1);
                $printerqueue=~s/[^A-Za-z0-9]//g;
-               system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"insert into printers (printername,printqueue,printtype) values ('$printername', '$printerqueue', '')\"");
-               }
+               startsysout();  
+               system("$mysqldir/bin/mysql -u$user $dbname -e \"insert into printers (printername,printqueue,printtype) values ('$printername', '$printerqueue', '')\"");
+#              }
        my $language=showmessage(getmessage('Language'), 'free', 'en');
-       system("$::mysqldir/bin/mysql -u$::mysqluser $::mysqlpass_quoted $::dbname -e \"update systempreferences set value='$language' where variable='opaclanguages'\"");
+       startsysout();  
+       system("$mysqldir/bin/mysql -u$user $dbname -e \"update systempreferences set value='$language' where variable='opaclanguages'\"");
        }
 }
 
+
+=item restartapache
+
+    restartapache;
+
+Asks the user whether to restart Apache, and restart it if the user
+wants so.
+
+FIXME: If the installer does not know how to restart the Apache
+server (e.g., if the user is not actually using Apache), it still
+asks the question.
+
+=cut
+
 $messages->{'RestartApache'}->{en} = heading('RESTART APACHE') . qq|
 Apache needs to be restarted to load the new configuration for Koha.
+This requires the root password.
 
-Would you like to restart Apache now?  [Y]/N: |;
+Would you like to try to restart Apache now?  [Y]/N: |;
 
 sub restartapache {
 
@@ -1405,23 +1704,63 @@ sub restartapache {
 
 
     unless ($response=~/^n/i) {
+       startsysout();
        # Need to support other init structures here?
        if (-e "/etc/rc.d/init.d/httpd") {
-           system('/etc/rc.d/init.d/httpd restart');
+           system('su root -c /etc/rc.d/init.d/httpd restart');
        } elsif (-e "/etc/init.d/apache") {
-           system('/etc//init.d/apache restart');
+           system('su root -c /etc/init.d/apache restart');
        } elsif (-e "/etc/init.d/apache-ssl") {
-           system('/etc/init.d/apache-ssl restart');
+           system('su root -c /etc/init.d/apache-ssl restart');
        }
     }
 
 }
 
 
+=item finalizeconfigfile
+
+   finalizeconfigfile;
+
+This function must be called when the installation is complete,
+to rename the koha.conf.tmp file to koha.conf.
+
+Currently, failure to rename the file results only in a warning.
+
+=cut
+
+sub finalizeconfigfile {
+       restoremycnf();
+   rename "$etcdir/koha.conf.tmp", "$etcdir/koha.conf"
+      || showmessage(<<EOF, 'PressEnter', undef, 1);
+An unexpected error, $!, occurred
+while the Koha config file is being saved to its final location,
+$etcdir/koha.conf.
+
+Couldn't rename file at $etcdir. Must have write capability.
+
+Press Enter to continue.
+EOF
+#'
+}
+
+
+=item loadconfigfile
+
+   loadconfigfile
+
+Open the existing koha.conf file and get its values,
+saving the values to some global variables.
+
+If the existing koha.conf file cannot be opened for any reason,
+the file is silently ignored.
+
+=cut
+
 sub loadconfigfile {
     my %configfile;
 
-    open (KC, "<$::etcdir/koha.conf");
+    open (KC, "<$etcdir/koha.conf");
     while (<KC>) {
      chomp;
      (next) if (/^\s*#/);
@@ -1437,25 +1776,58 @@ sub loadconfigfile {
      }
     }
 
-    $::intranetdir=$configfile{'intranetdir'};
-    $::opacdir=$configfile{'opacdir'};
-    $::kohaversion=$configfile{'kohaversion'};
-    $::kohalogdir=$configfile{'kohalogdir'};
-    $::database=$configfile{'database'};
-    $::hostname=$configfile{'hostname'};
-    $::user=$configfile{'user'};
-    $::pass=$configfile{'pass'};
+    $intranetdir=$configfile{'intranetdir'};
+    $opacdir=$configfile{'opacdir'};
+    $kohaversion=$configfile{'kohaversion'};
+    $kohalogdir=$configfile{'kohalogdir'};
+    $database=$configfile{'database'};
+    $hostname=$configfile{'hostname'};
+    $user=$configfile{'user'};
+    $pass=$configfile{'pass'};
 }
 
 END { }       # module clean-up code here (global destructor)
 
-=back
+### These things may move
 
-=head1 BUGS
+sub setecho {
+my $state=shift;
+my $t = POSIX::Termios->new;
 
-A lot of variables like $::opacdir. Because Perl does not check
-for strictness when a package name is explicitly specified,
-using such variables defeats the purpose of "use strict".
+$t->getattr();
+if ($state) {
+  $t->setlflag(($t->getlflag) | &POSIX::ECHO);
+  }
+else {
+  $t->setlflag(($t->getlflag) & !(&POSIX::ECHO));
+  }
+$t->setattr();
+}
+
+sub setmysqlclipass {
+       my $pass = shift;
+       open(MYCNF,">$mycnf");
+       chmod(0600,$mycnf);
+       print MYCNF "[client]\npassword=$pass\n";
+       close(MYCNF);
+}
+
+sub backupmycnf {
+       if (-e $mycnf) {
+               rename $mycnf,$mytmpcnf;
+       }
+}
+
+sub restoremycnf {
+       if (-e $mycnf) {
+               unlink($mycnf);
+       }
+       if (-e $mytmpcnf) {
+               rename $mytmpcnf,$mycnf;
+       }
+}
+
+=back
 
 =head1 SEE ALSO