<?php

/**
 * Project:
 * Contenido Content Management System
 *
 * Description:
 * Contenido Database Functions
 *
 * Requirements:
 * @con_php_req 5.0
 *
 *
 * @package    Contenido Backend includes
 * @version    1.3.1
 * @author     Timo A. Hummel
 * @copyright  four for business AG <www.4fb.de>
 * @license    http://www.contenido.org/license/LIZENZ.txt
 * @link       http://www.4fb.de
 * @link       http://www.contenido.org
 * @since      file available since contenido release <= 4.6
 *
 * {@internal
 *   created 2003-06-04
 *   modified 2008-06-25, Frederic Schneider, add security fix
 *   modified 2008-07-11, Dominik Ziegler, removed deprecated functions
 *   modified 2011-05-17, Murat Purc, documented functions and some optimizations
 *
 *   $Id$:
 * }}
 *
 */
if (!defined('CON_FRAMEWORK')) {
    die('Illegal call');
}

/**
 * Returns existing indexes of a specific table.
 * @param   DB_ConLite  $db
 * @param   string  $table
 * @return  array  Assoziative array where the key and the value is the index name
 */
function dbGetIndexes($db, $table) {
    if (!is_object($db)) {
        return false;
    }

    $sql = "SHOW INDEX FROM " . Contenido_Security::escapeDB($table, $db);
    $db->query($sql);

    $indexes = array();

    while ($db->next_record()) {
        $indexes[$db->f("Key_name")] = $db->f("Key_name");
    }

    return ($indexes);
}

/**
 * Updates a specific table. Used e. g. by Contenido setup to create or update
 * tables.
 * Function logic:
 * 1 .) Check, if the table exists
 * 2a.) If not, create it with the field specification, exit
 * 2b.) If the table exists, check, if the field exist
 * 3 .) If not, try to find the field using previous names (if specified in $field like "name1,name2")
 * 4a.) If the field hasn't been found, create the field as specified, exit
 * 4b.) If the field has been found using a previous name (if specified) rename the column to $field
 * 5 .) As the field has been found, check, if the field's type is matching
 * 5a.) If the type is matching, exit
 * 5b.) If the field's content type is not matching, try to convert first (e.g. string to int
 *      or int to string), then use the upgrade statement if applicable
 *
 * Note about the upgrade statement:
 *  - the code must be eval'able
 *  - the code needs to read $oldVal (old field value) and needs to set $newVal (value to which the field will be set)
 *  - $oldVal might be empty if the field didn't exist
 *  - $tableValues['fieldname'] contains the already existing values
 *
 * @param  DB_ConLite  $db  Database instance
 * @param  string  $table  Name of table to create/update
 * @param  string  $field  Name of field to create/update
 * @param  string  $type  Data type of field. Feasible values are all possible data types
 *                        e. g. int(10), varchar(32), datetime, varchar(255), text, tinyint(1)
 * @param  string  $null  Parameter to forbid null values, feasible values "", "NULL" or "YES"
 *                        where "NULL" or "YES" allows null values and "" doesn't
 * @param  string  $key   The field will be added as a primary key, if value is "PRI",
 *                        otherwhise the value should be empty ""
 * @param  string  $default  The default value for the field. Feasible is each possible
 *                           value depending on passed $type
 * @param  string  $extra  Additional info for the field, e. g. "auto_increment", if the
 *                         field should have the AUTO_INCREMENT attribute and empty otherwise.
 * @param  string  $upgradeStatement  NOT USED AT THE MOMENT
 * @param  bool  $bRemoveIndexes  Flag to remove all indexes
 * @return  bool
 */
