Location: PHPKode > projects > XMBForum > XMB-1.9.11.09/upgrade/upgrade.lib.php
<?php
/**
 * eXtreme Message Board
 * XMB 1.9.11
 *
 * Developed And Maintained By The XMB Group
 * Copyright (c) 2001-2010, The XMB Group
 * http://www.xmbforum.com
 *
 * Sponsored By iEntry, Inc.
 * http://www.ientry.com
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 **/

class Upgrade {
    var $db;
    var $tablepre;
    var $tables;
    var $tc;

    var $xmb_tables = array(
        'attachments',
        'banned',
        'buddys',
        'captchaimages',
        'favorites',
        'forums',
        'lang_base',
        'lang_keys',
        'lang_text',
        'logs',
        'members',
        'posts',
        'ranks',
        'restricted',
        'settings',
        'smilies',
        'templates',
        'themes',
        'threads',
        'u2u',
        'whosonline',
        'words',
        'vote_desc',
        'vote_results',
        'vote_voters'
    );

    function Upgrade(&$db, $file='', $tablepre) {
        if ($file == '') {
            return null;
        }

        $this->db = &$db;
        $this->tablepre = $tablepre;
        $c = $this->fileGetContents($file) or die('Could not open '.$file);
        $this->tables = unserialize($c) or die('Syntax Error: Could not unserialize upgrade file');
        return 1;
    }

    function fileGetContents($filename) {
        $stream = @fopen($filename, 'r');
        if (!$stream) {
            return false;
        } else {
            $c = fread($stream, filesize($filename));
            fclose($stream);
            return $c;
        }
    }

    function getTablesByTablepre($tablepre=null) {
        if ($tablepre === null) {
            $tablepre = $this->tablepre;
        }

        $tbl = array();
        $q = $this->db->query("SHOW TABLES LIKE '".str_replace('_', '\_', $tablepre)."%'");
        while($t = $this->db->fetch_array($q)) {
            $t = array_values($t);
            if (in_array(str_replace($tablepre, '', $t[0]), $this->xmb_tables)) {
                $tbl[] = $t[0];
            }
        }
        return $tbl;
    }

    function loadTables($tables) {
        foreach($tables as $k=>$t) {
            $this->loadTable($t);
        }
        return true;
    }


    function decode_keylen(& $keydata) {
    // we take a piece of data that looks like this: (`foo` (num))
        // find the second (. Return if there isn't one
        $sp = strpos($keydata, "(", 1);
        if ($sp === false) {
            return '';
        }
        $keylen = str_replace(array('(', ')', ' ', ','), '', substr($keydata, $sp));
        $keydata = str_replace(array('(', ')', ' ', '`', ','), '', substr($keydata, 0, $sp));
        return $keylen;
    }

    function loadTable($table) {
        $q = $this->db->query("SHOW CREATE TABLE `$table`");
        $tbl = $this->db->fetch_array($q);
        $tbl = $tbl['Create Table'];
        // now, to extract all of this and save it :|
        $tbl = explode("\n", $tbl);
        $ct = count($tbl);
        unset($tbl[0]); // CREATE TABLE `table` (
        unset($tbl[$ct-1]); // TYPE = MyISAM
        $cols = array();
        $indices = array();
        foreach($tbl as $line=>$data) {
            $data = trim($data);
            if (strpos($data, 'PRIMARY KEY') !== 0 && strpos($data, 'KEY') !== 0) {
                // we have a column
                if (strpos($data, '`') === 0) {
                    // we have a col for sure
                    preg_match_all('#`([A-Za-z0-9_]+)` (VARCHAR|TINYINT|TEXT|DATE|SMALLINT|MEDIUMINT|INT|BIGINT|FLOAT|DOUBLE|DECIMAL|DATETIME|TIMESTAMP|TIME|YEAR|CHAR|TINYBLOB|TINYTEXT|BLOB|MEDIUMBLOB|MEDIUMTEXT|LONGBLOB|LONGTEXT|ENUM|SET){1}(\([\d]+\)|\([\'\d\w,\W]+\))?[\s]?(UNSIGNED ZEROFILL|UNSIGNED|BINARY)?[\s]?(NOT NULL|NULL)?[\s]?(default \'?(.*)\'?)?(auto_increment)?[\s]?(PRIMARY KEY|KEY)?#i', $data, $d);
                    /*
                    [1][0] = name
                    [2][0] = type
                    [3][0] = length
                    [4][0] = unsigned/signed/zerofill
                    [5][0] = null/not null
                    [6][0] = full default
                    [7][0] = default value (doesn't always work)
                    [8][0] = extra (auto_increment)
                    [9][0] = optional keys
                    */
                    if (substr($d[7][0], -1) == ',') {
                        $d[7][0] = substr($d[7][0], 1, -1); // strip quotes
                    }

                    $col['name']    = $d[1][0];
                    $col['type']    = $d[2][0].$d[3][0];
                    $col['null']    = $d[5][0];
                    $col['default'] = trim(substr($d[6][0], strlen('default')));
                    $col['extra']   = $d[8][0];
                    $col['keys']    = $d[9][0];
                    if (substr($col['default'], -1) == ',') {
                        $col['default'] = substr($col['default'], 0, -1);
                    }

                    $cols[] = $col;
                }
            } else {
                unset($index);
                unset($d);
                $index = array();
                $d = array();

                if (strpos(trim($data), 'PRIMARY KEY') === 0) {
                    // primary key :)
                    $d = explode(' ', trim($data));
                    if (strpos($d[3], "))")) {
                        $index['keylen'] = $this->decode_keylen($d[3]);
                    } else {
                        $index['keylen'] = '';
                        // Now make this utility aware of simple multi-column primary keys.
                        $keyname = $d[3];
                        $matches = array();
                        preg_match('@\\(`([^)]*)`\\)@', $keyname, $matches);
                        if (count($matches) == 2) {
                            $d[3] = $matches[1];
                        } else {
                            $d[3] = str_replace(array('(', ')', ' ', '`', ','), '', $d[3]);
                        }
                    }
                    $index['type'] = 'PRIMARY KEY';
                    $index['field'] = $d[3];
                    $index['name'] = '';
                    $indices[] = $index;
                } else if (strpos($data, 'KEY') !== false) {  // not primary
                    // detect if index has a length ie KEY name (field (len))
                    $d = explode(' ', trim($data));
                    if (strpos($d[2], "))")) {
                        $index['keylen'] = $this->decode_keylen($d[2]);
                    } else {
                        $index['keylen'] = '';
                        $d[2] = str_replace(array('(', ')', ' ', '`', ','), '', $d[2]);
                    }

                    $index['type'] = 'KEY';
                    $index['name'] = str_replace(array('(', ')', ' ', '`', ','), '', $d[1]);
                    $index['field'] = $d[2];
                    $indices[] = $index;
                }
            }
        }
        $table = str_replace($this->tablepre, '', $table);
        $this->tc[$table]['cols'] = $cols;
        $this->tc[$table]['indices'] = $indices;
    }

