<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2006-01-26
 * Modified    : 2009-03-10
 * For LOVD    : 2.0-17
 *
 * Access      : Public
 * Purpose     : Declare standard LOVD functions that are needed on every page.
 *
 * 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
 *
 *************/

if (!function_exists('array_combine')) {
    // array_combine is available from PHP 5.0.0. Redefine if necessary.
    function array_combine ($a1, $a2)
    {
        // Creates an array by using one array for keys and another for its values.
        if (!is_array($a1) || !is_array($a2) || !count($a1) || !count($a2) || count($a1) != count($a2)) {
            return false;
        }
        
        $aReturn = array();
        foreach ($a1 as $key => $val) {
            $aReturn[$val] = $a2[$key];
        }
        
        return $aReturn;
    }
}





if (!function_exists('file_get_contents')) {
    function file_get_contents ($sFile) {
        // Reads out the contents of a file in a string.
        $aFile = @file($sFile); // Won't bother with checking.
        if ($aFile) {
            $sFile = implode('', $aFile);
            return $sFile;
        }
        return false;
    }
}




if (!function_exists('mysql_real_escape_string')) {
    // mysql_real_escape_string is available from PHP 4.3.0. Redefine if necessary.
    function mysql_real_escape_string ($s, $db)
    {
        // Escapes special characters in a string for use in a SQL statement,
        // taking into account the current charset of the connection.

        // STUB: we're not really redefining the function here, we're just
        // settling for a less functional equivalent.
        return mysql_escape_string($s);
    }
}





function lovd_buildLinks (& $zData)
{
    // Builds the custom links in the $zData array.
    static $aLinks = array();

    if (!count($aLinks)) {
        // Retrieve Custom Link information from the database.
        // Backwards compatible with MySQL 4.0 and earlier; GROUP_CONCAT is available since MySQL 4.1.
        $qLinks = mysql_query('SELECT l.pattern_text, l.replace_text, c2l.colid FROM ' . TABLE_LINKS . ' AS l LEFT JOIN ' . TABLE_COLS2LINKS . ' AS c2l USING (linkid) WHERE l.active = 1 AND c2l.colid IS NOT NULL');

        // Loop through and build array.
        while ($r = mysql_fetch_row($qLinks)) {
            list($sPattern, $sReplace, $sColID) = $r;

            if (!isset($aLinks[$sColID])) {
                $aLinks[$sColID] = array();
            }

            // LOVD v.1.1.0 code:
            // $a_link[$val][]['patt'] = "/" . preg_replace("/\[[0-9]+\]/", "([A-Za-zÀ-ÖØ-öø-ÿ0-9 :;,._-]*)", $z_link['patt']) . "/";
            // $a_link[$val][count($a_link[$val])-1]['repl'] = ereg_replace("\[([0-9]+)\]","\\\\1",$z_link['repl']);
            // Replace [1] and others to the patterns to match these references.
            // 2007-04-04; 2.0-alpha-10
            // Added 'U' modifier to the pattern to prevent multiple patterns to collide.
            $sPattern = '/' . preg_replace('/\\\[[0-9]+\\\]/', '(.*)', preg_quote($sPattern, '/')) . '/U';
            $sReplace = preg_replace('/\[([0-9]+)\]/', "\$$1", $sReplace);

            $aLinks[$sColID][$sPattern] = $sReplace;
        }
    }



    // Actually replace the Custom Link patterns.
    $aCols = array_keys($zData);
    foreach ($aCols as $sCol) {
        if (isset($aLinks[$sCol])) {
            foreach ($aLinks[$sCol] as $sPattern => $sReplace) {
                $zData[$sCol] = preg_replace($sPattern, $sReplace, $zData[$sCol]);
            }
        }
    }
}