function dbUpgradeTable($db, $table, $field, $type, $null, $key, $default, $extra, $upgradeStatement, $bRemoveIndexes = false) {
    global $columnCache;
    global $tableCache;

    if (!is_object($db)) {
        return false;
    }

    $bDebug = false;
    if (($table == 'pica_alloc') && ($field == 'parentid')) {
        $bDebug = true;
    }

    // Parameter checking for $null. If parameter is "" or "NULL" or "YES", we 
    // know that we want the colum to forbid null entries.
    if ($null == "NULL" || $null == "YES") {
        $parameter['NULL'] = "NULL";
        $null = "YES";
    } else {
        $parameter['NULL'] = "NOT NULL";
        $null = "";
    }

    // Parameter checking for $key. If parameter is "" or "NULL" or "YES", we 
    // know that we want the primary key.
    if ($key == "PRI") {
        $parameter['KEY'] = "PRIMARY KEY";
    } else {
        $parameter['KEY'] = "";
    }

    // Parameter check for $default. If set, create a default value
    if ($default != "") {
        if (((strpos($type, 'timestamp') !== FALSE) && ($default != '')) || ($default == 'NULL')) {
            $parameter['DEFAULT'] = "DEFAULT " . Contenido_Security::escapeDB($default, $db);
        } else {
            $parameter['DEFAULT'] = "DEFAULT '" . Contenido_Security::escapeDB($default, $db) . "'";
        }
    } else {
        $parameter['DEFAULT'] = '';
    }

    if (!dbTableExists($db, $table)) {
        $createTable = "  CREATE TABLE " . Contenido_Security::escapeDB($table, $db) . " (" . Contenido_Security::escapeDB($field, $db) . " $type " . $parameter['NULL'] . " " . $parameter['DEFAULT'] . " " . $parameter['KEY'] . ")  ENGINE = MYISAM";
        $db->query($createTable);
        $tableCache[] = $table;
        return true;
    }

    // Remove auto_increment
    $structure = dbGetColumns($db, $table);

    if (isset($structure[$field]) && !empty($structure[$field]["Extra"]) && $structure[$field]["Extra"] == "auto_increment") {
        if ($structure[$field]['NULL'] == "") {
            $structure[$field]['NULL'] = "NOT NULL";
        }
        $alterField = "ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . " CHANGE COLUMN " . Contenido_Security::escapeDB($field, $db) . " " . Contenido_Security::escapeDB($field, $db) . "
                       " . Contenido_Security::escapeDB($type, $db) . " " . $structure[$field]['NULL'] . " " . $structure[$field]['DEFAULT'] . " " . $structure[$field]['KEY'];

        $db->query($alterField);
    }

    // Remove all keys, as they are being recreated during an upgrade
    if ($bRemoveIndexes == true) {
        $indexes = dbGetIndexes($db, $table);

        foreach ($indexes as $index) {
            if ($index == "PRIMARY") {
                if ($structure[$field]['Key'] == "PRI") {
                    $sql = "   ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . " DROP PRIMARY KEY";
                } else {
                    $sql = "";
                }
            } else {
                $sql = "   ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . "' DROP INDEX " . Contenido_Security::escapeDB($index, $db);
            }

            $db->query($sql);
            unset($columnCache[$table]);
        }
    }

    $structure = dbGetColumns($db, $table);

    // If $field contains "," previous names has been specified; separate from $field
    $sepPos = strpos($field, ",");
    if ($sepPos === false) {
        $previousName = "";
    } else {
        $previousName = substr($field, $sepPos + 1);
        $field = substr($field, 0, $sepPos);
    }

    if (!array_key_exists($field, $structure)) {
        // HerrB: Search field using $previousName
        $blnFound = false;
        if ($previousName != "") {
            $arrPreviousName = explode(",", $previousName);
            foreach ($arrPreviousName as $strPrevious) {
                // Maybe someone has used field1, field2, ..., trim spaces
                $strPrevious = trim($strPrevious);
                if (array_key_exists($strPrevious, $structure)) {
                    $blnFound = true;
                    break;
                }
            }
        }

        if ($blnFound) {
            // Rename column, update array, proceed
            if ($structure[$strPrevious]['Null'] == 'YES') {
                $alterField = "  ALTER TABLE `" . Contenido_Securiy::escapeDB($table, $db) . "` CHANGE COLUMN `" . Contenido_Security::escapeDB($strPrevious, $db) . "` `" . Contenido_Security::escapeDB($field, $db) . "`
                " . $structure[$strPrevious]['Type'] . " DEFAULT '" . $structure[$strPrevious]['Default'] . "'";
            } else {
                $alterField = "  ALTER TABLE `" . Contenido_Security::escapeDB($table, $db) . "` CHANGE COLUMN `" . Contenido_Security::escapeDB($strPrevious, $db) . "` `" . Contenido_Security::escapeDB($field, $db) . "`
                " . $structure[$strPrevious]['Type'] . " NOT NULL DEFAULT '" . $structure[$strPrevious]['Default'] . "'";
            }

            $db->query($alterField);

            $columnCache[$table] = "";
            $structure = dbGetColumns($db, $table);
        } else {
            switch ($type) {
                case "datetime":
                    if ($parameter['DEFAULT'] == "DEFAULT '0000-00-00 00:00:00'") {
                        $parameter['DEFAULT'] = "DEFAULT '1000-01-01 00:00:00'";
                    }
                    break;
                case "date":
                    if ($parameter['DEFAULT'] == "DEFAULT '0000-00-00'") {
                        $parameter['DEFAULT'] = "DEFAULT '1000-01-01'";
                    }
                    break;
            }

            $createField = "  ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . " ADD COLUMN " . Contenido_Security::escapeDB($field, $db) . " " . Contenido_Security::escapeDB($type, $db) . "
            " . $parameter['NULL'] . " " . $parameter['DEFAULT'] . " " . $parameter['KEY'];
            $db->query($createField);
            $sDebugData = sprintf("%s:%s:ErrorNo. %s:%s\n", $createField, $parameter['DEFAULT'], $db->getErrorNumber(), $db->getErrorMessage());

            if ($bDebug) {
                file_put_contents('../data/logs/setup_queries.txt', $sDebugData, FILE_APPEND);
                echo 'createField:' . $createField . '<br />';
            }
            $columnCache[$table] = "";
            return true;
        }
    }

    $structure = dbGetColumns($db, $table);

    // Third check: Compare field properties
    if (($structure[$field]['Type'] != $type) || ($structure[$field]['Null'] != $null) || ($structure[$field]['Key'] != $key) || ($structure[$field]['Default'] != $default) || ($structure[$field]['Extra'] != $extra)) {

        if ($structure[$field]['Key'] == "PRI") {
            $alterField = "  ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . " ADD PRIMARY KEY ('" . Contenido_Security::escapeDB($field, $db) . "') ";
        } else {
            if($type == "datetime" || $type == "date") {
                $db->query("SET SESSION sql_mode='ALLOW_INVALID_DATES'");
            }
            $alterField = "  ALTER TABLE " . Contenido_Security::escapeDB($table, $db) . " CHANGE COLUMN $field $field $type " . $parameter['NULL'] . " " . $parameter['DEFAULT'] . " " . $parameter['KEY'];
        }

        $db->query($alterField);
        if ($bDebug) {
            $sDebugData = sprintf("%s:ErrorNo. %s:%s\n", $alterField, $db->getErrorNumber(), $db->getErrorMessage());
            file_put_contents('../data/logs/setup_queries.txt', $sDebugData, FILE_APPEND);
            echo 'updateField:' . $alterField . '<br />';
        }
        $columnCache[$table] = "";
    }

    return true;
}