    function getMissingTables() {
        if (!isset($this->tc)) {
            $this->error('Load Tables first (Upgrade::loadTables())');
        }

        if (!isset($this->tables)) {
            $this->error('FATAL: Missing Upgrade-file');
        }
        $tc = array_keys($this->tc);
        $ts = array_keys($this->tables);
        $missing = array_diff($tc, $ts);
        $overhead = array_diff($ts, $tc);
        return array('+'=>$missing, '-'=>$overhead);
    }

    function createTableQueryByTablename($tbl) {
        $table = $this->tables[$tbl];
        $parts = array();
        foreach($table['cols'] as $col) {
            $p = array();
            $p[] = '`'.$col['name'].'`';
            $p[] = $col['type'];
            $p[] = $col['null'];
            if ($col['default'] != '') {
                if ($col['default'] == 'NULL') {
                    $p[] = 'default null';
                } else {
                    $p[] = 'default '.$col['default'];
                }
            }

            if ($col['extra'] != '') {
                $p[] = $col['extra'];
            }

            if ($col['keys'] != '') {
                $p[] = $col['keys'];
            }
            $parts[] = implode(' ', $p);
        }

        foreach($table['indices'] as $index) {
             if ($index['type'] == 'KEY') {
                $keylen = $index['keylen'];
                if (is_numeric($keylen) && $keylen > 0) {
                    $parts[] = 'KEY `'.$index['name'].'` (`'.$index['field'].'` ('.$keylen.'))';
                } else {
                    $parts[] = 'KEY `'.$index['name'].'` (`'.$index['field'].'`)';
                }
             } else if ($index['type'] == 'PRIMARY KEY') {
                $keylen = $index['keylen'];
                if (is_numeric($keylen) && $keylen > 0) {
                    $parts[] =  'PRIMARY KEY (`'.$index['field'].'` ('.$keylen.'))';
                } else {
                    $parts[] =  'PRIMARY KEY (`'.$index['field'].'`)';
                }
             }
        }
        $part = 'CREATE TABLE `'.$this->tablepre.$tbl.'` ('."\n";
        $part .= implode(",\n", $parts);
        $part .= "\n) TYPE=MyISAM;";
        return $part;
    }


    function getColsByTable($table) {
        if (!isset($this->tc[$table])) {
            $this->loadTable($this->tablepre.$table);
        }
        return $this->tc[$table]['cols'];
    }

    function getIndicesByTable($table) {
        if (!isset($this->tc[$table])) {
            $this->loadTable($table);
        }
        return $this->tc[$table]['indices'];
    }

