<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2007-06-05
 * Modified    : 2009-02-25
 * For LOVD    : 2.0-16
 *
 * Access      : Administrator and managers.
 * Purpose     : Scan for new LOVD modules, manage modules, edit settings, etc.
 *
 * 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-init.php';

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

// Require manager clearance.
lovd_requireAUTH(LEVEL_MANAGER);





if ($_GET['action'] == 'view_all') {
    // View all modules.

    require ROOT_PATH . 'inc-lib-list.php';
    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');

    $nTotal = count($_MODULES->getModuleList());
    if (!$nTotal) {
        print('      There are no modules found yet. Do you want to <A href="' . $_SERVER['PHP_SELF'] . '?action=scan">scan for available modules</A>?<BR>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Standard query, will be extended later on.
    // The columns are specified, to prevent the settings column to be retrieved (which can be large).
    $sQ = 'SELECT moduleid, name, version, LEFT(description, 100) AS description, active, date_install, date_updated FROM ' . TABLE_MODULES;

    // SORT: Current settings.
    // 2008-08-07; 2.0-10; Implement XSS check on order variable.
    if (isset($_GET['order']) && $_GET['order'] && $_GET['order'] == strip_tags($_GET['order'])) {
        $aOrder = explode(',', $_GET['order']);
    } else {
        $aOrder = array('', '');
    }

    // SORT: Column data.
    $aOrderList = array('moduleid' => array('moduleid', 'ASC'), 'name' => array('name', 'ASC'), 'active' => array('active', 'ASC'), 'date_install' => array('date_install', 'DESC'), 'date_updated' => array('date_updated', 'DESC'));
    if (!array_key_exists($aOrder[0], $aOrderList)) {
        $aOrder[0] = 'moduleid';
    }
    if ($aOrder[1] != 'ASC' && $aOrder[1] != 'DESC') {
        $aOrder[1] = $aOrderList[$aOrder[0]][1];
    }

    $sQueryLimit = lovd_pagesplitInit($nTotal, 25);
    $sQ .= ' ORDER BY ' . $aOrderList[$aOrder[0]][0] . ' ' . $aOrder[1] . ' ' . $sQueryLimit;



    // Show form; required for sorting and searching.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '" method="get" style="margin : 0px;">' . "\n" .
          '        <INPUT type="hidden" name="action" value="' . $_GET['action'] . '">' . "\n" .
          '        <INPUT type="hidden" name="order" value="' . implode(',', $aOrder) . '">');
    print('</FORM>' . "\n\n");

    $q = mysql_query($sQ);
    if (!$q) {
        lovd_dbFout('Mods' . ucfirst($_GET['action']), $sQ, mysql_error());
    }

    $n = (isset($nResults)? $nResults : $nTotal);
    print('      <SPAN class="S11">' . $n . ' entr' . ($n == 1? 'y' : 'ies') . '</SPAN><BR>' . "\n");

    // Array which will make up the table (header and data).
    $aTable =
             array(
                    'moduleid' => array('ID', '120'),
                    'name' => array('Name', '*'),
                    'version' => array('Version', '100'),
                    'active' => array('Active', '60', 'align="center"'),
                    'date_install' => array('Installed', '110'),
                    'date_updated' => array('Updated', '110'),
                  );

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

    foreach ($aTable as $sField => $aCol) {
        print("\n" . '          <TH' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '" onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' .
              (array_key_exists($sField, $aOrderList)? "\n" .
                                                           '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                           '              <TR>' . "\n" .
                                                           '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                           '                <TD align="right"><IMG src="gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR></TABLE>' : $aCol[0]) . '</TH>');
    }
    print('</TR>');

    while ($zData = mysql_fetch_assoc($q)) {
        print("\n" .
              '        <TR style="cursor : pointer; cursor : hand;" onmouseover="this.className = \'hover\';" onmouseout="this.className = \'\';" onclick="window.location.href = \'' . $_SERVER['PHP_SELF'] . '?action=view&amp;view=' . $zData['moduleid'] . lovd_showSID(true, true) . '\';">');

        $zData['name']   = '<A href="' . $_SERVER['PHP_SELF'] . '?action=view&amp;view=' . $zData['moduleid'] . '" class="data">' . $zData['name'] . '</A>';
        $zData['active'] = '<IMG src="' . ROOT_PATH . 'gfx/mark_' . $zData['active'] . '.png" alt="' . ($zData['active']? 'Active' : 'Disabled') . '" width="11" height="11">';

        foreach ($aTable as $sField => $aCol) {
            print("\n" . '          <TD' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . ($aOrder[0] == $sField? ' class="ordered"' : '') . '>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD>');
        }
        print('</TR>');
    }
    print('</TABLE>' . "\n\n");

    // URL pagelink.
    $sPageLink = 'order=' . rawurlencode(implode(',', $aOrder));

    lovd_pagesplitShowNav($sPageLink);

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





} elseif ($_GET['action'] == 'view' && !empty($_GET['view'])) {
    // View specific module.

    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');

    $zData = @mysql_fetch_assoc(mysql_query('SELECT * FROM ' . TABLE_MODULES . ' WHERE moduleid = "' . $_GET['view'] . '"'));
    if (!$zData) {
        // Wrong ID, apparently.
        lovd_showInfoTable('No such ID!', 'stop');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Array which will make up the data table.
    $aTable =
             array(
                    'moduleid' => 'Module ID',
                    'name' => 'Name',
                    'version' => 'Version',
                    'description' => 'Description',
                    'active_' => 'Active',
                    'settings' => 'Settings (decoded)',
                    'date_install' => 'Date installed',
                    'date_updated' => 'Date last updated',
                  );

    // Remove unnecessary columns.
    if ($zData['date_updated'] == NULL) {
        // Never been updated.
        unset($aTable['date_updated']);
    }

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

    $zData['active_']  = '<IMG src="' . ROOT_PATH . 'gfx/mark_' . ($zData['active']? 1 : 0) . '.png" alt="Active" width="11" height="11">';
    $zData['settings'] = print_r(($zData['active']? $_MODULES->getSettings($zData['moduleid']) : unserialize($zData['settings'])), true);

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

    $sNavigation = '';
    // Authorized user (admin or manager) is logged in. Provide tools.
//    $sNavigation = '<A href="' . $_SERVER['PHP_SELF'] . '?action=edit&amp;edit=' . $zData['userid'] . '">Edit module settings</A>';
    $sNavigation = '<A style="color : #999999;">Edit module settings</A>';
    $sNavigation .= ' | <A href="' . $_SERVER['PHP_SELF'] . '?action=' . ($zData['active']? 'dis' : 'en') . 'able&amp;module=' . $zData['moduleid'] . '">' . ($zData['active']? 'Dis' : 'En') . 'able module</A>';
    $sNavigation .= ' | <A href="' . $_SERVER['PHP_SELF'] . '?action=uninstall&amp;module=' . $zData['moduleid'] . '">Uninstall module</A>';

    if ($sNavigation) {
        print('      <IMG src="gfx/trans.png" alt="" width="1" height="5"><BR>' . "\n");
        lovd_viewNavigation($sNavigation);
    }

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





} elseif (in_array($_GET['action'], array('enable', 'disable')) && !empty($_GET['module'])) {
    // Enable / disable a module.

    $zData = @mysql_fetch_assoc(mysql_query('SELECT moduleid, active FROM ' . TABLE_MODULES . ' WHERE moduleid = "' . $_GET['module'] . '"'));
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');
        lovd_showInfoTable('No such ID!', 'stop');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // What are we doing?
    if ($zData['active']) {
        $sAction = 'disable';
        $b = $_MODULES->disable($_GET['module']);
    } else {
        $sAction = 'enable';
        $b = $_MODULES->enable($_GET['module']);
    }

    // Write to log...
    if (!$b) {
        // FIXME; add error message somehow.
        lovd_writeLog('MySQL:Error', 'Module' . ucfirst($sAction), $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') failed to ' . $sAction . ' module ' . $zData['moduleid'] . "\n" . 'Error : Unknown');
    } else {
        lovd_writeLog('MySQL:Event', 'Module' . ucfirst($sAction), $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') successfully ' . $sAction . 'd module ' . $zData['moduleid']);
    }

    // Forward back to the view module page.
    header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view&view=' . $_GET['module'] . lovd_showSID(true));
    exit;





} elseif ($_GET['action'] == 'scan') {
    // Scan for new modules.
    if (!isset($_GET['sent'])) {
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_modules_scan', 'LOVD Setup - Scan for new modules');
        lovd_showInfoTable('Please note that this short procedure scans for modules in the modules directory. LOVD will then run the found modules to see if they respond properly.<BR><B>Please make sure that the modules directory only contains modules that you trust!</B> Modules can easily be used to introduce untrusted code into LOVD.', 'warning');
        print('      <FORM action="' . $_SERVER['PHP_SELF'] . '" method="get">' . "\n" .
              '        <INPUT type="hidden" name="action" value="' . $_GET['action'] . '">' . "\n" .
              '        <INPUT type="hidden" name="sent">' . "\n" .
              '        <INPUT type="submit" value="Next &gt;&gt;">' . "\n" .
              '      </FORM>' . "\n\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }



    // Read out modules directory...
    $hDir = @opendir(MODULE_PATH);
    if (!$hDir) {
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_modules_scan', 'LOVD Setup - Scan for new modules');
        lovd_showInfoTable('Could not open modules directory!', 'stop');
        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:Event', 'ModuleScan', 'New module installed: ' . $sModuleID . '/module.php returns "' . $aModule['name'] . '"');
        $aSuccess[] = $sModuleID;
    }

    // Print result of module scan to screen!
    $sFailed = '';
    $sSuccess = '';

    if (count($aFailed)) {
        $sFailed = '<SPAN style="color : #FF0000;">Failed installing "' . implode('".<BR>Failed installing "', $aFailed) . '".</SPAN><BR>';
    }
    if (count($aSuccess)) {
        $sSuccess = 'Successfully installed "' . implode('".<BR>Successfully installed "', $aSuccess) . '".<BR>';
    }

    // Thank the user...
    header('Refresh: ' . ($sSuccess? 3 : 5) . '; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view_all' . lovd_showSID(true));

    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_modules_scan', 'LOVD Setup - Scan for new modules');
    if ($sSuccess) {
        lovd_showInfoTable('Successfully installed new module' . (count($aSuccess) == 1? '' : 's') . '!<BR>' . $sSuccess .
                           ($sFailed? '<BR>Not installed due to errors (more info in the logs):<BR>' . $sFailed : ''), 'success');
    } elseif (!$sFailed) {
        lovd_showInfoTable('No new modules found to install!', 'information');
    } else {
        lovd_showInfoTable('Failed installing new modules! More information can be found in the error logs.<BR>' . $sFailed, 'stop');
    }

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





} elseif ($_GET['action'] == 'uninstall' && !empty($_GET['module'])) {
    // Uninstall a module.

    $zData = @mysql_fetch_assoc(mysql_query('SELECT moduleid FROM ' . TABLE_MODULES . ' WHERE moduleid = "' . $_GET['module'] . '"'));
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');
        lovd_showInfoTable('No such ID!', 'stop');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Disable module first! That'll make sure the module is not active in the inc-bot or so.
    $_MODULES->disable($_GET['module']);

    // The actual query.
    $sQ = 'DELETE FROM ' . TABLE_MODULES . ' WHERE moduleid = "' . $_GET['module'] . '"';
    $q = @mysql_query($sQ);
    if (!$q) {
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');
        lovd_dbFout('ModuleUninstall', $sQ, mysql_error());
    }

    // Write to log...
    lovd_writeLog('MySQL:Event', 'ModuleUninstall', $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') successfully uninstalled module ' . $_GET['module']);

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

    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_modules_manage', 'LOVD Setup - Manage installed modules');
    lovd_showInfoTable('Successfully uninstalled "' . stripslashes($_GET['module']) . '"!', 'success');
    require ROOT_PATH . 'inc-bot.php';
    exit;





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