/**
 * Checks, if passed table exists in the database
 * @param   DB_ConLite  $db
 * @param   string  $table
 * @return  bool
 */
function dbTableExists($db, $table) {
    global $tableCache;

    if (!is_object($db)) {
        return false;
    }

    if (!is_array($tableCache)) {
        $sql = "SHOW TABLES";
        $db->query($sql);

        $tableCache = array();

        while ($db->next_record()) {
            $tableCache[] = $db->f(0);
        }
    }

    if (in_array($table, $tableCache)) {
        return true;
    } else {
        return false;
    }
}

/**
 * Returns the column structure of a table
 * @param   DB_ConLite  $db
 * @param   string  $table
 * @return  array|bool  Either assoziative column array or false
 */
function dbGetColumns($db, $table) {
    global $columnCache;

    if (!is_object($db)) {
        return false;
    }

    if (isset($columnCache[$table]) && is_array($columnCache[$table])) {
        return $columnCache[$table];
    }

    $sql = "SHOW COLUMNS FROM " . Contenido_Security::escapeDB($table, $db);
    $db->query($sql);

    $structure = array();

    while ($db->next_record()) {
        $structure[$db->f("Field")] = $db->toArray();
    }

    $columnCache[$table] = $structure;

    return $structure;
}

/**
 * Returns the primary key column of a table
 * @param   DB_ConLite  $db
 * @param   string  $table
 * @return  string
 */
function dbGetPrimaryKeyName($db, $table) {
    $sReturn = "";
    $structure = dbGetColumns($db, $table);

    if (is_array($structure)) {
        foreach ($structure as $mykey => $value) {
            if ($value['Key'] == "PRI") {
                $sReturn = $mykey;
            }
        }
    }

    return $sReturn;
}

/**
 * Updates the sequence table, stores the highest primary key value of a table in it.
 * Retrieves the primary key field of the table, retrieves the highes value and
 * saves the value in the sequence table.
 *
 * @param   string  $sequencetable  Name of sequence table
 * @param   string  $table  Name of table
 * @param   DB_ConLite|bool  $db  Database instance or false
 */
function dbUpdateSequence($sequencetable, $table, $db = false) {
    if ($db === false) {
        $bClose = true;
        $db = new DB_Upgrade;
    } else {
        $bClose = false;
    }

    $key = dbGetPrimaryKeyName($db, $table);

    if ($key != "" && $key != $sequencetable) {
        $sql = "SELECT " . Contenido_Security::escapeDB($key, $db) . " FROM " . Contenido_Security::escapeDB($table, $db) . " ORDER BY " . Contenido_Security::escapeDB($key, $db) . " DESC";
        $db->query($sql);

        if ($db->next_record()) {
            $highestval = $db->f($key);
        } else {
            $highestval = 0;
        }

        $sql = "DELETE FROM " . Contenido_Security::escapeDB($sequencetable, $db) . " WHERE seq_name = '" . Contenido_Security::escapeDB($table, $db) . "'";
        $db->query($sql);

        $sql = "INSERT INTO " . Contenido_Security::escapeDB($sequencetable, $db) . " SET seq_name = '" . Contenido_Security::escapeDB($table, $db) . "', nextid = '" . Contenido_Security::toInteger($highestval) . "'";
        $db->query($sql);
    }

    if ($bClose == true) {
        $db->close();
    }
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbDumpStructure($db, $table, $return = false) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbDumpArea($db, $id) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbDumpAreasAsArray($arrayname, $db) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbDumpNavSub($arrayname, $db, $nextidarea) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbInsertData($table, $data) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbDumpData($table) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

/**
 * @deprecated
 * @since 2008-07-11
 */
function dbUpgradeData($table, $valuesArray) {
    /* this function is deprecated since Contenido 4.8.7 - 2008-07-11 */
    return;
}

?>