    function makeDiff($table) {
        if (!isset($this->tc[$table])) {
            $this->error('Could not allocate table, please Load the Tables first');
        } else if (!isset($this->tables[$table])) {
            // skip tables that don't belong in XMB (hacks!)
            return array('cols'=> array('+'=>null, '-'=>null), 'indices'=>array('+'=>null,'-'=>null));
        }

        $diff = array();

        $cols = $this->getColsByTable($table);

        foreach($cols as $c) {
            $col[] = implode('-', $c);
        }

        foreach($this->tables[$table]['cols'] as $c) {
            $mstr[] = implode('-', $c);
        }

        $p = array_diff($col, $mstr);
        $m = array_diff($mstr, $col);

        $diff['cols'] = array('+'=>$p, '-'=>$m);

        $mstr = array();

        $indices = $this->getIndicesByTable($table);
        $ind = array();
        $mstr = array();
        foreach($indices as $c) {
            $ind[] = implode('-', $c);
        }

        foreach($this->tables[$table]['indices'] as $c) {
            $mstr[] = implode('-', $c);
        }

        $p = array_diff($ind, $mstr);
        $m = array_diff($mstr, $ind);
        $diff['indices'] = array('+'=>$p, '-'=>$m);
        return $diff;
    }

    function makeLocationDiff($table) {
        if (!isset($this->tc[$table])) {
            $this->error('Could not allocate table, please Load the Tables first');
        } else if (!isset($this->tables[$table])) {
            // skip tables that don't belong in XMB (hacks!)
            return array('cols'=> array('+'=>null, '-'=>null), 'indices'=>array('+'=>null,'-'=>null));
        }

        $diff = array();

        $cols = $this->getColsByTable($table);
        foreach($cols as $c) {
            $col[] = $c['name']; // name
        }

        foreach($this->tables[$table]['cols'] as $c) {
            $mstr[] = $c['name']; // name again
        }

        if ($col !== $mstr) {
            // not the same locations it seems... :)
            // let's assume we DO have the right cols though
            // we return a list of the columns in the order we EXPECT them, so we can use them in a query
            return $mstr;
        } else {
            return null;
        }
        // indices are ok :) They can't be wrong
    }

    function createLocationChangeQuery($table, $temptbl, $def) {
        $defs = '`' . implode('`, `', $def) . '`';
        return "INSERT INTO `$table` ($defs) SELECT $defs FROM `$temptbl`";
    }

    function makeIntelligentDiff($d) {
        $newdiff = array();
        foreach($d as $t=>$diff) {
            if ($diff['cols'] == null && $diff['indices'] == null) {
                continue;
            } else {
                if (isset($diff['indices']['-'])) {
                    foreach($diff['indices']['-'] as $min) {
                        $m = explode('-', $min);
                        $newdiff[$t]['indices']['add'][] = $m[2];
                    }
                }

                if (isset($diff['indices']['+'])) {
                    foreach($diff['indices']['+'] as $max) {
                        $m = explode('-', $max);
                        $newdiff[$t]['indices']['drop'][] = $m[2];
                    }
                }

                $drop = array();
                $add = array();
                if (isset($diff['cols']['-'])) {
                    foreach($diff['cols']['-'] as $min) {
                        $m = explode('-', $min);
                        $drop[] = $m[0];
                    }
                }

                if (isset($diff['cols']['+'])) {
                    foreach($diff['cols']['+'] as $max) {
                        $m = explode('-', $max);
                        $add[] = $m[0];
                    }
                }

                // only change drop/add the fields that are not in both, otherwise, we just need to MODIFY them
                $ad = array_diff($drop, $add);
                $dr = array_diff($add, $drop);

                foreach($dr as $k=>$name) {
                    // drop $name;
                    $newdiff[$t]['cols']['drop'][] = $name;
                }

                foreach($ad as $k=>$name) {
                    $newdiff[$t]['cols']['add'][] = $name;
                }

                $alter = array_diff($drop, $dr); // this one should be exactly the same as the other one :)
                foreach($alter as $key=>$name) {
                    foreach($diff['cols']['+'] as $k=>$d) {
                        if (strpos($d, $name) === 0) {
                            // just change everything except name :P
                            $newdiff[$t]['cols']['alter'][] = $name;
                            break;
                        }
                    }
                }
            }
        }
        return $newdiff;
    }

    function getColInfoByName($table, $col) {
        foreach($this->tables[$table]['cols'] as $c) {
            if ($c['name'] == $col) {
                return $c;
            }
         }
         $this->error('Could not locate column <i>'.$col.'</i> in table <i>'.$table.'</i>');
    }

    function getIndexInfoByName($table, $index) {
        foreach($this->tables[$table]['indices'] as $c) {
            if ($c['name'] == $index) {
                return $c;
            }
         }
         return false;
    }

    function getIndexInfoByField($table, $index) {
        foreach($this->tables[$table]['indices'] as $c) {
            if ($c['field'] == $index) {
                return $c;
            }
         }
         $this->error('Could not locate index <i>'.$index.'</i> in table <i>'.$table.'</i>');
         return false;
    }

    function getExistingIndexInfoByName($table, $index) {
        foreach($this->tc[$table]['indices'] as $c) {
            if ($c['name'] == $index) {
                return $c;
            }
         }
         return false;
    }

    function columnExists($table, $col) {
        if (isset($this->tc[$table])) {
            foreach($this->tc[$table]['cols'] as $c) {
                if ($c['name'] == $col) {
                    return true;
                }
            }
        }
        return false;
    }