function lovd_calculateVersion ($sVersion)
{
    // Builds version formatted for string-comparing LOVD versions to determine
    // correct version order.

    // Slightly different preg_match pattern.
    if (preg_match('/^([1-9]\.[0-9](\.[0-9])?)(\-([0-9a-z-]{2,11}))?$/', $sVersion, $aVersion)) {
        $sReturn = str_pad(str_replace('.', '', $aVersion[1]), 4, '0');
        if (isset($aVersion[3])) {
            if (preg_match('/^(pre|dev)(\-([0-9]{2})([a-z])?)?$/', $aVersion[4], $aSub)) {
                $sReturn -= 3;
                if (isset($aSub[2])) {
                    // 2.0-dev-01 => 1997-010 (2.0-dev-01a => 1997-01a)
                    return $sReturn . '-' . $aSub[3] . (isset($aSub[4])? $aSub[4] : '0');
                } else {
                    // 2.0-dev => 1997-000
                    return $sReturn . '-000';
                }

            } elseif (preg_match('/^alpha(\-([0-9]{2})([a-z])?)?$/', $aVersion[4], $aSub)) {
                $sReturn -= 2;
                if (isset($aSub[1])) {
                    // 2.0-alpha-01 => 1998-010 (2.0-alpha-01a => 1998-01a)
                    return $sReturn . '-' . $aSub[2] . (isset($aSub[3])? $aSub[3] : '0');
                } else {
                    // 2.0-alpha => 1998-000
                    return $sReturn . '-000';
                }

            } elseif (preg_match('/^beta(\-([0-9]{2})([a-z])?)?$/', $aVersion[4], $aSub)) {
                $sReturn -= 1;
                if (isset($aSub[1])) {
                    // 2.0-beta-01 => 1999-010 (2.0-beta-01a => 1999-01a)
                    return $sReturn . '-' . $aSub[2] . (isset($aSub[3])? $aSub[3] : '0');
                } else {
                    // 2.0-beta => 1999-000
                    return $sReturn . '-000';
                }

            } elseif (preg_match('/^([0-9]{2})([a-z])?$/', $aVersion[4], $aSub)) {
                if (isset($aSub[2])) {
                    // 2.0-01a => 2000-01a
                    return $sReturn . '-' . $aSub[1] . $aSub[2];
                } else {
                    // 2.0-01 => 2000-010
                    return $sReturn . '-' . $aSub[1] . '0';
                }
            }
        }

        // 2.0 => 2000-000
        return $sReturn . '-000';

    } else {
        return false;
    }
}





function lovd_cleanDirName ($s)
{
    // Cleans a given path by resolving a relative path.
    if (!is_string($s)) {
        // No input.
        return false;
    }

    // Clean up the pwd; remove '//'
    $s = str_replace('//', '/', $s);
    // Clean up the pwd; remove '/./'
    $s = preg_replace('/\/\.\//', '/', $s);
    // Clean up the pwd; remove '/dir/../'
    $s = preg_replace('/\/[^\/]+\/\.\.\//', '/', $s);

    if (preg_match('/\/(\.)?\.\//', $s)) {
        // Still not clean... Pff...
        $s = lovd_cleanDirName($s);
    }

    return $s;
}





function lovd_dbFout ($sError, $sQ, $sMessage, $bHalt = true)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Halts the system because of a Database Error.
    global $_AUTH;

    // Put in error log.
    $sError = $_SERVER['PHP_SELF'] . ' returned ' . $sError . ' error for ' . $_AUTH['username'] . "\n" .
              'Query : ' . $sQ . "\n" .
              'Error : ' . $sMessage;
    // 2009-02-16; 2.0-16; Start logging to the database; nobody checks the error.log file anyway.
    if ($bHalt) {
        lovd_displayError('Query', $sError);
    } else {
        lovd_writeLog('MySQL:Error', 'Query', $sError);
    }
}





