<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2006-06-22
 * Modified    : 2011-01-21
 * For LOVD    : 2.0-30
 *
 * Access      : Submitters and up.
 * Purpose     : Let the user/submitter view and edit his/her own account.
 *
 * Copyright   : 2004-2010 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-init.php';

if (HAS_AUTH) {
    // If authorized, check for updates.
    require ROOT_PATH . 'inc-upgrade.php';
}





if ($_GET['action'] == 'view') {
    // View account details.

    // Require valid user.
    lovd_requireAUTH();

    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('account_details', 'Account - View details');

    // Copy account data.
    $zData = $_AUTH;
    if (!$zData) {
        // Wrong ID, apparently.
        print('      No such ID!<BR>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Array which will make up the data table.
    $aTable =
             array(
                    'submitterid' => 'Submitter ID',
                    'userid' => 'User ID',
                    'name' => 'Name',
                    'institute' => 'Institute',
                    'department' => 'Department',
                    'address' => 'Address',
                    'city' => 'City',
                    'country_' => 'Country',
                    'email' => 'Email address',
                    'telephone' => 'telephone',
                    'reference' => 'Reference',
                    'username' => 'Username',
                    'submits' => 'Submits',
                    'current_db' => 'Current gene',
                    'curates' => 'Curator for',
                    'list_size' => 'Number of entries per page',
                    'level_' => 'User level',
                    'allowed_ip' => 'Allowed IP address list',
                    'last_login' => 'Last login',
                    'created_by' => 'Created by',
                    'created_date' => 'Date created',
                    'edited_by' => 'Last edited by',
                    'edited_date' => 'Date last edited',
                  );

    // Remove unnecessary columns.
    if (HAS_AUTH) {
        unset($aTable['submitterid'], $aTable['department'], $aTable['address'], $aTable['city'], $aTable['country_'], $aTable['telephone'], $aTable['reference'], $aTable['submits']);
    } else {
        $aTable['created_date'] = 'Registration date';
        unset($aTable['userid'], $aTable['current_db'], $aTable['curates'], $aTable['list_size'], $aTable['level'], $aTable['allowed_ip'], $aTable['level_']);
        list($zData['submits']) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_PAT2VAR . ' AS p2v LEFT JOIN ' . TABLE_PATIENTS . ' AS p USING (patientid) WHERE p.submitterid = "' . $_AUTH['submitterid'] . '"'));
    }

    // Prepare information for created by column.
    if (!$zData['created_by']) {
        // Self-registration.
        $zData['created_by'] = $zData['name'];
    } else {
        list($zData['created_by']) = mysql_fetch_row(mysql_query('SELECT name FROM ' . TABLE_USERS . ' WHERE userid = "' . $zData['created_by'] . '"'));
    }

    // Remove unnecessary columns.
    if ($zData['edited_by'] == NULL || !$zData['edited_by']) {
        // Never have been edited, or edited by him/herself (SUBS).
        unset($aTable['edited_by']);
    } else {
        // Edited by user.
        list($zData['edited_by']) = mysql_fetch_row(mysql_query('SELECT name FROM ' . TABLE_USERS . ' WHERE userid = "' . $zData['edited_by'] . '"'));
    }

    if ($zData['edited_date'] == NULL) {
        // Never been edited.
        unset($aTable['edited_date']);
    }

    // Table.
    print('      <TABLE border="0" cellpadding="0" cellspacing="1" width="600" class="data">');

    // Update user-specific values.
    if (HAS_AUTH) {
        sort($_AUTH['curates']);
        $zData['curates'] = implode(', ', $_AUTH['curates']);
        $zData['level_']   = $_SETT['user_levels'][$zData['level']];

    } else {
        $zData['address']        = str_replace(array("\r\n", "\r", "\n"), '<BR>', $zData['address']);
        list($zData['country_']) = mysql_fetch_row(mysql_query('SELECT country FROM ' . TABLE_COUNTRIES . ' WHERE code = "' . $zData['country'] . '"'));
        $zData['email']          = str_replace(array("\r\n", "\r", "\n"), ', ', trim($zData['email']));
    }

    foreach ($aTable as $sField => $sHeader) {
        print("\n" .
              '        <TR>' . "\n" .
              '          <TH valign="top">' . $sHeader . '</TH>' . "\n" .
              '          <TD>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD></TR>');
    }
    print('</TABLE>' . "\n\n");

    // Provide tools.
    print('      <IMG src="gfx/trans.png" alt="" width="1" height="5"><BR>' . "\n");
    lovd_viewNavigation('<A href="' . $_SERVER['PHP_SELF'] . '?action=edit">Update your registration</A> | <A href="' . $_SERVER['PHP_SELF'] . '?action=change_passwd">Change your password</A>');

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





} elseif ($_GET['action'] == 'edit') {
    // Edit your own account - submitter or user.

    // Require valid user.
    lovd_requireAUTH();

    // Copy account data.
    $zData = $_AUTH;
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('account_update', 'Account - Update');
        print('      No such ID!<BR>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

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

        // Mandatory fields.
        $aCheck =
                 array(
                        'name' => 'Name',
                        'firstname' => 'First name',
                        'lastname' => 'Last name',
                        'institute' => 'Institute',
                        'address' => 'Address',
                        'city' => 'City',
                        'country' => 'Country',
                        'email' => 'Email address',
                        'allowed_ip' => 'Allowed IP address list',
                        'password' => 'Enter your password for authorization',
                      );

        // Remove unnecessary columns.
        if (HAS_AUTH) {
            unset($aCheck['firstname'], $aCheck['lastname'], $aCheck['address'], $aCheck['city'], $aCheck['country']);
        } else {
            unset($aCheck['name'], $aCheck['allowed_ip']);
        }

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

        // 2008-08-07; 2.0-10; Block newlines to prevent registration form from being used as a spam sender.
        // Fields not allowed to contain newlines (hack attempt - sending spam using the form).
        if (HAS_AUTH) {
            $aCheck =
                     array(
                            'name' => 'Name',
                          );
        } else {
            $aCheck =
                     array(
                            'firstname' => 'First name',
                            'lastname' => 'Last name',
                          );
        }

        foreach ($aCheck as $key => $val) {
            if (substr_count($_POST[$key], "\n")) {
                lovd_errorAdd('Disallowed newline found in form field "' . $val . '". SPAM attack?');
            }
        }

        // Email address.
        if ($_POST['email']) {
            $aEmail = explode("\r\n", trim($_POST['email']));
            foreach ($aEmail as $sEmail) {
                if (!lovd_matchEmail($sEmail)) {
                    lovd_errorAdd('Email "' . $sEmail . '" is not a correct email address.');
                }
            }
        }

        // 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 \'New 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('If you want to change the current password, please fill in both \'New password\' fields.');
            }
        }

        // Check given security IP range.
        if (HAS_AUTH) {
            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.');
                }

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

        // User had to enter his/her password for authorization. (MODIFIED)
        if ($_POST['password'] && md5($_POST['password']) != $zData['password']) {
            lovd_errorAdd('Please enter your correct password for authorization.');
        }

        // Manager can select genes to curate.
        if ($_AUTH['level'] >= LEVEL_MANAGER) {
            if (!isset($_POST['curates'])) {
                // Why is this variable unset if nothing has been selected?!!??
                $_POST['curates'] = array();
            }

            $q = mysql_query('SELECT t1.symbol, t1.gene, COUNT(t2.userid) AS count FROM ' . TABLE_DBS . ' AS t1 LEFT OUTER JOIN ' . TABLE_CURATES . ' AS t2 ON (t1.symbol = t2.symbol AND t2.userid != "' . $_AUTH['userid'] . '") GROUP BY t1.symbol ORDER BY t1.symbol');
            while ($z = mysql_fetch_assoc($q)) {
                // Gene has no curator, and user hasn't been selected as one.
                if (!$z['count'] && !in_array($z['symbol'], $_POST['curates'])) {
                    lovd_errorAdd('This gene has no curator: ' . $z['symbol'] . ' (' . $z['gene'] . '). Please (re)select yourself as a curator for this gene.');
                }
            }
        }

        // 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()) {
            if (HAS_AUTH) {
                // Query text.
                $sQ = 'UPDATE ' . TABLE_USERS . ' SET ';

                // Standard fields to be used.
                $aQ = array('name', 'institute', 'email', 'list_size', 'allowed_ip');
            } else {
                // Query text.
                $sQ = 'UPDATE ' . TABLE_SUBS . ' SET ';

                // Standard fields to be used.
                $aQ = array('firstname', 'lastname', 'institute', 'department', 'address', 'city', 'country', 'email', 'telephone');
            }

            // In case the password is getting changed...
            if ($_POST['password_1']) {
                $_POST['password'] = md5($_POST['password_1']);
                $aQ[] = 'password';
            }

            foreach ($aQ as $key => $val) {
                $sQ .= ($key? ', ' : '') . $val . ' = "' . $_POST[$val] . '"';
            }

            // Finish query.
            if (HAS_AUTH) {
                $sQ .= ', edited_by = userid, edited_date = NOW() WHERE userid = ' . $_AUTH['userid'];
            } else {
                $sQ .= ', edited_by = 0, edited_date = NOW() WHERE submitterid = ' . $_AUTH['submitterid'];
            }

            $q = mysql_query($sQ);
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('account_update', 'Account - Update');
                lovd_dbFout('SelfEdit', $sQ, $sError);
            }

            if (HAS_SUBS) {
                $_POST['name'] = $_POST['firstname'] . ' ' . $_POST['lastname'];
            }

            // Write to log...
            lovd_writeLog('MySQL:Event', 'SelfEdit', $_AUTH['username'] . ' (' . $_POST['name'] . ') successfully edited own account');

            // If manager; change curator?
            if ($_AUTH['level'] >= LEVEL_MANAGER) {
                // Fetch genes user is currently curator for.
                $aCurates = $_AUTH['curates'];

                // Remove as curator.
                foreach ($aCurates AS $sSymbol) {
                    if (!in_array($sSymbol, $_POST['curates'])) {
                        // User has requested removal...
                        $q = mysql_query('DELETE FROM ' . TABLE_CURATES . ' WHERE symbol = "' . $sSymbol . '" AND userid = "' . $_AUTH['userid'] . '"');
                        if (!$q) {
                            // Silent error.
                            lovd_writeLog('MySQL:Error', 'SelfEdit', $_AUTH['username'] . ' (' . $_POST['name'] . ') could not be removed as curator for ' . $sSymbol);
                        } else {
                            lovd_writeLog('MySQL:Event', 'SelfEdit', $_AUTH['username'] . ' (' . $_POST['name'] . ') successfully removed as curator for ' . $sSymbol);
                        }
                    }
                }

                // Add as curator.
                if (isset($_POST['curates'])) {
                    foreach ($_POST['curates'] AS $sSymbol) {
                        if (!in_array($sSymbol, $aCurates)) {
                            // User has requested addition...
                            $q = mysql_query('INSERT INTO ' . TABLE_CURATES . ' VALUES ("' . $_AUTH['userid'] . '", "' . $sSymbol . '", 1)');
                            if (!$q) {
                                // Silent error.
                                lovd_writeLog('MySQL:Error', 'SelfEdit', $_AUTH['username'] . ' (' . $_POST['name'] . ') could not be added as curator for ' . $sSymbol);
                            } else {
                                lovd_writeLog('MySQL:Event', 'SelfEdit', $_AUTH['username'] . ' (' . $_POST['name'] . ') successfully added as curator for ' . $sSymbol);
                            }
                        }
                    }
                }
            }

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

            // Only email in case of a submitter.
            if (HAS_SUBS) {
                // Extra data.
                $_POST['submitterid'] = $zData['submitterid'];
                // Need both because of the match done when sending the email.
                list($zData['country']) = mysql_fetch_row(mysql_query('SELECT country FROM ' . TABLE_COUNTRIES . ' WHERE code = "' . $zData['country'] . '"'));
                // 2009-06-11; 2.0-19; Added mysql_real_escape_string() to prevent SQL injection.
                list($_POST['country']) = mysql_fetch_row(mysql_query('SELECT country FROM ' . TABLE_COUNTRIES . ' WHERE code = "' . mysql_real_escape_string($_POST['country']) . '"'));

                // 2009-02-27; 2.0-16; Sending emails on Windows requires removal of names from the email addresses.
                $sSubmitter = (ON_WINDOWS? '' : '"' . $_POST['name'] . '" ') . '<' . str_replace(array("\r\n", "\r", "\n"), '>, <', trim($_POST['email'])) . '>';

                $sBody = 'LOVD ' . $_SETT['system']['version'] . ' @ ' . $_CONF['location_name'] . "\n" .
                         'To : ' . str_replace('"', '', $sSubmitter) . "\n\n" .
                         'Dear ' . $_POST['name'] . ',' . "\n\n" .
                         'You\'ve just edited your LOVD submitter account at ' . $_CONF['system_title'] . '.' . "\n\n" .
                         'Below is a copy of your updated account information.' . "\n\n";
                // 2009-04-06; 2.0-17; Add the location of the database, so that the submitter can just click the link.
                if ($_CONF['location_url']) {
                    $sBody .= 'To log in to LOVD, click this link:' . "\n" .
                              $_CONF['location_url'] . 'account_login.php' . "\n\n";
                }
                $sBody .= 'Regards,' . "\n" .
                          '    LOVD system at ' . $_CONF['location_name'] . "\n\n" .
                          str_repeat('-', 80) . "\n";

                // Array met data.
                $aMail = array(
                                'submitterid' => 'Submitter ID',
                                'firstname' => 'First name',
                                'lastname' => 'Last name',
                                'institute' => 'Institute',
                                'department' => 'Department',
                                'address' => 'Full address',
                                'city' => 'City',
                                'country' => 'Country',
                                'email' => 'Email address',
                                'telephone' => 'Telephone',
                               );

                if ($_POST['password_1'] && md5($_POST['password_1']) != $zData['password']) {
                    // Password changed.
                    $aMail['password_1'] = 'New password';
                    $zData['password_1'] = '';
                }

                // Padding to...
                $lPad = 0;
                foreach ($aMail as $val) {
                    $l = strlen($val);
                    if ($l > $lPad) {
                        $lPad = $l;
                    }
                }

                foreach ($aMail as $key => $val) {
                    // $lPad '± 2' specific for editing!
                    $sBody .= str_pad($val, $lPad) . (isset($_POST[$key]) && $zData[$key] != $_POST[$key]? ' *' : '  ') . ' : ' . str_replace("\n", "\n" . str_repeat(' ', $lPad + 3 + 2), lovd_wrapText($_POST[$key], 80 - $lPad - 3 - 2)) . "\n";
                }
                $sBody .= str_repeat('-', 80) . "\n";
                $sSubject = 'LOVD registration update';

                // Send mail.
                $bMail = @mail($sSubmitter,
                               $sSubject,
                               lovd_wrapText($sBody),
                               $_SETT['headers']);

                if ($bMail) {
                    if ($_CONF['send_fwd']) {
                        // Sent mail to submitter, send copy to database administrator.
                        $sBody = preg_replace('/^(New password[\s*]+: )' . preg_quote($_POST['password_1']) . '/m', "$1" . '<password hidden>', $sBody);
                        $sBody = 'L.S.,' . "\n\n" .
                                 'As requested, a copy of the message I\'ve just sent.' . "\n\n" .
                                 str_repeat('-', 30) . ' Forwarded  Message ' . str_repeat('-', 30) . "\n\n" .
                                 $sBody . "\n" .
                                 str_repeat('-', 27) . ' End of Forwarded Message ' . str_repeat('-', 27) . "\n";

                        // Send mail.
                        // 2009-02-27; 2.0-16; Sending emails on Windows requires removal of names from the email addresses.
                        $bMail = @mail((ON_WINDOWS? '' : '"' . $_SETT['admin']['name'] . '" ') . '<' . $_SETT['admin']['email'] . '>',
                                       'Fw: ' . $sSubject,
                                       lovd_wrapText($sBody),
                                       $_SETT['headers']);

                        if (!$bMail) {
                            // Couldn't sent confirmation to admin...
                            lovd_writeLog('MySQL:Error', 'EmailNotify', $_SERVER['PHP_SELF'] . ' returned SelfEditNotifyAdmin error for submitter ' . $_AUTH['username'] . ' (' . mysql_real_escape_string($_POST['name']) . ')');
                        }
                    }

                    // Thank the user...
                    header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . lovd_showSID());

                    require ROOT_PATH . 'inc-top.php';
                    lovd_printHeader('account_update', 'Account - Update');
                    print('      Your account has successfully been updated!<BR>' . "\n" .
                          '      We\'ve sent you an email containing your updated account information.<BR><BR>' . "\n\n");

                } else {
                    // Couldn't sent confirmation...
                    lovd_writeLog('MySQL:Error', 'EmailNotify', $_SERVER['PHP_SELF'] . ' returned SelfEditNotify error for submitter ' . $_AUTH['username'] . ' (' . mysql_real_escape_string($_POST['name']) . ')');

                    require ROOT_PATH . 'inc-top.php';
                    lovd_printHeader('account_update', 'Account - Update');
                    print('      Your account has successfully been updated!<BR>' . "\n" .
                          '      Due to an error, we couldn\'t send you an email containing your updated account information. Our apologies for the inconvenience.<BR><BR>' . "\n\n");
                }

            } else {
                // Thank the user...
                header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . lovd_showSID());

                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('account_update', 'Account - Update');
                print('      Your account has successfully been updated!<BR>' . "\n\n");
            }

            // Change password, if requested.
            if ($_POST['password_1']) {
                // Was already md5'ed!
                $_SESSION[(HAS_AUTH? 'auth' : 'subs')]['password'] = $_POST['password'];
            }

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

        } else {
            // Errors, so the whole lot returns to the form.
            lovd_magicUnquoteAll();

            // Because we're sending the data back to the form, I need to unset the password fields!
            unset($_POST['password'], $_POST['password_1'], $_POST['password_2']);
        }

    } else {
        foreach ($zData as $key => $val) {
            if (!isset($_POST[$key]) || !$_POST[$key]) {
                $_POST[$key] = $val;
            }
        }
        $_POST['password'] = '';

        // Manager: curator for?
        if ($_AUTH['level'] >= LEVEL_MANAGER) {
            $_POST['curates'] = $_AUTH['curates'];
        }
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('account_update', 'Account - Update');

    lovd_errorPrint();

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

    // Retrieve country list.
    $qCountryList = mysql_query('SELECT code, country FROM ' . TABLE_COUNTRIES . ' ORDER BY country');

    // Array which will make up the form table.
    if (HAS_AUTH) {
        $aForm = array(
                        array('POST', '', '', '50%', '50%'),
                        array('', 'print', '<B>User details</B>'),
                        array('Name', 'text', 'name', 30),
                        array('Institute', 'text', 'institute', 40),
                        array('Email address', 'text', 'email', 30),
                        array('New password (optional)', 'password', 'password_1', 20),
                        array('New password (confirm, optional)', '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>'));

        if ($_AUTH['level'] >= LEVEL_MANAGER) {
            // Get gene list, to select user as curator.
            // 2010-03-12; 2.0-25; This will make this query very complicated, but it will also shorten the gene names quite properly.
            $qGenes = mysql_query('SELECT symbol, CONCAT(SUBSTRING(CONCAT(symbol, " (", gene), 1, 50), SUBSTRING("...)", (((LENGTH(CONCAT(symbol, gene))<49)+1)*3)-2)) AS gene FROM ' . TABLE_DBS . ' ORDER BY symbol');
            $nGenes = mysql_num_rows($qGenes);
            $nGeneSize = ($nGenes < 5? $nGenes : 5);
            $aForm = array_merge($aForm,
                 array(
                        'skip',
                        array('', 'print', '<B>Curated genes</B>'),
                        array('Curator for', 'select', 'curates', $nGeneSize, $qGenes, false, true, true),
                      ));
        }
        
        $aForm = array_merge($aForm,
                 array(
                        'skip',
                        array('Enter your password for authorization', 'password', 'password', 20),
                        array('', 'submit', 'Update registration'),
                      ));
    } else {
        $aForm = array(
                        array('POST', '', '', '50%', '50%'),
                        array('First name', 'text', 'firstname', 30),
                        array('Last name', 'text', 'lastname', 30),
                        array('Institute', 'text', 'institute', 40),
                        array('Department (optional)', 'text', 'department', 40),
                        array('Full address', 'textarea', 'address', 35, 3),
                        array('City', 'text', 'city', 30),
                        array('', 'print', '<SPAN class="form_note">In addition to your full address, please enter the city for sorting purposes.</SPAN>'),
                        array('Country', 'select', 'country', 1, $qCountryList, true, false, false),
                        array('Email address(es), one per line', 'textarea', 'email', 30, 3),
                        array('Telephone (optional)', 'text', 'telephone', 20),
                        array('New password (optional)', 'password', 'password_1', 20),
                        array('New password (confirm, optional)', 'password', 'password_2', 20),
                        'skip',
                        array('Enter your password for authorization', 'password', 'password', 20),
                        array('', 'submit', 'Update registration'),
                      );
    }
    $_MODULES->processForm('AccountUpdateEdit', $aForm);
    lovd_viewForm($aForm);

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

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





// 2008-11-21; 2.0-14; Add form used only to change the password.
} elseif ($_GET['action'] == 'change_passwd') {
    // Edit password only.

    // Require valid user.
    lovd_requireAUTH();

    // Copy account data.
    $zData = $_AUTH;
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('account_update', 'Account - Update');
        print('      No such ID!<BR>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

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

        // Mandatory fields.
        $aCheck =
                 array(
                        'password' => 'Current password',
                        'password_1' => 'New password',
                        'password_2' => 'Confirm new password',
                      );

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

        // 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 \'New 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('To change the current password, please fill in both \'New password\' fields.');
            }
        }

        // User had to enter his/her password for authorization. (MODIFIED)
        if ($_POST['password'] && md5($_POST['password']) != $zData['password']) {
            lovd_errorAdd('Please enter your correct password for authorization.');
        }

        if (!lovd_error()) {
            if (HAS_AUTH) {
                // Query text.
                $sQ = 'UPDATE ' . TABLE_USERS . ' SET ';
            } else {
                // Query text.
                $sQ = 'UPDATE ' . TABLE_SUBS . ' SET ';
            }

            // Standard fields to be used.
            $aQ = array('password', 'password_autogen', 'password_force_change');
            $_POST['password'] = md5($_POST['password_1']);
            $_POST['password_autogen'] = '';
            $_POST['password_force_change'] = 0;

            foreach ($aQ as $key => $val) {
                $sQ .= ($key? ', ' : '') . $val . ' = "' . $_POST[$val] . '"';
            }

            // Finish query.
            if (HAS_AUTH) {
                $sQ .= ' WHERE userid = ' . $_AUTH['userid'];
            } else {
                $sQ .= ' WHERE submitterid = ' . $_AUTH['submitterid'];
            }

            $q = mysql_query($sQ);
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('account_update', 'Account - Update');
                lovd_dbFout('ChangePasswd', $sQ, $sError);
            }

            if (HAS_SUBS) {
                $_AUTH['name'] = $_AUTH['firstname'] . ' ' . $_AUTH['lastname'];
            }

            // Write to log...
            lovd_writeLog('MySQL:Event', 'ChangePasswd', $_AUTH['username'] . ' (' . $_AUTH['name'] . ') successfully edited own account');

            // Thank the user...
            header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . lovd_showSID());

            require ROOT_PATH . 'inc-top.php';
            lovd_printHeader('account_update', 'Account - Update');
            print('      Your password has successfully been updated!<BR>' . "\n\n");

            // Change password, was already md5'ed!
            $_SESSION[(HAS_AUTH? 'auth' : 'subs')]['password'] = $_POST['password'];
            unset($_SESSION['password_force_change']);

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

        } else {
            // Errors, so the whole lot returns to the form.
            lovd_magicUnquoteAll();

            // Because we're sending the data back to the form, I need to unset the password fields!
            unset($_POST['password'], $_POST['password_1'], $_POST['password_2']);
        }
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('account_update', 'Account - Update');

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?action=' . $_GET['action'] . '&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('Current password', 'password', 'password', 20),
                    array('New password', 'password', 'password_1', 20),
                    array('Confirm new password', 'password', 'password_2', 20),
                    array('', 'submit', 'Change password'),
                  );
    $_MODULES->processForm('AccountPasswordChange', $aForm);
    lovd_viewForm($aForm);

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

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