    function indexExistsOnColumn($table, $column, $specific=null) {
        if (isset($this->tc[$table])) {
            foreach($this->tc[$table]['indices'] as $i) {
                if ($i['field'] == $column) {
                    if ($specific === null) {
                        return true;
                    } else if ($i['name'] == $specific) {
                        return true;
                    }
                }
            }
        }
        return false;
    }


    function createQueryFromDiff($diff, $table) {
        $queries = array();
        $preface = "ALTER TABLE `" . $this->tablepre . $table . "` ";
        $ps = '';

        // indices will never be given here
        if (!isset($diff['cols'])) {
            $diff['cols'] = array();
        }

        if (!isset($diff['indices'])) {
            $diff['indices'] = array();
        }

        // Make sure indexes are never dropped on auto_increment fields.
        $autocolumn = '';
        foreach($this->tc[$table]['cols'] as $k=>$i) {
            if ($i['extra'] == 'auto_increment') {
                $autocolumn = $i['name'];
                break;
            }
        }
        $origautocolumn = $autocolumn; // Use this value after adding and dropping columns.
        $autoindexdropped = TRUE;

        $query = '';
        if (isset($diff['indices']['drop'])) {
            foreach($diff['indices']['drop'] as $name) {
                // check that it is not a primary key!!
                foreach($this->tc[$table]['indices'] as $k=>$i) {
                    if ($i['name'] == $name || $i['field'] == $name) {
                        $info = $i;
                        break;
                    }
                }

                if ($info['field'] != $autocolumn) {
                    if ($info['type'] == 'PRIMARY KEY') {
                        // dropping primary key gives HUGE problems
                        // we should instead drop the entire column and possibly recreate it later.
                        $query .= 'DROP PRIMARY KEY, ';
                    } else {
                        $query .= "DROP INDEX `".$name."`, ";
                    }
                } else {
                    $autoindexdropped = FALSE;
                }
            }
        }

        if ($query != '') {
            if (substr($query, -2) == ", ") {
                $query = substr($query, 0, -2);
            }
            $queries[] = $preface . $query;
            $query = '';
        }

        if (isset($diff['cols']['add'])) {
            foreach($diff['cols']['add'] as $name) {
                // find the position of it first =/
                $info = $this->getColInfoByName($table, $name);
                $p = array();
                $p[] = '`'.$info['name'].'`';
                $p[] = $info['type'];
                $p[] = $info['null'];
                if ($info['default'] != '') {
                    if ($info['default'] == 'NULL') {
                        $p[] = 'default null';
                    } else {
                        $p[] = 'default '.$info['default'];
                    }
                }

                if ($info['extra'] != '') {
                    $p[] = $info['extra'];
                    if ($autocolumn == '' And $info['extra'] == 'auto_increment') {
                        $autocolumn = $info['name'];
                    }
                }

                if (trim($info['keys']) != '') {
                    $p[] = $info['keys'];
                }

                if (isset($diff['indices']['add']) && in_array($name, $diff['indices']['add'])) {
                    if (($info = $this->getIndexInfoByName($table, $name)) === false) {
                        $info = $this->getIndexInfoByField($table, $name);
                    }

                    if ($info['type'] == 'PRIMARY KEY') {
                        $ps = ', ADD PRIMARY KEY (`'.$info['field'].'`)';
                        unset($diff['indices']['add'][array_search($name, $diff['indices']['add'])]);
                    }
                }

                $parts = implode(' ', $p);
                if ($this->tables[$table]['cols'][0]['name'] == $name) {
                    $query .= " ADD COLUMN ".$parts.' FIRST'.$ps . ", ";
                } else {
                    $c = count($this->tables[$table]['cols']);
                    for($i=0;$i<($c-1);$i++) {
                        if ($this->tables[$table]['cols'][$i+1]['name'] == $name) {
                            $after = $this->tables[$table]['cols'][$i];
                            $query .= " ADD COLUMN ".$parts.' AFTER `'.$after['name'].'`'.$ps .", ";
                            break;
                        }
                    }
                }
            }
        }

        if ($query != '') {
            if (substr($query, -2) == ", ") {
                $query = substr($query, 0, -2);
            }
            $queries[] = $preface . $query;
            $query = '';
        }

        if (isset($diff['cols']['alter'])) {
            foreach($diff['cols']['alter'] as $name) {
                $info = $this->getColInfoByName($table, $name);
                $p = array();
                $p[] = '`'.$info['name'].'`';
                $p[] = $info['type'];
                $p[] = $info['null'];
                if ($info['default'] != '') {
                    if ($info['default'] == 'NULL') {
                        $p[] = 'default null';
                    } else {
                        $p[] = 'default '.$info['default'];
                    }
                }

                if ($info['extra'] != '') {
                    $p[] = $info['extra'];
                    if ($autocolumn == '' And $info['extra'] == 'auto_increment') {
                        $autocolumn = $info['name'];
                    }
                }
                if ($autocolumn == $info['name'] And $info['extra'] != 'auto_increment') {
                    $autocolumn = '';
                }

                if ($info['keys'] != '') {
                    $p[] = $info['keys'];
                }
                $parts = implode(' ', $p);
                $query .= "MODIFY " . $parts . ", ";
            }
         }

        if ($query != '') {
            if (substr($query, -2) == ", ") {
                $query = substr($query, 0, -2);
            }
            $queries[] = $preface . $query;
            $query = '';
        }

         if (isset($diff['cols']['drop'])) {
            foreach($diff['cols']['drop'] as $name) {
                $query .= "DROP COLUMN `".$name."`, ";

                if ($autocolumn == $name) {
                    $autocolumn = '';
                }
            }
         }

        if ($query != '') {
            if (substr($query, -2) == ", ") {
                $query = substr($query, 0, -2);
            }
            $queries[] = $preface . $query;
            $query = '';
        }

        if (isset($diff['indices']['add'])) {
             foreach($diff['indices']['add'] as $name) {
                 if (($info = $this->getIndexInfoByName($table, $name)) === false) {
                     $info = $this->getIndexInfoByField($table, $name);
                 }

                 // Now drop all old auto_increment indexes, if necessary, by combining all of them with the first ADD KEY on the same column.
                 if ($info['field'] == $origautocolumn And !$autoindexdropped) {
                    foreach($diff['indices']['drop'] as $name) {
                        // check that it is not a primary key!!
                        foreach($this->tc[$table]['indices'] as $k=>$i) {
                            if ($i['name'] == $name || $i['field'] == $name) {
                                $originfo = $i;
                                break;
                            }
                        }

                        if ($originfo['field'] == $origautocolumn) {
                            if ($originfo['type'] == 'PRIMARY KEY') {
                                // dropping primary key gives HUGE problems
                                // we should instead drop the entire column and possibly recreate it later.
                                $query .= 'DROP PRIMARY KEY, ';
                            } else {
                                $query .= "DROP INDEX `".$name."`, ";
                            }
                        }
                    }
                    $autoindexdropped = TRUE;
                 }

                 if ($info['type'] == 'PRIMARY KEY') {
                    $keylen = $info['keylen'];
                    if (is_numeric($keylen) && $keylen > 0) {
                        $query .= "ADD PRIMARY KEY (`".$info['field'].'` ('.$keylen.')), ';
                    } else {
                        $query .= "ADD PRIMARY KEY (`".$info['field'].'`), ';
                    }
                 } else {
                    $keylen = $info['keylen'];
                    if (is_numeric($keylen) && $keylen > 0) {
                        $query .= "ADD INDEX `".$info['field'].'` (`'.$info['name'].'` ('.$keylen.')), ';
                    } else {
                        $query .= "ADD INDEX `".$info['field'].'` (`'.$info['name'].'`), ';
                    }
                 }
             }
        }

        if ($query != '') {
            if (substr($query, -2) == ", ") {
                $query = substr($query, 0, -2);
            }
            $queries[] = $preface . $query;
        }
        return $queries;
    }