function lovd_displayError ($sError, $sMessage, $bFatal = true)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Writes an error message to the errorlog and displays the same message on
    // screen for the user.
    global $_SETT, $_CONF, $_STAT;

    // Get top; if not included before, this error becomes fatal.
    if (!defined('_INC_TOP_INCLUDED_') && !defined('_INC_TOP_CLEAN_INCLUDED_')) {
        if (is_readable('inc-top-clean.php')) {
            $sFile = 'inc-top-clean.php';
        } elseif (is_readable(ROOT_PATH . 'inc-top-clean.php')) {
            $sFile = ROOT_PATH . 'inc-top-clean.php';
        } elseif (is_readable('inc-top.php')) {
            $sFile = 'inc-top.php';
        } else {
            $sFile = ROOT_PATH . 'inc-top.php';
        }

        require $sFile;
        $bFatal = true;
    }

    // Write to log file.
    if (substr_count($sError, '|')) {
        list($sErrorLog, $sError) = explode('|', $sError, 2);
    } elseif ($sError != 'Init') {
        $sErrorLog = 'MySQL:Error';
    } else {
        $sErrorLog = 'error';
    }
    $bLog = @lovd_writeLog($sErrorLog, $sError, $sMessage);

    // Display error.
    print("\n" . '
      <TABLE border="0" cellpadding="0" cellspacing="0" align="center" width="900" class="error">
        <TR>
          <TH>Error: ' . $sError . ($bLog? ' (Logged)' : '') . '</TH></TR>
        <TR>
          <TD>' . str_replace("\n", '<BR>', $sMessage) . '</TD></TR></TABLE>' . "\n\n");

    // If fatal, get bottom and exit.
    if ($bFatal == true) {
        if (defined('_INC_TOP_INCLUDED_')) {
            if (is_readable('inc-bot.php')) {
                require 'inc-bot.php';
            } else {
                require ROOT_PATH . 'inc-bot.php';
            }
        } elseif (defined('_INC_TOP_CLEAN_INCLUDED_')) {
            if (is_readable('inc-bot-clean.php')) {
                require 'inc-bot-clean.php';
            } else {
                require ROOT_PATH . 'inc-bot-clean.php';
            }
        }
        exit;
    }
}





function lovd_getExternalSource ($sSource, $nID = false, $bHTML = false)
{
    // Retrieves URL for external source and returns it, including the ID.
    static $aSources = array();
    if (!count($aSources)) {
        $q = mysql_query('SELECT * FROM ' . TABLE_SOURCES);
        while ($z = mysql_fetch_assoc($q)) {
            $aSources[$z['source']] = $z['url'];
        }
    }
    
    if (array_key_exists($sSource, $aSources)) {
        $s = $aSources[$sSource];
        if ($bHTML) {
            $s = str_replace('&', '&amp;', $s);
        }
        if ($nID !== false) {
            // ID provided; include it in the URL.
            $s = str_replace('{{ ID }}', $nID, $s);
        }
        return $s;
    }

    return false;
}





function lovd_getGeneList ()
{
    // Gets the list of genes (symbols only), to prevent repeated queries.
    static $aGenes = array();
    if (!count($aGenes)) {
        $q = mysql_query('SELECT symbol FROM ' . TABLE_DBS . ' ORDER BY symbol');
        while ($r = mysql_fetch_row($q)) {
            $aGenes[] = $r[0];
        }
    }

    return $aGenes;
}





function lovd_getProjectFile ()
{
    // Gets project file name (file name including possible project subdirectory).
    // 2008-11-14; 2.-14; #23; Added rtrim to prevent double slashes in $sDir when we're in the root directory, causing #23.
    $sDir = rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/';
    $sProjectDir = lovd_cleanDirName($sDir . ROOT_PATH);
    $sDir = substr($sDir, strlen($sProjectDir) - 1);
    return $sDir . basename($_SERVER['PHP_SELF']);
}





function lovd_includeJS ($sFile, $nPrefix = 3)
{
    // Searches for and includes a .js include file.
    
    // 2009-03-09; 2.0-17; Added check on http, to include js file over HTTP.
    if (substr($sFile, 0, 4) != 'http' && (!file_exists($sFile) || !is_readable($sFile))) {
        return false;
    }
    
    $sPrefix = str_repeat('  ', $nPrefix);
    print($sPrefix . '<SCRIPT type="text/javascript" src="' . $sFile . '"></SCRIPT>' . "\n");
    return true;
}





function lovd_isCurator ($sGene = '')
{
    // Returns true if current user is allowed to act as a curator for the given
    // gene. Returns true by default for any user higher than LEVEL_CURATOR.
    global $_AUTH;

    if (!HAS_AUTH) {
        return false;
    }

    if (!$sGene || !is_string($sGene)) {
        return ($_AUTH['level'] >= LEVEL_MANAGER);
    }

    if ($_AUTH['level'] >= LEVEL_MANAGER || in_array($sGene, $_AUTH['curates'])) {
        return true;
    } else {
        return false;
    }
}