// 2008-11-26; 2.0-14; Allow unlocking the account and resetting the password.
} elseif ($_GET['action'] == 'reset_passwd' && !$_AUTH && $_CONF['allow_unlock']) {
    // User forgot password - replace.

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

    if (isset($_GET['sent']) && !empty($_POST['username'])) {
        lovd_errorClean();

        // Find account.
        $zData = mysql_fetch_assoc(mysql_query('SELECT *, CONCAT(firstname, " ", lastname) AS name FROM ' . TABLE_SUBS . ' WHERE username = "' . $_POST['username'] . '" AND deleted = 0'));
        if (!$zData) {
            $zData = mysql_fetch_assoc(mysql_query('SELECT * FROM ' . TABLE_USERS . ' WHERE username = "' . $_POST['username'] . '" AND deleted = 0'));
        }

        if (!$zData) {
            lovd_errorAdd('This username does not exist.');
            lovd_writeLog('MySQL:Auth', 'ResetPasswdError', $_SERVER['REMOTE_ADDR'] . ' (' . gethostbyaddr($_SERVER['REMOTE_ADDR']) . ') tried to reset password for inexistent/denied account ' . $_POST['username']);
        }

        if (!lovd_error()) {
            // Found account... unlock and generate new passwd.

            $aChars =
                     array(
                            'A' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                            '0' => '0123456789',
                            '!' => '!@#$%^&*()',
                          );
            $sFormat = 'AA!00!AA!00!';
            $nLength = strlen($sFormat);
            $sPasswd = '';
            for ($i = 0; $i < $nLength; $i ++) {
                $sType = $sFormat{$i};
                $sPasswd .= $aChars[$sType]{mt_rand(0, strlen($aChars[$sType]) - 1)};
            }

            // Update database.
            $q = @mysql_query('UPDATE ' . (isset($zData['submitterid'])? TABLE_SUBS : TABLE_USERS) . ' SET password_autogen = MD5("' . $sPasswd . '") WHERE username = "' . $_POST['username'] . '"');
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('account_reset_passwd', 'Account - Reset password');
                lovd_dbFout('ResetPasswd', $sQ, $sError);
            }

            lovd_writeLog('MySQL:Auth', 'ResetPasswd', $_SERVER['REMOTE_ADDR'] . ' (' . gethostbyaddr($_SERVER['REMOTE_ADDR']) . ') successfully reset password for account ' . $_POST['username']);

            // Send email confirmation.
            $_POST['password_autogen'] = $sPasswd;

            $sBody = 'LOVD ' . $_SETT['system']['version'] . ' @ ' . $_CONF['location_name'] . "\n\n" .
                     'Dear ' . $zData['name'] . ',' . "\n\n" .
                     'Your password from your LOVD account has been reset, as requested. Your new, randomly generated, password can be found below. Please log in to LOVD and choose a new password.' . "\n\n" .
                     'Below is a copy of your updated account information.' . "\n\n" .
                     'If you did not request a new password, you can disregard this message. Your old password will continue to function normally. However, you may then want to report this email to the LOVD manager, who can investigate possible misuse of the system.' . "\n\n";
            // 2009-04-06; 2.0-17; Add the location of the database, so that the user can just click the link.
            if ($_CONF['location_url']) {
                $sBody .= 'To log in to LOVD, click this link:' . "\n" .
                          $_CONF['location_url'] . 'account_login.php' . "\n\n";
            }
            $sBody .= str_repeat('-', 80) . "\n";

            // 2010-03-11; 2.0-25; Move this up, because users didn't see this.
            // Array met data.
            $aMail = array(
                            'password_autogen' => 'New password / unlocking code',
                           );

            // Padding to...
            $lPad = 0;
            foreach ($aMail as $val) {
                $l = strlen($val);
                if ($l > $lPad) {
                    $lPad = $l;
                }
            }

            foreach ($aMail as $key => $val) {
                $sBody .= str_pad($val, $lPad) . ' : ' . str_replace("\n", "\n" . str_repeat(' ', $lPad + 3), lovd_wrapText($_POST[$key], 80 - $lPad - 3)) . "\n";
            }
            $sBody .= str_repeat('-', 80) . "\n\n" .
                      'Regards,' . "\n" .
                      '    LOVD system at ' . $_CONF['location_name'] . "\n\n";
            $sSubject = 'LOVD password reset';

            // For submitters, we need to take the FIRST email address only.
            if (isset($zData['submitterid'])) {
                $aEmail = preg_split('/(\r\n|\r|\n)+/', trim($zData['email']));
                $zData['email'] = $aEmail[0];
            }

            // Send mail.
            // 2009-02-27; 2.0-16; Sending emails on Windows requires removal of names from the email addresses.
            $bMail = @mail((ON_WINDOWS? '' : '"' . $zData['name'] . '" ') . '<' . $zData['email'] . '>',
                           $sSubject,
                           lovd_wrapText($sBody),
                           $_SETT['headers']);

            // Thank the user...
            require ROOT_PATH . 'inc-top.php';
            lovd_printHeader('account_reset_passwd', 'Account - Reset password');

            if ($bMail) {
                print('      Successfully reset your password.<BR>' . "\n" .
                      '      We\'ve sent you an email containing your new password. With this new password, you can <A href="' . ROOT_PATH . 'account_login.php">unlock your account</A> and choose a new password.<BR><BR>' . "\n\n");
            } else {
                // Couldn't sent confirmation...
                lovd_writeLog('MySQL:Error', 'EmailNotify', $_SERVER['PHP_SELF'] . ' returned ResetPasswdErrorNotify error for account ' . $_AUTH['username'] . ' (' . mysql_real_escape_string($zData['name']) . ')');
                print('      Due to an error, we couldn\'t send you an email containing your new password. Our apologies for the inconvenience.<BR><BR>' . "\n\n");
            }

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

        } else {
            lovd_writeLog('MySQL:Auth', 'ResetPasswd', $_SERVER['REMOTE_ADDR'] . ' (' . gethostbyaddr($_SERVER['REMOTE_ADDR']) . ') tried to reset password for denied account ' . $_POST['username']);
        }
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('account_reset_passwd', 'Account - Reset password');

    print('      If you forgot your password, please fill in your username here. A new random password will be generated and emailed to the known email address. You need this new password to unlock your account and choose a new password.<BR>' . "\n" .
          '      <BR>' . "\n\n");

    lovd_errorPrint();

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

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '42%', '58%'),
                    array('Username', 'text', 'username', 20),
                    'skip',
                    array('', 'submit', 'Reset password'),
                  );
    $_MODULES->processForm('AccountPasswordReset', $aForm);
    lovd_viewForm($aForm);
    print('</TABLE></FORM>' . "\n\n");

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





// Default action:
header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view' . lovd_showSID(true));
exit;
?>