    function createUpgradeFile($tablepre=null) {
        if ($tablepre === null) {
            $tablepre = $this->tablepre;
        }

        foreach($this->tc as $key=>$val) {
            $tc[str_replace($tablepre, '', $key)] = $val;
        }
        return serialize($tc);
    }

    function error($msg) {
        exit($msg);
    }

    function findColumn(& $columns, $column) {
        foreach($columns as $col) {
            if ($col['name'] == $column)
                return $col;
        }
        return false;
    }

    function dropTableFromCache($tbl, $dropInDb=false) {
        if (!isset($this->tc[$tbl])) {
            return false;
        } else {
            if ($dropInDb) {
                $this->db->query("DROP TABLE `".$this->tablepre.$tbl."`");
            }
            unset($this->tc[$tbl]);
        }
    }

    function upgradeU2U() {
        $this->db->query("DROP TABLE IF EXISTS `".$this->tablepre."u2u_new`");
        $this->db->query("CREATE TABLE `".$this->tablepre."u2u_new` (
            `u2uid` bigint(10) NOT NULL auto_increment,
            `msgto` varchar(32) NOT NULL default '',
            `msgfrom` varchar(32) NOT NULL default '',
            `type` set('incoming','outgoing','draft') NOT NULL default '',
            `owner` varchar(32) NOT NULL default '',
            `folder` varchar(32) NOT NULL default '',
            `subject` varchar(64) NOT NULL default '',
            `message` text NOT NULL,
            `dateline` int(10) NOT NULL default '0',
            `readstatus` set('yes','no') NOT NULL default '',
            `sentstatus` set('yes','no') NOT NULL default '',
            PRIMARY KEY  (`u2uid`),
            KEY `msgto` (`msgto`),
            KEY `msgfrom` (`msgfrom`),
            KEY `folder` (`folder`),
            KEY `readstatus` (`readstatus`),
            KEY `owner` (`owner`)
            ) TYPE=MyISAM"
        );