function lovd_magicQuote (&$var)
{
    // Mimics PHP's GPC magic quoting. Is only called from inc-init when magic
    // quoting is turned off.

    if (is_array($var)) {
        foreach ($var as $key => $val) {
            if (is_array($val)) {
                lovd_magicQuote($var[$key]);
            } else {
                $var[$key] = str_replace(array('\r', '\n'), array("\r", "\n"), mysql_real_escape_string($val));
            }
        }
    } else {
        $var = str_replace(array('\r', '\n'), array("\r", "\n"), mysql_real_escape_string($val));
    }
}





function lovd_magicQuoteAll ()
{
    // Calls lovd_magicQuote() on all needed variables.

    lovd_magicQuote($_GET);
    lovd_magicQuote($_POST);
    lovd_magicQuote($_COOKIE);
}





function lovd_magicUnquote (&$var)
{
    // Counterpart of the magicQuote() function. Basically for printing correct
    // values on screen or in email notifications.

    if (is_array($var)) {
        foreach ($var as $key => $val) {
            if (is_array($val)) {
                lovd_magicUnquote($var[$key]);
            } else {
                $var[$key] = stripslashes($val);
            }
        }
    } else {
        $var = stripslashes($var);
    }
}





function lovd_magicUnquoteAll ()
{
    // Calls lovd_magicUnquote() on all needed variables.

    lovd_magicUnquote($_GET);
    lovd_magicUnquote($_POST);
    lovd_magicUnquote($_COOKIE);
}





function lovd_printGeneFooter ()
{
    // Prints the current gene's footer, if any is stored.
    global $_SETT;
    if (!empty($_SESSION['currdb']) && !empty($_SETT['currdb']['footer'])) {
        print('      <DIV style="text-align : ' . $_SETT['notes_align'][$_SETT['currdb']['footer_align']] . ';">' . $_SETT['currdb']['footer'] . '</DIV>' . "\n\n");
    }
}





function lovd_printGeneHeader ()
{
    // Prints the current gene's header, if any is stored.
    global $_SETT;
    if (!empty($_SESSION['currdb']) && !empty($_SETT['currdb']['header'])) {
        print('      <DIV style="text-align : ' . $_SETT['notes_align'][$_SETT['currdb']['header_align']] . ';">' . $_SETT['currdb']['header'] . '</DIV>' . "\n\n");
    }
}





function lovd_printHeader ($sImg, $sAlt)
{
    // Prints the page's header.
    print('      <IMG src="' . ROOT_PATH . 'gfx/header_' . $sImg . '.png" alt="' . $sAlt . '" width="500" height="30"><BR>' . "\n" .
          '      <BR>' . "\n");
}





function lovd_requireAUTH ($nLevel = 0)
{
    // Creates friendly output message if $_AUTH does not exist (or level too
    // low), and exits.
    // $_AUTH is for authorization; $_SETT is needed for the user levels;
    // $_CONF and $_STAT are for the top and bottom includes.
    global $_AUTH, $_SETT, $_CONF, $_STAT;

    $aKeys = array_keys($_SETT['user_levels']);
    if ($nLevel !== 0 && !in_array($nLevel, $aKeys)) {
        lovd_writeLog('MySQL:Error', 'LibInit', $_SERVER['PHP_SELF'] . ' returned lovd_requireAUTH nLevel error (' . $nLevel . ')');
        $nLevel = max($aKeys);
    }

    // $nLevel is now 0 ($_AUTH required) or taken
    // from the levels list (HAS_AUTH required).
    if ((!$nLevel && !$_AUTH) || ($nLevel && (!HAS_AUTH || $_AUTH['level'] < $nLevel))) {
        if (!defined('_INC_TOP_INCLUDED_')) {
            if (is_readable('inc-top.php')) {
                require 'inc-top.php';
            } else {
                require ROOT_PATH . 'inc-top.php';
            }
        }

        $sMessage = 'To access this area, you need ' . (!$nLevel? 'to <A href="account_login.php">log in</A>.' : ($nLevel == max($aKeys)? '' : 'at least ') . $_SETT['user_levels'][$nLevel] . ' clearance.');
        if (lovd_getProjectFile() == '/submit.php') {
            $sMessage .= '<BR>If you are not registered as a submitter, please <A href="submitters.php?action=register">do so here</A>.';
        }
        lovd_showInfoTable($sMessage, 'stop');

        if (defined('_INC_TOP_INCLUDED_')) {
            if (is_readable('inc-bot.php')) {
                require 'inc-bot.php';
            } else {
                require ROOT_PATH . 'inc-bot.php';
            }
        } elseif (defined('_INC_TOP_CLEAN_INCLUDED_')) {
            if (is_readable('inc-bot-clean.php')) {
                require 'inc-bot-clean.php';
            } else {
                require ROOT_PATH . 'inc-bot-clean.php';
            }
        }
        exit;
    }
}





