<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2003-04-23
 * Modified    : 2009-09-07
 * For LOVD    : 2.0-21
 *
 * Access      : Public
 * Purpose     : Install all LOVD tables into the database, insert standard
 *               settings.
 *
 * Copyright   : 2004-2009 Leiden University Medical Center; http://www.LUMC.nl/
 * Programmer  : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 * Last edited : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *
 *
 * This file is part of LOVD.
 *
 * LOVD is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * LOVD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LOVD; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *************/

define('ROOT_PATH', '../');
require ROOT_PATH . 'inc-lib-form.php';
require ROOT_PATH . 'inc-init.php';

// Array containing install steps.
$aInstallSteps =
         array(
                array(0, 'Introduction'),
                array(5, 'Administrator account details'),
                array(10, 'Creating tables'),
                array(90, 'Configuring LOVD system settings'),
                array(99, 'Configuring LOVD modules'),
                array(100, 'Done'),
              );

if (!isset($_GET['step']) || !is_numeric($_GET['step']) || $_GET['step'] < 0 || $_GET['step'] >= count($aInstallSteps)) {
    $_GET['step'] = 0;
}



// FIXME; integrate into the install dir's own top include?
function lovd_showInstallBar () {
    // Shows sidebar with installation steps and the installation progress bar.
    global $aInstallSteps;
    
    // Top include has to be included already.
    if (!defined('_INC_TOP_INCLUDED_')) {
        // This is not right; bail out.
        print('      Install Bar error: No top include found');
        return false;
    }

    // Sidebar with the steps laid out.
    print('<TABLE border="0" cellpadding="0" cellspacing="0" width="100%" style="padding : 0px 10px;">' . "\n" .
          '  <TR valign="top">' . "\n" .
          '    <TD width="200">' . "\n" .
          '      <TABLE border="0" cellpadding="5" cellspacing="0" align="left" width="100%" class="S11">' . "\n" .
          '        <TR align="center" class="S13">' . "\n" .
          '          <TH style="background : #224488; color : #FFFFFF; height : 20px; border : 1px solid #002266;"><IMG src="' . ROOT_PATH . 'gfx/trans.png" alt="" width="188" height="1"><BR><I>Installation steps</I></TH></TR>');

    foreach ($aInstallSteps as $nStep => $aStep) {
        // Loop through install steps.
        print("\n" .
              '        <TR align="center">' . "\n" .
              '          <TD style="height : 60px; border : 1px solid #002266; border-top : 0px; background : #' . ($nStep == $_GET['step']? 'CCE0FF; font-weight : bold' : ($nStep < $_GET['step']? 'F0F0F0; color : #666666' : 'FFFFFF')) . ';">' . $aStep[1] . '</TD></TR>');
    }

    // Close table.
    print('</TABLE></TD>' . "\n" .
          '    <TD style="padding-left : 10px;">' . "\n\n");



    // Top progress bar.
    print('
      <TABLE border="0" cellpadding="0" cellspacing="0" class="S11" width="100%">
        <TR>
          <TD colspan="2">Installation progress</TD></TR>
        <TR>
          <TD colspan="2" style="border : 1px solid black; height : 5px;">
            <IMG src="' . ROOT_PATH . 'gfx/trans.png" alt="" title="' . $aInstallSteps[$_GET['step']][0] . '%" width="' . $aInstallSteps[$_GET['step']][0] . '%" height="5" id="lovd_install_bar" style="background : #99EE66;"></TD></TR>
        <TR>
          <TD align="left">0%</TD>
          <TD align="right">100%</TD></TR></TABLE><BR>' . "\n\n");
}



function lovd_printInstallForm () {
    // Prints FORM tag providing the 'Next' button.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] + 1) . '" method="post">' . "\n");
    foreach ($_POST as $key => $val) {
        // 2009-04-23; 2.0-18; Added stripslashes and htmlspecialchars to prevent XSS and allow values to include quotes.
        print('        <INPUT type="hidden" name="' . $key . '" value="' . htmlspecialchars(stripslashes($val)) . '">' . "\n");
    }
    print('        <INPUT type="submit" value="' . ($_GET['step']? 'Next' : 'Start') . ' &gt;&gt;" style="font-weight : bold; font-size : 11px;">
      </FORM>' . "\n\n");
}





if ($_GET['step'] == 0 && defined('_NOT_INSTALLED_')) {
    // Show some intro.
    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    print('      <B>Welcome to the LOVD v.' . $_STAT['tree'] . '-' . $_STAT['build'] . ' installer</B><BR>
      <BR>' . "\n\n");

    // Requirements. This is where we would want to check software versions, PHP modules, etc.
    // Maybe we should add an additional step.

    // Check for PHP, MySQL versions.
    $sPHPVers = str_replace('_', '-', PHP_VERSION) . '-';
    $sPHPVers = substr($sPHPVers, 0, strpos($sPHPVers, '-'));
    $bPHP = ($sPHPVers >= $aRequired['PHP']);
    $sPHP = '<IMG src="../gfx/mark_' . (int) $bPHP . '.png" alt="" width="11" height="11">&nbsp;PHP : ' . $sPHPVers . ' (' . $aRequired['PHP'] . ' required)';
    $sMySQLVers = str_replace('_', '-', mysql_get_server_info()) . '-';
    $sMySQLVers = substr($sMySQLVers, 0, strpos($sMySQLVers, '-'));
    $bMySQL = ($sMySQLVers >= $aRequired['MySQL']);
    $sMySQL = '<IMG src="../gfx/mark_' . (int) $bMySQL . '.png" alt="" width="11" height="11">&nbsp;MySQL : ' . $sMySQLVers . ' (' . $aRequired['MySQL'] . ' required)';
    if (!$bPHP || !$bMySQL) {
        // Failure!
        lovd_showInfoTable('One or more requirements are not met!<BR>I will now bluntly refuse to install.<BR><BR>' .
                           $sPHP . '<BR>' .
                           $sMySQL, 'warning');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    } else {
        // Success!
        lovd_showInfoTable('System check for requirements all OK!<BR><BR>' .
                           $sPHP . '<BR>' .
                           $sMySQL, 'success');
    }

    // Check the config.ini protection.
    if (ini_get('allow_url_fopen')) {
        $sURL = PROTOCOL . $_SERVER['HTTP_HOST'] . lovd_cleanDirName(dirname($_SERVER['PHP_SELF']) . '/' . CONFIG_URI);
        $sResult = @file_get_contents($sURL);
        $sConfig = file_get_contents(CONFIG_URI);
        if ($sResult == $sConfig) {
            // Quite simple, really. Assuming this works.
// FIXME; incorporate link to manual.
            lovd_showInfoTable('Your configuration file is <B>NOT</B> protected!<BR>I will now bluntly refuse to install.<BR><BR>If your configuration file is not protected, attackers can read out your MySQL username and password. This may leave all data stored in LOVD public. Please read the manual now to find out how to protect the LOVD configuration file.', 'warning');
            require ROOT_PATH . 'inc-bot.php';
            exit;
        }
    }

    print('      The installation of LOVD consists of ' . (count($aInstallSteps) - 1) . ' simple steps.
      This installer will create the LOVD tables in the MySQL database, create the Administrator account and will help you configuring LOVD. Installation and initial configuration of LOVD should be simple for a relatively experienced computer user.<BR>
      <BR>
      The installation progress bar at the top of the screen shows how far you are in the installation process. The installation steps are shown at the left of the screen.<BR>
      <BR>' . "\n\n");

    lovd_printInstallForm();

    require ROOT_PATH . 'inc-bot.php';
    exit;
} elseif ($_GET['step'] == 0) { $_GET['step'] ++; }





if ($_GET['step'] == 1 && defined('_NOT_INSTALLED_')) {
    // Step 1: Administrator account details.
    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    print('      <B>Administrator account details</B><BR>' . "\n" .
          '      <BR>' . "\n\n");

    if (isset($_GET['sent'])) {
        lovd_errorClean();

        // Mandatory fields.
        $aCheck =
                 array(
                        'name' => 'Name',
                        'institute' => 'Institute',
                        'email' => 'Email address',
                        'username' => 'Username',
                        'password_1' => 'Password',
                        'password_2' => 'Password (repeated)',
                      );

        foreach ($aCheck as $key => $val) {
            if (empty($_POST[$key])) {
                lovd_errorAdd('Please fill in the \'' . $val . '\' field.');
            }
        }

        // Email address.
        if ($_POST['email'] && !lovd_matchEmail($_POST['email'])) {
            lovd_errorAdd('Please fill in a correct email address.');
        }

        // Username.
        if ($_POST['username'] && !lovd_matchUsername($_POST['username'])) {
            lovd_errorAdd('Please fill in a correct username; 4 to 20 characters and starting with a letter followed by letters, numbers, dots, underscores and dashes only.');
        }

        // One of two password fields entered... check 'em.
        if ($_POST['password_1'] || $_POST['password_2']) {
            if ($_POST['password_1'] && $_POST['password_2']) {
                // Both entered.
                if ($_POST['password_1'] != $_POST['password_2']) {
                    lovd_errorAdd('The \'Password\' fields are not equal. Please try again.');
                } else {
                    // Password quality.
                    if (!lovd_matchPassword($_POST['password_1'])) {
                        lovd_errorAdd('Your password is found too weak. Please fill in a proper password; at least 4 characters long and containing at least one number or special character.');
                    }
                }
            } else {
                lovd_errorAdd('Please fill in both \'Password\' fields.');
            }
        }

        // Check given security IP range.
        if (trim($_POST['allowed_ip'])) {
            // This function will throw an error itself (second argument).
            $bIP = lovd_matchIPRange($_POST['allowed_ip'], true);

            // Check given security IP range.
            if ($bIP && !lovd_validateIP($_POST['allowed_ip'], $_SERVER['REMOTE_ADDR'])) {
                // This IP range is not allowing the current IP to connect. This ain't right.
                lovd_errorAdd('Your current IP address is not matched by the given IP range. This would mean you would not be able to finish this installation.');
            }

        } else {
            $_POST['allowed_ip'] = '*';
        }

        // 2006-12-07; 2.0-alpha-02
        // XSS attack prevention. Simply deny input of HTML, PHP other stuff blocked by strip_tags().
        lovd_checkXSS();

        if (!lovd_error()) {
            // Gather information and go to next page.

            // Prepare password...
            $_POST['password'] = md5($_POST['password_1']);
            unset($_POST['password_1'], $_POST['password_2']);

            // Good habit when mailing and printing.
            lovd_magicUnquoteAll();

            print('      Account details OK. Ready to proceed to the next step.<BR>' . "\n" .
                  '      <BR>' . "\n\n");

            lovd_printInstallForm();

            require ROOT_PATH . 'inc-bot.php';
            exit;

        } else {
            // Errors, thus we must return to the form.
            lovd_magicUnquoteAll();

            // Because everything will be sent back to the form, I have to remove the password fields!
            unset($_POST['password_1'], $_POST['password_2']);
        }

    } else {
        // Default values.
        $_POST['list_size'] = 100;
        $_POST['allowed_ip'] = '*';
    }



    if (!isset($_GET['sent'])) {
        print('      Please fill in the Administrator\'s account details and press \'Continue\' to continue the installation.<BR>' . "\n" .
              '      <BR>' . "\n\n");
    }

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?step=' . $_GET['step'] . '&amp;sent=true" method="post">' . "\n" .
          '        <TABLE border="0" cellpadding="0" cellspacing="1" width="760">');

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '50%', '50%'),
                    array('', 'print', '<B>User details</B>'),
                    array('Name', 'text', 'name', 30),
                    array('Institute', 'text', 'institute', 30),
                    array('Email address', 'text', 'email', 30),
                    array('Username', 'text', 'username', 20),
                    array('Password', 'password', 'password_1', 20),
                    array('Password (confirm)', 'password', 'password_2', 20),
                    'skip',
                    array('', 'print', '<B>General user settings</B>'),
                    array('Number of entries per page', 'select', 'list_size', 1, array_combine($_SETT['variant_listsizes'], $_SETT['variant_listsizes']), false, false, false),
                    array('', 'print', '<SPAN class="form_note">Variant listings will be split in pages. How many variants do you want to show per page by default?</SPAN>'),
                    'skip',
                    array('', 'print', '<B>Security</B>'),
                    array('Allowed IP address list', 'text', 'allowed_ip', 20),
                    array('', 'print', '<SPAN class="form_note"><I>Your current IP address: ' . $_SERVER['REMOTE_ADDR'] . '</I><BR>To help prevent others to try and guess the username/password combination, you can restrict access to the account to a number of IP addresses or ranges.<BR><B>Please be extremely careful using this setting.</B> Using this setting too strictly, can deny the user access to LOVD, even if the correct credentials have been provided.<BR>Set to \'*\' to allow all IP addresses, use \'-\' to specify a range and use \';\' to separate addresses or ranges.</SPAN>'),
                    'skip',
                    array('', 'submit', 'Continue'),
                  );
    lovd_viewForm($aForm);

    print('</TABLE></FORM>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;
} elseif ($_GET['step'] == 1) { $_GET['step'] ++; }





if ($_GET['step'] == 2 && defined('_NOT_INSTALLED_')) {
    // Step 2: Install database tables.
    if (!isset($_POST['username'])) {
        // Didn't finish previous step correctly.
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] - 1));
        exit;
    }

    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    function lovd_rollback () {
        global $_SETT, $_STAT, $aTableCreated;

        if (count($aTableCreated)) {
            print('Reversing installation [');
            flush();

            foreach ($aTableCreated as $key => $sTable) {
                $sTable = constant($sTable);
                if (@mysql_query('DROP TABLE ' . $sTable)) {
                    print('<GARBAGE>.</GARBAGE>');
                    if ($key + 1 == count($aTableCreated)) {
                        print('] OK' . "\n");
                    } else {
                        flush();
                        usleep(50000);
                    }

                } else {
                    print ('] Failed!' . "\n");
                    break;
                }
            }
            print("\n" .
                  'A failed installation is most likely caused by a bug in LOVD.' . "\n" .
                  'Please <A href="' . $_SETT['upstream_URL'] . 'bugs/" target="_blank">file a bug</A> and include the above messages to help us solve the problem.' . "\n\n");
            print('      </PRE>' . "\n");
            require ROOT_PATH . 'inc-bot.php';
            exit;
        }
    }

    print('      <B>Creating tables</B><BR>' . "\n" .
          '      <BR>' . "\n\n");

    print('      Please wait while the installer is trying to create all necessary LOVD tables...<BR>' . "\n" .
          '      <BR>' . "\n\n" .
          '      <PRE>' . "\n");
    print('<SCRIPT type="text/javascript">var bar = document.getElementById(\'lovd_install_bar\');</SCRIPT>');

    // List of tables that need to be generated.
    require 'inc-sql-tables.php';
    $nTables = count($_TABLES);

    print('Need to create ' . $nTables . ' tables...' . "\n");

    flush();
    usleep(100000);

    // Does any of these tables exist yet?
    print('Checking for existing tables...');
    $aMatched = array();
    $aFound = array();
    $q = mysql_query('SHOW TABLES LIKE "' . TABLEPREFIX . '\_%"');
    while ($r = mysql_fetch_row($q)) {
        $aMatched[] = $r[0];
        if (in_array($r[0], $_TABLES)) {
            $aFound[] = $r[0];
        }
    }
    $nMatched = count($aMatched);
    $nFound = count($aFound);

    if ($nFound) {
        if ($nFound == $nTables) {
            // Maybe an existing LOVD install...
            list($sVersion) = @mysql_fetch_row(mysql_query('SELECT version FROM ' . TABLE_STATUS));
            if ($sVersion) {
                print(' seem to have found existing LOVD install (' . $sVersion . ').' . "\n" .
                      '    <B>Installation can not continue using the current database or table prefix.</B>' . "\n" .
                      '    Please change the database settings in the config.ini or remove the existing LOVD install, and re-run the installation.' . "\n\n");
                print('      </PRE>' . "\n");
                require ROOT_PATH . 'inc-bot.php';
                exit;
            }
        }

        print(' found ' . $nFound . ' existing table' . ($nFound == 1? '' : 's') . '!' . "\n" .
              '    <B>Installation can not continue if tables exist with the same name I wish to create.</B>' . "\n" .
              '    I found:' . "\n" .
              '    - ' . implode("\n" . '    - ', $aFound) . "\n" .
              '    Please remove th' . ($nFound == 1? 'is table' : 'ese tables') . ' and re-run the installation.' . "\n\n");
        print('      </PRE>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    if ($nMatched) {
        print(' found ' . $nMatched . ' possibly interfering table' . ($nMatched == 1? '' : 's') . '.' . "\n" .
              '    <B>Please note that tables are found that may interfere with LOVD at a later stage.</B>' . "\n" .
              '    I found:' . "\n" .
              '    - ' . implode("\n" . '    - ', $aMatched) . "\n" .
              '    You may want to consider removing th' . ($nMatched == 1? 'is table' : 'ese tables') . '.' . "\n\n");
    } else {
        print(' OK' . "\n");
    }

    flush();
    usleep(100000);

    // Actually create the tables...
    print('Creating LOVD tables [');

    // Meant for rollback();
    $aTableCreated = array();

    $nSubStep = 1;
    $nIncrement = floor(($aInstallSteps[$_GET['step'] + 1][0] - $aInstallSteps[$_GET['step']][0])/($nTables + 10));
    foreach ($aTableSQL as $sTable => $sSQL) {
        $q = mysql_query($sSQL);
        if (!$q) {
            // Error when creating table.
            print('] Failed!' . "\n" .
                  '    Error while creating table ' . $sTable . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        $aTableCreated[] = $sTable;
        print('.<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');
        if ($nSubStep == $nTables) {
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $nSubStep ++;
        }
    }


    flush();
    usleep(100000);


    // (1) Initializing the log files.
    print('Initializing system logs...');
    $bLogs = (lovd_writeLog('MySQL:Event', 'LogInit', 'Log touched') && lovd_writeLog('MySQL:Auth', 'LogInit', 'Log touched') && lovd_writeLog('MySQL:Error', 'LogInit', 'Log touched') && lovd_writeLog('MySQL:Install', 'LogInit', 'Log touched'));
    if (!$bLogs) {
        // Error when creating table.
        print(' Failed!' . "\n" .
              '    Error while initializing system logs.' . "\n" .
              '    I last got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
        lovd_rollback();
    } else {
        print(' OK' . "\n");
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (2) Creating LOVD status.
    print('Registering LOVD system status...');
    $q = mysql_query('INSERT INTO ' . TABLE_STATUS . ' VALUES (0, "' . $_SETT['system']['version'] . '", "' . md5($_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . time()) . '", NULL, NULL, NULL, NULL, NULL, NOW(), NULL, 0)');
    if (!$q) {
        // Error when inserting.
        print(' Failed!' . "\n" .
              '    Error while registering LOVD system status.' . "\n" .
              '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
        lovd_rollback();
    } else {
        lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Registered LOVD system status');
        print(' OK' . "\n");
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (3) Registering LOVD variant pathogenicities.
    print('Registering LOVD variant pathogenicities [');
    $nStatuses = count($_SETT['var_pathogenic_short']);
    $n = 1;
    foreach ($_SETT['var_pathogenic_short'] as $nStatus => $sStatus) {
        $q = mysql_query('INSERT INTO ' . TABLE_PATHOGENIC . ' VALUES (' . $nStatus . ', "' . $sStatus . '")');
        if (!$q) {
            // Error when inserting variant pathogenicity.
            print('] Failed!' . "\n" .
                  '    Error while registering LOVD variant pathogenicity ' . $sStatus . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nStatuses) {
            lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Registered LOVD variant pathogenicities');
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (4) Registering LOVD variant statuses.
    print('Registering LOVD variant statuses [');
    $nStatuses = count($_SETT['var_status']);
    $n = 1;
    foreach ($_SETT['var_status'] as $nStatus => $sStatus) {
        $q = mysql_query('INSERT INTO ' . TABLE_VAR_STATUS . ' VALUES (' . $nStatus . ', "' . $sStatus . '")');
        if (!$q) {
            // Error when inserting variant status.
            print('] Failed!' . "\n" .
                  '    Error while registering LOVD variant status ' . $sStatus . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nStatuses) {
            lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Registered LOVD variant statuses');
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (5) Creating standard custom links.
    // List of links that need to be generated.
    require 'inc-sql-links.php';
    $nLinks = count($aLinkSQL);

    // Create the links...
    print('Creating LOVD custom links [');

    $n = 1;
    foreach ($aLinkSQL as $sLink => $sSQL) {
        $q = mysql_query($sSQL);
        if (!$q) {
            // Error when inserting custom link.
            print('] Failed!' . "\n" .
                  '    Error while creating custom link ' . $sLink . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nLinks) {
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (6) Creating standard external sources.
    // List of sources that need to be generated.
    require 'inc-sql-sources.php';
    $nSources = count($aSourceSQL);

    // Create the sources...
    print('Creating external sources [');

    $n = 1;
    foreach ($aSourceSQL as $sSource => $sSQL) {
        $q = mysql_query($sSQL);
        if (!$q) {
            // Error when inserting external source.
            print('] Failed!' . "\n" .
                  '    Error while creating external source ' . $sSource . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nSources) {
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (7) Creating standard LOVD custom columns.
    // List of columns that need to be generated.
    require 'inc-sql-columns.php';
    $nCols = count($aColSQL);

    // Create the columns...
    print('Creating LOVD custom columns [');

    $n = 1;
    foreach ($aColSQL as $sCol => $sSQL) {
        $q = mysql_query($sSQL);
        if (!$q) {
            // Error when inserting custom column.
            print('] Failed!' . "\n" .
                  '    Error while creating custom column ' . $sCol . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nCols) {
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (8) Adding standard patient columns.
    // Gather info on standard custom patient columns.
    $aColsToCopy = array('colid', 'col_order', 'width', 'mandatory', 'description_form', 'description_legend_short', 'description_legend_full', 'select_options', 'public', 'public_form', 'created_by', 'created_date');
    $qCols = mysql_query('SELECT * FROM ' . TABLE_COLS . ' WHERE (hgvs = 1 OR standard = 1) AND colid LIKE "Patient/%"');
    $nCols = mysql_num_rows($qCols);

    // Create the columns...
    print('Registering LOVD standard patient columns [');
    require ROOT_PATH . 'class/currdb.php';
    $_CURRDB = new CurrDB(false);

    $n = 1;
    $aColsOrder = array_keys($aColSQL);
    
    while ($z = mysql_fetch_assoc($qCols)) {
        // $z comes from the database, and is therefore not quoted.
        lovd_magicQuote($z);

        $z['col_order'] = array_search($z['colid'], $aColsOrder);

        // Calculate the standard width of the column based on the maximum number of characters.
        $nHeadLength = strlen($z['head_column']);
        $nColLength = $_CURRDB->getFieldLength($z['colid']) / 2;
        $nColLength = ($nColLength < $nHeadLength? $nHeadLength : $nColLength);
        // Compensate for small/large fields.
        $nColLength = ($nColLength < 5? 5 : ($nColLength > 35? 35 : $nColLength));
        if ($nColLength < 10) {
            $z['width'] = 10*$nColLength;
        } else {
            $z['width'] = 8*$nColLength;
        }
        $z['width'] = ($z['width'] > 200? 200 : $z['width']);

        // Created_* columns...
        $z['created_by'] = 0; // 'LOVD'
        $z['created_date'] = date('Y-m-d H:i:s');

        $sQ = 'INSERT INTO ' . TABLE_PATIENTS_COLS . ' (';
        $aCol = array();
        foreach ($aColsToCopy as $sCol) {
            if (isset($z[$sCol])) {
                $sQ .= (substr($sQ, -1) == '('? '' : ', ') . $sCol;
                $aCol[] = $z[$sCol];
            }
        }
        $sQ .= ') VALUES (';

        foreach ($aCol as $key => $val) {
            $sQ .= ($key? ', ' : '') . '"' . $val . '"';
        }
        $sQ .= ')';

        // Insert default LOVD custom column.
        $q = @mysql_query($sQ);
        if (!$q) {
            // Error when inserting custom link.
            print('] Failed!' . "\n" .
                  '    Error while creating custom column ' . $z['colid'] . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        // Alter patient table to include column.
        // 2009-02-16; 2.0-16; Added stripslashes to allow receiving quotes. This variable has been checked using regexps, so can be considered safe.
        $sQ = 'ALTER TABLE ' . TABLE_PATIENTS . ' ADD COLUMN `' . $z['colid'] . '` ' . stripslashes($z['mysql_type']) . ' NOT NULL AFTER patientid';
        $q = @mysql_query($sQ);
        if (!$q) {
            // Error when altering table.
            print('] Failed!' . "\n" .
                  '    Error while registering custom column ' . $z['colid'] . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
            lovd_rollback();
        }

        print('<GARBAGE>.</GARBAGE>');
        if ($n == $nCols) {
            print('] OK' . "\n");
        } else {
            flush();
            usleep(50000);
            $n ++;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (9) Creating submitter country list.
    // List of countries that need to be generated.
    require 'inc-sql-countries.php';
    $nCountries = count($aCountrySQL);

    // Create the links...
    print('Creating submitter country list ...<INPUT type="text" id="lovd_install_progress_country" size="3" value="0" style="border : 0px; margin : 0px; padding : 0px; text-align : right;">%');
    print('<SCRIPT type="text/javascript">var progressCountry = document.getElementById(\'lovd_install_progress_country\');</SCRIPT>');

    $nProgress = 0;
    $nProgressPrev = 0;
    foreach ($aCountrySQL as $n => $sSQL) {
        $n ++;
        $q = mysql_query($sSQL);
        if (!$q) {
            // Error when inserting country.
            print(' Failed!' . "\n" .
                  '    Error while creating country #' . $n . '.' . "\n" .
                  '    I got: ' . str_replace("\n", "\n" . '       ', lovd_wrapText(mysql_error(), 80)) . "\n");
            lovd_rollback();
        }

        $nProgress = round(($n*100)/$nCountries);
        if ($nProgress != $nProgressPrev) {
            print('<SCRIPT type="text/javascript">progressCountry.value = \'' . $nProgress . '\';</SCRIPT>');
        }

        if ($n == $nCountries) {
            print(' OK' . "\n");
        } else {
            flush();
            usleep(2000);
            $nProgressPrev = $nProgress;
        }
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');


    flush();
    usleep(100000);


    // (10) Creating administrator.
    print('Creating LOVD database administrator account...');
    $q = mysql_query('INSERT INTO ' . TABLE_USERS . ' VALUES (NULL, "' . $_POST['name'] . '", "' . $_POST['institute'] . '", "' . $_POST['email'] . '", "' . $_POST['username'] . '", "' . $_POST['password'] . '", "", 0, "' . session_id() . '", "", "' . $_POST['list_size'] . '", ' . LEVEL_ADMIN . ', "' . $_POST['allowed_ip'] . '", 0, NOW(), 0, 0, NOW(), NULL, NULL)');
    if (!$q) {
        // Error when inserting.
        print(' Failed!' . "\n" .
              '    Error while creating database administrator account.' . "\n" .
              '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
        lovd_rollback();

    } else {
        lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Created LOVD database administrator account');
        print(' OK' . "\n");
    }
    $nSubStep ++;
    print('<SCRIPT type="text/javascript">bar.style.width = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; bar.title = \'' . ($aInstallSteps[$_GET['step']][0] + ($nSubStep * $nIncrement)) . '%\'; </SCRIPT>');

    print('      </PRE>' . "\n\n" .
          '      Creating tables OK. Ready to proceed to the next step.<BR>' . "\n" .
          '      <BR>' . "\n\n");

    // Log user in.
    $_SESSION['auth'] = mysql_fetch_assoc(mysql_query('SELECT * FROM ' . TABLE_USERS . ' WHERE username = "' . $_POST['username'] . '" AND password = "' . $_POST['password'] . '"'));

    $_POST = array();
    lovd_printInstallForm();

    require ROOT_PATH . 'inc-bot.php';
    exit;
} elseif ($_GET['step'] == 2) { $_GET['step'] ++; }





if ($_GET['step'] == 3 && !@mysql_num_rows(mysql_query('SELECT * FROM ' . TABLE_CONFIG))) {
    // Step 3: Configuring general LOVD system settings.
    if (@mysql_num_rows(mysql_query('SHOW TABLES LIKE "' . TABLE_CONFIG . '"')) != 1) {
        // Didn't finish previous step correctly.
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] - 1));
        exit;
    }

    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    print('      <B>Configuring LOVD system settings</B><BR>' . "\n" .
          '      <BR>' . "\n\n");

    if (isset($_GET['sent'])) {
        lovd_errorClean();

        // Mandatory fields.
        $aCheck =
                 array(
                        'email_address' => 'LOVD email address',
                      );

        foreach ($aCheck as $key => $val) {
            if (empty($_POST[$key])) {
                lovd_errorAdd('Please fill in the \'' . $val . '\' field.');
            }
        }

        // 2009-03-09; 2.0-17; Database URL should be an URL.
        if ($_POST['location_url'] && !lovd_matchURL($_POST['location_url'])) {
            lovd_errorAdd('Please fill in a correct URL in the \'Database URL\' field.');
        }

        // 2009-03-09; 2.0-17; Database URL is mandatory, if the option "Include in the global LOVD listing" is selected.
        if ($_POST['send_listings'] && empty($_POST['location_url'])) {
            lovd_errorAdd('Please fill in an URL in the \'Database URL\' field, if you want this LOVD installation to be included in the global LOVD listing.');
        }

        // Email address.
        if ($_POST['email_address'] && !lovd_matchEmail($_POST['email_address'])) {
            lovd_errorAdd('Please fill in a correct email address.');
        }

        // 2006-12-07; 2.0-alpha-02
        // XSS attack prevention. Simply deny input of HTML, PHP other stuff blocked by strip_tags().
        lovd_checkXSS();

        // 2008-02-28; 2.0-04; SSL check.
        if ($_POST['use_ssl'] && !SSL) {
            lovd_errorAdd('You\'ve selected to use SSL, but SSL is not currently activated for this session. To force SSL, I must be sure it\'s possible to approach LOVD through an SSL connection (use <A href="https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . ($_SERVER['QUERY_STRING']? '?' . $_SERVER['QUERY_STRING'] : '') . '" target="_blank">https://</A> in stead of http://).');
        }

        if (!lovd_error()) {
            // Gather information and go to next page.
            // Default values.
            // System title.
            if (empty($_POST['system_title'])) {
                $_POST['system_title'] = 'LOVD - Leiden Open Variation Database';
            }
            // Location name.
            if (empty($_POST['location_name'])) {
                $_POST['location_name'] = $_SERVER['HTTP_HOST'];
            }

            print('      Please wait while the installer is trying to process the received data...<BR>' . "\n" .
                  '      <BR>' . "\n\n" .
                  '      <PRE>' . "\n");
            print('<SCRIPT type="text/javascript">var bar = document.getElementById(\'lovd_install_bar\');</SCRIPT>');

            // Configuring LOVD system settings...
            print('Registering LOVD system settings...');
            $q = mysql_query('INSERT INTO ' . TABLE_CONFIG . ' VALUES ("' . $_POST['system_title'] . '", "' . $_POST['location_name'] . '", "' . $_POST['location_url'] . '", "' . $_POST['email_address'] . '", "' . $_POST['send_fwd'] . '", "' . $_POST['send_signature'] . '", "' . $_POST['send_stats'] . '", "' . $_POST['send_listings'] . '", "' . $_POST['use_cookies'] . '", "' . $_POST['use_ssl'] . '", "' . $_POST['my_submissions'] . '", "' . $_POST['search_count_entries'] . '", "' . $_POST['hide_nonpublic'] . '", "' . $_POST['api_feed_history'] . '", "' . $_POST['lock_curators'] . '", "' . $_POST['lock_submitters'] . '", "' . $_POST['allow_unlock'] . '", "' . $_POST['lock_uninstall'] . '")');
            if (!$q) {
                // Error when inserting.
                print(' Failed!' . "\n" .
                      '    Error while registering LOVD system settings.' . "\n" .
                      '    I got: ' . str_replace("\n", "\n" . '       ', mysql_error()) . "\n");
                require ROOT_PATH . 'inc-bot.php';
                exit;
            } else {
                lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Registered LOVD system settings');
                $nWidth = floor(($aInstallSteps[$_GET['step'] + 1][0] - $aInstallSteps[$_GET['step']][0])/2) + $aInstallSteps[$_GET['step']][0];
                print('<SCRIPT type="text/javascript">bar.style.width = \'' . $nWidth . '%\'; bar.title = \'' . $nWidth . '%\'; </SCRIPT>');
                print(' OK' . "\n");
            }

            print('      </PRE>' . "\n\n" .
                  '      All OK. Ready to proceed to the next step.<BR>' . "\n" .
                  '      <BR>' . "\n\n");

            $_POST = array();
            lovd_printInstallForm();

            require ROOT_PATH . 'inc-bot.php';
            exit;

        } else {
            // Errors, thus we must return to the form.
            lovd_magicUnquoteAll();
        }

    } else {
        // Default values.
        $aCheckboxes =
                 array(
                        'send_fwd',
                        'my_submissions',
                        'send_signature',
                        'send_stats',
                        'send_listings',
                        'allow_download',
                        'use_cookies',
                        'use_ssl',
                        'hide_nonpublic',
                        'lock_curators',
                        'lock_submitters',
                        'allow_unlock',
                        'lock_uninstall',
                      );
        foreach ($aCheckboxes as $val) {
            $_POST[$val] = 1;
        }

        // SSL on only, when SSL is now active.
        if (!SSL) {
            $_POST['use_ssl'] = 0;
        }
        $_POST['api_feed_history'] = 3;
    }



    // 2009-03-09; 2.0-17; Include javascript for checking the database location BUT through HTTP so that the PHP code in there gets executed.
    lovd_includeJS(PROTOCOL . $_SERVER['HTTP_HOST'] . lovd_cleanDirName(dirname($_SERVER['PHP_SELF']) . '/' . ROOT_PATH) . 'inc-js-database_url.php');

    if (!isset($_GET['sent'])) {
        print('      Please review / complete the form below and press \'Continue\' to continue the installation.<BR>' . "\n" .
              '      <BR>' . "\n\n");
    }

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?step=' . $_GET['step'] . '&amp;sent=true" method="post">' . "\n" .
          '        <TABLE border="0" cellpadding="0" cellspacing="1" width="760">');

    $aFeedHistory = array('Not available');
    for ($i = 1; $i <= 12; $i ++) {
        $aFeedHistory[$i] = $i . ' month' . ($i == 1? '' : 's');
    }

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '40%', '60%'),
                    array('', 'print', '<B>General system settings</B>'),
                    'hr',
                    array('Title of this LOVD installation', 'text', 'system_title', 50),
                    array('', 'print', '<SPAN class="form_note">This will be shown on the top of every page. Default is &quot;LOVD - Leiden Open Variation Database&quot;.</SPAN>'),
                    'hr',
                    array('Database location (optional)', 'text', 'location_name', 50),
                    array('', 'print', '<SPAN class="form_note">The database location is displayed in the public area and in emails sent by LOVD. Is commonly set to a laboratory name or a website name. If not specified, LOVD will use &quot;' . $_SERVER['HTTP_HOST'] . '&quot;.</SPAN>'),
                    'hr',
                    array('Database URL (optional)', 'print', '<INPUT type="text" name="location_url" size="50" id="location_url" value="' . (empty($_POST['location_url'])? '' : htmlspecialchars($_POST['location_url'])) . '">&nbsp;<SPAN id="location_url_check">(<A href="#" onclick="javascript:lovd_checkURL(); return false;">check</A>)</SPAN>'),
                    array('', 'print', '<SPAN class="form_note">This is the URL with which the database can be accessed by the outside world, including "http://" or "https://". It will also be used in emails sent by LOVD. This field is mandatory if you select the "Include in the global LOVD listing" option.<BR>If you click the "check" link, LOVD will verify or try to predict the value.</SPAN>'),
                    'hr',
                    array('LOVD email address', 'text', 'email_address', 40),
                    array('', 'print', '<SPAN class="form_note">This email address will be used to send emails from LOVD to users. We need this address to make sure that emails from LOVD arrive. Please note that although strictly spoken this email address does not need to exist, we recommend that you use a valid address.</SPAN>'),
                    'hr',
                    array('Forward messages to database admin', 'checkbox', 'send_fwd', 1),
                    array('', 'print', '<SPAN class="form_note">This will forward messages to the database administrator about submitter registrations, submissions, and such.</SPAN>'),
                    'hr',
                    array('Enable \'My Submissions\'', 'checkbox', 'my_submissions', 1),
                    array('', 'print', '<SPAN class="form_note">Enabling \'My Submissions\' allows submitters to gather a list of submissions they\'ve made to the database and make changes to them.</SPAN>'),
                    'hr',
                    array('Enable searching through hidden entries', 'checkbox', 'search_count_entries', 1),
                    array('', 'print', '<SPAN class="form_note">Enabling this feature allows the public to find the number of entries in the database (including hidden entries) matching one or more search terms on a specified set of columns. This feature will only mention the number of variant entries matched, without showing them.</SPAN>'),
                    'hr',
                    array('List database changes in feed for how long?', 'select', 'api_feed_history', 1, $aFeedHistory, false, false, false),
                    array('', 'print', '<SPAN class="form_note">LOVD includes a "newsfeed" that allows users to get a list of changes recently made in the database. Select here how many months back you want changes to appear on this list. Set to "Not available" to disable the newsfeed.</SPAN>'),
                    'hr',
                    'skip',
                    'skip',
                    array('', 'print', '<B>Global LOVD statistics</B>'),
                    array('', 'print', '<SPAN class="form_note">The following settings apply to the kind of information your LOVD install sends to the development team to gather statistics about global LOVD usage.</SPAN>'),
                    'hr',
                    array('Send signature', 'checkbox', 'send_signature', 1),
                    array('', 'print', '<SPAN class="form_note">This sends an unique <I>anonymous</I> signature allowing us to determine the number of unique installations of LOVD.</SPAN>'),
                    'hr',
                    array('Send statistics', 'checkbox', 'send_stats', 1),
                    array('', 'print', '<SPAN class="form_note">This sends <I>anonymous</I> statistics about the number of submitters, genes, patients and mutations in your installation of LOVD.</SPAN>'),
                    'hr',
                    array('Include in the global LOVD listing', 'checkbox', 'send_listings', 1),
                    array('', 'print', '<SPAN class="form_note">We keep a public listing of LOVD installations, their genes and their URLs. Deselect this checkbox if you do not want to be included in this public listing.</SPAN>'),
                    'hr',
                    'skip',
                    'skip',
                    array('', 'print', '<B>Security settings</B>'),
                    array('', 'print', '<SPAN class="form_note">Using the following settings you can control some security settings of LOVD.</SPAN>'),
                    'hr',
                    array('Use cookies for user authentication', 'checkbox', 'use_cookies', 1),
                    array('', 'print', '<SPAN class="form_note">Leaving this option set is <I>highly recommended</I>, as disabling this allows others to hijack LOVD user accounts.</SPAN>'),
                    'hr',
                    array('Use SSL only to access LOVD', 'checkbox', 'use_ssl', 1),
                    array('', 'print', '<SPAN class="form_note">SSL is a secure protocol allowing for encryption of data sent between you and LOVD. When you will record sensitive patient information in LOVD, you <B>should</B> enable this setting, as the patient information can otherwise be \'sniffed\' off the network. If you do not record sensitive information, enabling SSL is <I>recommended</I>.</SPAN>'),
                    'hr',
                    array('Hide non public entries from the unique variant listing', 'checkbox', 'hide_nonpublic', 1),
                    array('', 'print', '<SPAN class="form_note">If you turn this off, information on non public variants will be included in the unique variant listing. This setting does not effect the safety of the patient data.</SPAN>'),
                    'hr',
                    array('Lock <I>curators</I> after 3rd failed login', 'checkbox', 'lock_curators', 1),
                    array('', 'print', '<SPAN class="form_note">Do you want to lock curators and managers after three failed attempts to log in using their username?<BR>(This does <I>not</I> affect the database administrator account)</SPAN>'),
                    'hr',
                    array('Lock <I>submitters</I> after 3rd failed login', 'checkbox', 'lock_submitters', 1),
                    array('', 'print', '<SPAN class="form_note">Do you want to lock submitters after three failed attempts to log in using their username?</SPAN>'),
                    'hr',
                    array('Allow locked users to retrieve a new password', 'checkbox', 'allow_unlock', 1),
                    array('', 'print', '<SPAN class="form_note">Do you want to enable an "I forgot my password" option that allows users who forgot their password to retrieve a new one?</SPAN>'),
                    'hr',
                    array('Set uninstall lock (prevents uninstall)', 'checkbox', 'lock_uninstall', 1),
                    array('', 'print', '<SPAN class="form_note">Please note that this uninstall lock can only be removed by directly accessing the MySQL database.</SPAN>'),
                    'hr',
                    'skip',
                    array('', 'submit', 'Continue'),
                  );
    lovd_viewForm($aForm);

    print('</TABLE></FORM>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;
} elseif ($_GET['step'] == 3) { $_GET['step'] ++; }





if ($_GET['step'] == 4 && !@mysql_num_rows(mysql_query('SELECT * FROM ' . TABLE_MODULES))) {
    // Step 4: Configuring LOVD modules.
    if (@mysql_num_rows(mysql_query('SHOW TABLES LIKE "' . TABLE_MODULES . '"')) != 1) {
        // Didn't finish previous step correctly.
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] - 2));
        exit;
    }
    if (!@mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_CONFIG))) {
        // Didn't finish previous step correctly.
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] - 1) . lovd_showSID(true));
        exit;
    }

    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    print('      <B>Configuring LOVD modules</B><BR>' . "\n" .
          '      <BR>' . "\n\n");

    print('      Please wait while the installer is trying to detect the available LOVD modules...<BR>' . "\n" .
          '      <BR>' . "\n\n" .
          '      <PRE>' . "\n");
    print('<SCRIPT type="text/javascript">var bar = document.getElementById(\'lovd_install_bar\');</SCRIPT>');

    // Read out modules directory...
    $hDir = @opendir(MODULE_PATH);
    if (!$hDir) {
        print('Failed to open modules directory.' . "\n");
        print('      </PRE>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    $aFailed = array();
    $aSuccess = array();
    while (($sModuleID = readdir($hDir)) !== false) {
        if ($_MODULES->isInstalled($sModuleID) || substr($sModuleID, 0, 1) == '.' || !is_dir(MODULE_PATH . $sModuleID) || !is_readable(MODULE_PATH . $sModuleID . '/module.php')) {
            // Already installed module, ignored file, not a directory, or not a modules directory.
            continue;
        }

        // Try and load the module. THIS WILL EXECUTE THE MODULE CODE!!!
        $sFile = file_get_contents(MODULE_PATH . $sModuleID . '/module.php');

        // 'Cause you never know who tries to mess with us...
        $sModuleID = mysql_real_escape_string($sModuleID);

        $b = @eval($sFile);
        if ($b === false) {
            // Apparently, malformed module code (parse error or such).
            lovd_writeLog('MySQL:Error', 'ModuleScan', 'Error while scanning for new modules: ' . $sModuleID . '/module.php returns error');
            $aFailed[] = $sModuleID;
            continue;
        } elseif (!class_exists($sModuleID)) {
            // Apparently, not defined.
            lovd_writeLog('MySQL:Error', 'ModuleScan', 'Error while scanning for new modules: ' . $sModuleID . '/module.php does not define module class');
            $aFailed[] = $sModuleID;
            continue;
        } else {
            // Load module!
            $Tmp = new $sModuleID;

            if (!method_exists($Tmp, 'getInfo')) {
                // Apparently, not defined getInfo() method.
                lovd_writeLog('MySQL:Error', 'ModuleScan', 'Error while scanning for new modules: ' . $sModuleID . '/module.php does not have getInfo() method');
                $aFailed[] = $sModuleID;
                continue;
            }
        }

        $aModule = $Tmp->getInfo();
        if (!is_array($aModule) || empty($aModule['name']) || empty($aModule['version']) || empty($aModule['description'])) {
            // Apparently, missing information from getInfo() method.
            lovd_writeLog('MySQL:Error', 'ModuleScan', 'Error while scanning for new modules: ' . $sModuleID . '/module.php does not return expected array using getInfo()');
            $aFailed[] = $sModuleID;
            continue;
        }

        // 'Cause you never know who tries to mess with us...
        $aModule['settings'] = serialize($aModule['settings']);
        lovd_magicQuote($aModule);

        // All seems to be OK... install module, but do not activate right now.
        $sQ = 'INSERT INTO ' . TABLE_MODULES . ' VALUES ("' . $sModuleID . '", "' . $aModule['name'] . '", "' . $aModule['version'] . '", "' . $aModule['description'] . '", 0, "' . $aModule['settings'] . '", NOW(), NULL)';
        $q = mysql_query($sQ);
        if (!$q) {
            lovd_writeLog('MySQL:Error', 'ModuleScan', 'Error while scanning for new modules: ' . $sModuleID . '/module.php does not install properly:' . "\n" . 'Query : ' . $sQ . "\n" . 'Error : ' . mysql_error());
            $aFailed[] = $sModuleID;
            continue;
        }

        lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'New module installed: ' . $sModuleID . '/module.php returns "' . $aModule['name'] . '"');
        $aSuccess[] = $sModuleID;
    }

    // Print result of module scan to screen!
    $sFailed = '';
    $sSuccess = '';
    $nSuccess = count($aSuccess);

    if (count($aFailed)) {
        $sFailed = 'Failed installing "' . implode('"' . "\n" . 'Failed installing "', $aFailed) . '"' . "\n";
    }
    if ($nSuccess) {
        $sSuccess = 'Successfully installed "' . implode('"' . "\n" . 'Successfully installed "', $aSuccess) . '"' . "\n";
    }

    if ($sSuccess) {
        print('Successfully installed new module' . ($nSuccess == 1? '' : 's') . '!' . "\n" . $sSuccess .
              ($sFailed? "\n" . 'Not installed due to errors (more info in the logs):' . "\n" . $sFailed : ''));
    } elseif (!$sFailed) {
        print('No new modules found.' . "\n");
    } else {
        print('Failed installing new modules! More information can be found in the error logs.' . "\n" . $sFailed);
    }

    lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Installed ' . $nSuccess . ' LOVD module' . ($nSuccess == 1? '' : 's'));

    print('      </PRE>' . "\n\n" .
          '      Ready to proceed to the next step.<BR>' . "\n" .
          '      <BR>' . "\n\n");

//    $_POST = array();
    lovd_printInstallForm();

    require ROOT_PATH . 'inc-bot.php';
    exit;
} elseif ($_GET['step'] == 4) { $_GET['step'] ++; }





if ($_GET['step'] == 5) {
    // Step 5: Done.
    if (!@mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_CONFIG))) {
        // Didn't finish previous step correctly.
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?step=' . ($_GET['step'] - 2) . lovd_showSID(true));
        exit;
    }

    require 'inc-top.php'; // Install dir's own top include.
    lovd_showInstallBar();

    lovd_writeLog('MySQL:Install', 'Step' . $_GET['step'], 'Installation of LOVD ' . $_STAT['version'] . ' complete');

    print('      <B>Done</B><BR>' . "\n" .
          '      <BR>' . "\n\n");

    print('      The installation of LOVD ' . $_STAT['version'] . ' is now complete.<BR>' . "\n" .
          '      <BR>' . "\n\n");

    print('      <BUTTON onclick="window.location.href=\'' . ROOT_PATH . 'setup.php?newly_installed' . lovd_showSID(true, true) . '\';" style="font-weight : bold; font-size : 11px;">Continue to Setup area &gt;&gt;</BUTTON>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;
}
?>