        $query = $this->db->query("SELECT * FROM `".$this->tablepre."u2u`");
        while($u2u = $this->db->fetch_array($query)) {
            if ($u2u['folder'] == 'inbox') {
                $type = 'incoming';
                $owner = $u2u['msgto'];
            } else if ($u2u['folder'] == 'outbox') {
                $type = 'outgoing';
                $owner = $u2u['msgfrom'];
            } else {
                $type = 'incoming';
                $owner = $u2u['msgfrom'];
            }

            if (!isset($u2u['readstatus']) || $u2u['readstatus'] == '') {
                $u2u['readstatus'] = 'no';
            }

            if (!isset($u2u['new']) || $u2u['new'] == '') {
                $u2u['new'] = 'yes';
            }
            $this->db->query("INSERT INTO `".$this->tablepre."u2u_new` VALUES('', '".$u2u['msgto']."', '".$u2u['msgfrom']."', '".$type."', '".$owner."', '".$u2u['folder']."', '".addslashes($u2u['subject'])."', '".addslashes($u2u['message'])."', '".$u2u['dateline']."', '".$u2u['readstatus']."', '".$u2u['new']."')");
        }
        $this->db->free_result($query);
        $this->db->query("DROP TABLE `".$this->tablepre."u2u`");
        $this->db->query("ALTER TABLE `".$this->tablepre."u2u_new` RENAME `".$this->tablepre."u2u`");
    }

    // this function gets rid of a corner case which the upgrade process has difficulty handling.
    function removeSid() {
        $tbl = 'settings';
        $cols = $this->getColsByTable($tbl);
        $sid = $this->findColumn($cols, 'sid');

        if ($sid !== false) {
            $this->db->query("ALTER TABLE `". $this->tablepre . $tbl ."` DROP COLUMN `sid`");
        }
    }

    function doU2U() {
        $tbl = 'u2u';
        $cols = $this->getColsByTable($tbl);
        $readStatus = $this->findColumn($cols, 'readstatus');
        $ownerCol = $this->findColumn($cols, 'owner');

        if ($readStatus === false || $ownerCol === false || $readStatus['type'] == 'char(3)') {
            // 1.11 through 1.8 SP3
            $this->upgradeU2U();
            return true;
        } else {
            // 1.9.1 schema already.
            // let's do a quick check to see if the u2u table is okay and fix it if not
            $query = $this->db->query("SELECT u2uid, msgto, msgfrom, folder FROM `".$this->tablepre."u2u` where owner=''");
            if ($this->db->num_rows($query) != 0) {
                while($u2u = $this->db->fetch_array($query)) {
                    if ($u2u['folder'] == 'inbox') {
                        $type = 'incoming';
                        $owner = $u2u['msgto'];
                    } else if ($u2u['folder'] == 'outbox') {
                        $type = 'outgoing';
                        $owner = $u2u['msgfrom'];
                    } else {
                        $type = 'incoming';
                        $owner = $u2u['msgfrom'];
                    }
                    $this->db->query("UPDATE ".$this->tablepre."u2u SET type='".$type."', owner='".$owner."' WHERE u2uid = '".$u2u['u2uid']."'");
                }
            }
            $this->db->free_result($query);
        }
        return true;
    }

    function findThemeIDByName($themename) {
        $r = $this->db->query("SELECT themeid FROM ".$this->tablepre."themes WHERE name='".$themename."'");
        if ($this->db->num_rows($r) > 0) {
            $retval = $this->db->result($r, 0);
            $this->db->free_result($r);
            return $retval;
        } else {
            return false;
        }
    }

    function deleteThemeByName($themename) {
        $r = $this->db->query("SELECT themeid FROM ".$this->tablepre."themes WHERE name='".$themename."'");
        if ($this->db->num_rows($r) > 0) {
            $this->db->free_result($r);
            $this->db->query("DELETE FROM `".$this->tablepre."themes` WHERE name='".$themename."'");
        }
    }

    function fixIndex() {
        $this->loadTable($this->tablepre . 'banned');

        $changes = false;

        if (($col1 = $this->getExistingIndexInfoByName('banned', 'ip1')) !== false && ($col1['field'] != 'ip1')) {
            $this->db->query("ALTER TABLE `" . $this->tablepre . "banned` DROP INDEX `ip1`");
            $this->db->query("CREATE INDEX `ip1` ON `" . $this->tablepre . "banned` (`ip1`) ");
            $changes = true;
        }

        if (($col4 = $this->getExistingIndexInfoByName('banned', 'ip4')) !== false && ($col4['field'] != 'ip4')) {
            $this->db->query("ALTER TABLE `" . $this->tablepre . "banned` DROP INDEX `ip4`");
            $this->db->query("CREATE INDEX `ip4` ON `" . $this->tablepre . "banned` (`ip4`) ");
            $changes = true;
        }

        // no need to do any heavy operations if there were no actual changes performed on the table
        if ($changes) {
            $this->loadTable($this->tablepre . 'banned');
        }
    }

    function fixPPP($mysqlver) {
        $tblmem = $this->tablepre.'members';
        $tblset = $this->tablepre.'settings';

        if (($mysqlver[0] == 4 && $mysqlver[1] > 3) || ($mysqlver[0] > 4)) {
            $this->db->query("UPDATE `". $tblset. "`, `". $tblmem ."` SET `". $tblmem ."`.ppp=". $tblset. ".postperpage WHERE `". $tblmem ."`.ppp=0");
            $this->db->query("UPDATE `". $tblset. "`, `". $tblmem ."` SET `". $tblmem ."`.tpp=". $tblset. ".topicperpage WHERE `". $tblmem ."`.tpp=0");
        } else {
            $this->db->query("UPDATE `". $tblmem ."` SET ppp=30 WHERE ppp=0");
            $this->db->query("UPDATE `". $tblmem ."` SET tpp=30 WHERE tpp=0");
        }
    }

    function fixBirthdays($v) {
        static $cache, $cachedLanguages;

        $lang = array();

        require_once ROOT.'lang/English.lang.php';

        $baselang = $lang;

        switch($v) {
            case 0:
                // store
                $cache = array();
                $cachedLanguages = array();

                $q = $this->db->query("SELECT uid,bday,langfile FROM ".$this->tablepre."members");
                while($m = $this->db->fetch_array($q)) {
                    if (strlen($m['bday']) == 0) {
                        continue;
                    }

                    // check if the birthday isn't in proper format anyway
                    $parts = explode('-', $m['bday']);
                    if (count($parts) == 3 && (is_numeric($parts[0]) && is_numeric($parts[1]) && is_numeric($parts[2]))) {
                        continue;
                    }

                    $lang = array();

                    if (!isset($cachedLanguages[$m['langfile']])) {
                        require_once ROOT.'lang/'.$m['langfile'].'.lang.php';
                        $cachedLanguages[$m['langfile']] = $lang;
                    }

                    if (isset($cachedLanguages[$m['langfile']])) {
                        $lang = array_merge($baselang, $cachedLanguages[$m['langfile']]);
                    } else {
                        $lang = $baselang;
                    }

                    $day = 0;
                    $month = 0;
                    $year = 0;
                    $monthList = array($lang['textjan'] => 1,$lang['textfeb'] => 2,$lang['textmar'] => 3,$lang['textapr'] =>4,$lang['textmay'] => 5,$lang['textjun'] => 6,$lang['textjul'] => 7,$lang['textaug'] => 8,$lang['textsep'] => 9,$lang['textoct'] => 10,$lang['textnov'] => 11,$lang['textdec'] => 12);

                    if (isset($monthList[$parts[0]])) {
                        $month = $monthList[$parts[0]];
                        $day = substr($parts[1], 0, -1); // cut off trailing comma
                        $year = $parts[3];
                        $cache[$m['uid']] = $this->iso8601_date($year, $month, $day);
                    }
                }
                break;

            case 1:
                // restore
                if (count($cache) > 0) {
                    $this->db->query("UPDATE ".$this->tablepre."members SET bday='0000-00-00'");
                    foreach($cache as $uid=>$bd) {
                        $this->db->query("UPDATE ".$this->tablepre."members SET bday='$bd' WHERE uid=$uid");
                    }
                }
                break;
        }
    }

    function iso8601_date($year=0, $month=0, $day=0) {
        $year = (int) $year;
        $month = (int) $month;
        $day = (int) $day;

        if ($year < 1 || $month < 1 || $day < 1) {
            return '0000-00-00';
        }

        if ($year < 100) {
            // assume 19xx is meant
            $year += 1900;
        }

        if ($month > 12 || $month < 1) {
            $month = 1;
        }

        if ($day > 31 || $day < 1) {
            $day = 1;
        }

        return $year.'-'.str_pad($month, 2, 0, STR_PAD_LEFT).'-'.str_pad($day, 2, 0, STR_PAD_LEFT);
    }

    function fixForumPerms($v) {
        static $cache;
        /***
            OLD FORMAT:
            "NewTopics|NewReplies|ViewForum". Each field contains a number between 1 and 4:
            - 1 normal (all ranks),
            - 2 admin only,
            - 3 admin/mod only,
            - 4 no posting/viewing.
        ***/

        /***
            NEW FORMAT:
            NewPolls,NewThreads,NewReplies,View. Each field contains a number between 0-63 (a sum of the following:)
            - 1  Super Administrator
            - 2  Administrator
            - 4  Super Moderator
            - 8  Moderator
            - 16 Member
            - 32 Guest
        ***/
        switch($v) {
            case 0:
                // store
                if ($this->columnExists('forums', 'private') || $this->columnExists('forums', 'guestposting') || $this->columnExists('forums', 'pollstatus')) {
                    $q = $this->db->query("SELECT fid, private, userlist, postperm, guestposting, pollstatus FROM ".$this->tablepre."forums WHERE (type='forum' or type='sub')");
                    while($forum = $this->db->fetch_array($q)) {
                        // check if we need to change it first
                        $parts = explode('|', $forum['postperm']);
                        if (count($parts) == 1) {
                            // no need to upgrade these; new format in use [we hope]
                            continue;
                        }
                        $newFormat = array(0,0,0,0);

                        $fid            = $forum['fid'];
                        $private        = $forum['private'];
                        $permField      = $forum['postperm'];
                        $guestposting   = $forum['guestposting'];
                        $polls          = $forum['pollstatus'];

                        $translationFields = array(0=>1, 1=>2);
                        foreach($parts as $key=>$val) {
                            switch($val) {
                                case 1:
                                    $newFormat[$translationFields[$key]] = 31;
                                    break;
                                case 2:
                                    $newFormat[$translationFields[$key]] = 3;
                                    break;
                                case 3:
                                    $newFormat[$translationFields[$key]] = 15;
                                    break;
                                case 4:
                                    $newFormat[$translationFields[$key]] = 1;
                                    break;
                                default:
                                    // allow only superadmin
                                    $newFormat[$translationFields[$key]] = 1;
                                    break;
                            }
                        }
                        switch($private) {
                            case 1:
                                $newFormat[3] = 63;
                                break;
                            case 2:
                                $newFormat[3] = 3;
                                break;
                            case 3:
                                $newFormat[3] = 15;
                                break;
                            case 4:
                                $newFormat[3] = 1;
                                break;
                            default:
                                // allow only superadmin
                                $newFormat[3] = 1;
                                break;
                        }
                        if ($guestposting == 'yes' || $guestposting == 'on') {
                            $newFormat[0] |= 32;
                            $newFormat[1] |= 32;
                            $newFormat[2] |= 32;
                        }

                        if ($polls == 'yes' || $polls == 'on') {
                            $newFormat[0] = $newFormat[1];
                        } else {
                            $newFormat[0] = 0;
                        }

                        $cache[$fid] = $newFormat;
                    }
                }
                break;

            case 1:
                // restore
                if (isset($cache) && count($cache) > 0) {
                    foreach($cache as $fid=>$format) {
                        $this->db->query("UPDATE ".$this->tablepre."forums SET postperm='".implode(',', $format)."' WHERE fid=$fid");
                    }
                }
                break;
        }
    }

    function fixPolls() {
        $q = $this->db->query("SELECT tid, subject, pollopts_temp FROM ".$this->tablepre."threads WHERE pollopts_temp != ''");
        while($thread = $this->db->fetch_array($q)) {
            // Some users find their thread subjects aren't escaped, so escape them. Strip any existing slashes so we don't double escape
            $thread['subject'] = addslashes(stripslashes($thread['subject']));

            $this->db->query("INSERT INTO ".$this->tablepre."vote_desc (`topic_id`, `vote_text`, `vote_start`) VALUES ('".$thread['tid']."', '".$thread['subject']."', 0)");
            $poll_id = $this->db->insert_id();

            $options = explode("#|#", $thread['pollopts_temp']);
            $num_options = count($options);

            $voters = explode('    ', trim($options[$num_options-1]));

            $name = array();
            foreach($voters as $v) {
                $name[] = trim($v);
            }
            $name = "'".implode("', '", $name)."'";
            $query = $this->db->query("SELECT uid FROM ".$this->tablepre."members WHERE username IN($name)");
            while($u = $this->db->fetch_array($query)) {
                $this->db->query("INSERT INTO ".$this->tablepre."vote_voters (`vote_id`, `vote_user_id`) VALUES (".$poll_id.", ".$u['uid'].")");
            }

            for($i=0; $i<$num_options-1; $i++) {
                $bit = explode('||~|~||', $options[$i]);
                $option_name = addslashes(trim($bit[0]));
                $num_votes = (int) trim($bit[1]);
                $this->db->query("INSERT INTO ".$this->tablepre."vote_results (`vote_id`, `vote_option_id`, `vote_option_text`, `vote_result`) VALUES (".$poll_id.", ".($i+1).", '".$option_name."', ".$num_votes.")");
            }
        }
    }

    function createTempFields() {
        $q = $this->db->query("SHOW COLUMNS FROM ".$this->tablepre."threads LIKE 'pollopts_temp'");
        if ($this->db->num_rows($q) != 0) {
            $this->db->query("ALTER TABLE ".$this->tablepre."threads DROP `pollopts_temp`");
        }
        $this->db->free_result($q);

        $this->db->query("ALTER TABLE ".$this->tablepre."threads ADD `pollopts_temp` text NOT NULL");
        $q = $this->db->query("SELECT tid, pollopts FROM ".$this->tablepre."threads WHERE pollopts != ''");
        while($t = $this->db->fetch_array($q)) {
            $this->db->query("UPDATE ".$this->tablepre."threads SET pollopts_temp='".addslashes($t['pollopts'])."', pollopts='1' WHERE tid=".$t['tid']);
        }
    }

    function dropTempFields() {
        $this->db->query("ALTER TABLE ".$this->tablepre."threads DROP `pollopts_temp`");
        $this->db->query("DROP TABLE IF EXISTS ".$this->tablepre."u2u_old");
    }
}

define('X_ALTER', 1);
define('X_DROP', 2);
define('X_ADD', 3);
?>
Return current item: XMBForum