function lovd_shortenString ($s, $l)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Shortens string nicely to a given length.
    if (strlen($s) > $l) {
        $s = substr($s, 0, $l - 3) . '...';
    }
    return $s;
}





function lovd_showInfoTable ($sMessage, $sType = 'information', $sWidth = '100%', $sHref = '')
{
    $aTypes =
             array(
                    'information' => 'Information',
                    'question' => 'Question',
                    'save' => 'Save',
                    'stop' => 'Stop!',
                    'success' => 'Success!',
                    'warning' => 'Warning',
                  );

    if (!array_key_exists($sType, $aTypes)) {
        $sType = 'information';
    }

    if (!preg_match('/^\d+%?$/', $sWidth)) {
        $sWidth = '100%';
    }

    print('      <TABLE border="0" cellpadding="2" cellspacing="0" width="' . $sWidth . '" class="info"' . (!empty($sHref)? ' style="cursor : pointer; cursor : hand;" onclick="window.location.href=\'' . $sHref . '\';"' : '') . '>' . "\n" .
          '        <TR>' . "\n" .
          '          <TD valign="top" align="center" width="40"><IMG src="' . ROOT_PATH . 'gfx/lovd_' . $sType . '.png" alt="' . $aTypes[$sType] . '" title="' . $aTypes[$sType] . '" width="32" height="32" hspace="4" vspace="4"></TD>' . "\n" .
          '          <TD valign="middle">' . $sMessage . '</TD></TR></TABLE><BR>' . "\n\n");
}





function lovd_showSID ($bAmp = false, $bHTML = false)
{
    // Shows SID if requested (to be added to URL's).
    global $_CONF;
    if (isset($_CONF['use_cookies']) && !$_CONF['use_cookies']) {
        // Show SID as requested.
        return ($bAmp? ($bHTML? '&amp;' : '&') : '?') . SID;
    }
    return false;
}





function lovd_switchDB ()
{
    // Outputs the HTML to allow user to switch genes.
    // $_AUTH is for authorization; $_SETT, $_CONF and $_STAT are for the top
    // and bottom includes.
    global $_AUTH, $_SETT, $_CONF, $_STAT;

    $qGenes = mysql_query('SELECT symbol, gene FROM ' . TABLE_DBS . ' ORDER BY symbol');
    $nGenes = mysql_num_rows($qGenes);

    if (!defined('_INC_TOP_INCLUDED_') && $nGenes == 1) {
        // Just one gene, redirect to the gene's homepage.
        list($_SESSION['currdb']) = mysql_fetch_row($qGenes);
        header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?select_db=' . $_SESSION['currdb'] . lovd_showSID(true));
        exit;
    }

    // Because we want the right menu item to be selected.
    $_GET['action'] = 'switch_db';

    if (!defined('_INC_TOP_INCLUDED_')) {
        if (is_readable('inc-top.php')) {
            require 'inc-top.php';
        } else {
            require ROOT_PATH . 'inc-top.php';
        }
        lovd_printHeader('home_select', 'Home - Select gene database');
    }

    if (!$nGenes) {
        print('      There is currently no gene configured in LOVD yet.<BR>' . "\n\n");
        if (HAS_AUTH && $_AUTH['level'] >= LEVEL_MANAGER) {
            print('      <BR>' . "\n\n");
            lovd_showInfoTable('Go here to <A href="setup_genes.php?action=create">create new genes in LOVD</A>.');
        }
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    print('      Please select a gene database:<BR>' . "\n" .
          '      <FORM action="' . $_SERVER['PHP_SELF'] . '" id="SelectGeneDB" method="get">' . "\n" .
          '        <SELECT name="select_db" onchange="document.getElementById(\'SelectGeneDB\').submit();">' . "\n");
    while ($zGenes = mysql_fetch_assoc($qGenes)) {
        print('          <OPTION value="' . $zGenes['symbol'] . '"' . ($_SESSION['currdb'] == $zGenes['symbol']? ' selected' : '') . '>' . $zGenes['symbol'] . ' (' . $zGenes['gene'] . ')</OPTION>' . "\n");
    }
    print('        </SELECT><BR>' . "\n" .
          '        <INPUT type="submit" value="Select gene database">' . "\n" .
          '      </FORM>' . "\n\n");

    if ($nGenes == 1) {
        // Just one gene, redirect to the gene's homepage.
        print('      <SCRIPT type="text/javascript">' . "\n" .
              '        <!--' . "\n" .
              '        document.forms[0].submit();' . "\n" .
              '        // -->' . "\n" .
              '      </SCRIPT>' . "\n\n");
    }

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





function lovd_validateIP ($sRange, $sIP)
{
    // Checks if a given IP address matches a given IP range.
    $aRange = split('[;,]', $sRange);
    $b = false;
    foreach ($aRange as $val) {
        if ($val == '*' || $val == $sIP) {
            $b = true;
            break;
        }

        // Break pattern apart.
        $aIPRef = explode('.', $val);
        $aIP    = explode('.', $sIP);

        $bPart = true;
        foreach ($aIPRef as $nSub => $sSub) {
            if ($sSub == '*' || $sSub == $aIP[$nSub]) {
                // So far, so good.
                continue;
            }

            if (preg_match('/^([0-9]{1,3})\-([0-9]{1,3})$/', $sSub, $aRegs)) {
                // A range is specified.
                $bPart = ($aIP[$nSub] >= $aRegs[1] && $aIP[$nSub] <= $aRegs[2]);
                if (!$bPart) {
                    break;
                }

            } else {
                $bPart = false;
                // 2009-03-10; 2.0-17; This break should be here, otherwise ranges will be falsely accepted in some cases.
                break;
            }            
        }
        $b = $bPart;
    }
    return $b;
}





function lovd_viewNavigation ($sBody, $nPrefix = 3)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Displays navigation table to the screen with given contents, accepting a
    // number of settings.

    // Spaces prepended to HTML code for proper alignment.
    $sPrefix = str_repeat('  ', $nPrefix);

    print($sPrefix . '<TABLE border="0" cellpadding="0" cellspacing="0" class="navigation">' . "\n");

    print($sPrefix . '  <TR align="center">' . "\n" .
          $sPrefix . '    <TD>' . $sBody . '</TD></TR></TABLE>'. "\n\n");
}





function lovd_writeLog ($sLog, $sError, $sMessage)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Writes timestamps and messages to given logfiles or into database.

    if (substr($sLog, 0, 6) == 'MySQL:') {
        // Log into MySQL.
        $sLog = mysql_real_escape_string(substr(strtolower($sLog), 6));
        $sMessage = mysql_real_escape_string($sMessage);

        // Timestamp, serves as an unique identifier.
        $aTime = explode(' ', microtime());
        $sTime = substr($aTime[0], 2, -2);

        // Insert new line in logs table.
        $sQ = 'INSERT INTO ' . TABLE_LOGS . ' VALUES ("' . $sLog . '", NOW(), ' . $sTime . ', "' . $sError . '", "' . $sMessage . '")';
        $q = @mysql_query($sQ);
        if (!$q) {
            // Can't update logs table, this is not fatal.
            return lovd_writeLog('error', 'WriteLogToDB', 'Can\'t log to ' . $sLog . ' (' . $sError . '): ' . mysql_error() . "\n" . str_pad($sError, 14) . str_replace("\n", "\n" . str_repeat(' ', 14), $sMessage));
        } else {
            return true;
        }
    }



    $sLog = ROOT_PATH . $sLog . '.log';

    if (!file_exists($sLog) || !is_writable($sLog)) {
        return false;
    }

    if ($fLog = @fopen($sLog, 'a')) {
        if (@fwrite($fLog, date('Y-m-d H:i:s') . '  ' . str_pad($sError, 14) . str_replace("\n", "\n" . str_repeat(' ', 35), $sMessage) . "\n")) {
            fclose($fLog);
            return true;
        } else {
            fclose($fLog);
            return false;
        }
    }
}
?>
